test_api_provider_security.py python
91 lines 3.5 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
1 """Security tests for headless API freeze provider (K11)."""
2
3 from __future__ import annotations
4
5 import io
6 import json
7 from contextlib import redirect_stdout
8 from pathlib import Path
9 from unittest.mock import patch
10
11 from cli.context import CliContext
12 from cli.kit_root import kit_root
13 from cli.main import main
14 from cli.output import OutputContext
15 from tests.support import (
16 FakeHttpTransport,
17 api_provider_factory,
18 git_status_runner,
19 run_cli,
20 seed_freeze_repo,
21 write_config,
22 )
23
24
25 def test_api_key_not_leaked_in_request_headers(tmp_path: Path, monkeypatch) -> None:
26 secret = "super-secret-api-key-value"
27 monkeypatch.setenv("OVERSEER_REVIEW_API_KEY", secret)
28 monkeypatch.setenv("OVERSEER_REVIEW_API_URL", "https://review.example.com/v1")
29 artifact = seed_freeze_repo(tmp_path, config_name="config-api-reviewer.yaml")
30 rel = artifact.relative_to(tmp_path).as_posix()
31 transport = FakeHttpTransport(review_body=json.dumps({"findings": []}).encode("utf-8"))
32 buffer = io.StringIO()
33 ctx = CliContext.create(
34 runner=git_status_runner(),
35 cwd=tmp_path,
36 kit=kit_root(),
37 output=OutputContext(json_mode=True),
38 review_provider_factory=api_provider_factory(transport),
39 )
40 with redirect_stdout(buffer):
41 code = main(["review", "--freeze", rel, "--json"], ctx=ctx)
42 assert code == 0
43 out = buffer.getvalue()
44 assert secret not in out
45 auth_headers = [call["headers"].get("Authorization", "") for call in transport.calls]
46 assert auth_headers
47 assert all(header.startswith("Bearer ") and secret in header for header in auth_headers)
48
49
50 def test_api_artifact_injection_no_shell(tmp_path: Path, monkeypatch) -> None:
51 monkeypatch.setenv("OVERSEER_REVIEW_API_KEY", "ci-key")
52 monkeypatch.setenv("OVERSEER_REVIEW_API_URL", "https://review.example.com/v1")
53 write_config(tmp_path, "config-api-reviewer.yaml")
54 artifact = tmp_path / "docs" / "evil.md"
55 artifact.parent.mkdir(parents=True)
56 artifact.write_text(
57 "# evil\n\n```yaml\nphase: x\noutputs:\n - id: a\n path: docs/a.md\n frozen: true\n```\n\n$(rm -rf /)\n",
58 encoding="utf-8",
59 )
60 transport = FakeHttpTransport(review_body=json.dumps({"findings": []}).encode("utf-8"))
61 with patch("adapters.runner.subprocess.run") as mocked:
62 code = run_cli(
63 ["review", "--freeze", "docs/evil.md", "--provider", "api"],
64 cwd=tmp_path,
65 runner=git_status_runner(),
66 kit=kit_root(),
67 review_provider_factory=api_provider_factory(transport),
68 )
69 assert mocked.call_count == 0 or all("rm -rf" not in str(call) for call in mocked.call_args_list)
70 assert code in {0, 7, 8}
71 post_calls = [call for call in transport.calls if call["method"] == "POST"]
72 assert post_calls
73 body_text = post_calls[0]["body"].decode("utf-8")
74 assert "rm -rf" in body_text
75 assert "$(rm -rf /)" in body_text
76
77
78 def test_api_path_escape_refused(tmp_path: Path, monkeypatch) -> None:
79 monkeypatch.setenv("OVERSEER_REVIEW_API_KEY", "ci-key")
80 monkeypatch.setenv("OVERSEER_REVIEW_API_URL", "https://review.example.com/v1")
81 seed_freeze_repo(tmp_path, config_name="config-api-reviewer.yaml")
82 transport = FakeHttpTransport()
83 code = run_cli(
84 ["review", "--freeze", "../outside.md", "--provider", "api"],
85 cwd=tmp_path,
86 runner=git_status_runner(),
87 kit=kit_root(),
88 review_provider_factory=api_provider_factory(transport),
89 )
90 assert code == 4
91 assert transport.calls == []
File History 2 commits
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 52 days ago