CycloneDX / CycloneDX/cyclonedx-python

environment subcommand: flag to NOT inherited `PYTHONPATH`

Đang mở
#1,045 2 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Python
Star
390
Fork
98
Merge trung bình
2 ngày 23 giờ
Pull request đã merge (30 ngày)
2

Mô tả

## Summary

When `cyclonedx-py environment ` is invoked from a process that has
`PYTHONPATH` set, the `PYTHONPATH` is inherited by the subprocess that
`cyclonedx-py` spawns to enumerate `sys.path` of the target interpreter. As a
result, packages from outside the target venv appear in the generated SBOM -
packages that are not installed in the environment being scanned at all.

## Root cause (probably)

In cyclonedx_py/_internal/environment.py, the __path4python method spawns a
subprocess to read the target interpreter's `sys.path`:

```python
# cyclonedx_py/_internal/environment.py, __path4python()
res = run(cmd, capture_output=True, encoding='utf8', shell=False) # nosec
```

`subprocess.run` is called without an explicit env= argument, so it inherits the
full environment of the calling process including `PYTHONPATH`. When
`PYTHONPATH` is set in the caller (e.g. by a build tool, test runner, or task
runner that places its own packages there), those paths are prepended to
`sys.path` inside the spawned interpreter. The resulting path list is then used
by `importlib.metadata.distributions(path=...)` to discover packages, so any
package reachable via the inherited `PYTHONPATH` is included in the SBOM as if
it were installed in the target venv.

## Minimal reproducible example

```python
uv tool install cyclonedx-bom

# Create the venv to be scanned (install only 'requests')
uv venv /tmp/scan_venv
uv pip install --python=/tmp/scan_venv/bin/python requests

# Create a separate directory with an unrelated package visible via PYTHONPATH
uv venv /tmp/outer_venv
uv pip install --python=/tmp/outer_venv/bin/python flask

# Run cyclonedx-py with PYTHONPATH pointing at the outer package directory
PYTHONPATH=$(/tmp/outer_venv/bin/python -c "import sysconfig; print(sysconfig.get_path('purelib'))") \
cyclonedx-py environment /tmp/scan_venv -o /tmp/sbom.json

# Inspect the result -> 'Flask' (and its dependencies) appear in the SBOM
# even though it is NOT installed in /tmp/scan_venv
uv run python -c "
import json
data = json.load(open('/tmp/sbom.json'))
names = [c['name'] for c in data.get('components', [])]
print('flask in SBOM:', 'Flask' in names)
print('All components:', sorted(names))
"
```

Expected: SBOM contains only requests (and its dependencies).

Actual: SBOM also contains flask (and its dependencies), which were never
installed in the scanned venv.

## Why it matters

Any tool that invokes cyclonedx-py in-process or as a subprocess after setting
PYTHONPATH including build systems, task runners, and CI pipelines that add
their own directories to PYTHONPATH will silently produce an inaccurate
SBOM. The SBOM over-reports dependencies that are not actually present in the
scanned environment, which undermines the entire purpose of SBOM generation for
supply-chain auditing and compliance.

## Suggested fix

Strip PYTHONPATH (and, for robustness, VIRTUAL_ENV and PYTHONHOME) from the
environment passed to the subprocess in `__path4python`:

```python
import os

def __path4python(self, python: str, import_site: bool) -> list[str]:
cmd = [self.__py_interpreter(python),
'-c', 'import json,sys;json.dump(sys.path,sys.stdout)']
if not import_site:
cmd.insert(1, '-S')

# Explicitly clear env vars that would pollute the target interpreter's
# sys.path and cause packages from outside the target venv to appear in
# the discovered distributions.
clean_env = {
k: v for k, v in os.environ.items()
if k not in ('PYTHONPATH', 'PYTHONHOME', 'VIRTUAL_ENV')
}

self._logger.debug('fetch `path` from python interpreter cmd: %r', cmd)
res = run(cmd, capture_output=True, encoding='utf8', shell=False, env=clean_env)
...
```

## Environment

- Package version: cyclonedx-bom 7.3.0
- Python version: 3.10+
- Platform: Ubuntu 24.04

## Contribution

- [x] I am willing to provide a fix
- [ ] I will wait until somebody else fixes it

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Đọc cyclonedx_py/_internal/environment.py, tập trung vào __path4python và lệnh gọi subprocess của nó. Chạy bản tái hiện PYTHONPATH tối thiểu được mô tả trong issue, sau đó xác minh rằng SBOM được tạo chỉ chứa các package được cài đặt trong môi trường ảo được quét và loại trừ các package được môi trường của bên gọi cung cấp.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
cli, tooling
Loại issue
Lỗi
Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
58/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.