From 3c7caa8cc6ea4ba2acced7ff24034970b4f5c53c Mon Sep 17 00:00:00 2001 From: ProfRandom92 <159939812+ProfRandom92@users.noreply.github.com> Date: Thu, 9 Jul 2026 01:47:06 +0200 Subject: [PATCH 1/2] fix(validation): address PR15 Gemini review comments --- modules/cli/status_screen.py | 3 +-- modules/evidence/evidence.py | 2 ++ modules/validation/schema_validator.py | 2 +- tests/evidence/test_state_log.py | 6 ++++++ tests/validation/test_schema_validator.py | 12 ++++++++++++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/modules/cli/status_screen.py b/modules/cli/status_screen.py index 2405848..a3df503 100644 --- a/modules/cli/status_screen.py +++ b/modules/cli/status_screen.py @@ -6,7 +6,7 @@ from modules.doctor.doctor import run_doctor from modules.validation.workspace_validation import validate_workspace_schemas from modules.runtime.sample_run import run_sample -from modules.evidence.evidence import verify_sample_evidence +from modules.evidence.evidence import verify_file_state_log, verify_sample_evidence def build_status_screen(repo_root: Path) -> tuple[int, str]: @@ -56,7 +56,6 @@ def build_status_screen(repo_root: Path) -> tuple[int, str]: try: evidence_res = verify_sample_evidence(sample=True) state_log_path = repo_root / "examples/workspace/evidence-state-log.sample.json" - from modules.evidence.evidence import verify_file_state_log state_log_res = verify_file_state_log(filepath=state_log_path) evidence_ok = bool(evidence_res.get("ok")) and bool(state_log_res.get("ok")) except Exception: diff --git a/modules/evidence/evidence.py b/modules/evidence/evidence.py index 3743aeb..e364931 100644 --- a/modules/evidence/evidence.py +++ b/modules/evidence/evidence.py @@ -146,6 +146,8 @@ def verify_file_evidence(*, filepath: str | Path) -> dict[str, Any]: def verify_state_log_chain(entries: list[dict[str, Any]]) -> dict[str, Any]: """Verify sequence, previous state hashes, git commit refs, and content hashes in a state log chain.""" + if not entries: + return {"ok": False, "error": "State log chain must contain at least 1 entry"} previous_hash = GENESIS_HASH for expected_sequence, entry in enumerate(entries): if not isinstance(entry, dict): diff --git a/modules/validation/schema_validator.py b/modules/validation/schema_validator.py index a4b7894..4e9f5c9 100644 --- a/modules/validation/schema_validator.py +++ b/modules/validation/schema_validator.py @@ -45,7 +45,7 @@ def validate_json_schema_instance(schema: dict[str, Any], instance: Any, locatio if "pattern" in schema and isinstance(instance, str): import re - if not re.match(schema["pattern"], instance): + if not re.search(schema["pattern"], instance): raise ValueError(f"{location} does not match pattern {schema['pattern']}") if "minItems" in schema and isinstance(instance, list): diff --git a/tests/evidence/test_state_log.py b/tests/evidence/test_state_log.py index 18c85db..4667365 100644 --- a/tests/evidence/test_state_log.py +++ b/tests/evidence/test_state_log.py @@ -24,6 +24,12 @@ def test_verify_state_log_chain_valid() -> None: assert len(result["root_hash"]) == 64 +def test_verify_state_log_chain_empty() -> None: + result = verify_state_log_chain([]) + assert result["ok"] is False + assert "at least 1 entry" in result["error"] + + def test_verify_file_state_log_valid() -> None: sample_file = ROOT / "examples" / "workspace" / "evidence-state-log.sample.json" result = verify_file_state_log(filepath=sample_file) diff --git a/tests/validation/test_schema_validator.py b/tests/validation/test_schema_validator.py index 4694965..7d8bff6 100644 --- a/tests/validation/test_schema_validator.py +++ b/tests/validation/test_schema_validator.py @@ -24,3 +24,15 @@ def test_validate_json_schema_instance_rejects_unsupported_types() -> None: def test_validate_json_schema_instance_does_not_treat_boolean_as_integer() -> None: with pytest.raises(ValueError, match="must be integer"): validate_json_schema_instance({"type": "integer"}, True) + + +def test_validate_json_schema_instance_pattern_search_semantics() -> None: + # pattern "foo" accepts "xxfooyy" (search semantics) + validate_json_schema_instance({"type": "string", "pattern": "foo"}, "xxfooyy") + + # pattern "^foo$" accepts "foo" + validate_json_schema_instance({"type": "string", "pattern": "^foo$"}, "foo") + + # pattern "^foo$" rejects "xxfooyy" + with pytest.raises(ValueError, match="does not match pattern"): + validate_json_schema_instance({"type": "string", "pattern": "^foo$"}, "xxfooyy") From 7fe2f91eea01ab05d82cd2adc6b06d86086bcdc2 Mon Sep 17 00:00:00 2001 From: ProfRandom92 <159939812+ProfRandom92@users.noreply.github.com> Date: Thu, 9 Jul 2026 01:55:20 +0200 Subject: [PATCH 2/2] fix(validation): harden schema pattern handling --- modules/validation/schema_validator.py | 11 +++++++++-- tests/validation/test_schema_validator.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/validation/schema_validator.py b/modules/validation/schema_validator.py index 4e9f5c9..7a35bfc 100644 --- a/modules/validation/schema_validator.py +++ b/modules/validation/schema_validator.py @@ -44,9 +44,16 @@ def validate_json_schema_instance(schema: dict[str, Any], instance: Any, locatio raise ValueError(f"{location} must be one of {schema['enum']}") if "pattern" in schema and isinstance(instance, str): + pattern = schema["pattern"] + if not isinstance(pattern, str): + raise ValueError(f"{location} pattern must be a string") import re - if not re.search(schema["pattern"], instance): - raise ValueError(f"{location} does not match pattern {schema['pattern']}") + try: + compiled_pattern = re.compile(pattern) + except re.error as e: + raise ValueError(f"{location} has invalid regex pattern '{pattern}': {e}") + if not compiled_pattern.search(instance): + raise ValueError(f"{location} does not match pattern {pattern}") if "minItems" in schema and isinstance(instance, list): if len(instance) < schema["minItems"]: diff --git a/tests/validation/test_schema_validator.py b/tests/validation/test_schema_validator.py index 7d8bff6..8f12bdb 100644 --- a/tests/validation/test_schema_validator.py +++ b/tests/validation/test_schema_validator.py @@ -36,3 +36,13 @@ def test_validate_json_schema_instance_pattern_search_semantics() -> None: # pattern "^foo$" rejects "xxfooyy" with pytest.raises(ValueError, match="does not match pattern"): validate_json_schema_instance({"type": "string", "pattern": "^foo$"}, "xxfooyy") + + +def test_validate_json_schema_instance_pattern_failures() -> None: + # pattern as non-string raises ValueError + with pytest.raises(ValueError, match="pattern must be a string"): + validate_json_schema_instance({"type": "string", "pattern": 123}, "some_string") + + # invalid regex pattern raises ValueError + with pytest.raises(ValueError, match="invalid regex pattern"): + validate_json_schema_instance({"type": "string", "pattern": "[invalid"}, "some_string")