Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions loopx/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@
r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*\s*[,)\]}]"
)

# A virtualenv is identified by its marker file rather than by its name.
# `DEFAULT_SKIP_DIRS` can only list the names it already knows, so a project
# that names its environment `.venv-conn`, `venv311`, or anything else would
# have `site-packages` scanned as repository content, producing credential and
# private-IP hits from vendored third-party source.
VIRTUALENV_MARKER_FILE = "pyvenv.cfg"


def is_virtualenv_root(path: Path) -> bool:
try:
return (path / VIRTUALENV_MARKER_FILE).is_file()
except OSError:
return False


def _credential_match_is_reference(line: str, match: re.Match[str]) -> bool:
value = line[match.start() :]
Expand Down Expand Up @@ -752,14 +766,18 @@ def iter_scan_files(scan_root: Path) -> list[Path]:
root_parts = set(scan_root.parts)
if any(part in DEFAULT_SKIP_DIRS or part.endswith(".egg-info") for part in root_parts):
return sorted(tracked_files)
if is_virtualenv_root(scan_root):
return sorted(tracked_files)

for dir_path, dir_names, file_names in os.walk(scan_root):
current_dir = Path(dir_path)
dir_names[:] = [
name
for name in dir_names
if name not in DEFAULT_SKIP_DIRS and not name.endswith(".egg-info")
if name not in DEFAULT_SKIP_DIRS
and not name.endswith(".egg-info")
and not is_virtualenv_root(current_dir / name)
]
current_dir = Path(dir_path)
for file_name in file_names:
path = (current_dir / file_name).resolve()
if path.name.endswith(".local.json"):
Expand Down
38 changes: 38 additions & 0 deletions tests/test_contract_scan_unreadable_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,44 @@ def test_scan_public_boundary_survives_dangling_symlink(tmp_path: Path) -> None:
assert payload["unreadable_files"] == []


def test_iter_scan_files_skips_virtualenv_roots_by_marker(tmp_path: Path) -> None:
"""A virtualenv is skipped by its marker file, not by its directory name.

The skip list can only name environments it already knows. A project whose
environment is called `.venv-conn` or `venv311` would otherwise have
`site-packages` scanned as repository content, and vendored third-party
source would be reported as credential and private-IP hits. The payload is
assembled from split literals so this fixture does not trip the scan it is
describing.
"""

keyword = "tok" + "en="
payload = f'AUTH = "{keyword}syntheticsyntheticsynthetic"\n'
(tmp_path / "module.py").write_text(payload, encoding="utf-8")

venv = tmp_path / ".venv-conn"
vendored = venv / "lib" / "site-packages" / "vendored.py"
vendored.parent.mkdir(parents=True)
(venv / "pyvenv.cfg").write_text("home = /usr/bin\n", encoding="utf-8")
vendored.write_text(payload, encoding="utf-8")

unmarked = tmp_path / "vendor-copy" / "lib" / "site-packages" / "vendored.py"
unmarked.parent.mkdir(parents=True)
unmarked.write_text(payload, encoding="utf-8")

scanned = iter_scan_files(tmp_path)

assert tmp_path / "module.py" in scanned
assert vendored not in scanned
# The marker is what decides: the same layout without `pyvenv.cfg` is a
# normal directory and is still scanned.
assert unmarked in scanned
# The same bytes outside a virtualenv are still scanned and still hit, so
# the marker skip cannot be passing by reading nothing at all.
assert scan_public_boundary([venv])["hits"] == []
assert scan_public_boundary([tmp_path / "module.py"])["hits"]


def test_tracked_product_runtime_source_is_not_private_state(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
Expand Down
Loading