Build1 distinct publisher2 min readUpdated
Codecov in 2021 and tj-actions/changed-files in March 2025 failed the same way: trusted third-party code running with pipeline privileges on a push nobody on your team made.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
The mechanism in both incidents is dull, which is part of why it keeps working. Trusted third-party code, running with pipeline privileges, on somebody else's push [4]. Roughly four years separate the Codecov bash uploader compromise from the tj-actions/changed-files compromise [17], and the referencing pattern that made the second one possible is still the default way workflow files name what they run.
That pattern is the tag. `uses: some-org/some-action@v3` resolves through a pointer that the upstream repository owner controls, so a compromised upstream account turns `v3` into attacker code on your next run with no change and no approval on your side [5]. A full commit SHA cannot be repointed [6]. The inventory of your exposure to this is one command: `grep -rn "uses:.*@v" .github/workflows/` [7]. Whatever else is true about supply chain security, the cost of finding out is not the obstacle here. The dev.to author's own expectation is that the count comes back worse than people guess [20].
The privilege arithmetic is where this gets expensive. Repository-wide secrets are readable by every workflow in the repository, including the lint job and including a job that runs a third-party formatter against a fork's pull request [8]. So the least-reviewed job in the repo carries the same read access as the deploy job, and the deploy key's blast radius is set by the sloppiest YAML file rather than the most careful one. The proposed test is a good one because it is falsifiable: for each secret, name the single job that needs it, and treat anything you cannot assign as over-shared [18]. Same logic on the token. A workflow with no `permissions:` block runs on the repository default, and on older repositories that default is write, which means a compromised step can push commits, edit releases or open pull requests wearing your CI's identity [12].
The failure that ties it together is detection. Log masking matches exact strings, so a secret that has been base64 encoded, JSON wrapped, or split across a variable expansion goes through unmasked [14], and on a public repository the logs are public and the artifacts are downloadable [15]. That is the tj-actions shape exactly: the leak arrived in build logs of every repository that referenced the action by tag [3].
One caveat on the evidence. This is a single practitioner writeup, and it offers no measurement of how many repositories are still tag-pinned or still running on default token permissions. The two named compromises are load-bearing; the prevalence is asserted, not counted.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
A CI pipeline holds deploy keys, registry credentials, cloud tokens and signing secrets, runs on every push, runs third-party code the team did not write, and runs with more privilege than any developer on the team; for most projects it is completely unreviewed.
The Codecov bash uploader compromise in 2021 exposed secrets from thousands of CI environments.
In March 2025 the widely used tj-actions/changed-files action was compromised and began leaking secrets into the build logs of every repository that referenced it by tag.
Both attacks worked the same way: trusted third-party code, running with pipeline privileges, on somebody else's push.
A tag is a pointer; whoever controls the repository controls where it points, so if that account is compromised, v3 silently becomes attacker code on the next run with nothing changed and nothing approved by the consumer.
The recommended fix is pinning every third-party action to a full commit SHA, because a SHA cannot be moved, with a comment recording the version.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Named incidents, verifiable mechanics, no primary citations
The two anchoring events are real, named, dated public compromises, and the technical mechanics (tag mutability, repository-wide secret visibility, pull_request_target with a head checkout, default token permissions, exact-match log masking) are checkable against platform behaviour and are internally consistent. Against that, the cluster is a single dev.to post with no links, advisories, or CVE references, and its scale statements ('thousands of CI environments', 'for most projects it is completely unreviewed', 'usually worse than expected') carry no measurement.
Incident scope implies weak uptake of the recommended controls
The only adoption signal available is indirect and negative: both cited compromises succeeded precisely because affected repositories consumed third-party code by mutable tag with credentials in scope, and the article notes that workflow-aware scanners such as Checkov, Semgrep and actionlint are not defaults in a new repository. The cluster provides no telemetry, survey, registry scan, or vendor disclosure quantifying how many projects now pin to SHAs, scope secrets to environments, or set explicit permissions blocks, so the score reflects a low-confidence inference from incident blast radius rather than a measurement of practice uptake.
Sound advice wrapped in unevidenced universals
The remediations are conventional and defensible, so the substantive gap is modest, but the framing overstates what the sourcing supports: second-person universals ('you have never scanned it', 'for most projects it is completely unreviewed'), a superlative ranking of pull_request_target as the single most reliable credential leak, and an unquantified prediction that the grep result will be worse than expected. Nothing in the cluster measures prevalence, and no counter-consideration is offered on the cost or maintenance burden of SHA pinning.
Engagement-shaped practitioner post, no disclosed commercial tie
The item is an individual-authored post on a developer-marketing platform, structured for engagement (mistake/fix/how-to-catch-it, 'four commands, ten minutes'), which rewards urgency and universal second-person framing. It name-checks Gitleaks, TruffleHog, Checkov, Semgrep and actionlint without disclosing any relationship, and it sells no product, subscription, or service of its own; the recommended actions are free platform features and shell commands, which limits commercial pull.
Mechanically credible but single-publisher and uncorroborated
Confidence is moderate: the technical claims are specific, self-consistent, and of a kind that is independently checkable, and the two anchoring incidents are widely documented public events. It is held down by structural thinness, namely one publisher, one source item, no primary links, no author disclosure, and no independent data on prevalence or on adoption of the recommended controls, so the prevalence and superlative claims should be treated as unverified.
build
Your agent needs the API call, not the API key1 distinct publisher
build
A cleanup commit deleted the sanitizer. Five days later a scanner cashed it in.1 distinct publisher
build
A Stripe SDK Major Bump Turned One Metadata Lookup Into a Silent Non-Delivery1 distinct publisher
build
The First Firewall Rule Is a Cutover: One Allowlist Entry, One Dead Production App1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 21, 2026