vendorcheck
Is the Python library bundled inside your app, add-on or extension byte-for-byte the official PyPI release?
Point it at a folder or an archive (.zip, .xpi, .vsix, .fda, .whl, any app bundle that is a zip). It finds the embedded Python packages, downloads the official release from PyPI, verifies the download against PyPI's published SHA-256, and compares file by file.
$ vendorcheck lib/ -v # idna with one file edited and one file added
idna 3.7: 8 files identical, 1 modified, 1 extra.
[modified] idna/core.py
[extra] idna/evil.py
Real run on a clean pip install --target of yt-dlp:
$ vendorcheck lib/
yt_dlp 2026.8.19: 1049 files identical, 0 modified, 0 extra.
Why
Software supply-chain attacks often hide in copies of popular libraries: a vendored package that was quietly patched, or has an extra file dropped in. Lockfiles, pip-audit and signature checks look at what PyPI serves, not at the copy shipped inside your artifact. vendorcheck closes that gap.
Install
pip install git+https://github.com/Ferdi-krbk/vendorcheck
# or just download vendorcheck.py - it is a single file, stdlib only, Python 3.9+
Usage
vendorcheck PATH [-v | -vv] [--json] [--lang en|tr] [--only NAME ...] [--ignore GLOB ...]
[--no-nested] [--cache DIR] [--allow-empty]
| Option | Meaning |
|---|---|
-v |
list every modified / extra / missing file |
-vv |
additionally print a unified diff (official → embedded) for modified text files |
--json |
machine-readable output (includes diffs with -vv) |
--only NAME ... |
check just these packages |
--ignore GLOB |
skip files matching the glob (relative to the package dir, e.g. yt_dlp/version.py); repeatable. For intentional local patches |
--no-nested |
don't open archives found inside the input |
--cache DIR |
reuse downloaded official releases |
--allow-empty |
exit 0 if no embedded package is found (default: exit 2, so CI can't pass by checking nothing) |
--lang tr |
Turkish output |
Archives nested inside the input (.zip .whl .egg .xpi .vsix .fda .pyz, up to 2 levels, 2 GiB guard) are opened automatically and reported as pkg 1.0 [inner.xpi!/].
Exit codes: 0 identical · 1 differences found · 2 error / nothing checked.
In GitHub Actions the result is also written to the job's step summary as a table.
GitHub Action
- uses: actions/checkout@v4
- uses: Ferdi-krbk/vendorcheck@v1
with:
path: build/addon # folder or .xpi/.vsix/.zip
# only: "yt-dlp requests"
The job fails when embedded code differs from the official release. The repo's own CI proves it: a clean vendored tree must pass and a tampered one must fail.
How it works
- Detect packages through
*.dist-info/METADATA,*.egg-info/PKG-INFO, or<pkg>/version.py(__version__ = "..."). - Fetch
https://pypi.org/pypi/<name>/<version>/json, prefer a purepy3-none-anywheel, fall back to the sdist, and verify its SHA-256 against PyPI's digest. - Compare each file's SHA-256. Line-ending-only differences (CRLF vs LF) are tolerated;
__pycache__and.pycare ignored. - Report identical / modified / extra (and missing) files.
Archives are extracted with zip-slip protection; nothing from the audited artifact is ever executed.
Limits (v1)
- Only packages with version metadata (see above). Guessing name/version for bare copies is out of scope.
- If PyPI only has platform wheels or an sdist, generated or compiled files show up as "extra".
- Archives nested deeper than 2 levels are not opened.
- It proves your copy equals what PyPI serves, not that the PyPI release itself is trustworthy. Pair it with
pip-auditand provenance tools liketrustcheck.
Related
- trustcheck verifies provenance/signatures of PyPI releases, not embedded code.
- vendoring automates creating vendored trees;
vendorcheckaudits them.
License
MIT
Metadata
Release files for vendorcheck 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vendorcheck-0.2.0.tar.gz | 10.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vendorcheck-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.2 kB
Release files / vendorcheck-0.2.0.tar.gz
| Download URL | vendorcheck-0.2.0.tar.gz |
|---|---|
| Size | 10.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a7b4aca4d6963c2dad55ac7ce519e8286c2eee6f9f845514ad705c837fcacc56
|
|
BLAKE2b-256 checksum How to use checksums |
82d7d8609b035f8409139c9dd0ea890c1e74e2116cdf897299c4f20b2e7529e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.0
|
Release files / vendorcheck-0.2.0-py3-none-any.whl
| Download URL | vendorcheck-0.2.0-py3-none-any.whl |
|---|---|
| Size | 11.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
23d78a745d78f558b9603c7dde953ceb666e6f0105f1eee04d89042cb5503ab9
|
|
BLAKE2b-256 checksum How to use checksums |
2251a70d78bab72919d9f8fcca274e8a9514e3f5a1dfe2617ccef9b592607639
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.0
|