Join our Newsletter — 33% off our NHI Course
Home› Guides› Supply chain NHI breaches: what 51 incidents teach…
Guide Software & AI Supply Chain

Supply chain NHI breaches: what 51 incidents teach about publishing identities

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 28 October 2026 14 min read
On this page

Supply chain is now the most common known way into the breaches we track, and almost every case runs on a stolen or misused machine identity: a publishing token, a bot account, a CI secret or a build signing key. In The State of NHI & AI Agent Breach Report 2026, NHI Mgmt Group counted 51 of the 200+ breaches in its database that came through a poisoned package, build tool or CI pipeline. This guide reads those cases side by side to find the steps that repeat, then sets out the controls that break each step and the order to put them in. It is written for platform, AppSec and identity teams who own package publishing, CI/CD and the credentials that sit inside them. For the general programme see the Software Supply Chain Security Guide; for market-wide numbers see the Supply Chain Attack Statistics Guide.

Key takeaways

  • The route is supply chain, the asset is an NHI. In the report, the most common pairing is supply chain with a CI/CD or publishing token (34 breaches), followed by a secret or password (25), a source control token (24) and a cloud credential (18).
  • One publishing identity reaches every downstream user. Shai-Hulud and ChainDrop show a stolen npm token being used to republish every package its owner could touch, so one leak becomes hundreds of poisoned packages within hours.
  • Provenance proves the pipeline, not the commit. ChainDrop and Tensorlake were published through trusted publishing with valid provenance, because the attacker pushed to the branch that feeds the release workflow.
  • Leftover credentials keep attacks alive. Trivy (incomplete rotation), Ultralytics (a PyPI token kept after the move to trusted publishing) and Ledger (a former employee's npm access) all turned a contained incident into a second wave.
  • The fix order is publish path, then CI secrets, then consumption. Remove stored publishing tokens, scope and shorten CI credentials, protect release branches and tags, then delay, pin and script-block what you install.

What the report data shows

All figures in this section are from NHI Mgmt Group, The State of NHI & AI Agent Breach Report 2026 (October 2026), which covers breaches and credential exposures from November 2017 to October 2026. A breach can have more than one route and more than one identity type, so the counts overlap.

MeasureFigure in the reportWhat it means for defenders
Supply chain breaches51 of 200+ (23%), the #1 known route inPublishing and build identities deserve the same protection as production admin
Supply chain with CI/CD or publishing token34Registry and pipeline tokens are the single most abused asset in this route
Supply chain with secret or password25Payloads harvest whatever secrets the install host can read
Supply chain with source control token24GitHub PATs and bot tokens are the step before the publish
Supply chain with cloud credential18CI runners and developer laptops hold cloud keys worth stealing
Supply chain share of 2026 breaches38%The route is growing, not fading
AI-related breaches that came through supply chain13Agent skills, MCP servers and AI libraries are now part of the same route

The attack chain that repeats

Across the cases below, the same five steps appear. Most incidents use four or five of them. Each step is a place to break the chain.

  1. Get a foothold on the publish path. A privileged workflow runs untrusted code (Trivy, the second Shai-Hulud wave at PostHog, Ultralytics), a maintainer account is taken over (ChainDrop, OVHcloud CLI) or an old credential is still valid (Ledger).
  2. Take a publishing or bot identity. A GitHub PAT, a bot account token, an npm or PyPI token, or simply write access to the branch that a trusted publishing workflow builds from.
  3. Ship through the trusted channel. A release, a moved Action tag or a replaced release asset reaches users who trust the name. Valid provenance does not help when the source itself is malicious.
  4. Harvest machine credentials at install or run time. A preinstall hook or poisoned Action reads tokens, cloud keys, SSH keys, Vault and Kubernetes tokens, and in 2026 also AI tool configuration files.
  5. Reuse what was stolen to spread. Stolen registry and source control tokens publish the victim's own packages, plant workflows or seed the next compromise in the chain.

Cases and the identity that failed

BreachFootholdIdentity abusedThe control that would have broken the chain
Trivy Supply Chain Attack 2026A pull_request_target workflow ran fork code with repository privilegesA privileged GitHub PAT, the aqua-bot service account, an Open VSX token held by a former employeeNo fork code in privileged workflows; atomic rotation of every reachable credential, then proof the old ones fail
Shai-Hulud npm Worm 2025At PostHog, a pull_request_target workflow ran attacker code and exposed a bot PATnpm publishing tokens, GitHub PATs, Actions secrets, cloud credentialsTrusted publishing; single-package tokens; install scripts off on CI
ChainDrop npm Worm 2026Maintainer GitHub account taken over, push to main triggered the release workflowMaintainer account, OIDC trusted publishing, stolen npm tokens and PATsRequired review on release jobs and protected main; minimum release age for consumers
tj-actions/changed-files Compromise 2025Bot PAT leaked through a compromised reviewdog Action in tj-actions' own CI@tj-actions-bot PAT; runner secrets printed to public logsPin Actions to full commit SHAs; replace bot PATs with GitHub App tokens or OIDC
Ultralytics PyPI Compromise 2024Branch name injected into a shell step in a pull_request_target workflowA long-lived PyPI token never revoked after the move to trusted publishingDelete legacy tokens once trusted publishing works; pass untrusted values through environment variables
Ledger Connect Kit npm Compromise 2023A former employee was phishednpm access that was not revoked at departure, used via a session token or API key past 2FAOffboarding that covers external registries; organisation-owned, pipeline-only publishing
OVHcloud CLI Compromise 2026A contributor's GitHub account, method not knownRelease edit rights on the repositoryImmutable releases; release rights treated as privileged access
SolarWinds Supply Chain CompromiseCompromised credentials into the development environmentBuild and code-signing trust, SAML token-signing certificates, service principal credentialsHardened, isolated builds; HSM-held signing keys; alerts on new service principal credentials

The newer cases are faster versions of the old ones. SolarWinds needed months inside one build system. ChainDrop went from a poisoned commit at 09:02 UTC to 444 packages and 2,212 malicious versions by 13:20 UTC on 4 August 2026, according to StepSecurity. Tensorlake shows the same family returning in October 2026 with valid provenance; see the Tensorlake npm Worm 2026 page.

Lesson 1: remove stored publishing tokens

A long-lived registry token is a standing key to every user of a package. Trusted publishing replaces it with a short-lived token minted per workflow run through OpenID Connect.

  • npm: configure a trusted publisher naming the exact workflow (GitHub Actions on GitHub-hosted runners, GitLab CI/CD on GitLab.com shared runners, or CircleCI cloud), then set the package's Publishing access to "Require two-factor authentication and disallow tokens". npm's documentation notes self-hosted runners are not yet supported. See the npm Trusted Publishing and Provenance Guide.
  • PyPI: trusted publishing mints an API token that PyPI documents as valid for 15 minutes. After switching, delete every project-scoped and user-scoped token that could still upload, which is exactly what Ultralytics missed. See the PyPI API Tokens and Trusted Publishing Security Guide.
  • Where a token is unavoidable: one package, the shortest expiry the registry allows, organisation-owned, stored only in the protected release environment. See the npm Tokens Security Guide.
  • Offboarding: leavers lose registry, Open VSX, Marketplace and GitHub organisation rights the same day as SSO. Ledger and Trivy both had external publishing access outlive employment. See the NHI Offboarding and Decommissioning Guide.

Lesson 2: protect what feeds the publish path

Trusted publishing moves the target from the token to the branch, tag and workflow. ChainDrop's maintainer used OIDC for npm and one-time codes for GitHub, and the release still shipped with valid provenance.

  • Privileged triggers: GitHub's secure use reference says to avoid pull_request_target when it is not necessary and that such workflows must not check out untrusted code. Prefer workflow_run for privilege separation.
  • Script injection: pass untrusted context (branch names, titles) through an intermediate environment variable, never inline in a run: step.
  • Release gates: run publish jobs in a protected environment with required reviewers, protect main and release tags, require signed commits, and alert on force pushes and tag deletions.
  • Bot identities: replace bot PATs with GitHub App installation tokens or OIDC, set GITHUB_TOKEN to read-only by default and raise permissions per job.
  • Release assets: turn on immutable releases so assets cannot be swapped after the fact, as happened to OVHcloud CLI.
  • Build isolation: keep release jobs from restoring caches that less-trusted jobs can write. SLSA v1.2 Build L3 asks for strong isolation between runs and signing secrets kept away from user-defined build steps.

See the GitHub and GitHub Actions Security Guide and the CI/CD Pipeline Identity Security Guide.

Lesson 3: make CI secrets worth less

The payloads in Trivy, tj-actions, Shai-Hulud and ChainDrop read runner memory and well-known file paths. Whatever a job can see, the attacker gets.

  • Federate instead of storing: use OIDC to cloud providers and Vault for short-lived, well-scoped tokens per run. See the GitHub Actions OIDC Federation Hardening Guide.
  • Scope per job: no cloud or publishing secrets in jobs that install third-party packages or run tests on external pull requests.
  • One credential, one system: a CI credential that can publish every package or reach every account turns one leak into a campaign.
  • Logs are a leak path: tj-actions printed secrets into public workflow logs. Restrict log visibility and scan logs for secrets after any incident.

Lesson 4: consume defensively

  • Pin by digest: pin Actions to full-length commit SHAs from the action's own repository, not a fork. Tags were moved in Trivy (75 of 76 trivy-action tags per the breach page) and tj-actions.
  • Delay new versions: set a minimum release age for dependency updates. In several of these cases the malicious versions were flagged and pulled within hours, Tensorlake within about 11 minutes of publication.
  • Block install scripts: run npm ci --ignore-scripts in CI with an allow list for packages that need scripts. See the npm Install-Time Security Guide.
  • Verify, then trust: check hashes and attestations on downloaded binaries. A pinned SHA-256 in a customer's CI caught the OVHcloud swap, not the publisher.
  • Keep secrets off developer disks: move cloud keys and tokens out of .env files and tool configs into a secrets manager or short-lived credentials.

Decision guidance

SituationChooseTrade-off
You publish open-source packages from GitHub-hosted runnersTrusted publishing plus "disallow tokens"Branch and workflow protection become the security boundary
You publish from self-hosted runnersSingle-package, short-expiry token in a protected environment, rotated on a scheduleA stored secret remains; monitor every publish against pipeline runs
Small team, few packagesTrusted publishing, required reviewer on release, SHA pinning via Dependabot or RenovateRelease reviews slow emergency fixes
Large organisation, hundreds of repositoriesCentral inventory of publishing identities, organisation-level Actions allow list, policy-as-code on workflowsNeeds ownership for every package and bot

Implementation in four phases

  1. Week 1 to 2, inventory: list every package you publish, every identity that can publish it (people, bots, tokens, trusted publishers) and every workflow using pull_request_target or workflow_run. See the NHI Discovery Across Cloud, SaaS, CI/CD and On-Premises Environments.
  2. Week 2 to 4, close the doors: fix privileged triggers, set GITHUB_TOKEN read-only by default, pin Actions to SHAs, remove former staff from every registry. Test changes on one repository first.
  3. Month 2, change the publish path: enable trusted publishing per package, run one release through it, then disallow tokens and revoke the old ones. Roll back by re-enabling a single scoped token in the protected environment, never a personal one.
  4. Month 3, detect and rehearse: alert on publishes without a matching pipeline run, rehearse an atomic rotation of all CI and publishing credentials, and confirm old credentials fail. Use the Supply Chain Compromise Response Playbook.

Detection and measurement

  • Publish without a pipeline run: compare registry publish events with release workflow runs; any mismatch is an incident.
  • Tag and release changes: force pushes, tag moves and edits to existing release assets.
  • New install scripts: a release that adds a preinstall or postinstall hook.
  • Runner egress: connections from runners to unexpected domains or new public repositories created by your accounts.
  • Developer tool persistence: unexpected changes to .vscode/tasks.json or .claude/settings.json, both used by ChainDrop.

Metrics that show progress:

  • Percentage of published packages using trusted publishing with tokens disallowed.
  • Number of long-lived registry and bot tokens remaining, and their maximum age.
  • Percentage of third-party Actions pinned to a full commit SHA.
  • Workflows using pull_request_target with secret access (target: zero).
  • Time to revoke all publishing access for a leaver.
  • Time from a rotation drill start to proof that every old credential fails.

Framework mapping

LessonMITRE ATT&CKNIST SP 800-53 Rev 5NIST CSF 2.0CIS Controls v8.1
Poisoned packages and ActionsT1195.001, T1195.002SR-3, SR-4, SR-11, CM-14GV.SC-05, GV.SC-0716 Application Software Security
Stolen publishing and bot tokensT1528, T1550.001IA-5, AC-6PR.AA-01, PR.AA-055 Account Management, 6 Access Control Management
Secrets harvested from hostsT1552.001, T1552.004IA-5, SC-12PR.AA-013 Data Protection
Hijacked maintainer and leaver accountsT1078, T1586AC-2, IA-2PR.AA-035 Account Management
Build system integrityT1195.002SA-10, SA-15DE.CM-0916 Application Software Security

ISO/IEC 27001:2022 Annex A controls 5.21 (managing information security in the ICT supply chain), 5.17 (authentication information) and 8.32 (change management) cover the same ground.

Common failure patterns

  • Rotating most credentials: Trivy's first rotation was not comprehensive and residual access let the attacker return weeks later.
  • Adding trusted publishing without removing tokens: the Ultralytics second wave used a token kept after the switch.
  • Offboarding only the identity provider: Ledger's former employee still had npm rights.
  • Treating provenance as proof of safety: ChainDrop and Tensorlake releases carried valid provenance.
  • Pinning to tags: moved tags delivered stealers in Trivy and tj-actions.
  • Revoking before cleaning: Shai-Hulud tried to wipe the home directory when it could not authenticate, and the ChainDrop and Tensorlake monitors can wipe when a watched token is revoked, so remove persistence first, then revoke from a clean machine. See the Leaked Credential Response Playbook.

Practitioner checklist

  • Inventory every package, its publishing identities and their owners.
  • Enable trusted publishing and disallow token publishing on every package that supports it.
  • Delete every legacy registry token after the first successful trusted publish.
  • Remove pull_request_target from workflows with secret access, or prove they never check out untrusted code.
  • Set GITHUB_TOKEN to read-only by default and replace bot PATs with GitHub App tokens or OIDC.
  • Pin third-party Actions to full commit SHAs and keep an organisation allow list.
  • Require reviewers on release environments, protect release tags and turn on immutable releases.
  • Block install scripts in CI and set a minimum release age for new dependency versions.
  • Remove leavers from registries and marketplaces on the same day as SSO.
  • Rehearse an all-at-once rotation and verify old credentials fail.

Standards and references

Related NHI Mgmt Group resources: Software Supply Chain Security Guide · CI/CD Pipeline Compromise Playbook · Shai-Hulud npm Worm Guide · Non-Human Identity and Secrets Statistics Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 28 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org