18/09/2026

The package was never the target

The package was never the target

elusive thoughts // supply chain // september 2026

Ask most teams what a successful supply chain attack looks like and they describe a malicious package getting installed. That is the delivery. The success condition is a valid credential leaving your CI runner, and by that measure the attackers are winning comfortably while the industry argues about CVE counts.

Two pieces landed this month that I have been reading side by side. GitGuardian wrote up what recent supply chain campaigns were actually after once they got in. Aikido wrote up why the number of critical CVEs at the biggest software companies went from under 100 a month to over 600 since spring, and why that number is the wrong thing to stare at. Put them together and you get an uncomfortable picture of a pipeline that has learned to ship faster than it can reason about what it is shipping.

Define success from the attacker's chair

GitGuardian's list of entry points is varied. Compromised maintainer accounts. Abused CI/CD workflows. Rewritten GitHub Action tags. Poisoned packages that were trusted yesterday. Their line is that whatever the door was, "the objective was remarkably consistent: get credentials."

The numbers behind that are not small. Shai-Hulud 2.0 left behind 20,649 exfiltration repositories holding 33,185 unique secrets, of which 3,760 were confirmed still valid when they were checked. The GhostAction campaign injected malicious workflows into 817 repositories across 327 GitHub users and pulled out 3,325 secrets, including PyPI, npm and DockerHub tokens, by plain HTTP POST. Nothing clever. The runner had the tokens in its environment, so the workflow read them and sent them somewhere.

And the speed. When a poisoned Axios version hit npm, Dependabot opened its first pull request within five minutes. 895 repositories were affected inside the attack window. The automation you set up to keep dependencies fresh is the same automation that hand-delivers the compromised version to your build.

entry pointmaintainer account · CI workflow · Action tag rewrite · poisoned version
automated pullDependabot / Renovate / lockfile update. first PR in ~5 min (Axios)
build runs itinstall scripts and workflows execute with the runner's environment
credentials outGitHub tokens · publish tokens · SSH keys · cloud keys
reuseopen the next system, or publish the next poisoned version
◀ publishing rights feed the next round. this is a loop, not a line.
Fig 1. The chain as GitGuardian describes it. The green box is the success condition. Everything to its left is logistics.

So the outcome that matters is the green box. A credential with reach. GitGuardian is explicit about what it buys: it can "open another system, expose additional secrets" or "give the attacker the publishing rights needed to push malicious software to another set of victims." One publish token turns a single compromised package into the next campaign. That is the whole business model of the mini Shai-Hulud waves we have seen since the original.

Why do fresh packages keep shipping with day-zero problems

Here is where the Aikido piece comes in, because it explains the environment that lets the chain above run so freely.

The a16z and Epoch AI numbers cover 21 major software companies including Apple, AWS, Microsoft, Google and Adobe. Reported critical vulnerabilities at those companies never cleared 100 per month in four years. Since spring they are running above 600 per month. Aikido's read, which I agree with, is that the software did not get six times worse in one season. AI made finding bugs cheap. Frontier models are surfacing vulnerabilities faster than humans can validate them, including old ones that survived years of manual review.

Detection got commoditised. Remediation did not move. Aikido calls it the remediation paradox and the Verizon 2026 DBIR figures back it up: only 26% of confirmed actively exploited vulnerabilities on CISA's KEV list were fully remediated in 2025, down from 38% the year before. Median patch time went from 32 to 43 days. Even the best resourced organisations fix 30 to 40% in the first week.

discovery (critical CVEs / month, 21 major vendors)
2022 to early 2026 ceiling
< 100
since spring 2026
> 600
remediation (KEV vulns fully fixed, Verizon DBIR)
2024
38%
2025
26%
median days to patch
previous year
32 days
2025
43 days
scoring backlog (NIST NVD)
18 months ago
13,000
May 2026 audit
27,000+
Fig 2. Finding goes up six-fold. Fixing goes down. The reference database gives up on scoring everything. Sources: a16z / Epoch AI via Aikido, Verizon 2026 DBIR, NIST federal audit May 2026.

The NIST line deserves its own sentence. The NVD backlog went from 13,000 to over 27,000 unscored CVEs in eighteen months, and the response after a May 2026 federal audit was to stop scoring everything and only prioritise CVEs tied to federal use, critical software, or the KEV list. The shared reference the whole industry keyed its SLAs to has formally admitted it cannot keep up.

Now connect the two articles. A maintainer publishes a new version. That version is either a poisoned one, or a legitimate one with a vulnerability that an AI-assisted researcher will find in a fortnight and a CVE that NVD may or may not score. Your dependency bot pulls it in minutes. Your build runs its install scripts with your publish tokens and cloud keys in the environment. And the thing that would tell you something was wrong, a scored CVE or a vendor advisory, arrives days or weeks later if it arrives at all. Aikido's own telemetry on StyleSmuggler is the tell: they had the Adobe Commerce RCE tracked before it had a CVE number, and 67% of the packages they found vulnerabilities in were never disclosed to any public database.

Is it too fast, or do we not understand the impact

Both, and they reinforce each other.

The speed problem is real and it is self-inflicted. We built automation that treats "new version exists" as sufficient reason to bring it into the build within minutes. That was a reasonable trade when the failure mode was a broken test. It is not a reasonable trade when the failure mode is a workflow that POSTs your npm token to a stranger. Five minutes is faster than any human triage, faster than any scanner refresh, and faster than the registry's own takedown.

The impact problem is subtler and I think it is the bigger one. Most organisations have an SBOM now. Compliance asked for it, a tool generated it, it lists every component and version. What almost nobody has is the other inventory: which credentials sit in which runner, what each of them can reach, and who owns them. GitGuardian's advice is blunt about this. Build the credential map before the incident, because "waiting until the incident to build that map means reconstructing it while the response clock is already running."

