Skip to content

Commit 23b28a7

Browse files
Merge main into spec-drift/auto-promotion: CodeQL scoping + review fixes
Brings in #239 (nine review defects) and #240 (CodeQL scoped to authored code), the latter being what clears this branch's only remaining red check — a high-severity js/incomplete-sanitization alert inside specs/_vendor/mcp-spec-docs/snapshot.html, a captured third-party page this branch carries as evidence and must not edit. Verified after merging: no conflicts; projection-freshness exit 0 with all four enforced surfaces CLEAN; pytest scripts/tests/ passes; the derived dashboards and vendor-meta provenance all check OK. # Conflicts: # specs/detector-health.json
2 parents 24fdcab + 3c93152 commit 23b28a7

17 files changed

Lines changed: 916 additions & 28 deletions

.github/codeql-config.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# CodeQL configuration for intent-eval-lab.
2+
#
3+
# WHY THIS FILE EXISTS
4+
#
5+
# This repo deliberately commits THIRD-PARTY BYTES it did not write and must not
6+
# edit: the spec-drift capture tiers. `specs/_vendor/<surface>/snapshot.*` and
7+
# `archive/raw/**` are verbatim upstream pages fetched by scripts/fetch-capture.py
8+
# — HTML with bundled/minified JS, atom feeds, JSON schemas — retained as the
9+
# EVIDENCE that a spec said what we claim it said on a given date.
10+
#
11+
# CodeQL analysing them produces high-severity alerts about other people's
12+
# bundled JavaScript (e.g. js/incomplete-sanitization in
13+
# specs/_vendor/mcp-spec-docs/snapshot.html, a captured modelcontextprotocol.io
14+
# page). Those alerts are unactionable by construction, and worse than
15+
# unactionable: the only way to "fix" one is to edit a capture, which corrupts the
16+
# provenance record every drift comparison and every re-vendor decision depends on
17+
# — and scripts/check-vendor-meta-integrity.py exists precisely to make such an
18+
# edit fail loudly.
19+
#
20+
# So this is not weakening the scanner. It is scoping it to code this repo
21+
# AUTHORS and EXECUTES. Nothing under these paths is imported, executed, packaged
22+
# or served; the extractors read them as text.
23+
#
24+
# Deliberately NOT excluded: scripts/, research/, and every workflow — the code we
25+
# actually write stays fully in scope, on the security-extended suite.
26+
paths-ignore:
27+
- specs/_vendor/**
28+
- archive/**
29+
- _vendor/**

.github/workflows/ci.yml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,26 @@ jobs:
9191
fi
9292
9393
# Offline + deterministic: the surface registry must equal the watcher's
94-
# SOURCES array, every extractor must exist, and every monitored surface
95-
# must carry a valid capture config (kind, expect_regex, min_bytes, ext).
94+
# SOURCES array, every extractor must exist, every monitored surface must
95+
# carry a valid capture config (kind, expect_regex, min_bytes, ext), and
96+
# every surface must state its semantic-coverage level — including, for
97+
# field-level surfaces, that the declared checker runs in its FRESHNESS
98+
# mode. Pinning the mode is the point: a checker named with `--check`
99+
# instead of `--check-fresh` compares frozen against frozen and prints an
100+
# authoritative green, which is precisely the failure this gate exists for.
96101
- name: Upstream-surface registry consistency
97102
run: python3 scripts/check-surface-registry.py
98103

104+
# Offline + deterministic: every vendor-meta.json must describe the bytes
105+
# actually on disk. Nothing else checks this — `--check` proves the
106+
# projection derives from the FILES and never reads sha256/bytes at all, so
107+
# a hand re-vendor that recorded a wrong hash passed every gate while the
108+
# provenance quietly described a file that was not there. Three reference
109+
# docs were re-vendored by hand during the freshness work, so this is the
110+
# concrete shape of the risk, not a theoretical one.
111+
- name: Vendored capture provenance integrity
112+
run: python3 scripts/check-vendor-meta-integrity.py
113+
99114
# Offline + deterministic: fixtures exercise all 6 fetch-taxonomy statuses
100115
# (FETCH_OK|UNREACHABLE|MOVED|RATELIMITED|SHAPE_CHANGED|TRUNCATED) plus the
101116
# tier-1 append/dedup, tier-2 FETCH_OK-only gating (052-AT-SPEC), and the

.github/workflows/codeql.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
name: CodeQL
22

33
# Security scanning ONLY (security-extended query suite — no quality/maintainability
4-
# queries). Code-quality, design, and correctness review is CodeRabbit's job (see
5-
# .coderabbit.yaml); the two are intentionally non-overlapping. Free on public repos.
4+
# queries). Free on public repos.
5+
#
6+
# The counterpart this comment used to name (CodeRabbit) is retired, and as of
7+
# 2026-07-22 NO AI reviewer runs on this repo — Gemini Code Assist is sunset and
8+
# Greptile is dark. CodeQL owns security; correctness and design review is the
9+
# repo's own CI (partner-name guard, spec-drift gates, projection freshness,
10+
# vendor-meta provenance, scripts/tests) plus human review.
611

712
on:
813
push:
@@ -39,6 +44,10 @@ jobs:
3944
languages: ${{ matrix.language }}
4045
build-mode: none
4146
queries: security-extended
47+
# Scopes analysis to code this repo authors and executes. The captured
48+
# upstream trees are third-party bytes retained as evidence and must
49+
# never be edited — see .github/codeql-config.yml for the full reasoning.
50+
config-file: ./.github/codeql-config.yml
4251
- name: Perform CodeQL Analysis
4352
uses: github/codeql-action/analyze@v3
4453
with:

.github/workflows/python-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,5 @@ jobs:
147147
- run: pip install --no-cache-dir 'pytest>=8' pyyaml jsonschema
148148
- name: Run scripts/ unit tests
149149
run: pytest scripts/tests/ -v --tb=short
150+
- name: Vendored capture provenance integrity
151+
run: python3 scripts/check-vendor-meta-integrity.py

.harness-hash

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ add245f35fb0dbd63d5ab475020bb367b53be4c611eb509d614c8a1f5ca8ea32 .audit-harness
55
0ffea346a8c310677f2176e9f367370c8b308f82633bf62ccd00a95985e09469 .audit-harness/scripts/escape-scan.sh
66
d29a2e9b94f7fd94f4f8953314323f96ca424cbaa6a985f6783b48ee2feae0f8 .audit-harness/scripts/gherkin-lint.sh
77
9c588a980e89dbc9ea2bcf0992daff51ec72b926ecb1644e21838b9fc5c2883d .audit-harness/scripts/harness-hash.sh
8-
a56b9ef535b85725c5740f1d5287ad12f96d7c5fd0fd62aa64ff1938892e950c .github/workflows/ci.yml
9-
7342d70d8e4a3531262c71127b6c285406807b677cc8f404c580fed9ed5349f7 .github/workflows/codeql.yml
8+
5b912f59eeef3c9b5f7de98d8ae27a0d8b2eeb03411f34d9fa0e205d061b7f7f .github/workflows/ci.yml
9+
a349d30f7da0d4f6d2b6c7b6011115b8c0e474a30f820bc1354a7efaa10b2c37 .github/workflows/codeql.yml
1010
a47735f905b1463c148d0b71f5db852beec873782be18b6d1e7a55ec54a1baf4 .github/workflows/doc-quality.yml
1111
592a86e79d00a0bbbaed2e20d2fba91aba6744baa4cfb8222ea41fe422543fce .github/workflows/e2e-integration.yml
1212
43194d2f4ce1c37f70b4db099262149a4cd28f541eaa363f73adf05801b89935 .github/workflows/harness-hash-verify.yml
1313
b0302fc487734c9d925accc83f58ac86587eb991ec35d675c43282aebf295ecf .github/workflows/leading-indicator-watch.yml
1414
87e8277670edd32734871ea373f37395329a19a572e9ccc39a9af3789087ecff .github/workflows/lint.yml
1515
64d1e82fe68d531d30f5f4815af7cf852c564c5c406d3db47329cacbed8e421b .github/workflows/partner-name-guard.yml
16-
f2c7225b9755299a3864584e8037f35cc74d4a1de890801f2f35605e0572e242 .github/workflows/python-tests.yml
16+
60ffea7476e7cb9f3fb8ac47e6ca2ebf109eb708960b802e83ce821089671d63 .github/workflows/python-tests.yml
1717
45fbc5b54171b6e10052a69894a1e0a6955013e7d88fa16706a0ed55eae830f4 .github/workflows/release.yml
1818
db162cbe380e2d61bbcc820cdd5a89807ca1a7cf221b8f5e28ef3c0ce9ed1767 .github/workflows/schema-drift.yml
1919
a71c2bf4169af59d3708009fc95d7d097fdcb68f3d4a70337e8578f93c085279 .github/workflows/sign-dogfood-bundle.yml

scripts/check-surface-registry.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,35 @@
4242
_COVERAGE_STATUSES = {"field-level", "byte-hash-only"}
4343
_ENFORCEMENTS = {"failing", "report-only"}
4444

45+
# Which flag makes each checker compare against the CAPTURED tree, i.e. which flag
46+
# actually performs a FRESHNESS check rather than a self-consistency one.
47+
#
48+
# Validating only that `checker[0]` is an existing file is not enough, and the gap
49+
# is not theoretical: swapping `--check-fresh` for `--check` on any extractor
50+
# re-arms the exact frozen-vs-frozen bug this machinery exists to end, and the
51+
# driver then prints an authoritative all-green board — including for surfaces
52+
# with real outstanding findings. `--surface` is a top-level arg, so the wrong
53+
# mode accepts it and silently ignores it; nothing else in the chain objects.
54+
#
55+
# The registry is precisely the file humans are told to edit ("flipping a surface
56+
# to `failing` is a one-line registry edit"), so the next person who sees red and
57+
# "fixes" the flag would get a green board. Hence: pin the SEMANTICS of the argv,
58+
# not just its head. A checker absent from this table is an error — adding one is
59+
# a deliberate, reviewed act.
60+
_FRESH_MODE_FLAG = {
61+
# spec-projection-diff's --check reads specs/_vendor/<surface>/snapshot<ext>
62+
# (repointed in #234); it has no separate --check-fresh.
63+
"scripts/spec-projection-diff.py": "--check",
64+
"scripts/extract-agent-definition-projection.py": "--check-fresh",
65+
"scripts/extract-hook-config-projection.py": "--check-fresh",
66+
"scripts/extract-marketplace-catalog-projection.py": "--check-fresh",
67+
"scripts/extract-plugin-manifest-projection.py": "--check-fresh",
68+
}
69+
70+
# Mode flags a checker must never carry instead of (or in addition to) its fresh
71+
# mode. `--write` would have the gate MUTATE the baseline it is meant to guard.
72+
_MODE_FLAGS = {"--check", "--check-fresh", "--extract", "--write", "--self-test", "--diff", "--list", "--strict"}
73+
4574

4675
def _check_semantic_coverage(name: str, surface: dict, problems: list[str]) -> None:
4776
"""Validate one surface's semantic_coverage block (projection-freshness.py contract)."""
@@ -74,11 +103,30 @@ def _check_semantic_coverage(name: str, surface: dict, problems: list[str]) -> N
74103
problems.append(f"{name}: field-level coverage needs `checker` as a non-empty list of argv strings")
75104
elif not os.path.isfile(os.path.join(REPO_ROOT, checker[0])):
76105
problems.append(f"{name}: semantic_coverage.checker script not found: {checker[0]}")
77-
if cov.get("enforcement") not in _ENFORCEMENTS:
106+
elif checker[0] not in _FRESH_MODE_FLAG:
107+
problems.append(
108+
f"{name}: '{checker[0]}' is not a registered freshness checker. Add it to _FRESH_MODE_FLAG in "
109+
"this script, naming the flag that makes it read the CAPTURED tree — a checker whose mode is "
110+
"unpinned can silently compare frozen against frozen."
111+
)
112+
else:
113+
required = _FRESH_MODE_FLAG[checker[0]]
114+
supplied = [a for a in checker[1:] if a in _MODE_FLAGS]
115+
if supplied != [required]:
116+
problems.append(
117+
f"{name}: checker must run '{checker[0]}' in its freshness mode '{required}', got mode flag(s) "
118+
f"{supplied or 'none'}. A checker in the wrong mode compares frozen against frozen and reports "
119+
"an authoritative green — the exact failure this gate exists to detect."
120+
)
121+
122+
enforcement = cov.get("enforcement")
123+
# str() first: a list/dict here is unhashable and `in` would raise TypeError,
124+
# killing the gate with a traceback instead of naming the problem.
125+
if not isinstance(enforcement, str) or enforcement not in _ENFORCEMENTS:
78126
problems.append(
79-
f"{name}: semantic_coverage.enforcement '{cov.get('enforcement')}' not one of {sorted(_ENFORCEMENTS)}"
127+
f"{name}: semantic_coverage.enforcement {enforcement!r} not one of {sorted(_ENFORCEMENTS)}"
80128
)
81-
if cov.get("enforcement") == "report-only" and not isinstance(cov.get("note"), str):
129+
if enforcement == "report-only" and not isinstance(cov.get("note"), str):
82130
problems.append(f"{name}: report-only coverage needs a `note` saying what is pending and when it flips")
83131

84132

@@ -173,6 +221,24 @@ def main() -> int:
173221
field_level = sum(
174222
1 for s in reg_surfaces.values() if (s.get("semantic_coverage") or {}).get("status") == "field-level"
175223
)
224+
# A floor, so shrinking semantic coverage is a visible edit rather than a
225+
# quietly smaller green board. Without it, "the map got smaller" and "the map
226+
# is clean" look identical downstream.
227+
floor = (reg.get("semantic_coverage_floor") or {}).get("field_level")
228+
if not isinstance(floor, int) or isinstance(floor, bool) or floor < 1:
229+
problems.append("registry: semantic_coverage_floor.field_level must be a positive integer")
230+
elif field_level < floor:
231+
problems.append(
232+
f"registry: {field_level} field-level surfaces is below the declared floor of {floor}. "
233+
"Semantic coverage SHRANK. If that is intended, lower the floor in the same change so the "
234+
"reduction is reviewed."
235+
)
236+
if problems:
237+
print(f"surface-registry consistency: {len(problems)} PROBLEM(S):")
238+
for problem in problems:
239+
print(f" - {problem}")
240+
print("\nFix: edit BOTH spec-drift-check.sh SOURCES and the registry in the same change.")
241+
return 1
176242
print(
177243
f"surface-registry consistency: OK — {len(reg_surfaces)} surfaces, registry == watcher "
178244
f"SOURCES, all extractors defined, all capture configs valid, all semantic-coverage levels "
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#!/usr/bin/env python3
2+
"""Verify every vendor-meta.json records the bytes actually on disk.
3+
4+
WHY THIS EXISTS
5+
---------------
6+
`vendor-meta.json` is the provenance record for a deep capture: per file, the
7+
source URL, the upstream commit where applicable, and the sha256 + byte count of
8+
the exact bytes vendored. Every downstream argument about what upstream said, and
9+
every re-vendor decision, rests on that record being true.
10+
11+
Nothing checked it. Each extractor's `--check` proves the projection is a faithful
12+
derivation of the FILES, and `--self-test` proves the anchors parse — neither reads
13+
`sha256` or `bytes` at all. So a hand re-vendor that copied a new page in but
14+
recorded the wrong hash (or forgot to update it) passed every gate in the repo
15+
while the provenance quietly described a file that no longer existed.
16+
17+
That is not a hypothetical shape: three reference docs were re-vendored BY HAND
18+
during the freshness work, each one editing sha256 and bytes in a text editor.
19+
A silent provenance lie is worse than a stale capture, because the stale capture
20+
is at least honestly labelled.
21+
22+
WHAT IT CHECKS, for every specs/_vendor/upstream/<contract>/vendor-meta.json:
23+
24+
1. every declared file exists;
25+
2. its sha256 matches the bytes on disk;
26+
3. its `bytes` matches the real size;
27+
4. every file in the directory is DECLARED (an undeclared file is an
28+
unprovenanced input the extractor could silently start parsing);
29+
5. the required top-level keys are present.
30+
31+
Stdlib only. Offline. Read-only.
32+
33+
Exit 0 = every capture's provenance is true; 1 = a mismatch; 2 = usage/parse error.
34+
35+
Usage:
36+
check-vendor-meta-integrity.py [--vendor-root DIR]
37+
"""
38+
39+
from __future__ import annotations
40+
41+
import argparse
42+
import hashlib
43+
import json
44+
import os
45+
import sys
46+
47+
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
48+
DEFAULT_VENDOR_ROOT = os.path.join(REPO_ROOT, "specs", "_vendor", "upstream")
49+
50+
# Files that live beside a capture but are documentation ABOUT it, not inputs to it.
51+
NOT_CAPTURE_INPUTS = {"vendor-meta.json", "projection.json", "projection.v1.json", "PROVENANCE.md"}
52+
53+
REQUIRED_META_KEYS = ("contract", "spec_version", "files")
54+
55+
56+
def _sha256(path: str) -> tuple[str, int]:
57+
digest = hashlib.sha256()
58+
size = 0
59+
with open(path, "rb") as fh:
60+
while chunk := fh.read(1 << 20):
61+
digest.update(chunk)
62+
size += len(chunk)
63+
return digest.hexdigest(), size
64+
65+
66+
def check_capture(capture_dir: str, problems: list[str]) -> int:
67+
"""Verify one capture directory. Returns the number of files checked."""
68+
name = os.path.basename(capture_dir)
69+
meta_path = os.path.join(capture_dir, "vendor-meta.json")
70+
try:
71+
with open(meta_path, encoding="utf-8") as fh:
72+
meta = json.load(fh)
73+
except (OSError, json.JSONDecodeError) as exc:
74+
problems.append(f"{name}: cannot read vendor-meta.json: {exc}")
75+
return 0
76+
77+
for key in REQUIRED_META_KEYS:
78+
if key not in meta:
79+
problems.append(f"{name}: vendor-meta.json is missing required key '{key}'")
80+
81+
declared: set[str] = set()
82+
checked = 0
83+
for entry in meta.get("files", []):
84+
filename = entry.get("file")
85+
if not filename:
86+
problems.append(f"{name}: a files[] entry has no 'file' key")
87+
continue
88+
declared.add(filename)
89+
path = os.path.join(capture_dir, filename)
90+
if not os.path.isfile(path):
91+
problems.append(f"{name}/{filename}: declared in vendor-meta.json but MISSING on disk")
92+
continue
93+
94+
actual_sha, actual_bytes = _sha256(path)
95+
checked += 1
96+
recorded_sha = entry.get("sha256")
97+
if recorded_sha and recorded_sha != actual_sha:
98+
problems.append(
99+
f"{name}/{filename}: sha256 MISMATCH — vendor-meta records {recorded_sha[:16]}…, "
100+
f"file is {actual_sha[:16]}…. The provenance record describes bytes that are not there."
101+
)
102+
elif not recorded_sha:
103+
problems.append(f"{name}/{filename}: no sha256 recorded — the capture has no verifiable provenance")
104+
recorded_bytes = entry.get("bytes")
105+
if isinstance(recorded_bytes, int) and recorded_bytes != actual_bytes:
106+
problems.append(
107+
f"{name}/{filename}: bytes MISMATCH — vendor-meta records {recorded_bytes}, file is {actual_bytes}"
108+
)
109+
110+
# An UNDECLARED file in a capture dir is an input with no provenance at all.
111+
# The extractors select inputs by vendor-meta `role`, so such a file is inert
112+
# today — and one vendor-meta edit away from being parsed as authority.
113+
on_disk = {
114+
f
115+
for f in os.listdir(capture_dir)
116+
if os.path.isfile(os.path.join(capture_dir, f)) and f not in NOT_CAPTURE_INPUTS
117+
}
118+
for orphan in sorted(on_disk - declared):
119+
problems.append(
120+
f"{name}/{orphan}: present in the capture directory but NOT declared in vendor-meta.json — "
121+
"an input with no provenance record"
122+
)
123+
return checked
124+
125+
126+
def main() -> int:
127+
parser = argparse.ArgumentParser(description="Verify vendored capture provenance against the bytes on disk.")
128+
parser.add_argument("--vendor-root", default=DEFAULT_VENDOR_ROOT, help="root holding <contract>/ capture dirs")
129+
args = parser.parse_args()
130+
131+
if not os.path.isdir(args.vendor_root):
132+
print(f"ERROR: vendor root not found: {args.vendor_root}", file=sys.stderr)
133+
return 2
134+
135+
captures = sorted(
136+
os.path.join(args.vendor_root, d)
137+
for d in os.listdir(args.vendor_root)
138+
if os.path.isfile(os.path.join(args.vendor_root, d, "vendor-meta.json"))
139+
)
140+
if not captures:
141+
# An empty sweep reporting success is the same failure class this repo
142+
# keeps finding: a gate that checked nothing must not look like a pass.
143+
print(f"ERROR: no captures found under {args.vendor_root} — this gate verified NOTHING.", file=sys.stderr)
144+
return 2
145+
146+
problems: list[str] = []
147+
total = sum(check_capture(c, problems) for c in captures)
148+
149+
if problems:
150+
print(f"vendor-meta integrity: {len(problems)} PROBLEM(S):")
151+
for problem in problems:
152+
print(f" - {problem}")
153+
print(
154+
"\nFix: re-derive the record from the bytes, never the other way round. "
155+
"After replacing a vendored file, recompute sha256 + bytes and re-run the extractor's --write."
156+
)
157+
return 1
158+
159+
print(
160+
f"vendor-meta integrity: OK — {len(captures)} capture(s), {total} vendored file(s); "
161+
"every sha256 and byte count matches the bytes on disk, and every file is declared."
162+
)
163+
return 0
164+
165+
166+
if __name__ == "__main__":
167+
sys.exit(main())

0 commit comments

Comments
 (0)