Skip to content

Search

Search the docs, blog, and marketplace.

pip

Last updated October 25, 2026View source (v0.30.0)

This page explains how Bomly builds a dependency graph for pip projects: the detector chain it runs, what that chain needs, and what it can report.

PropertyValue
Package managerpip
Ecosystempython
Detector chainpip-detector, syft-detector
Evidence patternsrequirements.txt, requirements-dev.txt, requirements.in, requirements.lock, *requirements*.txt
Ignored directories__pycache__
Ignored directory markerspyvenv.cfg
Multi-module resolutionNo
Install-first supportYes
Remediation hintsdirect-bump1, transitive-override2
Commands the chain may runpip. The Syft fallback is built in; the lite build runs an external syft from PATH instead.

How pip resolves

pip-detector is lockfile first, isolated environment second. It never accepts an unrelated Python environment as the project's graph.

PathStrategyCommand
requirements.lock present and readableLockfile parserNone
No lockfileIsolated install and inspectpython -m venv <tmp>/bomly-pyvenv-*, then <venv>/python -m pip install -r <requirements-file>, then <venv>/python -m pip inspect --local

The isolated virtual environment lives under the operating system's temp directory and is keyed by the project path. Bomly recreates it before installing so stale packages cannot leak into the scan, then inspects that environment directly.

The lockfile path reads one file and runs no Python, so it never uses the network. The install path may download packages from PyPI or the indexes your install arguments name. The only commands Bomly runs there are python -m venv, pip --version, your requirements install, and pip inspect. It never installs or upgrades pip itself. See Network and privacy for the shared rule.

Direct versus transitive comes from what your project declares by name: requirements files, the dependency tables in pyproject.toml, or the Pipfile, plus the installer's REQUESTED marker. Lockfiles never count as declarations, because they record the whole resolved set and would make every transitive package look direct. Everything else in the environment is linked to its parents through each distribution's Requires-Dist metadata.

A requirements.txt project has no name of its own. The graph's root node takes the first available of: the project name in pyproject.toml, the subproject directory in a recursive scan, the repository name for a --url target, or the project directory. Bomly's own temporary clone directories are never used, because their names are random.

Prerequisites

  • python on PATH with pip installed (python -m pip --version must work).
  • pip 22.2 or newer, the first release with pip inspect. python -m venv seeds the new environment with the ambient interpreter's pip, so an old system Python (macOS ships 3.9 with pip 21) creates an environment that cannot inspect itself. Bomly checks this before installing and fails with the pip version and this requirement named. The chain then falls back, so the scan still completes with a reduced graph. Fix it with python -m pip install --upgrade pip for that interpreter, or put a newer Python on PATH.
  • A requirements.txt, requirements-dev.txt, requirements.in, requirements.lock, or any *requirements*.txt file in the scan path. That is the evidence that triggers pip-detector.

Examples

Fix a direct vulnerability

Pin the fixed version in requirements.txt:

requests==2.32.4

Re-scan. Without a lockfile, Bomly installs the pinned requirements into its isolated environment.

Pin a transitive vulnerability

With pip-tools, add a constraint:

# constraints.txt
urllib3>=2.2.2

Then re-compile and re-scan:

pip-compile --constraint constraints.txt requirements.in

--install-first

pip supports --install-first, but it also runs the same isolated install on its own when no readable requirements.lock exists. With the flag, the install runs before graph resolution. Without it, the detector runs the install as part of resolution.

The install target is Bomly's temporary environment, never the working tree or the system Python. Bomly installs the primary requirements file, plus requirements-dev.txt when present unless you pass --scope runtime. The install can download packages but does not change project files. Commit requirements.lock when you need fully offline scans.

bomly scan --install-first

To append flags to the pip install command, repeat --install-arg. This requires --detectors pip-detector. Bomly records the install command, with secrets removed, in the scan JSON under the manifest's resolution metadata.

# Install from a private index in CI
bomly scan --install-first --detectors pip-detector \
  --install-arg --index-url --install-arg https://pypi.example.com/simple

# Honor hash-checking mode declared in requirements.txt
bomly scan --install-first --detectors pip-detector \
  --install-arg --require-hashes

Source details

Pinned packages in requirements.lock are registry packages. On the inspection path, Bomly uses pip's direct_url record to identify Git, URL, and local-file installs. Missing or malformed source data stays unknown.

Reachability (experimental)

With --analyze, the pyreach analyzer walks every .py file under the project root, records imports, and maps module names to PyPI distribution names. Unreachable is a triage signal, not proof of safety; see Reachability.

Limitations

  • Install failures are explicit. If Bomly cannot create the environment, install the requirements, or inspect the result, the detector fails instead of falling back to the ambient Python.
  • Several Python installations only decide which interpreter creates the isolated environment. The graph comes from that environment, not from the ambient site-packages.
  • Editable installs (pip install -e ./local-pkg) appear in the inspection. Their dependencies come from the local package's metadata.

Footnotes

  1. Update a package declared directly in the project. ↩

  2. Pin an indirect package with the package manager's override feature. ↩