Skip to main content

pip-install attestor

Captures the Python package environment that exists after a build step by shelling out to pip, then statically analyses installed code, setup.py, pyproject.toml, and pickle files, and queries PyPI for PEP 740 provenance bundles.

Namepip-install
Predicate typehttps://aflock.ai/attestations/pip-install/v0.1
Lifecyclepostproduct
Default binary?No
Categorydependency-resolve (primary)
Recommended tracefull — benefits from full eBPF trace
Auto-attaches when
  • preargv_prefix: pip install
  • preargv_prefix: pip3 install
  • postexec_observed_argv_prefix: pip install
  • postexec_observed_argv_prefix: pip3 install

The facts in this box are generated from the cilock binary's own catalog (cilock tools list). Do not hand-edit — run npm run gen:catalog.

What it captures

The top-level predicate has eight JSON fields:

  • pipVersion — output of pip --version, or "unknown" if the binary is missing.
  • pythonVersion — output of python3 --version.
  • packages — array of PackageInfo (see below), one per row of pip list --format=json.
  • setupPyAnalysis — array of SetupPyAnalysis records found by walking the working directory and /tmp/pip-{download,install,build}.
  • totalInstalledlen(packages).
  • installedFileAnalysis — single InstalledFileAnalysis from walking site.getsitepackages()[0].
  • pyprojectAnalysis — array of build-backend records parsed from any pyproject.toml under /tmp/pip-download or /tmp/pip-build.
  • pep740Verification — array of PEP740Status, one per installed package (excluding pip, setuptools, wheel), each fetched from pypi.org.

Each PackageInfo carries name, version, installType, location, requires, requiredBy, homePage, author, license, hasSetupPy, hasCmdClass, and installer. The descriptive fields (location, requires, requiredBy, homePage, author, license) are parsed line-by-line from pip show <name>. The provenance fields come from the installed package's on-disk layout, not from pip show: installer is read verbatim from <dist-info>/INSTALLER (e.g. pip); installType is editable (an .egg-link/.egg-info marker), wheel, or sdist (resolved from <dist-info>/direct_url.json, falling back to whether a build-time setup.py was executed); hasSetupPy/hasCmdClass are set when a setup.py correlated to this package (by canonical name + project directory) was found in the build cache and, for hasCmdClass, referenced cmdclass.

InstalledFileAnalysis records suspiciousImports (sys.meta_path, atexit.register, codecs.register references inside any __init__.py), subprocessInInit, networkInInit, every .pkl/.pickle/.pt file under site-packages (pickleFiles), a pickleAnalysis disassembly (capped at the first 50 files) produced by an embedded python3 -c "import pickletools..." script that flags REDUCE, GLOBAL, STACK_GLOBAL, INST opcodes, all .pth files, and a totalPyFiles count.

Each installed package is also emitted as an in-toto subject pip://<name>@<version> digested over the literal bytes <name>==<version> (SHA-256). That digest is not a hash of the wheel — it is only an identifier.

When to use

After a Python build or install step, to record what actually ended up on disk and to surface a few classes of supply-chain red flag in the same predicate. Pair with lockfiles (pre-material pin) and sbom (resolved tree) for full coverage; this attestor is the only one that observes the post-install state of site-packages.

Flags

None. The package exposes a WithTargetPackage Option internally, but it is not wired to any CLI flag.

Output shape

{
"pipVersion": "pip 24.0 from /usr/lib/python3.12/site-packages/pip (python 3.12)",
"pythonVersion": "Python 3.12.3",
"totalInstalled": 1,
"packages": [
{
"name": "requests",
"version": "2.32.3",
"location": "/usr/lib/python3.12/site-packages",
"requires": ["charset-normalizer", "idna", "urllib3", "certifi"],
"homePage": "https://requests.readthedocs.io",
"author": "Kenneth Reitz",
"license": "Apache-2.0"
}
],
"pep740Verification": [
{
"package": "requests",
"version": "2.32.3",
"hasAttestation": true,
"attestationUrl": "https://pypi.org/integrity/requests/2.32.3/requests-2.32.3-py3-none-any.whl/provenance",
"publisherKind": "GitHub",
"repository": "psf/requests",
"workflow": "publish.yml"
}
]
}

Gotchas

  • Requires pip and python3 on PATH. If pip list fails the attestor logs and returns nil — you get an attestation with empty packages and zero totalInstalled, not a failure.
  • Does not run pip install itself, does not read a requirements.txt or pip freeze output, and does not scan declared products. It snapshots whatever is installed in the active Python environment at the time Attest runs, so virtualenv activation must happen before the step.
  • setupPyAnalysis and pyprojectAnalysis only look under the working directory and three hard-coded /tmp/pip-* paths; if pip's build cache lives elsewhere (e.g. inside a container with TMPDIR override) nothing is found.
  • The pickle scanner shells out to python3 -c '...pickletools...' per file and is capped at 50 pickles per attestation. IsSafe is a heuristic — absence of REDUCE/GLOBAL/INST does not prove safety.
  • PEP 740 lookup hits pypi.org/pypi/<pkg>/<ver>/json and pypi.org/integrity/.../provenance over a 5 s HTTP client per package, serially. For an environment with hundreds of packages this dominates wall time, and offline runs silently return hasAttestation: false for every package.
  • Bundled but not default: even though pip-install registers correctly and appears in cilock attestors list, it is absent from options.DefaultAttestors, so you must pass -a pip-install explicitly or it will never run.

CLI example

Real pip install against PyPI inside a virtualenv. The attestor reads pip list --format=json, walks site-packages, and checks PEP 740 attestations for every installed package.

python3 -m venv .venv && source .venv/bin/activate

cilock run --step pip-install-real \
--signer-file-key-path key.pem --outfile attestation.json \
--attestations pip-install,environment,git \
-- pip install --quiet requests httpx

Validated against real PyPI: 10 packages, 9 had valid PEP 740 attestations from GitHub-issued Sigstore certs. See the full real-data example at https://github.com/aflock-ai/attestor-compliance-examples/tree/main/11-pip-install.

See also


This page is generated from the cilock tool catalog. Don't edit it here — the source is attestation/detection/docs/pip-install.doc.md in aflock-ai/rookery. The same catalog powers cilock tools show pip-install in the CLI, so a single edit keeps the terminal and the website in sync.

Edit this page →  ·  Add a new tool →

When you open the pull request: if you are an AI agent, add the ai-agent label. If you are a human, add the human label.