VNX-SEC-010: Package registry token
Detects npm access tokens (npm_ + 36 characters), PyPI and TestPyPI API tokens, literal auth tokens in .npmrc and .yarnrc.yml (npmAuthToken, npmAuthIdent) and literal .pypirc passwords, in any file outside tests; environment references and npm_config_* identifiers are not reported.
| Field | Value |
|---|---|
| Rule ID | VNX-SEC-010 |
| Severity | Critical |
| SARIF level | error |
| Kind | secrets |
| Languages | Any file |
| CWE | CWE-798 |
| CAPEC | CAPEC-191 |
| ATT&CK | T1195.002 |
| CWSS vector | CWSS:1.0/TI:C/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:C/DI:H/EX:H/EC:N/P:W |
| Tags | secrets npm pypi supply-chain credentials |
| Source | internal/sast/rules/vnx-sec-010.rego |
Overview
This rule detects npm access tokens matching npm_[0-9a-zA-Z]{36} and PyPI upload tokens matching pypi-AgEIcHlwaS5vcmc[0-9A-Za-z\-_]{50,} (and the TestPyPI pypi-AgENdGVzdC5weXBpLm9yZw form) in any file, plus a literal _authToken= / _auth= / _password= value in .npmrc , npmAuthToken: or npmAuthIdent: in .yarnrc.yml, and a literal password = in .pypirc. Values read from the environment (${NPM_TOKEN}), placeholders, identifiers such as npm_config_registry, and test, fixture and vendored paths are not reported. These tokens authenticate publish operations to package registries. A compromised publish token allows an attacker to release a malicious version of any package the token owner controls, potentially affecting every downstream user of that package, a textbook supply chain attack mapped to MITRE ATT&CK T1195.002.
Why This Matters
Package registry tokens are among the most dangerous credentials to expose because their impact extends far beyond the token owner. A single leaked npm publish token can be used to inject malicious code into a package that is downloaded by thousands or millions of developers and end users. The 2021 ua-parser-js and coa hijacking incidents demonstrated exactly this attack: malicious versions containing credential stealers were published to npm and downloaded hundreds of thousands of times before detection.
PyPI tokens similarly control package distribution for the Python ecosystem. A token with upload access to a popular package can introduce a backdoor that persists across any application that installs the package.
npm token types and their risk:
- Automation tokens (
npm_...): Bypass 2FA for CI/CD use, highest publish risk - Publish tokens: Require 2FA for interactive use but allow publishing
- Read-only tokens: Lower risk but still expose package metadata
What Gets Flagged
# FLAGGED: npm token hardcoded in .npmrc
//registry.npmjs.org/:_authToken=npm_EXAMPLE_REDACTED
# FLAGGED: PyPI token in setup script or CI config
PYPI_TOKEN = "pypi-EXAMPLE_REDACTED"
# FLAGGED: npm token in workflow file
- name: Publish
run: npm publish
env:
NODE_AUTH_TOKEN: npm_EXAMPLE_REDACTED
Remediation
-
Revoke the token immediately. For npm: go to npmjs.com → Account Settings → Access Tokens → find the token → Delete. For PyPI: go to pypi.org → Account Settings → API tokens → Revoke.
-
Audit for unauthorized package publishes. Check the package’s version history on the registry to verify no unexpected versions were released. For npm:
npm view <package> time. For PyPI: check the package’s release history on pypi.org. -
Remove from source code. Use environment variable substitution in
.npmrc:
# SAFE: .npmrc using environment variable (commit this file)
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
# SAFE: GitHub Actions workflow using secrets
- name: Publish to npm
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# SAFE: PyPI publish using Twine with environment variable
export TWINE_USERNAME=__token__
export TWINE_PASSWORD="${PYPI_TOKEN}"
twine upload dist/*
- Use OIDC-based trusted publishing for PyPI. GitHub Actions, GitLab CI, and other CI providers can publish to PyPI without storing a token at all using OpenID Connect trusted publishing:
# SAFE: PyPI trusted publishing via GitHub Actions OIDC (no token needed)
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
attestations: true
-
Create scoped tokens for CI/CD. For npm, create an automation token with the minimum required scope. For npm packages, consider using
npm token create --cidr=203.0.113.0/24to restrict usage by IP. -
Enable npm package provenance. npm supports SLSA-level package provenance linked to GitHub Actions workflows, making it much harder to publish malicious packages from outside your trusted CI environment.
-
Scan git history for exposed tokens:
gitleaks detect --source . --verbose
git filter-repo --replace-text <(echo 'npm_xxxx==>REDACTED_NPM_TOKEN')
References
- CWE-798: Use of Hard-coded Credentials
- npm: Access tokens documentation
- PyPI: API tokens
- PyPI: Trusted publishers
- npm: Package provenance
- OWASP: Software Supply Chain Security
- MITRE ATT&CK T1195.002 – Compromise Software Supply Chain
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-sec-010
// nosec vnx-sec-010
Or record an ignore rule: vulnetix ignore add --rule vnx-sec-010 --reason "...". See Suppressing findings.