GH Actions - npm Supply Chain Abuse
Overview
After an attacker gets code execution in a GitHub Actions release workflow, maintainer workstation, or package build pipeline, npm publishing becomes a high-impact pivot. The goal is usually to steal publisher identity material, publish malicious versions, and turn downstream installs into more credential-generation nodes.[1]
Typical credential sources:
~/.npmrc,NPM_TOKEN, registry sessions, and npm automation tokens.[1][2]- GitHub PATs,
GITHUB_TOKEN, release-bot credentials, SSH keys, and.netrc/ git credential helpers.[1] - GitHub Actions OIDC request material (
ACTIONS_ID_TOKEN_REQUEST_URLandACTIONS_ID_TOKEN_REQUEST_TOKEN) in jobs withid-token: write.[8] - Cloud credentials, Vault tokens, Kubernetes service account tokens, and
.envfiles present in the release environment.[1]
Install-Time Execution Primitives
Lifecycle hooks
The classic npm route is to publish a malicious package version with preinstall, install, postinstall, or prepare scripts. Any developer workstation or CI job that installs the version executes attacker-controlled code.[6]
{
"scripts": {
"postinstall": "node ./scripts/collect.js"
}
}
Defenders often monitor these scripts, so red-team reviews should also inspect less obvious execution paths.[11]
binding.gyp / node-gyp execution (Phantom Gyp)
Not every install-time execution path lives in package.json lifecycle hooks. node-gyp's configure step looks for a binding.gyp file in the package directory, so a compromised publisher can shift execution into the native build path and bypass controls that only audit preinstall / postinstall.[5][6][11]
Practical checks:
- Inspect the published tarball, not only the Git repo, for unexpected
binding.gyp,node-gyp, or native-addon metadata in packages that should be pure JavaScript.[5][11] - Treat a sudden
binding.gypaddition as an execution primitive, especially if defenders rely on lifecycle-hook monitoring or--ignore-scripts.[6][10][11] - Review release jobs that run
npm install,npm rebuild, or dependency build steps after restoring untrusted artifacts/caches.[2][11]
Wormable npm Publishing
Once code runs in a maintainer workstation or release workflow, a single stolen registry identity can be turned into self-propagating package compromise:[1]
- Harvest maintainer secrets (
~/.npmrc, PATs, OIDC request env vars, cloud creds, SSH keys).[1] - Enumerate packages the compromised identity or team can publish to.[1][7]
- Republish malicious versions across each writable package.[1]
- Let downstream installs create more credential-generation nodes.[1]
Useful enumeration from a compromised npm identity:[7]
npm whoami
npm access ls-packages
npm access ls-collaborators <scope-or-package>
Attackers usually prefer packages with frequent CI installs, transitive popularity, or release automation that will install the malicious version quickly.
Trusted Publishing and Provenance Limits
Trusted publishing/OIDC removes long-lived static npm tokens, but it does not make a compromised release workflow safe. If the attacker controls code that runs in a job with id-token: write, the malicious release can still receive valid provenance because the legitimate workflow really built and published it.[1][2][8]
Provenance answers which workflow built this artifact, not whether the workflow, source tree, cache, or build steps were clean.[1][2]
High-signal review points:
- Workflows combining
id-token: writewithnpm publish,pnpm publish,changesets, release bots, or custom publish wrappers.[2][8] - Release jobs that restore caches or artifacts from lower-trust workflows before publishing.[2]
- Jobs that publish without human approval, environment protection rules, or a second reviewer.[2][3]
- Workflows that request OIDC before all build inputs have been verified.[2][8]
Hardening
- Use trusted publishing/OIDC instead of static npm tokens, but pair it with protected environments and human approval for sensitive scopes.[2][3]
- Add staged publishing / human 2FA approval for high-impact packages where possible.[3]
- Use
minimumReleaseAgeor equivalent dependency quarantine controls before consuming newly published package versions.[1][9] - Separate cache keys by trust boundary and never execute restored cache contents before integrity checks.[1][2]
- Diff published tarballs against source repositories, and alert on unexpected native build metadata such as
binding.gyp.[5][11] - Disable or tightly review lifecycle scripts in CI (
npm config set ignore-scripts true) where builds do not need them.[1][6][10] - Monitor package access (
npm access ls-packages) and remove stale maintainers, bots, and teams.[7][12]
References
- [1] What the Miasma campaign reveals about the new supply chain threat model and the underground market for developer credentials
- [2] Trusted publishing for npm packages | npm Docs
- [3] Staged publishing for npm packages | npm Docs
- [4] npm orgs | npm Docs
- [5] node-gyp README
- [6] Scripts | npm Docs
- [7] npm-access | npm Docs (v6)
- [8] OpenID Connect reference | GitHub Docs
- [9] Config | npm Docs
- [10] npm-install | npm Docs
- [11] Miasma npm Supply Chain Attack: Self-Spreading Worm via Phantom Gyp | StepSecurity
- [12] Organizations | npm Docs
[!TIP] Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Browse the full HackTricks Training catalog.Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.


