Remove a live credential from a test fixture, and scan for the next one #33

Merged
jknapp merged 2 commits from fix/test-fixture-secret into main 2026-08-25 18:54:56 +00:00
Owner

Two commits, both consequences of a live Gitea site-admin token having been committed as a test fixture in 9b2f4fe (2026-08-11) and sitting in the public GitHub mirror for fourteen days. The token has been revoked and the API confirms 401.

d09e2a2 — stop using a live credential as a test fixture

the_custom_env_fingerprint_never_carries_the_value asserted that the fingerprint does not leak a secret, using the real one as the input. The fixture is now obviously fake.

Mutation-checked against the replacement, because the natural worry is that swapping the value weakens the test: a fingerprint that returns the raw value, one that returns the key name, and one that ignores its input are all still caught. Nothing about the test ever depended on the value being real.

Release bundles were never affected — the literal is inside #[cfg(test)], and a search of the shipped 0.4.62 AppImage finds nothing.

History is deliberately not rewritten. The value was public for two weeks, so rotation is the fix and the old value is now worthless; rewriting 92 commits of published history would break every clone to hide something already seen.

68b73a9 — refuse a commit that adds something shaped like a credential

Three rules: vendor-prefixed tokens, BEGIN … PRIVATE KEY blocks, and an opaque literal assigned to a secret-shaped name. That third one needs both halves — measured first, an entropy-only rule flags 317 literals in this tree and name-proximity alone flags four, three of which are the keychain key name aws-secret-access-key.

Validated, not asserted:

  • 0 false positives across every tracked file
  • catches the real incident when 9b2f4fe is replayed
  • twelve shaped cases behave correctly, including a sha256 in an assert_eq! and a git sha in a comment
  • the hook was proved to block an actual git commit