An SBOM tells you the poisoned package is in 40 repos. It does not tell you that 12 of those repos build on a runner holding a production deploy key and a publish token for your own internal packages. The first fact is a remediation ticket. The second fact is the incident. Teams are reading the bill of materials and not the bill of access, and that is why the impact keeps being underestimated until the exfiltration repos show up in someone's research post.

THE INVENTORY GAPSBOM answers "what is in the build". Nobody is answering "what can the build reach". The second question is the one the attacker is asking.

There is a third thing that makes it worse and it is cultural. Vulnerability programmes are still run on CVE volume. Count of criticals, days open, percentage closed. When the volume goes up six-fold the honest response is that the metric broke, and instead the response is usually to work harder on the same queue. Aikido's framing is the right one: "Risk is determined by how fast you can find out which vulnerabilities matter, and how fast they get fixed once they're validated." A number that says how many CVEs exist tells you about the researchers. It does not tell you about your exposure.

What I would actually do about it

Everything below is a gate in the execution path, not a policy document. The people and agents pulling packages will not read the document. They will notice the gate only if it is implemented wrong.

install
48h minimum package age verify against threat intel before disk
shell-level wrap. the caller does not know it is proxied.
build / CI
short-lived OIDC creds, no static tokens secrets scan blocks merge (CI, not pre-commit) one blocking status
nothing worth stealing sits on the runner.
triage
reachability before severity do not wait for a CVE ID
the queue is the CVEs you can execute, not the CVEs that exist.
fix
autofix opens the PR credential map ready before the incident
remediation measured in hours, owned by the pipeline.
Fig 3. Where the gates go. Amber is a control. Green text is what each stage looks like when the control is working and nobody notices it.

1. Put a clock on new versions

We run a 48-hour minimum package age at the install layer. Every npm, pnpm, yarn, bun, pip, uv and poetry call goes through a shell alias into a local proxy that checks the package and its dependency tree against threat intel before anything hits disk. The agent running the install does not know the proxy exists. It gets a package or it gets an error. Shai-Hulud spread across 160 plus packages in hours and was publicly identified inside two days. Two days of waiting removes most of that window and costs, in practice, almost nothing.

2. Make the runner boring to rob

If the success condition is a credential leaving the runner, the fix is to stop putting credentials worth having on the runner. Short-lived OIDC federation to the cloud instead of static keys. Publish tokens that exist for one job and expire. Agent identities on 30-day keys, separate from the human ones, so the audit trail can tell them apart. GitGuardian's recommendation to scope runner permissions to exactly the "publishing credentials, source code, deployment credentials, or other secrets" a job needs is the same point. GhostAction worked because the tokens were just there.

3. Scan in CI and stop pretending pre-commit counts

We run secret detection twice. Pre-commit is a developer courtesy and anyone can skip it with one flag on a machine we do not control. The CI run is the control. It blocks the merge and nobody overrides it, because an override that exists gets used at five on a Friday. One blocking status, not six advisory ones. Six advisory checks train people to scroll past, and that habit generalises to the check that mattered.

4. Triage on reachability, and stop waiting for the number

Aikido's first recommendation is reachability analysis, filtering out findings with no execution path to the vulnerable code, and it is the only way I know to survive 600 criticals a month. Their second is not to wait for a CVE ID or a vendor advisory before acting. The StyleSmuggler case is the proof. A working fix shipped through autofix across multiple pinned versions more than 24 hours before Adobe's own hotfix. When two thirds of the vulnerable packages you find were never disclosed anywhere, a process that begins at "CVE published" is a process that begins late by design.

5. Let the fix arrive as a diff

The adoption curve of a security fix depends almost entirely on the form it arrives in. A Jira ticket that says "upgrade X" competes with the sprint. A small pull request that already passes tests gets merged before lunch. Our SCA tooling opens the remediation PR itself. Same fix. Completely different outcome, and it is the only way the remediation side of the paradox ever catches up with the discovery side.

6. Build the credential map now

Which secrets exist, where they live, what they reach, who owns them. Per repo, per runner, per agent. This is the inventory that an SBOM was never going to give you and it is the one you will need at 2am when a research team publishes a list of exfiltration repos and one of them has your org name in it. GitGuardian's post-incident sequence is scope, ownership, prioritise by reach, revoke or rotate. Every one of those steps is faster if the map already exists.

The honest costs

None of this is free and I would rather say so than have you find out. The 48-hour hold generates a real argument every so often, usually when the fix you need shipped that morning. Moving CI to short-lived credentials means AppSec owns the integration work instead of pushing tickets to teams, and it took a lot longer than a tool rollout. Reachability analysis is only as good as the call graph, and it will occasionally file a real problem under "unreachable". The credential map rots the moment you stop maintaining it, and a stale map is worse than none because people trust it.

Verdict

A supply chain attack succeeds when a credential with reach leaves your build. Not when a package gets installed, and not when a CVE gets published. The industry is counting the wrong thing at both ends: installs and CVEs, when the attacker is counting valid tokens. Six hundred criticals a month is going to be the floor from here, because the tools that find them are not getting slower. The only variable you control is how little there is to steal when one gets through, and how many hours it takes you to close it once you know.

Read the two pieces that prompted this. GitGuardian on what supply chain attacks are really after, and Aikido on why the CVE spike is a remediation problem. They are better read together than apart.

No comments:

Attackers vibe code too

// threat intel // llmjacking // credential harvesting TL;DR Datadog Security Labs spent the summer watching two credential harvesting pane...