test_print_next_security.py python
150 lines 5.1 KB
Raw
sha256:8d7f41aafae41deee70035a92f93602ef1722a290153597451e5932af503a42c docs: queue board-identity follow-ups so they survive the session Human 1 day ago
1 """Security — path/lane refusal + honesty greps (§ONS.12 / §ONS.11)."""
2
3 from __future__ import annotations
4
5 import json
6 from pathlib import Path
7
8 from cli.kit_root import kit_root
9 from tests.support import FIXTURES, git_status_runner, run_cli, write_config
10
11 REQUIRED_SUBSTRINGS = (
12 "## CURRENT NEXT — paste this",
13 "ok next",
14 "read from disk after write",
15 "Session incomplete without it",
16 "do not guarantee an accurate open tab",
17 )
18
19 HONESTY = (
20 "Host niceties improve odds of tab refresh; they do **not** guarantee an accurate open"
21 )
22
23
24 def test_config_outside_repo_exit_4(tmp_path: Path) -> None:
25 write_config(tmp_path, "config-git-only.yaml")
26 outside = tmp_path.parent / "outside-config.yaml"
27 outside.write_text(
28 (FIXTURES / "config-git-only.yaml").read_text(encoding="utf-8"),
29 encoding="utf-8",
30 )
31 docs = tmp_path / "docs"
32 docs.mkdir(parents=True, exist_ok=True)
33 (docs / "OVERSEER-HANDOVER.md").write_text(
34 (FIXTURES / "print-next-handover.md").read_text(encoding="utf-8"),
35 encoding="utf-8",
36 )
37 code = run_cli(
38 ["next", "--config", str(outside)],
39 cwd=tmp_path,
40 runner=git_status_runner(),
41 kit=kit_root(),
42 )
43 assert code == 4
44
45
46 def test_lane_path_escape_exit_2(tmp_path: Path) -> None:
47 write_config(tmp_path, "config-git-only.yaml")
48 docs = tmp_path / "docs"
49 docs.mkdir(parents=True, exist_ok=True)
50 (docs / "OVERSEER-HANDOVER.md").write_text(
51 (FIXTURES / "print-next-handover.md").read_text(encoding="utf-8"),
52 encoding="utf-8",
53 )
54 code = run_cli(
55 ["next", "--lane", "../x"],
56 cwd=tmp_path,
57 runner=git_status_runner(),
58 kit=kit_root(),
59 )
60 assert code == 2
61
62
63 def test_git_only_zero_muse_argv(tmp_path: Path) -> None:
64 write_config(tmp_path, "config-git-only.yaml")
65 docs = tmp_path / "docs"
66 docs.mkdir(parents=True, exist_ok=True)
67 (docs / "OVERSEER-HANDOVER.md").write_text(
68 (FIXTURES / "print-next-handover.md").read_text(encoding="utf-8"),
69 encoding="utf-8",
70 )
71 runner = git_status_runner()
72 code = run_cli(["next"], cwd=tmp_path, runner=runner, kit=kit_root())
73 assert code == 0
74 assert runner.calls == []
75
76
77 def test_skill_rule_required_substrings() -> None:
78 root = kit_root()
79 skill = (root / "cursor" / "skills" / "print-next" / "SKILL.md").read_text(encoding="utf-8")
80 rule = (root / "cursor" / "rules" / "print-next-closeout.mdc").read_text(encoding="utf-8")
81 for needle in REQUIRED_SUBSTRINGS:
82 assert needle in skill, needle
83 assert needle in rule, needle
84
85
86 def test_print_next_doc_honesty_sentence() -> None:
87 text = (kit_root() / "docs" / "PRINT-NEXT.md").read_text(encoding="utf-8")
88 assert HONESTY in text
89
90
91 def test_stop_hook_fail_closed_false() -> None:
92 raw = (kit_root() / "cursor" / "hooks" / "print-next-stop.json").read_text(encoding="utf-8")
93 data = json.loads(raw)
94 stop = data["hooks"]["stop"][0]
95 assert stop["failClosed"] is False
96
97
98 def test_fake_secret_printed_as_data_not_executed(tmp_path: Path, capsys) -> None:
99 write_config(tmp_path, "config-git-only.yaml")
100 docs = tmp_path / "docs"
101 docs.mkdir(parents=True, exist_ok=True)
102 token = "sk-test-not-a-real-secret-ons"
103 (docs / "OVERSEER-HANDOVER.md").write_text(
104 "# H\n\n### Paste-ready prompt\n\n```text\n"
105 f"Token {token}\nModel: Auto\n```\n",
106 encoding="utf-8",
107 )
108 runner = git_status_runner()
109 code = run_cli(["next"], cwd=tmp_path, runner=runner, kit=kit_root())
110 out = capsys.readouterr().out
111 assert code == 0
112 assert token in out
113 for cmd, _cwd in runner.calls:
114 assert token not in cmd
115
116
117 def test_provenance_leaks_no_secrets(tmp_path: Path, capsys) -> None:
118 """§NXP.8 security: provenance emits name/path/lane/timestamp only."""
119 from tools.print_next.extract import set_read_at_clock
120
121 write_config(tmp_path, "config-git-only.yaml")
122 # Inject a fake token into config to ensure it is not echoed in provenance.
123 cfg = tmp_path / ".overseer" / "config.yaml"
124 cfg.write_text(
125 cfg.read_text(encoding="utf-8") + "\n# secret: sk-config-should-not-leak\n",
126 encoding="utf-8",
127 )
128 docs = tmp_path / "docs"
129 docs.mkdir(parents=True, exist_ok=True)
130 (docs / "OVERSEER-HANDOVER.md").write_text(
131 (FIXTURES / "print-next-handover.md").read_text(encoding="utf-8"),
132 encoding="utf-8",
133 )
134 (docs / "ROADMAP.md").write_text("# Roadmap\n", encoding="utf-8")
135 set_read_at_clock(lambda: "2026-09-04T12:00:00Z")
136 try:
137 code = run_cli(["next"], cwd=tmp_path, runner=git_status_runner(), kit=kit_root())
138 out = capsys.readouterr().out
139 assert code == 0
140 # Provenance line only — isolate the Source line.
141 prov = [ln for ln in out.splitlines() if ln.startswith("**Source:**")][0]
142 assert "sk-config-should-not-leak" not in prov
143 assert "token" not in prov.lower()
144 assert "github.com" not in prov
145 assert "remote" not in prov
146 assert "test-git" in prov
147 assert tmp_path.resolve().as_posix() in prov
148 assert "read `" in prov
149 finally:
150 set_read_at_clock(None)
File History 1 commit
sha256:8d7f41aafae41deee70035a92f93602ef1722a290153597451e5932af503a42c docs: queue board-identity follow-ups so they survive the session Human 1 day ago