Every source behind this page, graded. One host: this corpus is the
GitHub record, by construction; the ledger shipped beside this page says exactly what that
excludes.
Postmortem
GitHub Advisory DB2025-03-15
CVE-2025-30066: tj-actions/changed-files supply chain attack
The authoritative record: 23,000+ dependent repositories, all tags retroactively
moved to one malicious commit, secrets scraped from Runner Worker memory into public
logs, patched in v46.0.1.
Carry forwardA tag rewrite makes you vulnerable retroactively; your dependency review never saw a diff.
github.com/advisories/GHSA-mrrh-fwg8-r2c3
Postmortem
tj-actions community2025-03-14
Issue #2463: the incident, live
The detection report ("this tag was just updated 3 hours back"), the payload
dissection, the double-base64 confirmation, and the observation that Renovate was
updating SHA-pinned repos onto the malicious hash while the thread ran.
Carry forwardThe incident thread is the only place the auto-updater betrayal is recorded; advisories smooth it away.
github.com/tj-actions/changed-files/issues/2463
Postmortem
tj-actions community2025-03-14
Issue #2464: the bot answers a breach report
"Pretty sure this repo got hacked" was answered in 38 seconds by the project's bot:
"don't forget to star this project if you haven't already to help us reach a wider
audience."
Carry forwardResponse automation without an incident mode becomes part of the incident.
github.com/tj-actions/changed-files/issues/2464
Postmortem
GitHub Advisory DB / reviewdog2025-03-19
CVE-2025-30154: reviewdog/action-setup compromise
A one-hour-49-minute window on March 11, 2025; five downstream actions compromised
"regardless of version or pinning method" because they resolve the inner action at
run time.
Carry forwardYour pin is only as strong as the least-pinned reference in the transitive chain.
github.com/advisories/GHSA-qmg3-hpqr-gqvc
Postmortem
reviewdog (haya14busa)2025-03-18/23
Issue #2079: the maintainer's own investigation
Auto-invites had given 118 contributors write access; the stolen PAT "originated
from an attack on another repository"; remediation removed write from most members.
Includes the maintainer asking GitHub for the API logs to find the tag-mover.
Carry forwardAccess-growth automation is a credential issuer; audit it like one.
github.com/reviewdog/reviewdog/issues/2079
Postmortem
Nx (nrwl)2025-08-27
GHSA-cxm3-wv7p-598c: the s1ngularity advisory
A minute-by-minute timeline from PR-title bash injection under
pull_request_target, through npm token exfiltration to a webhook, to
malware that posted victims' credentials as repos on their own accounts. Ends with
npm tokens abolished in favour of trusted publishing.
Carry forwardThe strongest fix in the whole corpus: make the stealable credential stop existing.
github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c
Postmortem
Ultralytics community2024-12-05
Issue #18027: wheel does not match repository
A user diffed the published wheel against the tag and found a miner. The triage bot
recommended upgrading to the compromised latest; the human correction and registry
removal followed within about three hours.
Carry forwardRegistry-vs-repo diffing is a detection control nobody owns; the person who ran it was a user.
github.com/ultralytics/ultralytics/issues/18027
Source
Ultralytics2024-12-05/08
The remediation, read from git
Dec 5: stored PAT replaced with the ephemeral job token, PR-title shell interpolation
deleted. Dec 7: caches disabled and pruned. Dec 8: publish pipeline split "for
security". The staging shows what the first response missed, and the registry-only
versions 8.3.45/46 show what it cost.
Carry forwardRead remediation commits in order; the gaps between them are the lesson.
ultralytics commit 68c63a7e5
Source
Ultralyticschecked 2026-09-20
The tag record, with holes
v8.3.43 and v8.3.44 exist; v8.3.41/42 were deleted after the compromise; v8.3.45/46
never existed in git at all. Inferred, and stated as inference: those two versions were
published directly to the registry with a stolen token.
Carry forwardA version on the registry with no matching tag is a leaked-credential detector in itself.
github.com/ultralytics/ultralytics/tags
Source
GitHub (actions/runner)2020-05, open
Issue #475: how masking actually works
The runner maintainer, 2020: "The mask needs to be added before the output is
registered. … The output is streaming, so too late if value is already printed."
Literal registration plus streaming output is exactly the gap double-base64 walked
through five years later.
Carry forwardMasking is best-effort string replacement; nothing about it is a security boundary.
github.com/actions/runner/issues/475
Source
PyPI (pypa/warehouse)current
The disclosure receiver, in production code
HMAC-verified disclosures from GitHub and Deps.dev; verify_signature_only,
then delete_macaroon, then the email. The ordering is the policy:
revocation does not wait for a human.
Carry forwardIf you issue tokens, this ~300-line file is the reference implementation of the receiving end.
pypa/warehouse integrations/secrets/utils.py
Source
AWS, tracked by z0ph/MAMIP2020–2026
AWSCompromisedKeyQuarantine, three generations
The managed policy AWS attaches to exposed keys, diffed over time by a tracker repo:
28 denied actions in V1 (tracked 2020), 89 in V2 (2021), 98 in V3 (first seen
2024-08-21, still updated 2026-03). A deny list, not a kill: the fence model in
public.
Carry forwardThe deny list doubles as AWS's published model of what attackers do with a stolen key.
z0ph/MAMIP policies/AWSCompromisedKeyQuarantineV3
Source
Truffle Security2026-09
TruffleHog: verification as the product
"Over 800 secret types", each mapped to an identity, and for each "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."
Carry forwardAn unverified finding is a chore; a verified-live finding is an incident. Triage on liveness.
github.com/trufflesecurity/trufflehog
Source
Gitleaks2024-09, closed
PR #1515: the verify feature that was not merged
The gitleaks maintainer built verification himself, 43 commits, opt-in, status enums,
then closed the PR unmerged (as with its successor #1519) and later froze the project.
The README today: gitleaks is "a tool for detecting secrets". The reason is not
recorded; the outcome is.
Carry forwardA closed-unmerged PR from the maintainer is the strongest "we considered it" record there is.
github.com/gitleaks/gitleaks/pull/1515
Source
Betterleaks2026
The successor ships what the ancestor declined
"Maintained by the folks who made Gitleaks, including the original author", with
"Secrets Validation" as a headline feature: validate liveness with HTTP requests from
inside the rule definition. The 2024 argument resolved itself in the 2026 tool.
Carry forwardWhere a feature was rejected, check the author's next project before treating the rejection as doctrine.
github.com/betterleaks/betterleaks
Source
Thinkstcurrent
Canarytokens: credentials as tripwires
The self-hostable server behind canarytokens.org: mint deliberately plantable
credentials whose only legitimate use is never, so any use is a high-confidence alarm.
The inverse of everything else on this wall: a leak you want.
Carry forwardA planted key in your CI secrets answers "would we notice" empirically, before an attacker does.
github.com/thinkst/canarytokens
Decision record
Kubernetes SIG Auth2019-08-06
KEP-1205: Bound Service Account Tokens
The problem statement that still reads as the whole field's thesis: tokens that are
not audience bound let "any recipient of a JWT masquerade as the presenter to anyone
else", and tokens that are not time bound stay valid "for as long as the service
account exists".
Carry forwardAudience and expiry are the two attenuations that turn a leak from a compromise into an inconvenience.
kubernetes/enhancements KEP-1205
Decision record
Kubernetes SIG Auth2021-06-25
KEP-2799: Reduction of Secret-based Service Account Tokens
The removal plan for the legacy tokens: no-auto-generation, then tracking of
remaining use, then cleanup, three feature gates landing stable in v1.30. The
tracking gate exists because you cannot delete what you cannot prove unused, which is
the enumeration problem again, inside the fix.
Carry forwardPlan the migration off a credential class as: stop issuing, then measure remaining use, then delete. In that order, with telemetry.
kubernetes/enhancements KEP-2799
Decision record
OpenSSFcurrent
Scorecard: Token-Permissions, risk High
The ecosystem's measurable proxy for this failure class: does each workflow token
follow least privilege, because "attackers may use a compromised token with write
access to … push malicious code into the project". Both March 2025 attacks ran through
exactly that.
Carry forwardWorkflow token scope is auditable at scale today; it is the cheapest of these controls to check.
ossf/scorecard docs/checks.md
Platform docs
GitHubcurrent
Token revocation on public exposure
"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." Also:
unused tokens die after a year.
Carry forwardThe platform kills its own tokens without asking; assume the same posture for tokens you issue.
github/docs token-expiration-and-revocation.md
Platform docs
GitHubcurrent
The partner programme: notify, do not kill
For other issuers' secrets, GitHub forwards the finding and "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."
Carry forwardRevocation authority stays with whoever bears the outage; detection and authority are separable.
github/docs secret-scanning-for-partners.md
Platform docs
GitHubcurrent
Remediation guidance: rotate, do not scrub
"Rotate the affected credential immediately … removing secrets from your Git history
… is time-intensive and often unnecessary if you've already revoked the credential."
The docs also enumerate the scan surface: issues, PR comments, discussions, wikis,
gists.
Carry forwardThe secret is compromised at first exposure forever; only revocation changes its value.
github/docs secret-scanning.md
Platform docs
GitHubcurrent
Push protection: move detection before the leak
"Rather than alerting you to credential leaks after the fact, push protection blocks
pushes that contain secrets before they reach your repository", at the command line,
the UI, the API and file upload.
Carry forwardPre-receive blocking is the only control that keeps the secret out of history entirely; everything after it is damage control.
github/docs push-protection.md
Platform docs
GitHubcurrent
Validity checks: the platform logs in with your leak
Opt-in checks that "periodically check the validity of a detected credential by
sending the secret to the issuer and testing it against APIs provided by that
service", with deliberately least-intrusive endpoints.
Carry forwardLiveness triage has become a platform feature; wire your alert routing to the active state, not to detection.
github/docs validity-checks.md
Platform docs
GitHubcurrent
OIDC for deployments: the credential that is not there
The Actions OIDC concept doc: the cloud "issues a short-lived access token that is
only valid for a single job, and then automatically expires", and the first listed
benefit is "No cloud secrets". This is the per-job exit at the top of Figure 3.
Carry forwardFor CI-to-cloud, the leak-proof credential is generally available today; storing cloud keys in CI is now a choice.
github/docs openid-connect.md
Platform docs
AWScurrent
configure-aws-credentials: the vendor's default is keyless
The official action's quick start is titled "OIDC, recommended", and its security
recommendations open with "Use temporary credentials when possible. OIDC is
recommended because it provides temporary credentials and it's easy to set up."
Carry forwardWhen the cloud vendor's own quick start avoids stored keys, a stored key in CI needs a written justification.
aws-actions/configure-aws-credentials