Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 68 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,13 @@ jobs:

- name: Install gitleaks
run: |
GITLEAKS_VERSION=$(curl -sSf https://api.github.com/repos/gitleaks/gitleaks/releases/latest \
| grep -oP '"tag_name":\s*"v\K[^"]+')
# PINNED. Tracking releases/latest means a new upstream ruleset can turn
# this gate red with no change to this repository -- which is exactly what
# happened: a newer generic-api-key rule began flagging published BIP32 test
# vectors in 2014/2018 history, and because every build job declares
# `needs: [.., secret-scan]`, the whole build and test graph was SKIPPED
# rather than failed. Bump deliberately, with the scan re-verified. See #424.
GITLEAKS_VERSION=8.30.0
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
| tar -xz -C /usr/local/bin gitleaks
gitleaks version
Expand Down Expand Up @@ -745,6 +750,67 @@ jobs:

# ═══════════════════════════════════════════════════════════
# STAGE 4: PUBLISH — manual trigger only, all tests must pass

# ═══════════════════════════════════════════════════════════
# GATE: one authoritative answer for the whole graph
# ═══════════════════════════════════════════════════════════
#
# Every build and test job declares `needs: [lint-format, static-analysis,
# check-submodules, secret-scan]`. When one of those gate jobs fails, GitHub
# marks the whole downstream graph SKIPPED rather than failed -- and a skipped
# job is not a red check. The run summary then shows green ticks on whatever
# finished, which reads as healthy unless someone opens the job list.
#
# That has now happened three times on the 7.14.2 line: gitleaks failing on
# develop, lint-format timing out inside its apt.llvm.org install (#471), and
# gitleaks again after an unpinned upstream bump. Each time the ARM build,
# the unit tests and both python suites produced NOTHING while the run looked
# partially green.
#
# This job exists so that cannot happen quietly. It needs every required job,
# runs with `if: always()` so it executes even when they skip, and fails
# unless each one reports exactly `success`. failure, cancelled and skipped
# are all treated as not-success, because for a required job they are.
#
# publish-emulator is deliberately absent: it is workflow_dispatch-only and
# is legitimately skipped on every push and pull_request.
#
# Point branch protection at THIS job rather than the individual ones. It is
# the only check whose green means "the entire graph ran and passed".
ci-gate:
name: CI gate
if: always()
needs:
- lint-format
- secret-scan
- static-analysis
- check-submodules
- build-emulator
- build-arm-firmware
- unit-tests
- python-integration-tests
- python-dylib-tests
- generate-test-report
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Assert every required job succeeded
env:
NEEDS_JSON: ${{ toJSON(needs) }}
run: |
set -euo pipefail
echo "$NEEDS_JSON" | jq -r 'to_entries[] | "\(.value.result)\t\(.key)"' | sort
echo
NOT_SUCCESS=$(echo "$NEEDS_JSON" \
| jq -r 'to_entries[] | select(.value.result != "success") | .key')
if [ -n "$NOT_SUCCESS" ]; then
echo "::error::Required jobs did not succeed: $(echo $NOT_SUCCESS | tr '\n' ' ')"
echo "A skipped or cancelled required job is NOT a pass. If a gate-stage"
echo "job failed, everything downstream was skipped and produced no signal."
exit 1
fi
echo "All required jobs reported success."

# ═══════════════════════════════════════════════════════════

publish-emulator:
Expand Down
23 changes: 23 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,28 @@ Two U2F attestation artifacts are public by design and must not fail the scan.
be committed there in the first place, and this exemption is not a licence to do
so — it is an acknowledgement that git SHAs in prose are not secrets.

5. tests/ — historical, pre-submodule test vectors

This path does not exist in the working tree. The python test suite lived at the
repository root before it became the deps/python-keepkey submodule, and CI scans
with fetch-depth 0, so gitleaks still reads those commits.

Every finding here is the generic-api-key rule firing on published BIP32 test
vectors — the flagged lines are WIF private keys printed next to their own address
and public key as documentation, e.g. tests/test_ecies.py (2014, inherited from
Trezor) and tests/test_msg_eos_signtx.py (2018). They are upstream test fixtures,
published for a decade, and secure nothing.

Scoped by path for the same reason as deps/: the secrets are real key material by
format, so a regex exemption would be dangerous, whereas the path is provably dead.

The cost of a path exemption, stated plainly: a genuine secret committed under
tests/ would NOT be caught. That is acceptable only while the directory does not
exist. If a tests/ tree is ever reintroduced at the repository root, remove this
exemption in the same commit — verified by negative control, a real EC private key
planted under tests/ is ignored, while the same key under lib/firmware/ is still
detected.

Scope note: every exemption is scoped by path and the default ruleset is otherwise
intact, so a genuine secret committed in source still fails the scan. Verified by
negative control: a planted EC private key under lib/firmware/ is still detected.
Expand All @@ -72,4 +94,5 @@ paths = [
'''include/keepkey/firmware/u2f/u2f_keys\.h''',
'''^deps/''',
'''^docs/''',
'''^tests/''',
]
Loading