VNX-SEC-029 – PyPI Upload Token Hardcoded
Overview
This rule flags source files containing a string that matches the PyPI upload token format: the prefix pypi-AgEIcHlwaS5vcmc (which is the base64 encoding of the PyPI Macaroon header) followed by at least 50 additional base64url characters. PyPI upload tokens authenticate the twine upload and pip publish commands. They are scoped either to all packages owned by an account or to a specific project, but any valid token allows publishing new versions to the associated packages.
Publishing a malicious version of an existing package is one of the most impactful supply chain attacks available. Python packages are executed during installation (via setup.py) and at import time, giving an attacker arbitrary code execution on every machine that installs the compromised version — including developer workstations, build servers, and production containers.
This rule corresponds to CWE-798: Use of Hard-coded Credentials.
Severity: High | CWE: CWE-798 – Use of Hard-coded Credentials
Why This Matters
The Python package ecosystem is particularly exposed to supply chain attacks because of how widely packages are consumed and how automatically updates are applied. A hardcoded PyPI token gives an attacker the ability to publish a new version of any package the token has access to, with code that runs during pip install via setup.py entry points or during normal application use.
High-profile supply chain attacks against popular packages have demonstrated that even packages with millions of weekly downloads can be compromised through credential theft. Automated dependency update tools like Dependabot and Renovate can cause malicious versions to be pulled into downstream projects within hours.
PyPI tokens are also particularly dangerous because, unlike some credentials, they are long-lived by default and do not expire unless explicitly revoked. A token embedded in a Dockerfile or CI configuration file that was public even briefly could have been harvested and retained by a malicious actor.
What Gets Flagged
The rule matches any file (excluding lock files, checksums, and minified assets) containing a string matching the PyPI Macaroon token prefix followed by a sufficient-length suffix.
# FLAGGED: token hardcoded in .pypirc
[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
# FLAGGED: token in a CI environment file
TWINE_PASSWORD=pypi-AgEIcHlwaS5vcmcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Remediation
Revoke the token immediately at pypi.org/manage/account/token. Delete the exposed token, then audit recent package releases for unexpected versions.
Use Trusted Publishing (OIDC) for CI/CD. PyPI’s Trusted Publishing feature allows GitHub Actions, GitLab CI, and Google Cloud Build to publish without any long-lived credentials. The publisher is authenticated via short-lived OIDC tokens that cannot be stolen from source code:
# SAFE: GitHub Actions Trusted Publishing — no token required
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# No password needed — authentication via OIDC
- Use environment variables for token-based publishing. If Trusted Publishing is not available, load the token from a CI secret rather than hardcoding it:
# SAFE: token injected from CI secret
TWINE_USERNAME=__token__ TWINE_PASSWORD="$PYPI_TOKEN" twine upload dist/*
- Scope tokens to specific projects. When creating a new token at pypi.org, scope it to only the specific project it needs to publish rather than granting account-wide access.