claude-dev-env 7.4.0 → 7.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,121 @@
1
+ """Exact public-contact exceptions preserve all other scanner findings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+ from repository_checks.email_exemptions import load_email_exemptions
11
+ from repository_checks.tracked_secrets import collect_tracked_secret_findings
12
+
13
+ CONTACT = "fixture-contact@company.io"
14
+ CHANGED_CONTACT = "changed-contact@company.io"
15
+ DIGEST = hashlib.sha256(CONTACT.encode()).hexdigest()
16
+
17
+
18
+ def _write_config(root: Path, entries: list[object]) -> None:
19
+ directory = root / "config"
20
+ directory.mkdir(exist_ok=True)
21
+ (directory / "repository-policy.json").write_text(
22
+ json.dumps({"version": 1, "email_exemptions": entries}), encoding="utf-8"
23
+ )
24
+
25
+
26
+ def _entry(**changes: object) -> dict[str, object]:
27
+ return {
28
+ "path": "contacts.py",
29
+ "sha256": DIGEST,
30
+ "reason": "Published support contact",
31
+ **changes,
32
+ }
33
+
34
+
35
+ def test_only_matching_email_and_path_are_exempt(tmp_path: Path) -> None:
36
+ _write_config(tmp_path, [_entry()])
37
+ (tmp_path / "contacts.py").write_text(
38
+ f"contact = '{CONTACT}'\nchanged = '{CHANGED_CONTACT}'\n", encoding="utf-8"
39
+ )
40
+ (tmp_path / "other.py").write_text(f"contact = '{CONTACT}'\n", encoding="utf-8")
41
+ findings = collect_tracked_secret_findings(tmp_path, ["contacts.py", "other.py"])
42
+ assert [finding.relative_path for finding in findings] == [
43
+ "contacts.py",
44
+ "other.py",
45
+ ]
46
+ assert all(finding.message.startswith("[email]") for finding in findings)
47
+
48
+
49
+ def test_matching_contact_has_no_findings(tmp_path: Path) -> None:
50
+ _write_config(tmp_path, [_entry()])
51
+ (tmp_path / "contacts.py").write_text(f"contact = '{CONTACT}'\n", encoding="utf-8")
52
+ assert collect_tracked_secret_findings(tmp_path, ["contacts.py"]) == []
53
+
54
+
55
+ @pytest.mark.parametrize(
56
+ "path",
57
+ [
58
+ "/contacts.py",
59
+ "C:/contacts.py",
60
+ "../contacts.py",
61
+ "src/../contacts.py",
62
+ "*.py",
63
+ "src/?.py",
64
+ "src/[a].py",
65
+ "src\\contacts.py",
66
+ "src//contacts.py",
67
+ "./contacts.py",
68
+ "",
69
+ ],
70
+ )
71
+ def test_rejects_nonliteral_or_nonrelative_paths(tmp_path: Path, path: str) -> None:
72
+ _write_config(tmp_path, [_entry(path=path)])
73
+ with pytest.raises(ValueError):
74
+ load_email_exemptions(tmp_path)
75
+
76
+
77
+ @pytest.mark.parametrize(
78
+ "entry",
79
+ [
80
+ None,
81
+ {},
82
+ _entry(reason=" "),
83
+ _entry(sha256="f" * 63),
84
+ _entry(sha256="z" * 64),
85
+ _entry(category="secret"),
86
+ ],
87
+ )
88
+ def test_rejects_invalid_entries(tmp_path: Path, entry: object) -> None:
89
+ _write_config(tmp_path, [entry])
90
+ with pytest.raises(ValueError):
91
+ load_email_exemptions(tmp_path)
92
+
93
+
94
+ def test_rejects_duplicate_entries(tmp_path: Path) -> None:
95
+ _write_config(tmp_path, [_entry(), _entry(reason="Another explanation")])
96
+ with pytest.raises(ValueError):
97
+ load_email_exemptions(tmp_path)
98
+
99
+
100
+ @pytest.mark.parametrize(
101
+ "content",
102
+ [
103
+ "{",
104
+ "[]",
105
+ '{"version":true,"email_exemptions":[]}',
106
+ '{"version":1,"email_exemptions":{}}',
107
+ '{"version":1,"email_exemptions":[],"extra":0}',
108
+ '{"version":1,"version":1,"email_exemptions":[]}',
109
+ ],
110
+ )
111
+ def test_malformed_config_stops_scanning(tmp_path: Path, content: str) -> None:
112
+ (tmp_path / "config").mkdir()
113
+ (tmp_path / "config" / "repository-policy.json").write_text(
114
+ content, encoding="utf-8"
115
+ )
116
+ with pytest.raises(ValueError):
117
+ collect_tracked_secret_findings(tmp_path, [])
118
+
119
+
120
+ def test_missing_config_has_no_exemptions(tmp_path: Path) -> None:
121
+ assert load_email_exemptions(tmp_path) == frozenset()
@@ -17,6 +17,9 @@ from repository_checks.config.constants import (
17
17
  FINDINGS_EXIT_CODE,
18
18
  SUCCESS_EXIT_CODE,
19
19
  )
20
+ from repository_checks.env_var_documentation import (
21
+ collect_env_var_documentation_findings,
22
+ )
20
23
  from repository_policy_test_support import (
21
24
  commit_tracked_files,
22
25
  initialize_repository,
@@ -131,3 +134,26 @@ def record_read_paths(monkeypatch: pytest.MonkeyPatch) -> list[Path]:
131
134
 
132
135
  monkeypatch.setattr(Path, "read_text", record_read_path)
133
136
  return all_read_paths
137
+
138
+
139
+ @pytest.mark.parametrize("header", ["Symbol", "Component", "Constant"])
140
+ def test_should_ignore_source_tables_before_reading_named_code(
141
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, header: str
142
+ ) -> None:
143
+ repository_root = tmp_path / "repo"
144
+ initialize_repository(repository_root)
145
+ write_text(repository_root / "workflow.py", "def run():\n return\n")
146
+ write_text(
147
+ repository_root / "docs" / "source-map.md",
148
+ f"| {header} | Source |\n"
149
+ "| --- | --- |\n"
150
+ "| `PROCESSING_VISIBLE` | `workflow.py` |\n",
151
+ )
152
+ commit_tracked_files(repository_root)
153
+ patch_unreadable_named_file(
154
+ monkeypatch, "workflow.py", "unrelated source unreadable"
155
+ )
156
+ assert (
157
+ collect_env_var_documentation_findings(repository_root, ["docs/source-map.md"])
158
+ == []
159
+ )
@@ -10,6 +10,7 @@ import pytest
10
10
  from repository_checks.config.constants import (
11
11
  ALL_TRACKED_SECRET_EXACT_EXEMPTIONS,
12
12
  CHECK_ID_TRACKED_PERSONAL_DATA,
13
+ FAILED_CHECK_EXIT_CODE,
13
14
  FINDINGS_EXIT_CODE,
14
15
  )
15
16
  from repository_checks.hook_modules import load_hooks_module
@@ -55,6 +56,18 @@ def test_should_flag_a_tracked_secret(tmp_path: Path) -> None:
55
56
  assert _SYNTHETIC_GITHUB_TOKEN not in stdout_text
56
57
 
57
58
 
59
+ def test_should_fail_closed_for_malformed_repository_email_policy(
60
+ tmp_path: Path,
61
+ ) -> None:
62
+ repository_root = tmp_path / "repo"
63
+ initialize_repository(repository_root)
64
+ write_text(repository_root / "config" / "repository-policy.json", "{")
65
+ commit_tracked_files(repository_root)
66
+ exit_code, stdout_text, _stderr_text = run_policy(repository_root)
67
+ assert exit_code == FAILED_CHECK_EXIT_CODE
68
+ assert f"error: rule failed: {CHECK_ID_TRACKED_PERSONAL_DATA}" in stdout_text
69
+
70
+
58
71
  def test_should_fail_closed_before_reading_a_tracked_symlink_outside_repository(
59
72
  tmp_path: Path, monkeypatch: pytest.MonkeyPatch
60
73
  ) -> None: