Evidence ledger
One row per claim in When the secret leaks: the hour after exposure: who published it, what grade it carries, when it was written, when the link was last checked, and the quote or figure it rests on. Nothing in the guide is cited from memory, so anything not in this table is not in the guide.
Guide: When the secret leaks: what four supply-chain incidents and three platforms' revocation
machinery say about the hour after exposure
Category: security-and-identity · Research date: 2026-09-20 · All links fetched 2026-09-20.
Corpus limit, stated up front. This session's network egress policy resolved github.com
and raw.githubusercontent.com and no other host. Engineering blogs, conference sites, paper
hosts, vendor incident pages (CircleCI, Cloudflare, Heroku) and even blog.pypi.org were
refused by the proxy, and this skill does not permit citing from memory. So the corpus is the
GitHub record: security advisories and their database entries, incident issue threads written
while the incidents ran, the scanners' own repositories and rejected pull requests, Kubernetes
enhancement proposals, the AWS quarantine policy as tracked by a third-party diff repository,
platform documentation read from its source repository, and git history read from shallow
clones. The well-known 2022–2023 credential-leak postmortems (Heroku, CircleCI, Cloudflare)
could not be fetched and are therefore absent, not because they do not matter but because they
were unreachable; the page says what that absence costs. No engineering-blog post, talk or
paper is cited, because none could be fetched.
One row per claim. The right-hand column is copied text, not paraphrase.
| # | Org | Title | Tier | Published | Checked | URL | Claim taken from it | Supporting quote or figure |
|---|---|---|---|---|---|---|---|---|
| 1 | GitHub Advisory Database | CVE-2025-30066 / GHSA-mrrh-fwg8-r2c3 (tj-actions/changed-files) | postmortem | 2025-03-15, modified 2025-10-22 | 2026-09-20 | https://github.com/advisories/GHSA-mrrh-fwg8-r2c3 | One compromised action exposed the CI secrets of tens of thousands of repositories in a one-day window. | "A supply chain attack compromised the tj-actions/changed-files GitHub Action, impacting over 23,000 repositories"; "The vulnerability existed between March 14 and March 15, 2025". |
| 2 | GitHub Advisory Database | GHSA-mrrh-fwg8-r2c3, mechanism section | postmortem | 2025-03-15 | 2026-09-20 | https://github.com/advisories/GHSA-mrrh-fwg8-r2c3 | The payload harvested secrets from runner memory and published them in build logs rather than exfiltrating to an attacker host. | "This script extracted secrets from the Runner Worker process memory and printed them in GitHub Actions logs, making them publicly accessible in repositories with public workflow logs." |
| 3 | GitHub Advisory Database | GHSA-mrrh-fwg8-r2c3, IoC section | postmortem | 2025-03-15 | 2026-09-20 | https://github.com/advisories/GHSA-mrrh-fwg8-r2c3 | The attacker rewrote existing version tags rather than shipping a new version, so no consumer action was needed to become vulnerable. | "Retroactively updated tags pointing to the malicious commit: v1.0.0 … v35.7.7-sec … v44.5.1", all pointing at commit 0e58ed8671d6b60d0890c21b07f8835ace038e67. |
| 4 | tj-actions community | Issue #2463, "Multiple tags in this action are compromised" | postmortem | 2025-03-14 | 2026-09-20 | https://github.com/tj-actions/changed-files/issues/2463 | Detection came from an outside monitoring vendor noticing a tag move, not from the maintainer or the platform. | Opening report by varunsh-coder: "this tag was just updated 3 hours back and is potentially exfiltrating credentials … Reported the issue via the email address provided in the security.md file and also reported it via private vulnerability disclosure to generate a CVE." |
| 5 | tj-actions community | Issue #2463, comment by AdnaneKhan, 2025-03-15 00:01 UTC | postmortem | 2025-03-15 | 2026-09-20 | https://github.com/tj-actions/changed-files/issues/2463 | Commit-SHA pinning, the recommended defence, was actively undone by dependency-update automation during the incident. | "We even see some repositories using the backdoored commit by hash after updates by Renovate". |
| 6 | tj-actions community | Issue #2463, comments by ElijahLynn, 2025-03-15 | postmortem | 2025-03-15 | 2026-09-20 | https://github.com/tj-actions/changed-files/issues/2463 | The leak route was public logs, and the encoding was enough to defeat the platform's secret masking. | "I've just confirmed that it is printing double base64 encoded secrets in runner job logs … double base64 --decode that output and boom, there is a github_token." Also: "Every tag got pointed to this malicious commit". |
| 7 | tj-actions community | Issue #2464, first responses | postmortem | 2025-03-14 | 2026-09-20 | https://github.com/tj-actions/changed-files/issues/2464 | The project's first responder to its own compromise report was a marketing bot. | Report title: "Pretty sure this repo got hacked and if you use this it will send your secrets to a hacker"; first reply, from tj-actions-bot, 38 seconds later: "Thanks for reporting this issue, don't forget to star this project if you haven't already to help us reach a wider audience." |
| 8 | GitHub Advisory Database | CVE-2025-30154 / GHSA-qmg3-hpqr-gqvc (reviewdog/action-setup) | postmortem | 2025-03-19 | 2026-09-20 | https://github.com/advisories/GHSA-qmg3-hpqr-gqvc | The reviewdog compromise window was under two hours, three days before tj-actions, and pinning downstream actions did not help. | "reviewdog/action-setup@v1 was compromised March 11, 2025, between 18:42 and 20:31 UTC, with malicious code added that dumps exposed secrets to Github Actions Workflow Logs"; five downstream actions "would also be compromised, regardless of version or pinning method". |
| 9 | reviewdog | Issue #2079, maintainer security advisory by haya14busa | postmortem | 2025-03-18, updated 2025-03-23 | 2026-09-20 | https://github.com/reviewdog/reviewdog/issues/2079 | Write access had been granted automatically to a large contributor pool, which is the surface the attacker used. | "Contributors who submitted to reviewdog/action-* repositories were automatically invited to the @reviewdog/actions-maintainer team, which had write access to these repositories. The team consisted of 118 members at the time of writing." |
| 10 | reviewdog | Issue #2079, update of 2025-03-23 | postmortem | 2025-03-23 | 2026-09-20 | https://github.com/reviewdog/reviewdog/issues/2079 | The credential that opened this incident was stolen in a different, earlier incident; leaks chain. | "We have identified the contributor whose Personal Access Token (PAT) was compromised and subsequently used by attackers to overwrite the reviewdog tags. … The compromised PAT originated from an attack on another repository." Remediation: "most members of the reviewdog organization, including the contributor in question, no longer have write permissions." |
| 11 | Nx (nrwl) | GHSA-cxm3-wv7p-598c, "s1ngularity" advisory | postmortem | 2025-08-27, modified 2026-07-28 | 2026-09-20 | https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c | The npm publishing token was stolen through a PR-title bash injection combined with pull_request_target. |
"Validate PR title … echo \"Validating PR title: ${{ github.event.pull_request.title }}\""; "the PR validation workflows triggered a run of the publish.yml with this malicious commit and sent our npm token to an unfamiliar webhook. We believe this is how the user got a hold of the NPM token". |
| 12 | Nx (nrwl) | GHSA-cxm3-wv7p-598c, timeline | postmortem | 2025-08-27 | 2026-09-20 | https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c | From first malicious publish to full token revocation took about five and a half hours, and the durable fix was to stop having publish tokens at all. | Timeline (EDT, Aug 26–27 2025): malicious versions published from 6:32 PM; team noticed 9:58 PM; npm removed versions 10:44 PM; "11:57 PM - All NPM tokens with permissions for publishing were revoked"; next day, "All NPM packages have also been changed to use the new Trusted Publisher mechanism which does not utilize npm tokens". |
| 13 | Nx (nrwl) | GHSA-cxm3-wv7p-598c, malware behaviour | postmortem | 2025-08-27 | 2026-09-20 | https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c | The malware's output was more leaked credentials, published as GitHub repositories under victims' own accounts; the platform then deleted some of that evidence. | "a postinstall script that scanned user's file system for text files, collected paths, and credentials … posted as an encoded string to a github repo under the user's Gi[thub account]"; "Unfortunately, Github may have proactively deleted the repo for you." |
| 14 | Ultralytics community | Issue #18027, "Discrepancy between what's in GitHub and what's been published to PyPI for v8.3.41" | postmortem | 2024-12-05 | 2026-09-20 | https://github.com/ultralytics/ultralytics/issues/18027 | The compromise was detected by a user diffing the published wheel against the repository, not by the maintainer or either platform. | "Code in the published wheel 8.3.41 is not what's in GitHub and appears to invoke mining. Users of ultralytics who install 8.3.41 will unknowingly execute an xmrig miner." (metrizable, 2024-12-05 06:34 UTC). |
| 15 | Ultralytics community | Issue #18027, automated first response | postmortem | 2024-12-05 | 2026-09-20 | https://github.com/ultralytics/ultralytics/issues/18027 | The project's triage bot advised installing the compromised package while the report sat unread. | UltralyticsAssistant, 35 seconds after the report: "we recommend upgrading to the latest ultralytics version … pip install -U ultralytics". Human confirmation came 94 minutes later (Skillnoob, 08:08 UTC: "the package on PyPi is compromised … v8.3.40 is safe to use"). |
| 16 | Ultralytics | Commit 68c63a7e5, "8.3.43 PyPI publishing security fix" (PR #18052) | source | 2024-12-05 | 2026-09-20 | https://github.com/ultralytics/ultralytics/commit/68c63a7e5603f34069107e3bf60039eec071e620 | The day-one fix replaced a stored personal access token with the runner's ephemeral token and removed pull_request_target title handling from the publish workflow. |
Diff removes GH_TOKEN: ${{ secrets._GITHUB_TOKEN }} in favour of GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}, and deletes the branch reading github.event.pull_request / pull_request_target PR titles into shell variables. |
| 17 | Ultralytics | Commits bf6317011 and e0f8eda36 (PRs #18095, #18111) | source | 2024-12-07 / 2024-12-08 | 2026-09-20 | https://github.com/ultralytics/ultralytics/commit/e0f8eda366c7cf08a1c311a17eaaaf87914f8012 | Remediation arrived in stages over four days: cache disabled on Dec 7, publish pipeline split for security on Dec 8, three and four days after the first "fix". | Commit log: 2024-12-07 "Update publish.yml with --no-cache (#18095)", 2024-12-07 "Add CI cache pruning (#17664)", 2024-12-08 "ultralytics 8.3.48 split PyPI publish jobs for security (#18111)". |
| 18 | Ultralytics | Repository tag record | source | checked live | 2026-09-20 | https://github.com/ultralytics/ultralytics/tags | Versions 8.3.45 and 8.3.46 exist only on the registry, not in the repository: they were published with the stolen registry token after the repository-side fix. Inference from the tag record; the second wave itself is corroborated by the versions' absence and by press coverage this session could not fetch. | Shallow clone of the full tag list, 2026-09-20: v8.3.43 (2024-12-05) and v8.3.44 (2024-12-06) exist; git log v8.3.45 and v8.3.46 return "unknown revision"; v8.3.41 and v8.3.42 tags have been deleted. |
| 19 | GitHub (actions/runner) | Issue #475, "usage of add-mask still echoes the value to the log" | source | 2020-05-11, still open | 2026-09-20 | https://github.com/actions/runner/issues/475 | Log masking is literal string matching registered before output; anything transformed, or printed before registration, is not masked. This is the mechanism the 2025 attacks' double-base64 encoding exploited. | "The mask needs to be added before the output is registered. … The output is streaming, so too late if value is already printed." (ericsciple, runner maintainer, 2020-05-28). Issue open with 75 comments as of 2026-09-20. |
| 20 | GitHub Docs | token-expiration-and-revocation.md | vendor | current, checked from source repo | 2026-09-20 | https://github.com/github/docs/blob/main/content/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation.md | For its own tokens, GitHub revokes automatically on public exposure, with no owner in the loop. | "If a valid OAuth token, GitHub App token, or personal access token is pushed to a public repository or public gist, the token will be automatically revoked." |
| 21 | GitHub Docs | secret-scanning-for-partners.md | vendor | current | 2026-09-20 | https://github.com/github/docs/blob/main/content/code-security/concepts/secret-security/secret-scanning-for-partners.md | For partners' tokens, the same scanner only notifies; the issuing provider decides whether to revoke. | "The service provider validates the string and then decides whether they should revoke the secret, issue a new secret, or contact you directly. Their action will depend on the associated risks to you or them." |
| 22 | GitHub Docs | secret-scanning.md | vendor | current | 2026-09-20 | https://github.com/github/docs/blob/main/content/code-security/concepts/secret-security/secret-scanning.md | The platform's own remediation guidance is revocation first; history rewriting is optional. | "When you receive an alert, rotate the affected credential immediately to prevent unauthorized access. While you can also remove secrets from your Git history, this is time-intensive and often unnecessary if you've already revoked the credential." |
| 23 | GitHub Docs | push-protection.md | vendor | current | 2026-09-20 | https://github.com/github/docs/blob/main/content/code-security/concepts/secret-security/push-protection.md | The platform's newest control moves detection before the leak instead of after it. | "Rather than alerting you to credential leaks after the fact, push protection blocks pushes that contain secrets before they reach your repository." |
| 24 | GitHub Docs | validity-checks.md | vendor | current | 2026-09-20 | https://github.com/github/docs/blob/main/content/code-security/concepts/secret-security/validity-checks.md | The platform now tests found secrets against the issuer's API to triage them, the same design the gitleaks maintainer declined. | "{GitHub} will periodically check the validity of a detected credential by sending the secret to the issuer and testing it against APIs provided by that service." |
| 25 | GitHub Docs | what-is-scanned.md (reusable) | vendor | current | 2026-09-20 | https://github.com/github/docs/blob/main/data/reusables/secret-scanning/what-is-scanned.md | The leak surface the scanner covers goes far beyond code. | Scanned: "Descriptions and comments in issues … Titles, descriptions, and comments in pull requests … Wikis … Secret gists". |
| 26 | PyPI (pypa/warehouse) | warehouse/integrations/secrets/config.py | source | current | 2026-09-20 | https://github.com/pypa/warehouse/blob/main/warehouse/integrations/secrets/config.py | PyPI receives leak disclosures from two scanning origins, GitHub and Google's Deps.dev. | _github_origin = utils.DisclosureOrigin(name="GitHub", … verification_url="https://api.github.com/meta/public_keys/token_scanning"); _depsdev_origin = utils.DisclosureOrigin(name="Deps.dev", …). |
| 27 | PyPI (pypa/warehouse) | warehouse/integrations/secrets/utils.py | source | current | 2026-09-20 | https://github.com/pypa/warehouse/blob/main/warehouse/integrations/secrets/utils.py | On a verified disclosure PyPI deletes the token first and emails the owner second; no human is in the loop. | _analyze_disclosure sequence: verify_signature_only(raw_macaroon=disclosure.token) then macaroon_service.delete_macaroon(macaroon_id=…) then send_token_compromised_email_leak(…). |
| 28 | AWS via z0ph/MAMIP | policies/AWSCompromisedKeyQuarantineV3 | source | tracked 2024-08-21 → 2026-03-16 | 2026-09-20 | https://github.com/z0ph/MAMIP/blob/master/policies/AWSCompromisedKeyQuarantineV3 | AWS's automated response to a leaked key is a deny-list fence, not revocation: the key keeps working except for 98 enumerated actions. | Policy is a single "Effect": "Deny" statement over 98 actions (count from the fetched JSON, 2026-09-20) including iam:CreateUser, ec2:RunInstances, lambda:CreateFunction, cloudtrail:LookupEvents. |
| 29 | AWS via z0ph/MAMIP | Quarantine policy family change history | source | 2020-08-11 → 2026-03-16 | 2026-09-20 | https://github.com/z0ph/MAMIP/commits/master/policies/AWSCompromisedKeyQuarantineV3 | The deny list has grown for six years and across three policy generations: 28 actions (V1) to 89 (V2) to 98 (V3). | MAMIP git history (cloned 2026-09-20): V1 first tracked 2020-08-11; V2 first tracked 2021-04-22; V3 first appears 2024-08-21, last updated 2026-03-16 ("Policy Version v3"). Action counts computed from the three policy JSONs. |
| 30 | Kubernetes SIG Auth | KEP-1205, Bound Service Account Tokens | adr | created 2019-08-06, stable v1.22 | 2026-09-20 | https://github.com/kubernetes/enhancements/blob/master/keps/sig-auth/1205-bound-service-account-tokens/README.md | The project's own statement of why long-lived, unscoped tokens are unacceptable. | "JWTs are not audience bound. Any recipient of a JWT can masquerade as the presenter to anyone else. … JWTs are not time bound. A JWT compromised via 1 or 2, is valid for as long as the service account exists." |
| 31 | Kubernetes SIG Auth | KEP-2799, Reduction of Secret-based Service Account Tokens | adr | created 2021-06-25, stable v1.30 | 2026-09-20 | https://github.com/kubernetes/enhancements/blob/master/keps/sig-auth/2799-reduction-of-secret-based-service-account-token/README.md | Removing the legacy long-lived token took three feature gates and reached stable only in v1.30 (2024), eleven years after the original design. | kep.yaml: "creation-date: 2021-06-25 … stage: stable … latest-milestone: v1.30"; feature gates LegacyServiceAccountTokenNoAutoGeneration, LegacyServiceAccountTokenTracking, LegacyServiceAccountTokenCleanUp. |
| 32 | Truffle Security | trufflehog README | source | current | 2026-09-20 | https://github.com/trufflesecurity/trufflehog | The verification-first design: the scanner logs in with what it finds. | "TruffleHog classifies over 800 secret types … For every secret TruffleHog can classify, it can also log in to confirm if that secret is live or not. This step is critical to know if there's an active present danger or not." |
| 33 | Gitleaks | gitleaks README | source | current | 2026-09-20 | https://github.com/gitleaks/gitleaks | The other camp: detection only, and the project is now feature-frozen. | "Gitleaks is a tool for detecting secrets"; README warning: "Gitleaks is feature complete. I'm not merging new features into Gitleaks. Future releases will be security patches only. I'm shifting my focus to Betterleaks." |
| 34 | Gitleaks | PR #1515 "Verify feature" (also #1519) | source | opened 2024-09-21, closed unmerged | 2026-09-20 | https://github.com/gitleaks/gitleaks/pull/1515 | The maintainer himself built verification for gitleaks, 43 commits, and closed it without merging; the reason is not recorded on the PR. | PR header: "Verify feature #1515 · zricethezav wants to merge 43 commits … Closed"; body: "experimental verify feature"; commit messages include "feat(verify): add opt-in — This feature is a preview." |
| 35 | Betterleaks | betterleaks README | source | current | 2026-09-20 | https://github.com/betterleaks/betterleaks | In the successor tool, verification shipped as a headline feature; the rejected design won. | "Secrets Validation: Validate if a detected secret is active by making asynchronous HTTP requests directly from within the rule definition". "It is maintained by the folks who made Gitleaks, including the original author." |
| 36 | AWS | aws-actions/configure-aws-credentials README | vendor | current | 2026-09-20 | https://github.com/aws-actions/configure-aws-credentials | The cloud vendor's recommended CI credential is one that does not exist until the job runs. | "Quick Start (OIDC, recommended)"; "Use temporary credentials when possible. OIDC is recommended because it provides temporary credentials and it's easy to set up." |
| 37 | GitHub Docs | openid-connect.md (Actions concepts) | vendor | current | 2026-09-20 | https://github.com/github/docs/blob/main/content/actions/concepts/security/openid-connect.md | The structural alternative to storing secrets: per-job tokens that expire on their own. | "your cloud provider issues a short-lived access token that is only valid for a single job, and then automatically expires"; benefit listed first: "No cloud secrets." |
| 38 | OpenSSF | scorecard docs/checks.md, Token-Permissions | adr | current | 2026-09-20 | https://github.com/ossf/scorecard/blob/main/docs/checks.md | The ecosystem's measurable proxy for this whole failure class is workflow token scope. | "Token-Permissions — Risk: High (vulnerable to malicious code additions) … attackers may use a compromised token with write access to, for example, push malicious code into the project." |
| 39 | Thinkst | canarytokens README | source | current | 2026-09-20 | https://github.com/thinkst/canarytokens | Deliberately planted credentials as tripwires are an open-source, self-hostable technique. | "Canarytokens help track activity and actions on your network." Repository is the self-hostable server behind canarytokens.org. |