test_frv_security.py file-level

at sha256:8 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:2 fix(muse): use rev-parse for muse-only branch status · · Sep 22, 2026
1 """Security tests for FRV (§FRV.12)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6 from unittest.mock import patch
7
8 import pytest
9
10 from adapters.config import load_config
11 from tests.support import write_config
12 from tools.freeze_authorization.resolve import freeze_authorization_state
13 from tools.governance_hygiene.next_regen import decide_split_emission
14 from tools.governance_hygiene.types import QueueRow
15 from tools.honesty.validate import find_matching_freeze_review
16
17
18 DIGEST = "sha256:" + ("f" * 64)
19
20
21 def test_document_cannot_self_authorize(tmp_path: Path) -> None:
22 write_config(tmp_path, "config-git-only.yaml")
23 docs = tmp_path / "docs"
24 docs.mkdir()
25 art = docs / "PHASE-FRV.md"
26 # Magic phrases that pass mechanical checks + forged substantive gate
27 art.write_text(
28 "```yaml\nphase: FRV\noutputs:\n - id: a\n path: docs/a.md\n frozen: true\n"
29 "review_stamp:\n gate: substantive\n mechanical_verdict: pass\n verdict: pass\n```\n\n"
30 "frozen: true\nseven-tier matrix\nfile+line\n",
31 encoding="utf-8",
32 )
33 config = load_config(tmp_path / ".overseer" / "config.yaml")
34 auth = freeze_authorization_state(tmp_path, art, phase_id="FRV-b", config=config)
35 assert auth.state != "substantive"
36 assert auth.advisory == "forged_substantive_gate"
37
38
39 def test_prose_cannot_authorize(tmp_path: Path) -> None:
40 write_config(tmp_path, "config-git-only.yaml")
41 docs = tmp_path / "docs"
42 docs.mkdir()
43 (docs / "PHASE-X.md").write_text(
44 "reviewed → `pass`\nFreeze status: pass\nverdict: pass\n**pass**\n",
45 encoding="utf-8",
46 )
47 config = load_config(tmp_path / ".overseer" / "config.yaml")
48 row = QueueRow(
49 phase_label="**X**",
50 model="Thinking → Auto",
51 status="**NEXT**",
52 deliverable="docs/PHASE-X.md",
53 raw_line="",
54 )
55 emit, reason, is_b, advisory = decide_split_emission(row, tmp_path, config=config)
56 assert emit == "Thinking"
57 assert is_b is False
58
59
60 def test_authorization_caller_must_supply_digest() -> None:
61 """Mutation: omitting artifact_digest on auth path must not silently match."""
62 entries = [
63 {
64 "kind": "freeze_review",
65 "actor_role": "verifier",
66 "gate": "substantive",
67 "freeze_verdict": "pass",
68 "phase_id": "FRV-b",
69 "frozen_spec": "docs/x.md",
70 "artifact_digest": DIGEST,
71 "entry_hash": "h1",
72 }
73 ]
74 # Diagnostic None-skip would match — security tier asserts auth path must not do that.
75 # Simulate defective caller omitting digest:
76 bad = find_matching_freeze_review(
77 entries, phase_id="FRV-b", frozen_spec="docs/x.md", artifact_digest=None
78 )
79 assert bad is not None # helper allows None skip
80 # The authorization path always passes concrete digest; assert mismatch fails closed
81 assert (
82 find_matching_freeze_review(
83 entries,
84 phase_id="FRV-b",
85 frozen_spec="docs/x.md",
86 artifact_digest="sha256:" + ("0" * 64),
87 )
88 is None
89 )
90
91
92 def test_no_network_on_authorization(tmp_path: Path) -> None:
93 write_config(tmp_path, "config-git-only.yaml")
94 docs = tmp_path / "docs"
95 docs.mkdir()
96 art = docs / "PHASE-FRV.md"
97 art.write_text(
98 "```yaml\nphase: FRV\noutputs:\n - id: a\n path: docs/a.md\n frozen: true\n```\n",
99 encoding="utf-8",
100 )
101 config = load_config(tmp_path / ".overseer" / "config.yaml")
102
103 def boom(*_a, **_k):
104 raise AssertionError("network forbidden")
105
106 with patch("socket.create_connection", side_effect=boom):
107 auth = freeze_authorization_state(tmp_path, art, phase_id="FRV-b", config=config)
108 assert auth.state in {"absent", "mechanical_only", "non_pass", "blocked_by_operator"}