Option C Tools
Loading...
Searching...
No Matches
test_source_exporter.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oct/tests/tests_tools/test_source_exporter.py
4
5"""
6Purpose
7-------
8Tests for content-level secret scanning (OI-416) in ``oct.tools.source_exporter``.
9
10Responsibilities
11----------------
12- Verify ``_scan_content_for_secrets`` flags likely secret assignments.
13- Verify non-secret assignments are not flagged.
14- Verify ``run_exporter`` emits warnings when ``--warn-secrets`` is on.
15- Verify ``--block-secrets`` excludes files with potential secrets from
16 the exported content.
17
18Diagnostics
19-----------
20Domain: OCT-TEST
21L2: test lifecycle
22L3: assertion details
23L4: deep tracing
24
25Contracts
26---------
27Inputs: tmp_path fixtures
28Outputs: pass/fail assertions
29"""
30
31import json
32from pathlib import Path
33
34import pytest
35
36from oct.tools import source_exporter
37
38
39# ============================================================
40# _scan_content_for_secrets
41# ============================================================
42
44 content = 'password = "hunter2_is_bad"\n'
45 warnings = source_exporter._scan_content_for_secrets(content, "foo.py")
46 assert len(warnings) == 1
47 assert "password" in warnings[0]
48 assert "foo.py:1" in warnings[0]
49
50
52 content = 'api_key = "sk-abc123xyz"\n'
53 warnings = source_exporter._scan_content_for_secrets(content, "bar.py")
54 assert len(warnings) == 1
55 assert "api_key" in warnings[0]
56
57
59 content = 'name = "Alice"\nage = "30"\n'
60 warnings = source_exporter._scan_content_for_secrets(content, "baz.py")
61 assert warnings == []
62
63
65 """Very short values (< 4 chars) don't look like real secrets."""
66 content = 'password = "a"\n'
67 warnings = source_exporter._scan_content_for_secrets(content, "qux.py")
68 assert warnings == []
69
70
72 content = (
73 "# comment\n"
74 "import os\n"
75 "\n"
76 'secret_token = "abc1234xyz"\n'
77 )
78 warnings = source_exporter._scan_content_for_secrets(content, "m.py")
79 assert len(warnings) == 1
80 assert ":4:" in warnings[0]
81
82
83# ============================================================
84# run_exporter integration
85# ============================================================
86
87def _make_minimal_project(tmp_path: Path, file_name: str, content: str) -> Path:
88 """Create a minimal Option C project layout with one python file."""
89 proj = tmp_path / "proj"
90 (proj / "docs").mkdir(parents=True)
91 (proj / "tests").mkdir()
92 (proj / "oc_diagnostics").mkdir()
93 (proj / file_name).write_text(content, encoding="utf-8")
94 return proj
95
96
99 tmp_path, "app.py",
100 '#!/usr/bin/env python3\n'
101 '# -*- coding: utf-8 -*-\n'
102 '# proj/app.py\n'
103 '"""Doc."""\n'
104 'password = "hunter2_literal"\n'
105 )
106 source_exporter.run_exporter(proj, ["--warn-secrets"])
107 captured = capsys.readouterr()
108 assert "potential secret 'password'" in captured.err
109
110
113 tmp_path, "app.py",
114 'password = "hunter2_literal"\n'
115 )
117 captured = capsys.readouterr()
118 assert "potential secret" not in captured.err
119
120
123 tmp_path, "leaky.py",
124 '#!/usr/bin/env python3\n'
125 '# -*- coding: utf-8 -*-\n'
126 '# proj/leaky.py\n'
127 '"""Leaky."""\n'
128 'api_key = "sk-SECRET_VALUE_1234"\n'
129 )
130 source_exporter.run_exporter(proj, ["--block-secrets"])
131 captured = capsys.readouterr()
132 assert "potential secret 'api_key'" in captured.err
133 assert "Skipping" in captured.err
134
135 # Confirm the full-source export file does NOT contain the secret value
136 output_dirs = list(proj.glob("_source_code-*"))
137 assert output_dirs, "exporter should have produced at least one output dir"
138 found_secret = False
139 for p in output_dirs[0].rglob("*.txt"):
140 text = p.read_text(encoding="utf-8", errors="replace")
141 if "sk-SECRET_VALUE_1234" in text:
142 found_secret = True
143 break
144 assert not found_secret, "blocked file should not appear in exported content"
list[str] _scan_content_for_secrets(str content, str rel_path)
None run_exporter(Path root, list[str] args)
test_run_exporter_warns_on_hardcoded_secret(tmp_path, capsys)
test_run_exporter_block_secrets_skips_file(tmp_path, capsys)
Path _make_minimal_project(Path tmp_path, str file_name, str content)
test_run_exporter_no_warn_by_default_without_flag(tmp_path, capsys)