pwned-deps is itself a piece of supply-chain security software.
The same hygiene we ask of our users, we apply to ourselves.
Please do not open a public GitHub issue for security reports.
Use one of the following private channels:
- GitHub private vulnerability reporting — preferred: https://github.com/mkbhardwas12/pwned-deps/security/advisories/new
- Email —
mkbhardwas12@users.noreply.github.com(GitHub-routed noreply; messages are forwarded to the maintainer).
Please include:
- A description of the issue and its impact.
- A minimal reproducer as text — never as an attached
package archive (
.tgz,.whl,.zip). See CONTRIBUTING.md §"PoC handling". - Your assessment of severity (low / medium / high / critical).
- The version of
pwned-depsyou tested against (pwned-deps version).
We aim for the following timeline. Solo-OSS-friendly: triage may take up to 7 days, not 24 hours.
| Step | Target |
|---|---|
| Acknowledgement | within 7 days |
| Triage + reproduction | within 14 days |
| Fix + release on PyPI | within 90 days of triage |
| Public advisory + GHSA | concurrent with release |
If a coordinated disclosure window other than 90 days suits the reporter (e.g. embargoed industry coordination), please say so in the report and we will negotiate.
In scope:
- The published PyPI package
pwned-deps. - The repository at https://github.com/mkbhardwas12/pwned-deps,
including
release.yml/ci.yml,action.ymland the dev container. - The bundled
extras.jsoncampaign feed (false positives, fabricated entries, signature bypass) and the publish-timestamp resolution logic that promotes SUSPECT hits to CONFIRMED (a way to make it clear a genuinely compromised version would be in scope).
Out of scope (please don't report):
- Vulnerabilities in upstream dependencies that we already pin
(
requirements.lock) — report those upstream. We surface them via dogfooding (pwned-deps check ./requirements.lock). - The accuracy of OSV.dev advisories themselves — report those to https://github.com/google/osv.dev.
- Social-engineering of the maintainer's GitHub or PyPI accounts — hardware-key 2FA + OIDC trusted publishing is in place; please report account-takeover concerns to GitHub / PyPI directly.
-
No long-lived publishing tokens. PyPI publishes via OIDC trusted publishers. There is no
PYPI_API_TOKENin repository secrets. -
Hardware-key 2FA on the maintainer's GitHub and PyPI accounts.
-
SLSA Level 3 build provenance on every released artifact via the
slsa-github-generatorworkflow. Provenance generation is a hard gate (the release fails if it cannot be produced) and the release workflow runsslsa-verifieragainst every artifact before the PyPI upload. Verify yourself with:pip download --no-deps pwned-deps slsa-verifier verify-artifact pwned_deps-*.whl \ --provenance-path pwned-deps-vX.Y.Z.intoto.jsonl \ --source-uri github.com/mkbhardwas12/pwned-deps \ --source-tag vX.Y.Z -
SHA-pinned GitHub Actions. Every third-party action in
.github/workflows/andaction.ymlis pinned to a full commit SHA (Dependabot keeps them current). The one exception is the SLSA reusable workflow, which must be referenced by tag forslsa-verifierto accept the builder ID. -
Composite action hardening.
action.ymlpasses every input to the shell throughenv:, never by interpolating${{ inputs.* }}into a script, and installs the exactpwned-depsrelease that matches the action tag rather than an unpinned "latest". -
No
eval/exec/subprocess/pickle.loadof input content. Enforced bymake verify-safety(negative self-test proves the regex catches a plantedeval()). -
Locked-down dev container.
make testruns with--network none --read-only, source mounted read-only, base image pinned by SHA-256 digest. -
Pinned + hashed dependencies (
requirements.lockgenerated withpip-compile --generate-hashes). -
Dogfood gate on release.
release.ymlrunspwned-deps check ./pyproject.toml ./requirements.lockagainst the built wheel before publishing. Exit 1 (malicious) blocks the release; so do exit 3 (parse error) and exit 4 (incomplete scan) — an unchecked dependency is not a clean dependency. -
Signed campaign feed (Sigstore + Rekor). Two signing events:
- Every push to
mainthat changessrc/pwned_deps/extras_data/extras.jsontriggers.github/workflows/sign-feed.yml(interim bundle kept as a 90-day workflow artifact). - Every tagged release re-signs the exact feed that ships in the
wheel and attaches
extras-vX.Y.Z.json,.sha256and the sigstore bundle to the GitHub Release — the durable copy.
Both are keyless (GitHub Actions OIDC identity) and logged to the public Rekor transparency log; nobody can silently rewrite the project's campaign history without leaving an auditable trail.
- Every push to
The bundled feed (src/pwned_deps/extras_data/extras.json, also
shipped inside the wheel at pwned_deps/extras_data/extras.json) is
signed at every release. Verify with sigstore-python:
pip install "sigstore>=3.6,<4"
# 1. From the GitHub Release for the version you installed, download
# extras-vX.Y.Z.json.sigstore.json (the bundle). Compare the feed
# you actually have against the one that was signed:
python -c 'import pwned_deps.extras_data, pathlib, hashlib; p = pathlib.Path(pwned_deps.extras_data.__file__).with_name("extras.json"); print(hashlib.sha256(p.read_bytes()).hexdigest())'
# ...must equal the digest in extras-vX.Y.Z.json.sha256.
# 2. Verify the bundle was produced by THIS repository's release
# workflow, at THAT tag, via GitHub's OIDC issuer:
python -m sigstore verify identity \
--bundle extras-vX.Y.Z.json.sigstore.json \
--cert-identity 'https://github.com/mkbhardwas12/pwned-deps/.github/workflows/release.yml@refs/tags/vX.Y.Z' \
--cert-oidc-issuer 'https://token.actions.githubusercontent.com' \
<path-to-your-installed-extras.json>For an unreleased commit on main, the interim bundle from the
matching sign-feed.yml run (Actions → "Sign feed" → Artifacts →
extras-json-sigstore-bundle) verifies the same way with
--cert-identity '…/.github/workflows/sign-feed.yml@refs/heads/main'.
A passing run proves the file content was signed by this repo's GitHub Actions identity. To audit the full history of feed changes (including any silently-removed campaigns), query the Rekor transparency log directly:
# Install rekor-cli once: https://docs.sigstore.dev/system_config/installation/
rekor-cli search --sha $(sha256sum src/pwned_deps/extras_data/extras.json | awk '{print $1}')Rekor is append-only. Force-pushes, account takeovers, and revisionist history cannot remove a Rekor entry — only add new ones that anyone can spot.
pyproject.tomlprintingskipping … not a recognised lockfile shapeis intentional. Manifests are not lockfiles.- The dogfood scan currently surfaces 1 LOW/MEDIUM informational finding (pytest GHSA-6w46-j5rx-g56g). Tracked, non-blocking.