ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
suppression_governance_selftest.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Both-direction fixtures for typed repository-governance controls."""
4
5from __future__ import annotations
6
7import ast
8from pathlib import Path
9
10from selftest_assert import expect
11from suppression_checker_census import (
12 census_bindings,
13 mutation_findings,
14 shape_problem,
15 sink_findings,
16)
17from suppression_checker_nonfatal import _active_usages, _option_declarations
18from suppression_checker_scope import (
19 PolicyValue,
20 ResolvedAuthority,
21 _authority_value_digest,
22 _collect_authorities,
23 _condition_dependent,
24 _resolve,
25 _resolve_registered,
26 _scan_scope_file,
27)
28from suppression_generated_markers import effective_head_waiver, generated_records
29from suppression_governance import (
30 GLOBAL_EXCLUSION_AUTHORITIES,
31 scan_ansible_lint_config,
32 scan_ci_parity_exemptions,
33 scan_doxygen_controls,
34 scan_gitignore_exemptions,
35 scan_other_language_controls,
36 scan_registered_global_exclusions,
37 scan_security_controls,
38)
39from suppression_hardware_todo import scan_hardware_todo_controls
40from suppression_hash_lex import hash_lines
41from suppression_scope_registry import AUTHORITY_SCHEMAS, AuthoritySchema
42
43EXPECTED_ANSIBLE_FIXTURE_ROWS = 3
44EXPECTED_DOXYGEN_FIXTURE_ROWS = 7
45EXPECTED_SECURITY_FIXTURE_ROWS = 3
46EXPECTED_NONFATAL_FIXTURE_ROWS = 9
47EXPECTED_UNCLASSIFIED_FIXTURES = 2
48
49
50def _assert_generated_body(root: Path, generator: str, canonical: str, failures: list[str]) -> None:
51 """Assert generated provenance requires real generated content."""
52 record, findings = generated_records(
53 "generated.py", canonical, frozenset({"generated.py", generator}), root
54 )
55 expect(
56 len(record) == 1 and not findings,
57 "must fire: canonical generated marker has provenance and a real body",
58 failures,
59 )
60 for body in (
61 "",
62 "\n\n",
63 "# comment only\n",
64 "/* comment only */\n",
65 "/* unterminated comment only\n",
66 ):
67 marker_only = "# @generated by tools/generator.py -- fixture recipe\n" + body
68 record, problems = generated_records(
69 "generated.py", marker_only, frozenset({"generated.py", generator}), root
70 )
71 expect(
72 not record and any(item.code == "generated-marker-without-body" for item in problems),
73 "must fire: marker-only, blank-only, and comment-only artifacts do not waive",
74 failures,
75 )
76 for text in (
77 'value = "@generated by tools/generator.py -- string prose"\n',
78 "\n" * 40 + canonical,
79 ):
80 waiver, problems = effective_head_waiver(text, tracked_paths=frozenset({generator}))
81 expect(
82 waiver is None and not problems,
83 "quiet: prose and deep generated hints do not waive",
84 failures,
85 )
86
87
88def _assert_generated_syntax(generator: str, failures: list[str]) -> None:
89 """Assert malformed and punctuation-only marker syntax fails closed."""
90 for malformed in (
91 "# @generated\n",
92 "# @generated by\n",
93 "# FILE-SIZE-OK\n",
94 "# FILE-SIZE-OK:\n",
95 ):
96 waiver, problems = effective_head_waiver(malformed)
97 expect(
98 waiver is None and any(item.code == "malformed-generated-marker" for item in problems),
99 "must fire: bare and malformed generated/file-size markers are inventoried",
100 failures,
101 )
102 for punctuation in ("-", "--", ":", "()"):
103 text = f"# @generated by tools/generator.py {punctuation}\nVALUE = 1\n"
104 waiver, problems = effective_head_waiver(text, tracked_paths=frozenset({generator}))
105 expect(
106 waiver is None
107 and any(item.code == "non-substantive-waiver-reason" for item in problems),
108 "must fire: delimiter-only generated rationales cannot waive",
109 failures,
110 )
111 waiver, problems = effective_head_waiver("# FILE-SIZE-OK: -\nVALUE = 1\n")
112 expect(
113 waiver is None and any(item.code == "non-substantive-waiver-reason" for item in problems),
114 "must fire: delimiter-only FILE-SIZE-OK rationale cannot waive",
115 failures,
116 )
117
118
119def _assert_generated_provenance(root: Path, canonical: str, failures: list[str]) -> None:
120 """Assert generator identity is tracked, real, and distinct from output."""
121 self_marker = "# @generated by tools/generated.py -- deterministic self fixture\nVALUE = 1\n"
122 record, problems = generated_records(
123 "tools/generated.py", self_marker, frozenset({"tools/generated.py"}), root
124 )
125 expect(
126 not record and any(item.code == "self-generated-provenance" for item in problems),
127 "must fire: a generated artifact cannot name itself as its generator",
128 failures,
129 )
130 missing_marker = (
131 "# @generated by tools/missing.py -- deterministic missing fixture\nVALUE = 1\n"
132 )
133 record, problems = generated_records(
134 "generated.py",
135 missing_marker,
136 frozenset({"generated.py", "tools/missing.py"}),
137 root,
138 )
139 expect(
140 not record and any(item.code == "missing-generated-provenance" for item in problems),
141 "must fire: tracked text cannot impersonate a missing generator file",
142 failures,
143 )
144 _waiver, problems = effective_head_waiver(canonical, tracked_paths=frozenset({"generated.py"}))
145 expect(
146 any(item.code == "untracked-generated-provenance" for item in problems),
147 "must fire: generated provenance must resolve to a tracked path",
148 failures,
149 )
150 _assert_generator_identity(root, failures)
151
152
153def _assert_generator_identity(root: Path, failures: list[str]) -> None:
154 """Assert generator identity rejects symlink, hard-link, and nonregular routes."""
155 artifact = root / "generated.py"
156 artifact.write_text(
157 "# @generated by tools/alias.py -- symlink loop fixture\nVALUE = 1\n",
158 encoding="ascii",
159 )
160 (root / "tools" / "alias.py").symlink_to(artifact)
161 tracked = frozenset({"generated.py", "tools/alias.py", "tools/linked.py", "tools/dir.py"})
162 record, problems = generated_records("generated.py", artifact.read_text(), tracked, root)
163 expect(
164 not record and any(item.code == "symlinked-generated-provenance" for item in problems),
165 "must fire: a symlink generator cannot launder self-provenance",
166 failures,
167 )
168 escape = root / "tools" / "escape.py"
169 escape.symlink_to(root.parent / "outside-generator.py")
170 text = "# @generated by tools/escape.py -- escaping symlink fixture\nVALUE = 1\n"
171 record, problems = generated_records(
172 "generated.py", text, tracked | frozenset({"tools/escape.py"}), root
173 )
174 expect(
175 not record and any(item.code == "symlinked-generated-provenance" for item in problems),
176 "must fire: a symlink pointing outside the repository cannot waive",
177 failures,
178 )
179 (root / "tools" / "linked.py").hardlink_to(artifact)
180 text = "# @generated by tools/linked.py -- hard-link loop fixture\nVALUE = 1\n"
181 artifact.write_text(text, encoding="ascii")
182 record, problems = generated_records("generated.py", text, tracked, root)
183 expect(
184 not record and any(item.code == "self-generated-provenance" for item in problems),
185 "must fire: a hard-linked generator shares the artifact's file identity",
186 failures,
187 )
188 (root / "tools" / "dir.py").mkdir()
189 text = "# @generated by tools/dir.py -- nonregular generator fixture\nVALUE = 1\n"
190 record, problems = generated_records("generated.py", text, tracked, root)
191 expect(
192 not record and any(item.code == "missing-generated-provenance" for item in problems),
193 "must fire: a directory is not a regular generator file",
194 failures,
195 )
196 text = "# @generated by tools/generator.py -- distinct real generator fixture\nVALUE = 1\n"
197 artifact.write_text(text, encoding="ascii")
198 record, problems = generated_records(
199 "generated.py", text, frozenset({"generated.py", "tools/generator.py"}), root
200 )
201 expect(
202 len(record) == 1 and not problems,
203 "quiet: a distinct regular tracked generator still waives",
204 failures,
205 )
206 artifact.unlink()
207
208
209def _assert_generated(root: Path, failures: list[str]) -> None:
210 """Run every generated-marker grammar and provenance assertion."""
211 generator = "tools/generator.py"
212 (root / "tools").mkdir(exist_ok=True)
213 (root / generator).write_text("# fixture generator\n", encoding="ascii")
214 canonical = "# @generated by tools/generator.py -- deterministic fixture recipe\nVALUE = 1\n"
215 _assert_generated_body(root, generator, canonical, failures)
216 _assert_generated_syntax(generator, failures)
217 _assert_generated_provenance(root, canonical, failures)
218
219
220def _assert_config_parsers(failures: list[str]) -> None:
221 """Assert structured Ansible/Doxygen authorities and prose controls."""
222 ansible = """---
223skip_list:
224 - name[template] # role is generated from a pinned template
225warn_list:
226 - experimental # advisory rollout
227exclude_paths:
228 - fixtures/ # upstream syntax fixtures
229"""
230 rows, findings = scan_ansible_lint_config(".ansible-lint", ansible)
231 expect(
232 len(rows) == EXPECTED_ANSIBLE_FIXTURE_ROWS
233 and not findings
234 and all(not item.concerns for item in rows),
235 "must fire: typed ansible-lint list items carry item reasons",
236 failures,
237 )
238 rows, _findings = scan_ansible_lint_config("docs/readme.txt", "ansible-lint skip_list is prose")
239 expect(not rows, "quiet: ansible-lint prose is not configuration", failures)
240 expect(
241 not GLOBAL_EXCLUSION_AUTHORITIES
242 and not scan_registered_global_exclusions("pyproject.toml", "exclude_files = ['fixture']")[
243 0
244 ],
245 "quiet: no generic non-Ruff exclusion substring becomes an authority",
246 failures,
247 )
248 doxy = """# Shared warning policy.
249WARN_IF_UNDOCUMENTED = NO
250WARN_NO_PARAMDOC = NO
251WARN_AS_ERROR = NO
252# Exclude generated and vendor inputs.
253EXCLUDE = vendor \\
254 generated
255EXCLUDE_PATTERNS += */build/* \\
256 */tests/*
257"""
258 rows, findings = scan_doxygen_controls("Doxyfile", doxy)
259 expect(
260 len(rows) == EXPECTED_DOXYGEN_FIXTURE_ROWS and not findings,
261 "must fire: Doxyfile scalar, multiline, and += assignments split per value",
262 failures,
263 )
264 rows, _findings = scan_doxygen_controls("vendor/Doxyfile", doxy)
265 expect(not rows, "quiet: only the repository-root Doxyfile is authoritative", failures)
266
267
268def _assert_bound_markers(root: Path, failures: list[str]) -> None:
269 """Assert gitignore and CI markers bind to their exact consumers."""
270 text = "# gitignore-scope-ok: producer writes relative to cwd\ndownloads/\n"
271 rows, findings = scan_gitignore_exemptions(".gitignore", text)
272 expect(
273 len(rows) == 1 and rows[0].scope == "pattern:downloads/" and not findings,
274 "must fire: gitignore marker binds one unanchored directory pattern",
275 failures,
276 )
277 rows, findings = scan_gitignore_exemptions(".gitignore", "# gitignore-scope-ok:\ndownloads/\n")
278 expect(
279 not rows and bool(findings),
280 "must fire: blank gitignore marker reason cannot waive",
281 failures,
282 )
283 workflow = root / ".github" / "workflows" / "fixture.yml"
284 workflow.parent.mkdir(parents=True, exist_ok=True)
285 workflow.write_text(
286 """name: fixture
287on: [push]
288jobs:
289 fixture:
290 runs-on: ubuntu-latest
291 steps:
292 - name: Provision fixture runner
293 run: |
294 # ci-parity: infra -- installs fixture runner packages only
295 true
296""",
297 encoding="ascii",
298 )
299 rows, findings = scan_ci_parity_exemptions(root, [".github/workflows/fixture.yml"])
300 expect(
301 len(rows) == 1
302 and rows[0].scope == "job:fixture/step:Provision fixture runner"
303 and not findings,
304 "must fire: CI infra marker binds workflow/job/step identity",
305 failures,
306 )
307
308
309def _assert_language_controls(failures: list[str]) -> None:
310 """Assert exact security/other-language syntax and ownership boundaries."""
311 security = """# nosemgrep:python.lang.security.audit -- generated command is fixed
312# NOSONAR -- compatibility fixture is intentionally analyzer-neutral
313# lgtm[py/path-injection] -- legacy query evidence remains reviewable
314value = "NOSONAR"
315"""
316 rows, findings = scan_security_controls("scripts/checks/check_suppressions.py", security)
317 expect(
318 len(rows) == EXPECTED_SECURITY_FIXTURE_ROWS
319 and {item.tool for item in rows} == {"semgrep", "sonarqube", "codeql-legacy"}
320 and not findings,
321 "must fire: exact first-party Semgrep, Sonar, and legacy LGTM comments",
322 failures,
323 )
324 rows, _findings = scan_security_controls("libs/third_party/vendor/fixture.py", security)
325 expect(not rows, "quiet: vendor security evidence is not first-party debt", failures)
326 fixtures = {
327 "tools/fixture.java": (
328 '@SuppressWarnings("unchecked") // generated bridge has a checked boundary\n',
329 "java",
330 ),
331 "tools/fixture.kt": (
332 '@Suppress("UNCHECKED_CAST") // generated bridge has a checked boundary\n',
333 "kotlin",
334 ),
335 "tools/fixture.rs": (
336 "#[expect(clippy::cast_possible_truncation)] // bounded fixture conversion\n",
337 "rust",
338 ),
339 "tools/fixture.go": (
340 "//nolint:gosec // generated fixture uses a fixed command\n",
341 "go",
342 ),
343 }
344 for path, (text, tool) in fixtures.items():
345 rows, findings = scan_other_language_controls(path, text)
346 expect(
347 len(rows) == 1 and rows[0].tool == tool and not findings,
348 f"must fire: exact {tool} suppression syntax in a matching file",
349 failures,
350 )
351 rust = fixtures["tools/fixture.rs"][0]
352 rows, _findings = scan_other_language_controls("tools/fixture.py", rust)
353 expect(not rows, "quiet: other-language syntax in the wrong extension is data", failures)
354
355
356def _assert_cmake_bracket_lexing(failures: list[str]) -> None:
357 """Assert balanced bracket comments mask directives and EOF fails closed."""
358 text = """#[=[
359# nosemgrep:fixture.hidden -- bracket-comment payload
360]=]
361# nosemgrep:fixture.live -- ordinary CMake comment
362"""
363 lines, findings = hash_lines("CMakeLists.txt", text)
364 comments = [(item.line, item.comment.strip()) for item in lines if item.comment]
365 expect(
366 comments == [(4, "nosemgrep:fixture.live -- ordinary CMake comment")] and not findings,
367 "quiet: balanced CMake bracket-comment payload is masked while outside fires",
368 failures,
369 )
370 lines, findings = hash_lines("fixture.cmake", "#[[ nosemgrep:fixture.hidden ]]\n")
371 expect(
372 all(not item.comment for item in lines) and not findings,
373 "quiet: simple CMake bracket comments are masked too",
374 failures,
375 )
376 _lines, findings = hash_lines("fixture.cmake", "#[[ unterminated\n")
377 expect(
378 any(item.code == "unterminated-cmake-bracket" for item in findings),
379 "must fire: unterminated CMake bracket comment fails closed",
380 failures,
381 )
382
383
384def _assert_hardware_binding(root: Path, failures: list[str]) -> None:
385 """Assert only named-hardware CANNED candidates bind, never SHADOW."""
386 app = root / "apps" / "fixture"
387 app.mkdir(parents=True, exist_ok=True)
388 waived = app / "waived.c"
389 waived.write_text(
390 """/* TODO(fixture radio module is not installed) */
391int fixture_radio(int value) { (void)value; return k_ra8_err_not_supported; }
392""",
393 encoding="ascii",
394 )
395 generic = app / "generic.c"
396 generic.write_text(
397 """/* TODO: later */
398int fixture_generic(int value) { (void)value; return k_ra8_err_not_supported; }
399""",
400 encoding="ascii",
401 )
402 rows, findings = scan_hardware_todo_controls(
403 root, ["apps/fixture/waived.c", "apps/fixture/generic.c"]
404 )
405 expect(
406 len(rows) == 1 and rows[0].scope == "function:fixture_radio" and not findings,
407 "must fire: named hardware TODO binds one canned stub; generic TODO stays quiet",
408 failures,
409 )
410 real = app / "real.c"
411 real.write_text("int fixture_radio(int value) { return value + 1; }\n", encoding="ascii")
412 rows, _findings = scan_hardware_todo_controls(
413 root, ["apps/fixture/waived.c", "apps/fixture/real.c"]
414 )
415 expect(not rows, "quiet: SHADOW candidates are never hardware-waivable", failures)
416
417
418def _assert_scope_value_semantics(failures: list[str]) -> None:
419 """Assert explicit AST schemas preserve values, reasons, and identity digests."""
420 expr = ast.parse("VALUE = ('path with spaces/', 'two/')\n").body[0]
421 expect(isinstance(expr, ast.Assign), "fixture AST is one assignment", failures)
422 if not isinstance(expr, ast.Assign):
423 return
424 values = _resolve(expr.value, {}, "scripts/checks/check_suppressions.py")
425 expect(
426 [item.value for item in values] == ["path with spaces/", "two/"],
427 "must fire: paths containing spaces remain concrete values, never reasons",
428 failures,
429 )
430 reasoned = ast.parse("VALUE = (('path with spaces/', 'why'),)\n").body[0]
431 expect(isinstance(reasoned, ast.Assign), "reason fixture AST is one assignment", failures)
432 if isinstance(reasoned, ast.Assign):
433 values = _resolve(reasoned.value, {}, "fixture.py", reasoned_pairs=True)
434 expect(
435 values == [PolicyValue("path with spaces/", "why")],
436 "must fire: explicit pair schema accepts a one-word rationale",
437 failures,
438 )
439 malformed = ast.parse("VALUE = (('path/', ''),)\n").body[0]
440 if isinstance(malformed, ast.Assign):
441 expect(
442 not _resolve(malformed.value, {}, "fixture.py", reasoned_pairs=True),
443 "must fire: explicit pair schema rejects an empty or ambiguous rationale",
444 failures,
445 )
446 schema = AuthoritySchema("path-exclusion")
447 original = {
448 "fixture.py:EXCLUDED": ResolvedAuthority(
449 "fixture.py", 1, "EXCLUDED", schema, (PolicyValue("one"), PolicyValue("two"))
450 )
451 }
452 swapped = {
453 "fixture.py:EXCLUDED": ResolvedAuthority(
454 "fixture.py", 1, "EXCLUDED", schema, (PolicyValue("one"), PolicyValue("three"))
455 )
456 }
457 expect(
458 _authority_value_digest(original) != _authority_value_digest(swapped),
459 "must fire: same-count authority value swaps change the authenticated digest",
460 failures,
461 )
462
463
464def _assert_scope_registry(root: Path, failures: list[str]) -> None:
465 """Assert the census classifies every constant and new names fail closed."""
466 representative = {
467 "scripts/checks/annot_scope.py:SCAN_DIRS",
468 "scripts/checks/check_mcdc_floor.py:OUT_OF_SCOPE_PREFIXES",
469 "scripts/checks/annot_clang.py:GENERATED_HEADERS",
470 "scripts/checks/check_core_layering.py:HOSTED_HEADERS",
471 "scripts/checks/check_magic_numbers.py:IGNORED_INT",
472 "scripts/checks/line_citation_lex.py:THIRD_PARTY_RE",
473 "scripts/checks/suppression_catalog.py:VENDOR_PREFIXES",
474 }
475 expect(
476 representative <= set(AUTHORITY_SCHEMAS),
477 "must fire: every reported scope-authority class has an explicit schema",
478 failures,
479 )
480 fixture_root = root / "scope-registry-fixture"
481 unknown = fixture_root / "scripts" / "checks" / "check_suppressions.py"
482 unknown.parent.mkdir(parents=True)
483 unknown.write_text(
484 "SCAN_DIRS = ('fixture',)\nARBITRARY_NEW_ROOTS = ('fixture/',)\nplain = 1\n",
485 encoding="ascii",
486 )
487 _authorities, problems, _diagnosed, census = _scan_scope_file(
488 fixture_root, "scripts/checks/check_suppressions.py", {}
489 )
490 unclassified = [item for item in problems if item.code == "unclassified-checker-constant"]
491 expect(
492 len(unclassified) == len(census) and len(census) == EXPECTED_UNCLASSIFIED_FIXTURES,
493 "must fire: unregistered constants of any name fail closed; lowercase stays out",
494 failures,
495 )
496
497
498def _assert_scope_file_return_contract(root: Path, failures: list[str]) -> None:
499 """Assert every parse outcome returns the same census-identity type."""
500 rel = "scripts/checks/check_suppressions.py"
501
502 valid_root = root / "scope-return-valid"
503 valid = valid_root / rel
504 valid.parent.mkdir(parents=True)
505 valid.write_text("MAX_CHECK_DETAILS = 80\nplain = 1\n", encoding="ascii")
506 _authorities, quiet, _diagnosed, census = _scan_scope_file(valid_root, rel, {})
507 expect(
508 not quiet and census == {f"{rel}:MAX_CHECK_DETAILS"},
509 "quiet: a valid checker returns its exact census-identity set",
510 failures,
511 )
512
513 invalid_root = root / "scope-return-invalid"
514 invalid = invalid_root / rel
515 invalid.parent.mkdir(parents=True)
516 invalid.write_text("MAX_CHECK_DETAILS = (\n", encoding="ascii")
517 _authorities, problems, _diagnosed, census = _scan_scope_file(invalid_root, rel, {})
518 expect(
519 census == set() and any(item.code == "checker-scope-ast" for item in problems),
520 "must fire: an unreadable checker reports an AST finding with an empty census set",
521 failures,
522 )
523 _authorities, collected, _diagnosed = _collect_authorities(invalid_root, [rel])
524 expect(
525 any(item.code == "checker-scope-ast" for item in collected),
526 "must fire: repository collection preserves the parse finding without a type crash",
527 failures,
528 )
529
530
531def _census_mutation_fixtures() -> dict[str, str]:
532 """Return one fixture per authority-mutation route the census rejects.
533
534 Kept apart from the assertion so the assertion stays about the RULE
535 rather than about the table, and so a new route is added in one place.
536
537 Returns:
538 Label to Python source, each of which must produce a finding.
539 """
540 return {
541 "augmented assignment": "EXEMPT_PREFIXES = ('a/',)\nEXEMPT_PREFIXES += ('hidden/',)\n",
542 "mutator call": "EXEMPT_PREFIXES = ['a/']\nEXEMPT_PREFIXES.append('hidden/')\n",
543 "update call": "PATH_CLASS = {'a': 'b'}\nPATH_CLASS.update({'hidden/': 'x'})\n",
544 "conditional rebinding": (
545 "EXEMPT_PREFIXES = ('a/',)\nif True:\n EXEMPT_PREFIXES = ('b/',)\n"
546 ),
547 "double binding": "EXEMPT_PREFIXES = ('a/',)\nEXEMPT_PREFIXES = ('b/',)\n",
548 "subscript store": "PATH_CLASS = {'a': 'b'}\nPATH_CLASS['hidden/'] = 'x'\n",
549 "alias mutation": (
550 "EXEMPT_PREFIXES = ['a/']\nALIAS = EXEMPT_PREFIXES\nALIAS.append('hidden/')\n"
551 ),
552 "global rebinding": (
553 "EXEMPT_PREFIXES = ('a/',)\ndef f():\n"
554 " global EXEMPT_PREFIXES\n EXEMPT_PREFIXES = ()\n"
555 ),
556 "deletion": "EXEMPT_PREFIXES = ('a/',)\ndel EXEMPT_PREFIXES\n",
557 "imported mutation": "from check_fixture import AUTH\nAUTH.update(('hidden/',))\n",
558 "imported augment": "from check_fixture import AUTH as LOCAL\nLOCAL += ('hidden/',)\n",
559 "module attribute": "import check_fixture\ncheck_fixture.AUTH += ('hidden/',)\n",
560 "module setattr": "import check_fixture\nsetattr(check_fixture, 'AUTH', ())\n",
561 "dynamic namespace": (
562 "EXEMPT_PREFIXES = ('a/',)\ndef f():\n globals()['EXEMPT_PREFIXES'] = ()\n"
563 ),
564 "module attribute alias": (
565 "import check_fixture as mod\nalias = mod.AUTH\nalias.update({'hidden/': 'x'})\n"
566 ),
567 "imported alias append": (
568 "from check_fixture import AUTH\nalias = AUTH\nalias.append('hidden/')\n"
569 ),
570 "operator setitem": (
571 "import operator\nimport check_fixture as mod\n"
572 "operator.setitem(mod.AUTH, 'hidden/', 'x')\n"
573 ),
574 "namespace item mutation": (
575 "import check_fixture as mod\nvars(mod)['AUTH'].update({'hidden/': 'x'})\n"
576 ),
577 "namespace replacement": ("import check_fixture as mod\nvars(mod).update({'AUTH': {}})\n"),
578 "module dict store": ("import check_fixture as mod\nmod.__dict__['AUTH'] = {}\n"),
579 "dynamic import chain": "__import__('check_fixture').AUTH.append('hidden/')\n",
580 "module delattr": "import check_fixture as mod\ndelattr(mod, 'AUTH')\n",
581 "getattr alias": (
582 "import check_fixture as mod\nalias = getattr(mod, 'AUTH')\nalias.clear()\n"
583 ),
584 "unbound mutator": (
585 "import check_fixture as mod\ndict.update(mod.AUTH, {'hidden/': 'x'})\n"
586 ),
587 }
588
589
590def _assert_census_immutability(failures: list[str]) -> None:
591 """Assert every authority mutation route is rejected and clean reads pass."""
592 protected = {"EXEMPT_PREFIXES", "PATH_CLASS"}
593 modules = {"check_fixture": frozenset({"AUTH"})}
594 must_fire = _census_mutation_fixtures()
595 for label, source in must_fire.items():
596 tree = ast.parse(source)
597 found = mutation_findings("fixture.py", tree, protected, modules)
598 expect(bool(found), f"must fire: authority {label} is rejected", failures)
599 quiet = (
600 "import check_fixture as mod\n"
601 "EXEMPT_PREFIXES = ('a/', 'b/')\n"
602 "OTHER = tuple(sorted(EXEMPT_PREFIXES))\n"
603 "def f(rel):\n"
604 " seen = set()\n"
605 " seen.update(EXEMPT_PREFIXES)\n"
606 " seen.update(mod.AUTH)\n"
607 " seen.add(rel)\n"
608 " fields = vars(rel)\n"
609 " return rel.startswith(EXEMPT_PREFIXES) and seen and fields\n"
610 )
611 found = mutation_findings("fixture.py", ast.parse(quiet), protected, modules)
612 expect(not found, "quiet: single binding with read-only uses passes", failures)
613 expect(
614 _condition_dependent(ast.parse("X = ('a',) if True else ('b',)").body[0].value)
615 and not _condition_dependent(ast.parse("X = ('a', 'b')").body[0].value),
616 "must fire: condition-dependent authority values are detected exactly",
617 failures,
618 )
619 expression_schema = AuthoritySchema("suppression-control-plane", "expression-digest")
620 original = ast.parse("X = ('safe',) if flag else ('closed',)").body[0].value
621 mutated = ast.parse("X = ('safe',) if not flag else ('closed',)").body[0].value
622 original_values = _resolve_registered(original, {}, "fixture.py", expression_schema)
623 mutated_values = _resolve_registered(mutated, {}, "fixture.py", expression_schema)
624 expect(
625 _condition_dependent(original)
626 and len(original_values) == 1
627 and original_values[0].value.startswith("sha256:"),
628 "quiet: expression digest authenticates a conditional security authority",
629 failures,
630 )
631 expect(
632 original_values != mutated_values,
633 "must fire: expression digest changes with conditional security logic",
634 failures,
635 )
636
637
638def _census_sink_fixtures() -> tuple[str, ...]:
639 """Return one fixture per way a scope literal can reach a filter sink.
640
641 Returns:
642 Python sources that must each yield an inline-scope-literal finding.
643 """
644 return (
645 "def f(rel):\n return rel.startswith(('libs/third_party/', 'x/'))\n",
646 "def f(rel):\n excluded = {'third_party', 'build'}\n"
647 " return any(p in excluded for p in rel.parts)\n",
648 "def f(rel):\n return any(p in {'a', 'b'} for p in rel.parts)\n",
649 "def f(rel):\n scope = ('evil/',)\n return rel.startswith(scope)\n",
650 "def f(rel):\n scope = ('evil/',)\n return rel.endswith(scope)\n",
651 "def f(path):\n parts = {'vendor', 'generated'}\n"
652 " return bool(parts & set(path.parts))\n",
653 "def f(rel):\n return (scope := ('evil/',)) and rel.startswith(scope)\n",
654 "def f(rel):\n scope = ['evil/']\n alias = scope\n"
655 " return rel.startswith(tuple(alias))\n",
656 "def f(rel, flag):\n return rel.startswith(('evil/',) if flag else ('other/',))\n",
657 "def outer(rel):\n scope = ('evil/',)\n"
658 " def inner():\n return rel.startswith(scope)\n return inner\n",
659 )
660
661
662def _assert_census_sinks(failures: list[str]) -> None:
663 """Assert inline scope literals in filter sinks fail while authorities pass."""
664 must_fire = _census_sink_fixtures()
665 for source in must_fire:
666 found = sink_findings("fixture.py", ast.parse(source))
667 expect(
668 any(item.code == "inline-scope-literal" for item in found),
669 "must fire: inline scope literal reaches a filter sink",
670 failures,
671 )
672 quiet = (
673 "AUTH = ('libs/third_party/',)\nPARTS = frozenset({'third_party'})\n"
674 "def f(rel, path):\n"
675 " a = rel.startswith(AUTH)\n"
676 " b = any(p in PARTS for p in path.parts)\n"
677 " c = rel.startswith(('#', '//'))\n"
678 " d = rel.startswith(tuple(AUTH))\n"
679 " e = path.suffix in {'.c', '.h'}\n"
680 " names = ['alpha', 'beta']\n"
681 " names.append('gamma')\n"
682 " return a or b or c or d or e or names\n"
683 "def selftest(scope, failures):\n"
684 " expect(\n"
685 " not any(s.startswith(('libs/third_party/', 'x/')) for s in scope),\n"
686 " 'vendored SOUP stays out of scope',\n"
687 " failures,\n"
688 " )\n"
689 " assert all(p not in {'a/b'} for p in scope)\n"
690 )
691 found = sink_findings("fixture.py", ast.parse(quiet))
692 expect(
693 not found,
694 "quiet: declared authorities, non-path tokens, and test expectations pass sinks",
695 failures,
696 )
697 bindings = census_bindings(
698 ast.parse(
699 "A = 1\nB: int = 2\nC, D = 3, 4\nA += 1\nlower = 5\ndef f():\n E = 6\n return E\n"
700 )
701 )
702 expect(
703 [name for name, _line, _value in bindings] == ["A", "B", "C", "D", "A"],
704 "must fire: census sees every module-level constant binding form only",
705 failures,
706 )
707 walrus = census_bindings(
708 ast.parse("_ = (ROOTS := ('evil/',))\ndef f():\n (LOCAL := 1)\n return LOCAL\n")
709 )
710 expect(
711 [name for name, _line, _value in walrus] == ["ROOTS"],
712 "must fire: a module-level walrus constant is censused, a function-local one is not",
713 failures,
714 )
715
716
717def _assert_census_shapes(failures: list[str]) -> None:
718 """Assert non-authority categories reject scope-shaped values."""
719 tuple_value = ast.parse("X = ('a', 'b')").body[0].value
720 regular_checker = "scripts/checks/check_suppressions.py"
721 selftest_checker = "scripts/checks/suppression_governance_selftest.py"
722 fixture_publishers = (
723 "scripts/checks/hil_convergence_safety_runtime_loader_harness.py",
724 "scripts/checks/hil_convergence_safety_runtime_sources.py",
725 )
726 unreviewed_publisher = (
727 "scripts/checks/unreviewed_runtime_sources.py" # PATHREF-OK: non-publisher fixture
728 )
729 list_value = ast.parse("X = ['a', 'b']").body[0].value
730 int_value = ast.parse("X = 42").body[0].value
731 scope_value = ast.parse("X = 'libs/third_party/'").body[0].value
732 plain_text = ast.parse("X = 'run the gate'").body[0].value
733 expect(
734 shape_problem(regular_checker, "numeric-format", list_value, has_selftest=False) is not None
735 and shape_problem(regular_checker, "exit-code", tuple_value, has_selftest=False)
736 is not None,
737 "must fire: container values contradict numeric categories",
738 failures,
739 )
740 expect(
741 shape_problem(regular_checker, "diagnostic-text", scope_value, has_selftest=False)
742 is not None,
743 "must fire: a path-prefix literal is scope-shaped in any non-authority",
744 failures,
745 )
746 expect(
747 shape_problem(regular_checker, "selftest-fixture", plain_text, has_selftest=False)
748 is not None,
749 "must fire: selftest-fixture classification outside a selftest module",
750 failures,
751 )
752 expect(
753 shape_problem(unreviewed_publisher, "selftest-fixture", int_value, has_selftest=False)
754 is not None,
755 "must fire: unreviewed fixture publisher cannot own selftest fixtures",
756 failures,
757 )
758 expect(
759 shape_problem(regular_checker, "numeric-format", int_value, has_selftest=False) is None
760 and shape_problem(regular_checker, "diagnostic-text", plain_text, has_selftest=False)
761 is None
762 and shape_problem(selftest_checker, "selftest-fixture", plain_text, has_selftest=False)
763 is None,
764 "quiet: matching shapes pass their categories",
765 failures,
766 )
767 expect(
768 all(
769 shape_problem(publisher, "selftest-fixture", int_value, has_selftest=False) is None
770 for publisher in fixture_publishers
771 ),
772 "quiet: reviewed fixture publishers own fixture publication data",
773 failures,
774 )
775
776
777def _assert_nonfatal_parsers(root: Path, failures: list[str]) -> None:
778 """Assert declarations and resolved shell invocations model effective modes."""
779 tree = ast.parse(
780 """import argparse
781p = argparse.ArgumentParser()
782p.add_argument('--warn', action='store_true', help='fixture advisory mode')
783"""
784 )
785 rows, findings = _option_declarations("scripts/checks/check_world_tags.py", tree)
786 expect(
787 len(rows) == 1 and not findings,
788 "must fire: argparse nonfatal availability is a declaration row",
789 failures,
790 )
791 fixture_root = root / "nonfatal-usage-fixture"
792 caller = fixture_root / "scripts" / "ci" / "gates" / "checks.sh"
793 caller.parent.mkdir(parents=True, exist_ok=True)
794 caller.write_text(
795 """python3 scripts/checks/check_world_tags.py --warn
796checker=scripts/checks/check_world_tags.py; python3 "$checker" --warn
797checker_argv=(python3 scripts/checks/check_world_tags.py --warn)
798"${checker_argv[@]}"
799local local_checker=scripts/checks/check_world_tags.py; python3 "$local_checker" --warn
800readonly readonly_checker=scripts/checks/check_world_tags.py; python3 "$readonly_checker" --warn
801local -a local_argv=(python3 scripts/checks/check_world_tags.py)
802local_argv+=(--warn)
803"${local_argv[@]}"
804bash -c "python3 scripts/checks/check_world_tags.py --warn"
805python3 scripts/checks/check_world_tags.py
806python3 scripts/checks/cite_check.py --require-cites
807python3 scripts/checks/check_world_tags.py --strict
808python3 scripts/checks/cite_check.py --require-cites --strict
809echo scripts/checks/check_world_tags.py --warn
810# python3 scripts/checks/check_world_tags.py --warn
811""",
812 encoding="ascii",
813 )
814 quiet = caller.with_name("hygiene.sh")
815 quiet.write_text(
816 'text="scripts/checks/check_world_tags.py --warn"\necho "$text"\n',
817 encoding="ascii",
818 )
819 rows = _active_usages(
820 fixture_root,
821 ["scripts/ci/gates/checks.sh", "scripts/ci/gates/hygiene.sh"],
822 )
823 expect(
824 len(rows) == EXPECTED_NONFATAL_FIXTURE_ROWS
825 and all(item.directive == "nonfatal-invocation" for item in rows),
826 "must fire: direct/default/static-shell and declared scalar/argv callers only",
827 failures,
828 )
829
830
831def _assert_checker_control_parsers(root: Path, failures: list[str]) -> None:
832 """Run explicit scope and nonfatal control-plane assertions."""
833 _assert_scope_value_semantics(failures)
834 _assert_scope_registry(root, failures)
835 _assert_scope_file_return_contract(root, failures)
836 _assert_census_immutability(failures)
837 _assert_census_sinks(failures)
838 _assert_census_shapes(failures)
839 _assert_nonfatal_parsers(root, failures)
840
841
842def assert_governance_parsers(root: Path, failures: list[str]) -> None:
843 """Run every new governance family in both directions."""
844 _assert_generated(root, failures)
845 _assert_config_parsers(failures)
846 _assert_bound_markers(root, failures)
847 _assert_language_controls(failures)
848 _assert_cmake_bracket_lexing(failures)
849 _assert_hardware_binding(root, failures)
850 _assert_checker_control_parsers(root, failures)