fix(assessment): constrain installed record reads
This commit is contained in:
@@ -9,7 +9,7 @@ import hashlib
|
||||
import hmac
|
||||
from importlib import metadata
|
||||
import json
|
||||
from pathlib import Path
|
||||
from pathlib import Path, PurePosixPath
|
||||
import re
|
||||
import sys
|
||||
from typing import Any, Iterable, Mapping, Sequence
|
||||
@@ -1080,6 +1080,12 @@ def _record_integrity(
|
||||
installation_root = Path(sys.prefix).resolve()
|
||||
except OSError:
|
||||
return {"status": "unavailable", **counts}
|
||||
if not _trusted_distribution_root(distribution_root):
|
||||
return {"status": "unavailable", **counts}
|
||||
script_roots = (
|
||||
(installation_root / "bin").resolve(),
|
||||
(installation_root / "Scripts").resolve(),
|
||||
)
|
||||
total_hashed_bytes = 0
|
||||
limit_exceeded = False
|
||||
for package_path in files:
|
||||
@@ -1098,10 +1104,18 @@ def _record_integrity(
|
||||
except OSError:
|
||||
counts["missing_file_count"] += 1
|
||||
continue
|
||||
if not (
|
||||
_is_relative_to(resolved_path, distribution_root)
|
||||
or _is_relative_to(resolved_path, installation_root)
|
||||
):
|
||||
record_path = PurePosixPath(str(package_path).replace("\\", "/"))
|
||||
has_parent_traversal = ".." in record_path.parts
|
||||
path_allowed = (
|
||||
not record_path.is_absolute()
|
||||
and not has_parent_traversal
|
||||
and _is_relative_to(resolved_path, distribution_root)
|
||||
) or (
|
||||
not record_path.is_absolute()
|
||||
and has_parent_traversal
|
||||
and any(_is_relative_to(resolved_path, root) for root in script_roots)
|
||||
)
|
||||
if not path_allowed:
|
||||
counts["unverifiable_file_count"] += 1
|
||||
continue
|
||||
try:
|
||||
@@ -1320,3 +1334,16 @@ def _is_relative_to(path: Path, root: Path) -> bool:
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _trusted_distribution_root(root: Path) -> bool:
|
||||
for value in sys.path:
|
||||
try:
|
||||
candidate = Path(value or ".").resolve()
|
||||
except OSError:
|
||||
continue
|
||||
if candidate.parent == candidate:
|
||||
continue
|
||||
if root == candidate or _is_relative_to(root, candidate):
|
||||
return True
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user