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 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.
Real-world origin story: Auditing an extension
vendorcheck was built out of a real supply-chain audit: inspecting a popular Free Download Manager (FDM) extension package (Elephant.fda) that embedded its own Python runtime and bundled libraries like yt-dlp.
Instead of manually unzipping releases and writing one-off scripts, vendorcheck automates inspecting whole directories or extension packages in seconds:
$ vendorcheck Elephant.fda -v
yt_dlp 2026.8.19 [python/lib/site-packages!/]: 1049 files identical, 0 modified, 0 extra.
How it works
- Detect embedded packages through:
*.dist-info/METADATAor*.egg-info/PKG-INFOversion.py,_version.py, or__init__.py(__version__ = "...")
- Fetch official release metadata from PyPI:
- Prefers pure Python wheels (
-none-any.whl). - Intelligently matches compiled platform wheels for the host OS/Python (via
packaging.tags). - Falls back to official source distributions (
sdist). - Validates cryptographic SHA-256 against PyPI's published digest.
- Prefers pure Python wheels (
- Compare each file's SHA-256 digest:
- Normalizes text line endings (
CRLFvsLFgit differences won't falsely flag). - Automatically ignores
__pycache__and bytecode (.pyc). - Computes unified diffs (
-vv) for modified code.
- Normalizes text line endings (
- Report identical, modified, extra, or missing files.
Archives are extracted safely with strict directory traversal (zip-slip) protection; zero audited untrusted code is executed.
Limits
- Packages completely stripped of all version indicators require metadata or a version attribute.
- Unrecognized packages with missing version information trigger a warning (
--strictexits with code 2). - When embedded packages lack
dist-info, the directory name is assumed to be the PyPI package name. If import name and PyPI project name differ (e.g.,yamlvsPyYAML,PILvsPillow), PyPI lookups will return 404. - Zero mandatory dependencies (pure Python stdlib), but matching platform-specific wheels relies on the optional
packaginglibrary (packaging.tags). If absent, it safely falls back tosdistwhere compiled platform artifacts might be flagged as "extra". - In cases where only sdist is available for C-extensions, build artifacts not present upstream may appear 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 benign. Combine with tools like
pip-audit.
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.3.2
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.3.2.tar.gz | 12.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vendorcheck-0.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.6 kB
Release files / vendorcheck-0.3.2.tar.gz
| Download URL | vendorcheck-0.3.2.tar.gz |
|---|---|
| Size | 12.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1a5468730d836986f6355c26e3e585502ac2c8d6ed955629d68e19f49be63fdb
|
|
BLAKE2b-256 checksum How to use checksums |
a62de978acafe501a23931476b4f47b13528d0c97f424aa03cf3100b321d90da
|
| 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.3.2-py3-none-any.whl
| Download URL | vendorcheck-0.3.2-py3-none-any.whl |
|---|---|
| Size | 13.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aedf4daa3d75d1ee896f16f3517f9e29513d7842d47928534d84543f0ac8101a
|
|
BLAKE2b-256 checksum How to use checksums |
3ae173c42e9741651f2625b5c21622bce7b4a8d1ff5d1d62741b2295f9a11277
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.0
|