From 4c88920f3eb9487d2e37f90d209ad651ba184306 Mon Sep 17 00:00:00 2001 From: Steve-too Date: Tue, 7 Jul 2026 13:42:04 +0000 Subject: [PATCH] fix: contain source verification file reads --- src/vouch/verify.py | 21 ++++++++------------- tests/test_verify.py | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/vouch/verify.py b/src/vouch/verify.py index a6227252..373f73fb 100644 --- a/src/vouch/verify.py +++ b/src/vouch/verify.py @@ -9,8 +9,6 @@ from __future__ import annotations from dataclasses import dataclass -from pathlib import Path - from . import audit from .models import Source from .storage import ArtifactNotFoundError, KBStore, sha256_hex @@ -42,18 +40,15 @@ def verify_source(store: KBStore, source: Source) -> VerificationResult: external_status = "n/a" note: str | None = None if source.type.value == "file": - ext = Path(source.locator) - if ext.is_file(): - try: - external_status = ( - "match" if sha256_hex(ext.read_bytes()) == source.id else "drift" - ) - except OSError as e: - external_status = "missing" - note = f"unreadable: {e}" - else: + try: + _resolved, external_body = store.read_under_root(source.locator) + except (OSError, ValueError) as e: external_status = "missing" - note = "external file path no longer exists" + note = f"unreadable: {e}" + else: + external_status = ( + "match" if sha256_hex(external_body) == source.id else "drift" + ) return VerificationResult( source=source, stored_ok=stored_ok, diff --git a/tests/test_verify.py b/tests/test_verify.py index bd1c66ef..fd076b2b 100644 --- a/tests/test_verify.py +++ b/tests/test_verify.py @@ -30,6 +30,24 @@ def test_verify_detects_external_drift(store: KBStore, tmp_path: Path) -> None: assert target.external_status == "drift" +def test_verify_refuses_off_root_file_locator(store: KBStore, tmp_path: Path) -> None: + outside = tmp_path.parent / f"{tmp_path.name}-outside.txt" + outside.write_bytes(b"secret") + src = store.put_source( + b"placeholder", + title="outside", + locator=str(outside), + source_type="file", + ) + + result = verify.verify_source(store, src) + + assert result.stored_ok is True + assert result.external_status == "missing" + assert result.note is not None + assert "unreadable" in result.note + + def test_verify_handles_missing_stored_content(store: KBStore, tmp_path: Path) -> None: # Regression for #30: verify_source caught FileNotFoundError, but # store.read_source_content() raises ArtifactNotFoundError (a KeyError