-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_purge_gate.py
More file actions
125 lines (99 loc) · 5.51 KB
/
Copy pathtest_purge_gate.py
File metadata and controls
125 lines (99 loc) · 5.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""Purge gate: the engine contains zero person-specific facts (roadmap M1.6, exit criterion A).
Two enforcement layers:
1. Structural checks (committed): no real-looking email addresses, no 12-digit AWS account
IDs, no prototype real-profile filename references, across every authored source tree —
the Python engine (``src/openjobradar``), the CDK infrastructure app (``infra/{bin,lib,
test}``), and the web app (``web/src``, ``web/test``, ADR-0030). Infra is scanned
deliberately: roadmap item A8 names a *hardcoded account ID in the prototype's CDK stack* as
one of the exact failure modes this gate exists to catch, so the day infra source exists is
the day it must be covered, not a follow-up — the same discipline applies to the web app.
2. Strong local tokens (untracked): ``.purge-tokens.local`` (gitignored, one token per line,
case-insensitive substrings) carries the owner-specific identifiers that must never appear
in engine source. The file deliberately stays out of the repository so enforcing the gate
does not re-publish the very identifiers it guards. CI runs structural checks only;
locally ``make verify`` enforces both layers when the file exists.
Scan roots are authored-source directories only — never ``node_modules``, ``.venv``, or
``cdk.out`` — so third-party package metadata (e.g. a public npm maintainer's email baked into
a dependency's own deprecation notice) never produces noise unrelated to this product's own
person-specific facts.
"""
from __future__ import annotations
import re
from pathlib import Path
from typing import Final
import pytest
REPO_ROOT = Path(__file__).resolve().parents[1]
TOKEN_FILE = REPO_ROOT / ".purge-tokens.local"
SCAN_ROOTS: Final[tuple[tuple[Path, tuple[str, ...]], ...]] = (
(REPO_ROOT / "src" / "openjobradar", ("**/*.py", "**/*.json")),
(REPO_ROOT / "infra" / "bin", ("**/*.ts",)),
(REPO_ROOT / "infra" / "lib", ("**/*.ts",)),
(REPO_ROOT / "infra" / "test", ("**/*.ts",)),
(REPO_ROOT / "web" / "src", ("**/*.ts", "**/*.astro")),
(REPO_ROOT / "web" / "test", ("**/*.ts",)),
)
_EMAIL_RE = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
_ALLOWED_EMAIL_DOMAINS = {"example.com", "example.org", "example.net"}
_ACCOUNT_ID_RE = re.compile(r"\b\d{12}\b")
def _source_files() -> list[Path]:
files: list[Path] = []
for root, globs in SCAN_ROOTS:
if not root.exists():
continue
for pattern in globs:
files.extend(root.glob(pattern))
return sorted(files)
def _violations(pattern: re.Pattern[str], label: str) -> list[str]:
hits: list[str] = []
for path in _source_files():
for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
if pattern.search(line):
hits.append(f"{path.relative_to(REPO_ROOT)}:{lineno}: {label}")
return hits
def test_no_real_email_addresses_in_engine_source() -> None:
"""Domain-allowlisting needs the actual matched address, not `_violations`' formatted
label string (which never contains it) — a bare `_violations(_EMAIL_RE, ...)` call would
silently flag every `@example.com` fixture address as if the allowlist did nothing."""
violations: list[str] = []
for path in _source_files():
for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
for match in _EMAIL_RE.finditer(line):
domain = match.group(0).rsplit("@", 1)[-1].lower()
if domain not in _ALLOWED_EMAIL_DOMAINS:
rel = path.relative_to(REPO_ROOT)
violations.append(f"{rel}:{lineno}: email-like literal {match.group(0)!r}")
assert not violations, "\n".join(violations)
def test_no_aws_account_ids_in_engine_source() -> None:
violations = _violations(_ACCOUNT_ID_RE, "possible AWS account ID")
assert not violations, "\n".join(violations)
def test_no_prototype_real_profile_reference() -> None:
violations = _violations(re.compile(r"profile\.yaml"), "prototype real-profile filename")
assert not violations, "\n".join(violations)
def test_local_strong_tokens_absent_from_engine_source() -> None:
if not TOKEN_FILE.exists():
pytest.skip(".purge-tokens.local not present on this machine")
tokens = [
line.strip()
for line in TOKEN_FILE.read_text(encoding="utf-8").splitlines()
if line.strip() and not line.startswith("#")
]
assert tokens, ".purge-tokens.local exists but defines no tokens"
violations: list[str] = []
for token in tokens:
violations.extend(_violations(re.compile(re.escape(token), re.IGNORECASE), f"token {token!r}"))
assert not violations, "\n".join(violations)
def test_gate_covers_the_whole_engine_tree() -> None:
python_files = list((REPO_ROOT / "src" / "openjobradar").glob("**/*.py"))
assert len(python_files) >= 5, "gate silently lost coverage of the Python engine"
def test_gate_covers_the_infra_tree_once_it_exists() -> None:
if not (REPO_ROOT / "infra").exists():
pytest.skip("infra/ not present yet")
ts_files = [
path for path in _source_files() if path.suffix == ".ts" and "infra" in path.parts
]
assert len(ts_files) >= 3, "gate silently lost coverage of the CDK infra app"
def test_gate_covers_the_web_tree_once_it_exists() -> None:
if not (REPO_ROOT / "web").exists():
pytest.skip("web/ not present yet")
web_files = [path for path in _source_files() if "web" in path.parts]
assert len(web_files) >= 3, "gate silently lost coverage of the web app"