-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Title
Prevent path traversal in cache pull by validating manifest keys and resolved paths
Summary
iqb cache pull can potentially write files outside the intended cache root if a manifest contains traversal-style paths (e.g. ../...).
Type
Security bug
Severity
High
Affected files
library/src/iqb/ghremote/diff.pylibrary/src/iqb/cli/cache_pull.py
Why this is a problem
Manifest keys are used as relative file paths and joined with data_dir during pull.
Without strict path validation and final resolved-path boundary checks, a malicious manifest key can escape the cache directory and overwrite files outside it.
Potential impact
- Arbitrary file write outside cache root
- Overwrite of local files in parent directories
- Risk increases if manifest source is compromised or untrusted
Root cause
- Manifest keys are iterated in
diff()without mandatory cache-path validation. - Pull write path builds destination from
data_dir / entry.filewithout verifying that resolved destination is still underdata_dir.
Proof of Concept
I validated this by calling the pull write path with a traversal-style path (../escaped.txt) and a fake HTTP response.
PoC command
@'
from pathlib import Path
from tempfile import TemporaryDirectory
import hashlib
from rich.progress import Progress
from iqb.cli.cache_pull import _download_one
from iqb.ghremote.diff import DiffEntry, DiffState
payload = b"poc"
sha = hashlib.sha256(payload).hexdigest()
class FakeResp:
headers = {"Content-Length": str(len(payload))}
def raise_for_status(self): pass
def iter_content(self, chunk_size=8192):
yield payload
class FakeSession:
def get(self, url, stream=True):
return FakeResp()
with TemporaryDirectory() as td, Progress() as progress:
data_dir = Path(td) / "safe_cache"
data_dir.mkdir(parents=True, exist_ok=True)
entry = DiffEntry(
file="../escaped.txt",
url="https://example.com/poc",
remote_sha256=sha,
local_sha256=None,
state=DiffState.ONLY_REMOTE,
)
span = _download_one(entry, data_dir, FakeSession(), progress)
outside = (data_dir / "../escaped.txt").resolve()
print("ok:", span["ok"])
print("outside_exists:", outside.exists())
print("outside_path:", outside)
'@ | & "$HOME\.local\bin\uv.exe" run python -Screenshot Evidence
Expected Output (Secure Behavior)
ok: False- or an exception such as
ValueError: Unsafe manifest path outside_exists: False
Actual Output (Vulnerable Behavior)
ok: Trueoutside_exists: Trueoutside_pathpoints outsidesafe_cache
Impact of This Issue
If a malicious or compromised manifest includes traversal-style paths:
- The application can write files outside the intended cache directory.
- Attackers may overwrite arbitrary files in parent directories.
- Sensitive system files or project files could be modified.
- It may lead to privilege escalation depending on execution context.
- In CI/CD environments, it could corrupt build artifacts or inject malicious files.
- In worst-case scenarios, this could enable remote code execution if overwritten files are later executed.
This represents a high-severity path traversal vulnerability due to unsafe handling of untrusted manifest paths during file writes.