Fast Go scanner for known supply-chain compromise exposure across npm, PyPI, lockfiles, installed dependencies, and host IOCs. Built for incident response triage — answer one question quickly: "was this machine or repository exposed to a known compromise?"
It reports two things:
findings— confirmed compromised versions or IOC evidence. Exit code1.usages— every place a tracked package is referenced, even when the observed version is currently safe. This is your triage context: it tells you which projects use a package one patch away from a known compromise.
- npm packages — manifests and lockfiles:
package.json,package-lock.json,npm-shrinkwrap.json,yarn.lock,pnpm-lock.yaml,bun.lock,bun.lockb. - Installed Node dependencies across multiple linker layouts:
- flat
node_modules/<pkg>/(npm, yarn classic, bun hoisted), - scoped
node_modules/@scope/<pkg>/, - nested
node_modules/<outer>/node_modules/<inner>/(non-hoisted), - pnpm isolated
node_modules/.pnpm/<pkg>@<ver>/node_modules/<pkg>/, - bun isolated
node_modules/.bun/<pkg>@<ver>/node_modules/<pkg>/, - symlinks pointing to a global store (resolved only for tracked names),
- Yarn Berry PnP archives under
.yarn/cache/*.zip(filename parsing).
- flat
- Python packages — manifests and lockfiles:
requirements*.txt,constraints.txt,pyproject.toml,uv.lock,poetry.lock,Pipfile,Pipfile.lock,setup.py,setup.cfg. - Installed Python distributions via
METADATA/PKG-INFOinside virtualenvs andsite-packages/. - Basic host IOCs published by researchers for macOS, Linux, and Windows.
The current incident database is documented in docs/COVERAGE.md.
Roadmap (GitHub Actions, container images, Go modules, deeper Linux host
posture checks) lives in ROADMAP.md.
Beyond package fingerprints, the scanner can also check the host itself for known-bad security postures and report concrete remediation steps. This is intended for incident-response triage: "which of my Linux hosts is actually exposed to the recent kernel CVE wave?"
# Run only the host checks (no filesystem scan).
./supplychainchecker -host-checks-only
# Run both: package fingerprint scan AND host posture checks.
./supplychainchecker -root /opt -host-checksCoverage today (Linux only, expanding):
| Check ID | Severity | Detects |
|---|---|---|
linux-kernel-copy-fail-CVE-2026-31431 |
critical | running kernel below upstream fix; suggests modprobe blacklist of algif_* if unpatched |
linux-kernel-dirty-frag-CVE-2026-43284-43500 |
critical | running kernel below upstream fix; severity rebaixada para medium quando detecta modprobe blacklist de esp4/esp6/rxrpc |
linux-pending-reboot |
medium | newer kernel installed but not booted yet |
linux-ssh-password-auth-enabled |
medium | PasswordAuthentication yes in effective sshd config (parses sshd_config + sshd_config.d/*.conf) |
linux-unattended-upgrades-disabled |
medium | Debian/Ubuntu only — package missing, or APT::Periodic::Unattended-Upgrade not set |
linux-sudoers-integrity |
high | world-writable sudoers files; suspicious NOPASSWD: entries on regular users |
Each finding ships with evidence (what the scanner saw) and remediation
(commands the operator can run). On non-Linux hosts the scanner returns a
single "platform not supported" info entry — coverage for macOS and Windows
posture is on the roadmap.
- Not an SCA replacement (Snyk / Dependabot / Trivy).
- Not an SBOM generator.
- Not an antivirus.
- Not a generic vulnerability scanner.
- It does not rotate secrets or remove packages. Remediation is suggested, never executed.
go build -o supplychainchecker .Cross-compile:
GOOS=linux GOARCH=amd64 go build -o supplychainchecker-linux-amd64 .
GOOS=linux GOARCH=arm64 go build -o supplychainchecker-linux-arm64 .
GOOS=darwin GOARCH=arm64 go build -o supplychainchecker-darwin-arm64 .
GOOS=darwin GOARCH=amd64 go build -o supplychainchecker-darwin-amd64 .
GOOS=freebsd GOARCH=amd64 go build -o supplychainchecker-freebsd-amd64 .
GOOS=windows GOARCH=amd64 go build -o supplychainchecker.exe .By default, it scans the current user's home directory.
./supplychaincheckerScan specific roots:
./supplychainchecker -root ~/DEV -root ~/DocumentsJSON output (recommended for piping into other tools):
./supplychainchecker -root ~/DEV -jsonScan project files only, skip host-level IOC checks:
./supplychainchecker -root ~/DEV -no-iocSkip noisy directories:
./supplychainchecker -root ~/DEV -skip-dir vendor -skip-dir archiveWindows:
.\supplychainchecker.exe -root C:\Users\me\source -root D:\reposIf you want to scan HOME without descending into noisy folders, drop a
.checkignore file in the scanned root.
Example ~/.checkignore:
# ignored at any level
Library
.Trash
# ignored only at this relative path
Applications
Downloads
DEV/archive
Rules:
- Empty lines and comments (
#) are ignored. - A simple name (
Library) ignores any directory or file with that basename. - A path with
/(DEV/archive) ignores that prefix relative to the root. - Customize the file name with
-ignore-file.
0— nothing suspicious found.1— at least one finding (potential exposure).2— fatal runtime error.
CI tip: pipe -json into jq to gate a build on findings of a chosen severity
(once severity lands in v0.2).
- A
package.jsonwith^1.14.1or~1.14.1is a sign of risk, but does not prove installation — manifests express ranges. - A lockfile or
node_modules/<pkg>/package.jsonpinned to a compromised version is strong evidence of exposure. - A Python manifest pinning
litellm==1.82.7is a sign of risk; alitellm-1.82.7.dist-info/METADATAunder a venv'ssite-packages/is strong evidence. - Manifest-only references can show as
version=unknownbecause the declared spec alone does not always pin the installed version. - "Safe usage" is reported on purpose — it lets you spot projects sitting one patch away from a known compromise.
- If you find that this machine installed a compromised version inside the attack window, treat the environment as potentially compromised and rotate secrets accordingly.
The repo ships synthetic, inert fixtures under test/fixtures/ that
simulate the on-disk fingerprint of every covered incident — package names,
versions, lockfile entries, dist-info/METADATA headers — but contain zero
executable code. CI gates this: scripts/verify-fixtures-safe.sh rejects any
PR that introduces a *.py/*.js/*.sh/*.so file or sets an executable
bit inside that tree.
go test ./... -run TestEndToEndAgainstFixtures -vThis runs the scanner against test/fixtures/ and asserts that every
compromised package is detected and that safe-controls/ produces zero
findings.
A demo image is provided that contains only the scanner binary and the inert
fixtures, on a FROM scratch base — no shell, no libc, no package manager.
Combined with the runtime flags below, it's safe to run on any machine:
docker build -f docker/Dockerfile.demo -t supplychainchecker-demo .
docker run --rm \
--network=none \
--read-only \
--cap-drop=ALL \
--security-opt=no-new-privileges:true \
--pids-limit=64 \
--memory=256m \
supplychainchecker-demo--network=none is the critical safeguard. The image is ~3 MB and exits with
code 1 when it detects the seeded fixtures (which is the expected, intended
result — it proves the scanner works).
⚠️ Never runnpm install/pnpm install/pip install/uv sync/poetry installinsidetest/fixtures/. Some of the seeded incidents are wormable on install. The fixtures are designed to fail any install attempt (nosetup.py, nobin, no install hooks;pyproject.tomlpoints to a*.invalidindex URL), but that is defense-in-depth — the contract is "these are read-only metadata files, treat them as such."
The synthetic fixtures reference real compromised versions (axios@1.14.1,
litellm@1.82.7, etc.). To stop GitHub Dependabot, OSV-Scanner, Trivy, Snyk,
and CodeQL from treating those as real dependencies, the repo ships:
| File | Effect |
|---|---|
.gitattributes |
marks test/fixtures/** as linguist-vendored + linguist-generated. GitHub language stats, CodeQL, and Dependabot all respect these markers. |
.github/dependabot.yml |
only declares the project's real ecosystems (gomod, github-actions). The fixtures' npm/PyPI manifests are simply never visited. |
osv-scanner.toml |
tells OSV-Scanner to ignore the fixtures subtree. |
.trivyignore |
tells Trivy the same. |
.snyk |
excludes the fixtures from Snyk. |
If you fork this repo and use a different SCA tool, you may need to add an
equivalent exclude rule for that tool. See test/fixtures/README.md for
context.
New incidents live in incidents.go. To add a confirmed supply-chain case, add
one entry with:
EcosystemPackage- compromised
Versions - a short
Summary
For broader contributions (parsers for new ecosystems, host checks, IOC
expansions), see ROADMAP.md and open an issue using the
appropriate template.
- Bug reports, false positives, false negatives → issues.
- Incident coverage requests → use the
incident-coverageissue template (planned in v0.2). - Security issues → see
SECURITY.md.