Two halves: .githooks/pre-commit (fast, opt-in per clone via npm run hooks, skippable with --no-verify) and a Secret Scan workflow that nobody can bypass. The workflow carries no paths: filter on purpose — the leak lived in app/** and build.yml only runs for container/**, so a path-filtered scan would have missed the very thing it exists for.

Also fixed: core.hooksPath pointed at a directory that does not exist, so git hooks were disabled outright in this clone. Anything dropped in .git/hooks would have been ignored in silence.

Worth deciding separately

A high-entropy check would not have caught this on its own, and neither did five audit rounds — every review was pointed at the code under change. If more of this class is a concern, the next lever is scanning history on a schedule rather than only new commits.

Two commits, both consequences of a live Gitea **site-admin** token having been committed as a test fixture in `9b2f4fe` (2026-08-11) and sitting in the public GitHub mirror for fourteen days. The token has been revoked and the API confirms 401. ### `d09e2a2` — stop using a live credential as a test fixture `the_custom_env_fingerprint_never_carries_the_value` asserted that the fingerprint does not leak a secret, using the real one as the input. The fixture is now obviously fake. Mutation-checked against the replacement, because the natural worry is that swapping the value weakens the test: a fingerprint that returns the raw value, one that returns the key name, and one that ignores its input are all still caught. Nothing about the test ever depended on the value being real. Release bundles were never affected — the literal is inside `#[cfg(test)]`, and a search of the shipped 0.4.62 AppImage finds nothing. History is deliberately **not** rewritten. The value was public for two weeks, so rotation is the fix and the old value is now worthless; rewriting 92 commits of published history would break every clone to hide something already seen. ### `68b73a9` — refuse a commit that adds something shaped like a credential Three rules: vendor-prefixed tokens, `BEGIN … PRIVATE KEY` blocks, and an opaque literal assigned to a secret-shaped name. That third one needs **both** halves — measured first, an entropy-only rule flags 317 literals in this tree and name-proximity alone flags four, three of which are the keychain key name `aws-secret-access-key`. Validated, not asserted: - **0 false positives** across every tracked file - **catches the real incident** when `9b2f4fe` is replayed - twelve shaped cases behave correctly, including a sha256 in an `assert_eq!` and a git sha in a comment - the hook was proved to block an actual `git commit` Two halves: `.githooks/pre-commit` (fast, opt-in per clone via `npm run hooks`, skippable with `--no-verify`) and a `Secret Scan` workflow that nobody can bypass. The workflow carries **no `paths:` filter** on purpose — the leak lived in `app/**` and `build.yml` only runs for `container/**`, so a path-filtered scan would have missed the very thing it exists for. Also fixed: `core.hooksPath` pointed at a directory that does not exist, so git hooks were disabled outright in this clone. Anything dropped in `.git/hooks` would have been ignored in silence. ### Worth deciding separately A high-entropy check would not have caught this on its own, and neither did five audit rounds — every review was pointed at the code under change. If more of this class is a concern, the next lever is scanning history on a schedule rather than only new commits.
jknapp added 2 commits 2026-08-25 18:51:25 +00:00
`the_custom_env_fingerprint_never_carries_the_value` asserted that the custom-env
fingerprint does not leak a secret — using the maintainer's real Gitea token as
the secret. It was committed on 2026-08-11 in 9b2f4fe, reached 92 commits, and
was readable for fourteen days in the public GitHub mirror at
`shadowdao/triple-c`, confirmed by fetching the raw file.

The token was a **site-admin** token (`is_admin: true`, user id 1) with admin
rights on every repository the account can see, not a repo-scoped one. It has
been revoked; the API now answers 401.

Release bundles were never affected — the literal is inside `#[cfg(test)]`, and
a search of the shipped 0.4.62 AppImage finds nothing.

The fixture is now an obviously fake string, and the test is unchanged
otherwise. Mutation-checked against the replacement: a fingerprint that returns
the raw value, one that returns the key name, and one that ignores its input are
all still caught, so nothing about the test's power depended on the value being
real — which was true the whole time.

History is deliberately **not** rewritten. The value was public for two weeks, so
rotation is the fix and the old value is now worthless; rewriting 92 commits of
published history would break every clone to hide something already seen.

Worth noting how this survived: five audit rounds and two independent reviews
all pointed at new code, and this sat in a test file that none of them had reason
to open. A high-entropy-literal check in CI would have caught it on the day.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHL9ty7arp8FHwvE77ne7y
Refuse a commit that adds something shaped like a credential
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 4s
Secret Scan / scan (pull_request) Successful in 5s
Build App (Preview) / create-release (pull_request) Successful in 2s
Build App (Preview) / build-macos (pull_request) Successful in 2m34s
Build App (Preview) / prune-previews (pull_request) Canceled after 0s
Build App (Preview) / build-linux (pull_request) Canceled after 3m23s
Build App (Preview) / build-windows (pull_request) Canceled after 3m26s
68b73a9102
The companion to the fixture removal. A site-admin token sat in a test file for
92 commits and fourteen days on a public mirror, past five audit rounds and two
independent reviews, because all of them read the code under change and this was
not under change. A grep would have caught it the first day.

`scripts/scan-secrets.sh` is that grep, in three rules:

  * vendor-prefixed credentials — `ghp_`, `github_pat_`, `glpat-`, `xox*-`,
    `sk-`, `AKIA`/`ASIA`, `ya29.`, `AIza`, `npm_`, `dckr_pat_`. Shape alone
    identifies these, so there is no context to get wrong.
  * `BEGIN … PRIVATE KEY` blocks.
  * an opaque literal assigned to a secret-shaped name — the rule that would
    have caught this one.

The third rule needs **both** halves, and that is what makes it usable rather
than another disabled check. Measured before writing it: an entropy-only rule
flags 317 literals in this tree, and name-proximity alone flags four, three of
which are `secure::get_project_secret(&id, "aws-secret-access-key")` — a
keychain *key name* sitting next to the word `secret`. Requiring the literal
itself to be hex or base64 with no word structure is what excludes those.

Validated rather than asserted:

  * **0 false positives** across every tracked file.
  * **Catches the real incident** — `--range 9b2f4fe~1..9b2f4fe` is refused.
  * Twelve shaped cases pass and fail as intended, including a sha256 in an
    `assert_eq!`, a git sha in a comment and the new dummy fixture, none of
    which trip it.
  * The hook was proved to block an actual `git commit`, not just to exist.

Two halves, because each covers the other's gap:

  * `.githooks/pre-commit`, enabled per clone by `npm run hooks`. Git will not
    let a repository set its own hooks path — cloning would then be enough to
    run its code — so this is opt-in everywhere and `--no-verify` skips it.
  * `Secret Scan`, which nobody can bypass. It carries **no `paths:` filter** on
    purpose: the leak lived in `app/**` and `build.yml` only runs for
    `container/**`, so a path-filtered scan would have missed the very thing it
    exists for. It scans the whole tracked tree rather than a range, because a
    wrong range fails *open* and the full pass takes 0.5s.

Also fixed while here: `core.hooksPath` in this clone pointed at
`/workspace/.git/hooks`, a directory that does not exist — so git hooks were
disabled outright and anything dropped in `.git/hooks` would have been ignored
in silence. A hook that never runs is worse than no hook, because the checklist
says it is there.

`--tracked` skips binaries. Feeding a blob to grep gets "binary file matches"
instead of the line, so a genuine finding inside one would arrive as a sentence
nobody can act on.

A line ending `pragma: allowlist secret` is skipped — wordy on purpose, so it
reads as a claim and leaves something greppable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHL9ty7arp8FHwvE77ne7y
jknapp merged commit 1a79852f65 into main 2026-08-25 18:54:56 +00:00
jknapp deleted branch fix/test-fixture-secret 2026-08-25 18:54:56 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: CyberCoveLLC/Triple-C#33