diff --git a/.github/workflows/governance-reusable.yml b/.github/workflows/governance-reusable.yml index bf0b09d5..c07ba46c 100644 --- a/.github/workflows/governance-reusable.yml +++ b/.github/workflows/governance-reusable.yml @@ -273,9 +273,10 @@ jobs: # 1. `.hypatia-baseline.json` — array of acknowledged findings, # shape mirrors the Hypatia findings themselves. Schema is # `.machine_readable/hypatia-baseline.schema.json` in this - # repo. The validate-baseline job above schema-checks this. - # Added 2026-05-25 as part of the convergence on a single - # exemption mechanism. + # repo, enforced structurally by apply-baseline.sh itself + # (jq mirror; a malformed baseline exits 2 instead of + # silently matching nothing). Added 2026-05-25 as part of + # the convergence on a single exemption mechanism. # 2. `.hypatia-ignore` flat-file — legacy single-rule-per-line # format (`cicd_rules/banned_language_file:`). # Will be retired in a follow-up PR once .hypatia-baseline.json diff --git a/.github/workflows/push-email-notify.yml b/.github/workflows/push-email-notify.yml deleted file mode 100644 index 08167719..00000000 --- a/.github/workflows/push-email-notify.yml +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Dormant push-email notification. ARMED by setting the repo variable -# PUSH_EMAIL_ENABLED=true (the single on/off switch). Addresses are pre-filled; -# sending needs the org SMTP secrets (SMTP_HOST/PORT/USER/PASS). Inherited by -# new repos from the template; placed on existing repos by the farm sweep. -name: Push email notification -on: - push: {} -permissions: - contents: read -jobs: - notify: - name: Email on push - if: ${{ vars.PUSH_EMAIL_ENABLED == 'true' }} - runs-on: ubuntu-latest - steps: - - name: Send push notification email - uses: dawidd6/action-send-mail@c50dc4cc848ade21f848990889906d804fae78c5 # pinned - with: - server_address: ${{ secrets.SMTP_HOST }} - server_port: ${{ secrets.SMTP_PORT }} - secure: true - username: ${{ secrets.SMTP_USER }} - password: ${{ secrets.SMTP_PASS }} - from: "GitHub Push <${{ secrets.SMTP_USER }}>" - to: "jonathan.jewell@gmail.com j.d.a.jewell@open.ac.uk" - subject: "[${{ github.repository }}] push to ${{ github.ref_name }} by ${{ github.actor }}" - body: | - Repository: ${{ github.repository }} - Branch: ${{ github.ref_name }} - Pusher: ${{ github.actor }} - Compare: ${{ github.event.compare }} - Head msg: ${{ github.event.head_commit.message }} diff --git a/.github/workflows/scorecard-enforcer.yml b/.github/workflows/scorecard-enforcer.yml index 8c40f56c..379dfa4b 100644 --- a/.github/workflows/scorecard-enforcer.yml +++ b/.github/workflows/scorecard-enforcer.yml @@ -15,6 +15,7 @@ permissions: jobs: scorecard: runs-on: ubuntu-latest + timeout-minutes: 15 permissions: security-events: write id-token: write # For OIDC @@ -39,6 +40,7 @@ jobs: check-score: needs: scorecard runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -61,6 +63,7 @@ jobs: check-critical: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/.github/workflows/signed-push-smoke.yml b/.github/workflows/signed-push-smoke.yml index efc556c0..ab32b277 100644 --- a/.github/workflows/signed-push-smoke.yml +++ b/.github/workflows/signed-push-smoke.yml @@ -13,6 +13,7 @@ permissions: jobs: smoke: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 diff --git a/.hypatia-baseline.json b/.hypatia-baseline.json new file mode 100644 index 00000000..2311737c --- /dev/null +++ b/.hypatia-baseline.json @@ -0,0 +1,872 @@ +[ + { + "severity": "critical", + "rule_module": "cicd_rules", + "type": "banned_language_file", + "file": "scripts/check-ts-allowlist.ts", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Genuine TS-ban hit: portable Deno CLI script = the policy's 'convert-now bucket' for AffineScript. Non-trivial (needs compile-verified .affine wired in same PR, byte-identical behaviour). Baseline wit", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#254" + }, + { + "severity": "high", + "rule_module": "cicd_rules", + "type": "missing_requirement", + "file": ".github/workflows/scorecard.yml", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Scorecard requirement is functionally satisfied by scorecard-enforcer.yml (push+weekly cron+dispatch) and scorecard-reusable.yml; rule matches the literal filename scorecard.yml instead of scorecard-a", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#399" + }, + { + "severity": "critical", + "rule_module": "code_safety", + "type": "agda_postulate", + "file": "lol/proofs/theories/information_theory.agda", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Genuine postulates but documented justified axioms of Shannon/Gibbs/Lin theorems, unprovable over IEEE754 Float without an R formalisation. Proof debt tracked; lol/ awaits eviction (#494).", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#124" + }, + { + "severity": "medium", + "rule_module": "code_safety", + "type": "as_ptr", + "file": "avow-protocol/avow-lib/examples/rust/src/main.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): No dangling: every as_ptr comes from a named CString binding that outlives the synchronous FFI call. Idiomatic CString FFI; rule cannot see lifetimes." + }, + { + "severity": "medium", + "rule_module": "code_safety", + "type": "expect_in_hot_path", + "file": "a2ml/bindings/rust/benches/a2ml_bench.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Criterion bench harness, not a production hot path; expect on static fixtures is idiomatic and unreachable by user input. Rule should exclude benches/." + }, + { + "severity": "medium", + "rule_module": "code_safety", + "type": "expect_in_hot_path", + "file": "k9-svc/bindings/rust/benches/k9_bench.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Not a hot production path: criterion benchmark harness over constant fixtures. expect() with a message is the idiomatic bench failure mode. Rule should exclude benches/ directories." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "js_innerhtml", + "file": "avow-protocol/public/demo.js", + "note": "TRACKED (trivial fix available, not applied in this CI-hygiene PR): Genuine DOM XSS: user input flows unescaped into innerHTML in the public demo page. Low blast radius (self-XSS on a demo) but a real fix (textContent/escaping) is needed. Product evicting under #492.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#492" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "js_innerhtml", + "file": "axel-protocol/src/Tea.res.js", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Real XSS surface: any view interpolating user data reaches innerHTML unescaped. File is bsc compiled output of Tea.res — fix belongs in the .res source. Product evicting under #492.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#492" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "js_wildcard_cors", + "file": "consent-aware-http/examples/reference-implementations/deno/aibdp_middleware.js", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Wildcard CORS is correct here: public, credential-less, read-only discovery manifest (well-known-style metadata). Rule should exempt public manifest/metadata responses." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "lock_unwrap", + "file": "k9-svc/lsp/src/main.rs", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Production LSP server: one panicked holder poisons the mutex and every later request panics, killing the server. Fix (parking_lot or poison recovery) is non-trivial. k9-svc evicting under #491.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#491" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "ncl_http_url", + "file": "k9-svc/register.ncl", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): The http:// strings are an XML namespace URI and a DTD public identifier — opaque identifiers mandated by the freedesktop/Apple specs, never fetched. Changing them to https would break MIME registrati" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "rwlock_unwrap", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/src/session_manager.rs", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Genuine: a single panic while holding the lock poisons it and every subsequent session operation panics (DoS cascade). Needs poison handling or parking_lot. Product evicting under #492.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#492" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "rwlock_unwrap", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/tests-offline/src/lib.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Test-only mock of the production SessionManager; never shipped. The production copy is already flagged (findings 25/26, REAL_DEBT #492). Double report against test scaffolding." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "rwlock_unwrap", + "file": "rhodium-standard-repositories/examples/enterprise-service/src/handlers.rs", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Real: exemplar code in the estate standard demonstrates unhandled lock poisoning; copies propagate the antipattern. Estate-authored (examples/ dir, distinct from third-party satellites/). Tracked by R", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#493" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "shell_download_then_run", + "file": "agentic-a2ml/scripts/bootstrap.sh", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Same template-shared bootstrap as id 41 (byte-identical pattern). One template fix (download + sha256-verify + run) clears all instances.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#399" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "shell_download_then_run", + "file": "k9-svc/k9-sign/install.sh", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): The curl|sh is never executed — it is a printed installation hint. Rule should ignore pattern inside echo/string literals." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "shell_download_then_run", + "file": "neurosym-a2ml/scripts/bootstrap.sh", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Real download-and-execute. Template-shared bootstrap (identical in agentic-a2ml and rsr-deployer) — fix the template once: download to file, verify sha256, then run.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#399" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "shell_download_then_run", + "file": "rhodium-standard-repositories/satellites/palimpsest-license/TOOLS/validation/install.sh", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Pipe-to-shell appears only as printed install advice, never executed. Also a vendored satellite under #493 — never edit regardless. Rule should ignore echo/string contexts." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "shell_download_then_run", + "file": "rhodium-standard-repositories/satellites/rsr-deployer/scripts/bootstrap.sh", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Real download-and-execute, but inside a vendored satellite of rhodium-standard-repositories: flag-only, never propose in-place edits; real fix lands via the eviction campaign.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#493" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "shell_download_then_run", + "file": "setup.sh", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Real curl|bash of the just installer with no integrity check; root scaffold slated for cleanup under the scaffold-junk carve-out.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#496" + }, + { + "severity": "medium", + "rule_module": "code_safety", + "type": "unsafe_block", + "file": "avow-protocol/avow-lib/examples/rust/src/main.rs", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Genuine hygiene gap (missing SAFETY comments on FFI calls) in example code of a product awaiting eviction; baseline with expiry under the avow-protocol carve-out.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#492" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/src/session_manager.rs", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Same 5 lines as finding 26 (rwlock_unwrap) — generic rule double-counts the RwLock rule. Underlying issue is real: poison cascades panics in the production session manager. Evicting under #492.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#492" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/tests-offline/src/lib.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): tests-offline is a test/mock crate (offline mirror of session_manager for testing). Panic IS the test-failure mode; unwrap in test code is idiomatic. Rule should exclude test crates." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "a2ml/bindings/rust/benches/a2ml_bench.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): The sole unwrap match is text inside a benchmark fixture string, not code. Upstream rule should skip string-literal contents." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "a2ml/bindings/rust/src/parser.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): The single production unwrap is provably infallible (len>=3 guard guarantees a first char). Remaining unwraps are a doc-comment (L32) and #[test] code (L499+)." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "a2ml/lsp/src/completions.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): All 6 unwraps are const-pattern regex compiles or guaranteed capture groups — infallible. Same rule misfire class as ids 35/36." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "a2ml/lsp/src/diagnostics.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): All 15 unwraps are const-pattern Regex::new() or mandatory capture groups after a successful match — provably infallible, no user-input panic path. Upstream: whitelist const-regex unwrap." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "a2ml/lsp/src/hover.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same infallible const-regex/mandatory-capture pattern as diagnostics.rs; cannot panic at runtime. Baseline permanently." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "k9-svc/bindings/rust/src/parser.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): The single production unwrap is provably infallible (strip_prefix guarded by starts_with of the same literal). Remaining unwraps are a doc-comment (L60) and #[test] code (L421+)." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "k9-svc/lsp/src/diagnostics.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): All 14 unwraps are Regex::new on string-literal patterns (infallible at runtime) or caps.get(n) for groups guaranteed by those same literal patterns. No reachable panic on any input." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "rhodium-standard-repositories/examples/enterprise-service/benches/api_benchmarks.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Benchmark harness setup; panic on runtime-creation failure is the intended and only sensible failure mode. Not a DoS surface. Rule should exclude benches/ directories." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "unwrap_without_check", + "file": "rhodium-standard-repositories/examples/enterprise-service/src/handlers.rs", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Same 4 production lines as finding 30 (rwlock rule double-count). Estate-authored RSR example (examples/, not satellites/) that teaches the poison-cascade antipattern. RSR evicting under #493.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#493" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "zig_align_cast", + "file": "lol/ffi/zig/src/main.zig", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Same shim-template issue as id 47 (each @alignCast pairs with a flagged @ptrCast); one template fix with handle validation covers both rule hits.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#494" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "zig_ptr_cast", + "file": "lol/api/zig-gateway/src/lol_ffi.zig", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Rule matched @ptrCast inside a // comment; no actual cast in the file. Upstream rule should strip comments before matching." + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "zig_ptr_cast", + "file": "lol/ffi/zig/src/main.zig", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Real unchecked handle recovery (garbage handle = UB); this is the estate-wide vendored FFI shim pattern (~230x) — fix the shim template (magic-number handle validation), tracked with lol eviction.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#494" + }, + { + "severity": "high", + "rule_module": "code_safety", + "type": "zig_ptr_cast", + "file": "overlay-protocol/ffi/zig/src/main.zig", + "note": "TRACKED (trivial fix available, not applied in this CI-hygiene PR): The cast is sound only if termination is guaranteed; it is not when the message exactly fills the 1024-byte buffer, giving the C caller an out-of-bounds read. One-character fix.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#399" + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": ".github/workflows/changelog-reusable.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of Scorecard Token-Permissions; write perms are job-scoped and required to open the changelog PR. Baseline permanently." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": ".github/workflows/hypatia-scan-reusable.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of Scorecard Token-Permissions; security-events:write is the minimum for SARIF upload, not excess privilege. Baseline permanently." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": ".github/workflows/hypatia-scan.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of Scorecard Token-Permissions; the permissions block is deliberate and documented in-file. Baseline permanently." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": ".github/workflows/scorecard.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Stale 46-day-old mirror (standards#378) against a file since renamed to scorecard-enforcer.yml/scorecard-reusable.yml; the missing_requirement alert has no current target." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/src/session_manager.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378); underlying rwlock unwrap already tracked by the primary rule. repo-guardian-fs/** carved out to #492." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/tests-offline/src/lib.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378); also test code, where lock unwrap is idiomatic. repo-guardian-fs/** carved out to #492." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "a2ml/lsp/src/completions.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378); underlying alert already exists as the primary code_safety finding. a2ml/** carved out to #490." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "a2ml/lsp/src/diagnostics.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report of Hypatia's own unwrap_without_check alert (standards#378). a2ml/** carved out to #490." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "a2ml/lsp/src/hover.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378); underlying unwraps are compile-constant regexes plus a guarded caps.get(1). a2ml/** carved out to #490." + }, + { + "severity": "medium", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "avow-protocol/avow-lib/examples/rust/src/main.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378); as_ptr into an FFI param struct is the intended example-code pattern. avow-protocol/** carved out to #492." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "k9-svc/k9-sign/install.sh", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Doubly false: CSA mirror double-report (#378) AND underlying pattern is an echoed instruction string, not an executed pipeline. k9-svc/** carved out to #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "k9-svc/lsp/src/diagnostics.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report of Hypatia's own alert (standards#378). k9-svc/** carved out to #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "k9-svc/lsp/src/main.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378); the lock().unwrap() pattern is tracked by the primary code_safety rule. k9-svc/** carved out to #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "k9-svc/register.ncl", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Doubly false: CSA mirror double-report (#378) AND underlying ncl_http_url matched XML namespace identifiers which are opaque strings, never dereferenced. k9-svc/** carved out to #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "lol/api/zig-gateway/src/lol_ffi.zig", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Doubly false: CSA mirror double-report (#378) AND the underlying alert matched a comment, not code. lol/** is also carved out to #494." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "overlay-protocol/ffi/zig/src/main.zig", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report of Hypatia's own SARIF alert (standards#378). Underlying @ptrCast is already emitted by the primary code_safety rule; the CSA copy is a duplicate." + }, + { + "severity": "medium", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "rhodium-standard-repositories/templates/setup.sh.template", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of the Scorecard alert; file also lives under the #493 eviction carve-out. Baseline permanently as a mirror." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "scorecard.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378): hypatia re-ingesting its own workflow_audit alert. Underlying gap is tracked by the original alert; baseline permanently." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "scripts/check-ts-allowlist.ts", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378); underlying banned_language_file is emitted by cicd_rules directly. The .ts is the allowlist checker, in the grandfathered TS→AffineScript migration bucket (#252 t" + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "setup.sh", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report of Hypatia's own alert (standards#378). Underlying curl|bash is real and covered by the primary code_safety finding; root scaffold is also #496 territory." + }, + { + "severity": "medium", + "rule_module": "code_scanning_alerts", + "type": "CSA001", + "file": "setup.sh", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378): Scorecard's Pinned-Dependencies alert already tracks this line; hypatia re-mirroring it adds nothing. Baseline permanently." + }, + { + "severity": "medium", + "rule_module": "code_scanning_alerts", + "type": "CSA002", + "file": "hyperpolymath/standards", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378): aggregate counter of alerts hypatia already re-reports one-by-one; triple-counting by construction. Baseline permanently." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": ".github/workflows/changelog-reusable.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report per standards#378 of Scorecard TokenPermissionsID; workflow already follows the recommended pattern (read at top, write scoped per-job)." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": ".github/workflows/hypatia-scan-reusable.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report per standards#378; security-events: write at top level is the documented requirement for SARIF upload from the reusable scan." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": ".github/workflows/hypatia-scan.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report per standards#378 of Scorecard TokenPermissionsID; workflow already declares least-privilege top-level perms with a documented, deliberate grant for SARIF upload." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": ".github/workflows/scorecard.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report per standards#378 of hypatia's own missing_requirement alert; Scorecard coverage exists under scorecard-reusable.yml/scorecard-enforcer.yml, so the naming expectation is stale." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/src/session_manager.rs", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Mirror double-report per standards#378. Underlying rwlock-unwrap debt is real-but-tracked in the repo-guardian-fs eviction carve-out #492; the CSA003 staleness echo adds nothing.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#378" + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "0-ai-gatekeeper-protocol/repo-guardian-fs/tests-offline/src/lib.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report per standards#378. Also test-only code (tests-offline), where unwrap is conventional; product tracked under #492." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "a2ml/lsp/src/completions.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of hypatia's own alert; underlying unwrap debt rides the a2ml eviction carve-out #490." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "a2ml/lsp/src/diagnostics.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of hypatia's own alert; a2ml/** tracked under eviction #490." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "a2ml/lsp/src/hover.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of hypatia's own unwrap_without_check alert; a2ml/** product debt tracked under eviction #490." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "avow-protocol/avow-lib/examples/rust/src/main.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report per standards#378: hypatia re-ingests its own code-scanning alert. Underlying file is avow-protocol product awaiting eviction (#492); as_ptr in an FFI example is idiomatic." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "k9-svc/k9-sign/install.sh", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378), and the underlying alert itself misfires on an echoed string, not executed code. k9-svc/** also carved out under #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "k9-svc/lsp/src/diagnostics.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of hypatia's own unwrap_dangerous_default alert; k9-svc/** tracked under eviction #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "k9-svc/lsp/src/main.rs", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of hypatia's own lock_unwrap alert; k9-svc/** product debt tracked under eviction #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "k9-svc/register.ncl", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Double-false: mirror double-report per standards#378, AND the underlying ncl_http_url alert flags XML namespace/DTD identifiers, which are opaque IDs never fetched over HTTP. k9-svc carve-out #491." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "lol/api/zig-gateway/src/lol_ffi.zig", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378) of hypatia's own alert; lol/** is carved out for eviction under #494 where the real work is tracked." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "overlay-protocol/ffi/zig/src/main.zig", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378): hypatia re-ingesting its own code_safety alert. Underlying shim debt is the estate-wide template-rot campaign (fix the boj-server template once)." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "rhodium-standard-repositories/templates/setup.sh.template", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Mirror double-report per standards#378 of a Scorecard alert; file lives in rhodium-standard-repositories, carve-out #493 (template awaiting eviction).", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#378" + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "scorecard.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report per standards#378 of hypatia workflow_audit/missing_workflow; the scorecard workflow exists under different filenames — rule expects a fixed name." + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "scripts/check-ts-allowlist.ts", + "note": "TRACKED DEBT (hypatia triage 2026-07-21): Mirror double-report per standards#378. Underlying alert is also wrong at source: this is Deno-runtime tooling enforcing the TS allowlist itself; TS-tooling carve-outs exist in CLAUDE.md policy.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#378" + }, + { + "severity": "high", + "rule_module": "code_scanning_alerts", + "type": "CSA003", + "file": "setup.sh", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Mirror double-report (standards#378): third reporting channel (hypatia alert + Scorecard alert + this mirror) for the same two lines. Baseline permanently." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "a2ml/actions/validate/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same templated .envrc placeholder; commented out, value \"...\". No secret." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "a2ml/bindings/deno/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Commented-out template placeholder (value is literally \"...\"); no credential present. Upstream rule should skip comment lines / \"...\" values. a2ml eviction tracked in #490." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "a2ml/bindings/haskell/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same templated .envrc placeholder; commented out, value \"...\". No secret." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "a2ml/bindings/rust/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same templated .envrc placeholder as id 96; commented out, value \"...\". No secret." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "a2ml/editors/vscode/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same templated .envrc placeholder; commented out, value \"...\". No secret. Fix the upstream generic-API-key rule to ignore commented placeholders." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "a2ml/pandoc/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same templated .envrc placeholder; commented out, value \"...\". No secret." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "k9-svc/actions/validate/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same template placeholder; commented out, no value. Not a secret; baseline permanently, fix rule upstream." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "k9-svc/bindings/deno/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Placeholder in a direnv template, commented out, value is literal ellipsis. Not a secret. Upstream rule should ignore commented lines / \"...\" values. Dir tracked by #490/#491 eviction anyway." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "k9-svc/bindings/haskell/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same template placeholder; commented out, no value. Not a secret; baseline permanently, fix rule upstream." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "k9-svc/bindings/rust/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same template placeholder as deno/.envrc; commented out, no value. Not a secret; baseline permanently, fix rule upstream." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "k9-svc/editors/vscode/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same template placeholder; commented out, no value. Not a secret; baseline permanently, fix rule upstream." + }, + { + "severity": "critical", + "rule_module": "security_errors", + "type": "secret_detected", + "file": "k9-svc/pandoc/.envrc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Same template placeholder; commented out, no value. Not a secret; baseline permanently, fix rule upstream." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/TRUSTED-BASE-REDUCTION-POLICY.adoc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Path is inside the `proven` repo, cited in an estate-wide trusted-base table. SD022 wrongly resolves cross-repo paths against the standards tree. Rule needs cross-repo-doc awareness." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/archive/provenance/echidna/ECHIDNA-TODO.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Archived provenance doc describing the echidna repo's tree, not standards'. Also historical (docs/archive/). SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/audits/dogfooding-matrix-2026-04-04.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Estate-wide dogfooding audit; `src/scan/` is in the iseriser repo. Dated snapshot audit (2026-04-04) referencing another repo. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/REQUIREMENTS-MASTER.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Doc is 'Proof Requirements Master — All 292 Repos'; `src/vql/` is verisimdb's tree. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T1-critical/ProvenCrypto.jl.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Spec template explicitly targets the ProvenCrypto.jl repo; paths are relative to that repo. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T1-critical/echidna.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Spec template for the echidna repo; `src/rust/` is echidna's tree. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T1-critical/ephapax.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Spec template for the ephapax repo; `src/formal/` is ephapax's tree. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T1-critical/januskey.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Spec template for the januskey repo; path is in that repo. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T1-critical/proven.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Spec template explicitly targets the proven repo; `src/Proven/` is its Idris2 source tree. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T1-critical/svalinn.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Spec template targets the svalinn repo; path is in that repo. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T1-critical/verisimdb.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Spec template targets the verisimdb repo; `src/vcl/` is its tree. SD022 cross-repo resolution error." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T2-high/conflow.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Proof-spec template for the external conflow repo; src/pipeline/ is conflow's tree, not standards'. Not drift — upstream rule should skip cross-repo spec templates." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T2-high/panic-attacker.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Path belongs to the panic-attacker repo, not standards. Cross-repo spec template; baseline permanently." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T2-high/panll.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Describes panll's tree, not standards'. Cross-repo spec template; baseline permanently." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T2-high/reposystem.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): External-repo key-files list; not a local dangling reference." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "docs/proofs/spec-templates/T2-high/statistease.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): statistease repo's Julia sources, not standards'. Cross-repo spec template; baseline permanently." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "k9-coordination-protocol/spec/COORDINATION-K9-SPEC.adoc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Illustrative YAML example in a protocol spec (sample invariants/protected config for a hypothetical project). Not a reference to this repo's tree." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "k9-svc/examples/NOT-a-good-fit.adoc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Hypothetical anti-pattern example in a doc titled NOT-a-good-fit — the directory is deliberately fictional. (k9-svc is also carve-out #491, but the finding is simply wrong.)" + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "meta-a2ml/spec/SCM-FORMAT-FAMILY.adoc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Sample SCM session data in a format spec — the path is example payload content, not a repo reference." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "panll-panels/README.adoc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Explicitly a path in the external PanLL repo (future destination), not this tree. Intentional cross-repo reference." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "rhodium-standard-repositories/docs/haskell-registry-design.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Design doc laying out a proposed rsr-registry Haskell service; src/RSR/ is the design's target layout, not an existing dir. Also inside RSR carve-out #493." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "rhodium-standard-repositories/satellites/META.scm/spec/SCM-FORMAT-FAMILY.adoc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Vendored satellite copy of the SCM spec — same illustrative example payload as meta-a2ml. Satellite is third-party-treated: never edit; baseline permanently." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "rhodium-standard-repositories/satellites/palimpsest-license/PROJECT_MANAGEMENT/RECONCILIATION_AUDIT.md", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Historical audit record of a GitLab source repo path that was deliberately NOT imported — prose-history by design. Vendored satellite: never edit." + }, + { + "severity": "medium", + "rule_module": "structural_drift", + "type": "SD022", + "file": "testing-and-benchmarking/TESTING-TAXONOMY.adoc", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): All src/rust/ mentions are explicitly paths inside the external echidna repo (named on each line). Cross-repo reference, not local drift." + }, + { + "severity": "high", + "rule_module": "workflow_audit", + "type": "missing_workflow", + "file": "scorecard.yml", + "note": "TRACKED (trivial fix available, not applied in this CI-hygiene PR): Required caller workflow genuinely absent while the reusable already exists in-repo; a 15-line thin wrapper closes it.", + "expires_at": "2026-10-21", + "tracking_issue": "hyperpolymath/standards#399" + }, + { + "severity": "high", + "rule_module": "workflow_audit", + "type": "scorecard_publish_with_run_step", + "file": "scorecard-enforcer.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): OSSF publish verification restricts publishing workflows; enforcement run-steps belong apart. scorecard-reusable.yml already publishes — enforcer should not double-publish." + }, + { + "severity": "high", + "rule_module": "workflow_audit", + "type": "secret_action_without_presence_gate", + "file": "instant-sync.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): Presence gate exists via the env-mapped pattern (secrets context is illegal in step if:). Rule must learn to recognize job-level env secret mapping + env-based step if:. Baseline permanently." + }, + { + "severity": "high", + "rule_module": "workflow_audit", + "type": "secret_action_without_presence_gate", + "file": "mirror-reusable.yml", + "note": "FALSE POSITIVE (hypatia triage 2026-07-21): mirror-gitlab ssh-agent step IS presence-gated via env mapping; comment L54-57 documents why secrets can't appear in if:. Same upstream rule gap as instant-sync. Baseline permanently." + } +] diff --git a/.machine_readable/REGISTRY.a2ml b/.machine_readable/REGISTRY.a2ml index 283524fc..292c713d 100644 --- a/.machine_readable/REGISTRY.a2ml +++ b/.machine_readable/REGISTRY.a2ml @@ -45,7 +45,7 @@ name = "A2ML — Attested Markup Language" stream = "foundation" home = "a2ml/" canonical_doc = "a2ml/README.adoc" -source_hash = "sha256:7c51aab6fe43b04bc795647ae9b3d4813ca82f6182399c2987e17edfd50b36f5" +source_hash = "sha256:11ffe50a8a15e22d800383589a947ca76a56d96d4f43f673b2d820ef64000436" route = "the typed/verified machine-readable document format" [[spec]] @@ -54,7 +54,7 @@ name = "K9 Self-Validating Components" stream = "foundation" home = "k9-svc/" canonical_doc = "k9-svc/README.adoc" -source_hash = "sha256:ddef46e42399af08488cea00f95515614f54cb1aea8b8990d4ef049e9f57f073" +source_hash = "sha256:7b851b1bd94b5561059bff1a21ebff2618d2bb12aa811f1c3329a199cd1fa79b" route = "self-validating components with embedded contracts + deploy logic" [[spec]] @@ -72,7 +72,7 @@ name = "META.a2ml spec" stream = "foundation" home = "meta-a2ml/" canonical_doc = "meta-a2ml/README.adoc" -source_hash = "sha256:bd0f10114fb753377708b2fa07c5995a000060aa50472d02e067f890f005ab53" +source_hash = "sha256:4b00ebf2fa1ab6aee46b67be34715b4198d4643ce2025c82882afda502d9c038" route = "architecture decisions / governance metadata format" [[spec]] @@ -135,7 +135,7 @@ name = "0-AI Gatekeeper Protocol" stream = "protocol" home = "0-ai-gatekeeper-protocol/" canonical_doc = "0-ai-gatekeeper-protocol/README.adoc" -source_hash = "sha256:264806b26e710c04adbe7db357f638fdef1bd24a1bae7be9d9d50742a1e535ea" +source_hash = "sha256:00d9193c6bd4409e9c9928133d57a8b5afd93a26797e957fbb6d189707c241f6" route = "the AI-agent entry/gating protocol behind 0-AI-MANIFEST" [[spec]] @@ -180,7 +180,7 @@ name = "Consent-Aware HTTP" stream = "protocol" home = "consent-aware-http/" canonical_doc = "consent-aware-http/README.adoc" -source_hash = "sha256:92e1d1c0252a69b8812cf0fa48bf2178f7f246314b1238a6d98eea9ed99fdb40" +source_hash = "sha256:7cb83ee0414539d8594b93a83ecf67fe5744b9f3d40b1f209946370dab423652" route = "consent headers / AI-usage boundaries for HTTP" [[spec]] @@ -225,7 +225,7 @@ name = "RSR — Rhodium Standard Repositories" stream = "governance" home = "rhodium-standard-repositories/" canonical_doc = "rhodium-standard-repositories/README.adoc" -source_hash = "sha256:02b75b57a41d213e4d3e7687e22252ba60bbef0923f75ebe18ef1a962c382f1e" +source_hash = "sha256:fa0fcde2d5be8ed8e4c7067f2a309a3ce424f6fafc942b7a10981546844a971d" route = "the repository-compliance standard every repo is graded against" [[spec]] diff --git a/.machine_readable/hypatia-baseline.schema.json b/.machine_readable/hypatia-baseline.schema.json index d31466b4..75d01be5 100644 --- a/.machine_readable/hypatia-baseline.schema.json +++ b/.machine_readable/hypatia-baseline.schema.json @@ -28,9 +28,9 @@ "pattern": "^[a-z][a-z0-9_]*$" }, "type": { - "description": "Hypatia finding type within the rule module (e.g. `banned_language_file`, `obj_magic`, `deprecated_api`).", + "description": "Hypatia finding type within the rule module. Two shapes exist: lowercase snake_case (`banned_language_file`, `secret_detected`) and uppercase mnemonic codes emitted by structural_drift/git_state/code_scanning_alerts (`SD022`, `GS007`, `CSA001`). Entries must use the literal value the finding carries (standards#477).", "type": "string", - "pattern": "^[a-z][a-z0-9_]*$" + "pattern": "^([a-z][a-z0-9_]*|[A-Z]{2,3}[0-9]{3})$" }, "file": { "description": "Repo-relative path to a single file the entry exempts. Mutually exclusive with `file_pattern`.", diff --git a/0-ai-gatekeeper-protocol/.github/workflows/secret-scanner.yml b/0-ai-gatekeeper-protocol/.github/workflows/secret-scanner.yml index a078e9f7..abc9964d 100644 --- a/0-ai-gatekeeper-protocol/.github/workflows/secret-scanner.yml +++ b/0-ai-gatekeeper-protocol/.github/workflows/secret-scanner.yml @@ -10,19 +10,6 @@ on: permissions: read-all jobs: - trufflehog: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4 - with: - fetch-depth: 0 # Full history for scanning - - - name: TruffleHog Secret Scan - uses: trufflesecurity/trufflehog@116e7171542d2f1dad8810f00dcfacbe0b809183 # v3 - with: - extra_args: --only-verified --fail - gitleaks: runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c9d97b7..54c28a67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,6 +95,28 @@ Look for issues labelled: ## Development Workflow +### First: install the pre-commit guard + +```sh +just hooks-install +``` + +This installs `hooks/pre-commit`, which catches the most common CI failure +before it leaves your machine: **registry drift**. The spec registry +(`.machine_readable/REGISTRY.a2ml`) records a content hash of every tracked +file under a spec home, so *any* edit under one (including +`.machine_readable/` itself) must be followed by: + +```sh +just registry # regenerates REGISTRY.a2ml + TOPOLOGY.md +git add .machine_readable/REGISTRY.a2ml TOPOLOGY.md +``` + +If you skip this, the required "Registry + topology in sync" check fails — +and because it is a required check, stale registry hashes on `main` block +*every* open PR, not just yours (see #381). The hook fails the commit with +the exact fix commands whenever the registry is stale. + ### Branch Naming ``` docs/short-description # Documentation (P3) diff --git a/a2ml/.github/workflows/secret-scanner.yml b/a2ml/.github/workflows/secret-scanner.yml index a078e9f7..abc9964d 100644 --- a/a2ml/.github/workflows/secret-scanner.yml +++ b/a2ml/.github/workflows/secret-scanner.yml @@ -10,19 +10,6 @@ on: permissions: read-all jobs: - trufflehog: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4 - with: - fetch-depth: 0 # Full history for scanning - - - name: TruffleHog Secret Scan - uses: trufflesecurity/trufflehog@116e7171542d2f1dad8810f00dcfacbe0b809183 # v3 - with: - extra_args: --only-verified --fail - gitleaks: runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/a2ml/actions/validate/validate-a2ml.sh b/a2ml/actions/validate/validate-a2ml.sh index 2366ecf0..fbdd88cb 100755 --- a/a2ml/actions/validate/validate-a2ml.sh +++ b/a2ml/actions/validate/validate-a2ml.sh @@ -5,14 +5,32 @@ # validate-a2ml.sh — A2ML manifest validation script # # Scans for .a2ml files and validates: -# 1. Required fields: agent-id or pedigree name, version +# 1. Identity presence (warning — see below) # 2. SPDX-License-Identifier header presence # 3. Attestation block structure (if present) # 4. Section heading syntax ([section] or ## section) # +# On identity (standards#435): the A2ML SPEC's only identity requirement is +# per-record (`@record` needs author/tool/kind, SPEC §7) — there is NO +# normative file-level "must have agent-id/name" rule. An earlier version of +# this script required `agent-id|name|project =` as a hard error and flagged +# the majority of canonical estate files (scorecards identify via `spec_id`, +# contractile Xfiles via `@abstract` + filename, the six-file set via +# `[metadata]`). Identity is therefore checked as a lint WARNING against the +# real estate shapes, and skipped entirely for classes that are identity-free +# by design: +# - AI manifests (AI-MANIFEST*.a2ml, AI.a2ml): markdown prose +# - design-rationale/example trees (INPUT_DESIGN_TREES) +# - templates/scaffolds: basename contains "template", or the body carries +# {{PLACEHOLDER}} markers — a scaffold cannot validate as concrete +# # Environment variables: -# INPUT_PATH — Directory to scan (default: .) -# INPUT_STRICT — Promote warnings to errors (default: false) +# INPUT_PATH — Directory to scan (default: .) +# INPUT_STRICT — Promote warnings to errors (default: false) +# INPUT_DESIGN_TREES — Space-separated path fragments exempt from +# identity/version checks (default: +# "machine-readable-design/ self-validating/examples/ +# docs/templates/") # # Exit codes: # 0 — All files valid (or only warnings in non-strict mode) @@ -26,6 +44,12 @@ set -euo pipefail SCAN_PATH="${INPUT_PATH:-.}" STRICT="${INPUT_STRICT:-false}" +DESIGN_TREES="${INPUT_DESIGN_TREES:-machine-readable-design/ self-validating/examples/ docs/templates/}" + +# Outside GitHub Actions GITHUB_OUTPUT is unset; under `set -u` an unset +# expansion inside a redirection aborts the whole script (the `|| true` +# cannot catch an expansion error). Default to /dev/null for local runs. +GITHUB_OUTPUT="${GITHUB_OUTPUT:-/dev/null}" # Counters FILES_SCANNED=0 @@ -90,44 +114,65 @@ validate_a2ml() { "Missing SPDX-License-Identifier in first 10 lines" fi - # --- Check 2: Required identity fields --- - # A2ML files must contain either: - # - agent-id = "..." or agent_id = "..." - # - pedigree block with name field - # - name = "..." at top level (for AI manifests) - # - project = "..." (for STATE.a2ml) + # --- Check 2: Identity presence (lint warning; see header) --- + # Identity shapes actually used across the estate: + # - agent-id / agent_id / name / project / spec_id = "..." (TOML-ish) + # - name: "..." (colon dialect) + # - a [metadata] or [scorecard] section (the six-file set and + # scorecards: the filename + section carry the identity) + # - an @abstract directive (contractile Xfile dialect) local has_identity=false local has_version=false + local has_placeholders=false line_num=0 while IFS= read -r line; do line_num=$((line_num + 1)) # Check for identity fields (various A2ML patterns) - if [[ "$line" =~ ^[[:space:]]*(agent[-_]id|name|project)[[:space:]]*= ]]; then + if [[ "$line" =~ ^[[:space:]]*(agent[-_]id|name|project|spec_id)[[:space:]]*= ]] \ + || [[ "$line" =~ ^[[:space:]]*name[[:space:]]*: ]] \ + || [[ "$line" =~ ^\[(metadata|scorecard)\] ]] \ + || [[ "$line" =~ ^@abstract ]]; then has_identity=true fi - # Check for version field - if [[ "$line" =~ ^[[:space:]]*(version|schema_version)[[:space:]]*= ]]; then + # Check for version field (either separator) + if [[ "$line" =~ ^[[:space:]]*(version|schema_version)[[:space:]]*[=:] ]]; then has_version=true fi + # Template placeholder marker ({{PROJECT_NAME}}, {{VERSION}}, …) + if [[ "$line" == *"{{"*"}}"* ]]; then + has_placeholders=true + fi done < "$file" - # AI manifest files (0-AI-MANIFEST.a2ml, 0.1-AI-MANIFEST.a2ml, etc.) - # use markdown-style headers and free text, so identity check is relaxed + # Classes that are identity-free by design (see header): local basename basename="$(basename "$file")" - local is_manifest=false - if [[ "$basename" == *"AI-MANIFEST"* ]]; then - is_manifest=true + local identity_exempt=false + # AI manifests: markdown prose (0-AI-MANIFEST.a2ml, AI.a2ml, …) + if [[ "$basename" == *"AI-MANIFEST"* || "$basename" == "AI.a2ml" ]]; then + identity_exempt=true + fi + # Templates/scaffolds + if [[ "${basename,,}" == *"template"* || "$has_placeholders" == "true" ]]; then + identity_exempt=true fi + # Design-rationale / example trees (standards#435 option a) + local tree + for tree in $DESIGN_TREES; do + if [[ "$file" == *"$tree"* ]]; then + identity_exempt=true + break + fi + done - if [[ "$has_identity" == "false" && "$is_manifest" == "false" ]]; then - report_issue "error" "$file" 1 \ - "Missing required identity field (agent-id, name, or project)" + if [[ "$has_identity" == "false" && "$identity_exempt" == "false" ]]; then + report_issue "warning" "$file" 1 \ + "No identity found (agent-id/name/project/spec_id field, [metadata] or [scorecard] section, or @abstract directive)" fi - if [[ "$has_version" == "false" && "$is_manifest" == "false" ]]; then + if [[ "$has_version" == "false" && "$identity_exempt" == "false" ]]; then report_issue "warning" "$file" 1 \ "Missing version or schema_version field" fi diff --git a/consent-aware-http/.github/workflows/secret-scanner.yml b/consent-aware-http/.github/workflows/secret-scanner.yml index c9f823b7..f90cc8ec 100644 --- a/consent-aware-http/.github/workflows/secret-scanner.yml +++ b/consent-aware-http/.github/workflows/secret-scanner.yml @@ -10,19 +10,6 @@ on: permissions: read-all jobs: - trufflehog: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - with: - fetch-depth: 0 # Full history for scanning - - - name: TruffleHog Secret Scan - uses: trufflesecurity/trufflehog@8a8ef8526528d8a4ff3e2c90be08e25ef8efbd9b # v3 - with: - extra_args: --only-verified --fail - gitleaks: runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/k9-svc/actions/validate/validate-k9.sh b/k9-svc/actions/validate/validate-k9.sh index 58c969b8..24466c06 100755 --- a/k9-svc/actions/validate/validate-k9.sh +++ b/k9-svc/actions/validate/validate-k9.sh @@ -4,13 +4,37 @@ # # validate-k9.sh — K9 configuration file validation script # -# Scans for .k9 and .k9.ncl files and validates: -# 1. K9! magic number on line 1 -# 2. Pedigree block presence with required fields (name, version) +# K9 files come in two dialects, validated differently (standards#434): +# +# Plain dialect (.k9) — text/YAML-ish documents. The `K9!` magic line is +# the format marker and MUST be the first non-empty line. Fields use +# `key: value` form. +# +# Nickel dialect (.k9.ncl) — Nickel source. A bare `K9!` line is a Nickel +# syntax error, so the format marker is carried differently: a +# `magic_number = "K9!"` field, a literal `K9!` preamble line (template +# files that are preprocessed before evaluation), or by construction — +# the file imports/merges a K9 pedigree schema (`K9Pedigree`, +# `pedigree_schema`, or an `import ".…k9.ncl"` of a base template that +# itself carries the magic). Library/contractile modules are that last +# class and are first-class citizens, not violations. +# +# Checks: +# 1. Format marker (dialect-appropriate, see above) +# 2. Pedigree presence with required fields (name; version as warning) # 3. Security level is one of: kennel, yard, hunt (case-insensitive) # 4. Hunt-level files must have a signature or signature_required field # 5. SPDX-License-Identifier header presence # +# This is a LEXICAL linter (grep-grade), not a Nickel evaluator. Field +# checks are file-scope presence checks on purpose: Nickel lets authors +# factor the pedigree through `let` bindings and `&` merges, which no +# line-oriented block tracker can follow. (A previous version tracked +# brace depth to scope checks to the pedigree block; it missed every +# `let component_pedigree = {…}` factoring and miscounted its own opening +# brace. Do not reintroduce block scoping here — deep validation belongs +# to the Nickel contracts themselves.) +# # Environment variables: # INPUT_PATH — Directory to scan (default: .) # INPUT_STRICT — Promote warnings to errors (default: false) @@ -28,6 +52,11 @@ set -euo pipefail SCAN_PATH="${INPUT_PATH:-.}" STRICT="${INPUT_STRICT:-false}" +# Outside GitHub Actions GITHUB_OUTPUT is unset; under `set -u` an unset +# expansion inside a redirection aborts the whole script (the `|| true` +# cannot catch an expansion error). Default to /dev/null for local runs. +GITHUB_OUTPUT="${GITHUB_OUTPUT:-/dev/null}" + # Counters FILES_SCANNED=0 ERRORS=0 @@ -66,19 +95,25 @@ report_issue() { # --------------------------------------------------------------------------- # Helper: normalise a security level string # --------------------------------------------------------------------------- -# Strips quotes, leading/trailing whitespace, Nickel enum tick prefix +# Strips quotes, leading/trailing whitespace, Nickel enum tick prefix. +# Handles both separators: `leash = 'Hunt` (Nickel) and `leash: hunt` (plain). normalise_level() { local raw="$1" # Remove surrounding quotes, tick prefix ('Kennel -> Kennel), whitespace - raw="${raw#*=}" # Remove everything before = + if [[ "$raw" == *"="* ]]; then + raw="${raw#*=}" # Remove everything before = + else + raw="${raw#*:}" # Plain dialect: remove everything before : + fi raw="${raw//\"/}" # Remove double quotes raw="${raw//\'/}" # Remove single quotes (Nickel tick) raw="${raw//,/}" # Remove trailing commas - raw="${raw## }" # Trim leading space - raw="${raw%% }" # Trim trailing space raw="${raw%%#*}" # Remove inline comments - raw="${raw## }" # Trim again - raw="${raw%% }" + # Trim ALL leading/trailing whitespace (a single-space `%% ` pattern + # strips only one char and let `'Kennel # comment` survive as + # "kennel " — an invalid-level false positive) + raw="${raw#"${raw%%[![:space:]]*}"}" + raw="${raw%"${raw##*[![:space:]]}"}" echo "${raw,,}" # Lowercase } @@ -89,7 +124,13 @@ validate_k9() { local file="$1" FILES_SCANNED=$((FILES_SCANNED + 1)) - # --- Check 1: K9! magic number on first non-empty line --- + # Dialect: .k9.ncl is Nickel source; bare .k9 is the plain dialect. + local dialect="plain" + if [[ "$file" == *.k9.ncl ]]; then + dialect="ncl" + fi + + # --- Check 1: format marker (dialect-appropriate) --- local first_content_line="" local first_content_line_num=0 local line_num=0 @@ -105,9 +146,28 @@ validate_k9() { break done < "$file" - if [[ "$first_content_line" != "K9!" ]]; then - report_issue "error" "$file" "$first_content_line_num" \ - "Missing K9! magic number. First non-empty line must be exactly 'K9!'" + if [[ "$dialect" == "plain" ]]; then + if [[ "$first_content_line" != "K9!" ]]; then + report_issue "error" "$file" "$first_content_line_num" \ + "Missing K9! magic number. First non-empty line must be exactly 'K9!'" + fi + else + # Nickel dialect: a bare K9! line is a Nickel syntax error, so the + # marker may instead be a magic_number field or arrive by construction + # through a pedigree-schema import/merge (library modules, #434). + local has_marker=false + if [[ "$first_content_line" == "K9!" ]]; then + has_marker=true + elif grep -Eq '^[[:space:]]*magic_number[[:space:]]*=[[:space:]]*"K9!"' "$file"; then + has_marker=true + elif grep -Eq '(K9Pedigree|pedigree_schema)[[:space:]]*&|&[[:space:]]*(.*\.)?(K9Pedigree|pedigree_schema)|import[[:space:]]*"[^"]*\.k9\.ncl"' "$file"; then + has_marker=true + fi + + if [[ "$has_marker" == "false" ]]; then + report_issue "error" "$file" "$first_content_line_num" \ + "Missing K9 format marker. A .k9.ncl file needs a magic_number = \"K9!\" field, a K9! preamble line, or a K9 pedigree schema import/merge" + fi fi # --- Check 2: SPDX header --- @@ -129,7 +189,11 @@ validate_k9() { "Missing SPDX-License-Identifier in first 10 lines" fi - # --- Check 3: Pedigree block with required fields --- + # --- Check 3: Pedigree presence with required fields --- + # File-scope scans by design (see header): Nickel factoring means the + # pedigree may be `pedigree = {…}`, a let-bound `let component_pedigree + # = {…}`, a schema merge `X.pedigree_schema & {…}` / `X.K9Pedigree & + # {…}`, or — plain dialect — a `metadata:`/`pedigree:` YAML block. local has_pedigree=false local has_pedigree_name=false local has_pedigree_version=false @@ -137,70 +201,49 @@ validate_k9() { local security_level_value="" local security_level_line=0 local has_signature_field=false - local in_pedigree=false - local pedigree_depth=0 line_num=0 while IFS= read -r line; do line_num=$((line_num + 1)) - # Detect pedigree block start - if [[ "$line" =~ ^[[:space:]]*pedigree[[:space:]]*= ]]; then + # Pedigree construct, Nickel forms: direct, let-bound, schema merge + if [[ "$line" =~ ^[[:space:]]*(let[[:space:]]+)?[A-Za-z_]*pedigree[[:space:]]*= ]] \ + || [[ "$line" =~ (K9Pedigree|pedigree_schema)[[:space:]]*\& ]] \ + || [[ "$line" =~ \&[[:space:]]*([A-Za-z_][A-Za-z0-9_]*\.)?(K9Pedigree|pedigree_schema) ]]; then has_pedigree=true - in_pedigree=true - pedigree_depth=0 - continue fi - if [[ "$in_pedigree" == "true" ]]; then - # Track brace depth to know when pedigree block ends - local opens closes - opens="${line//[^\{]/}" - closes="${line//[^\}]/}" - pedigree_depth=$(( pedigree_depth + ${#opens} - ${#closes} )) - - if [[ $pedigree_depth -le 0 && "$has_pedigree" == "true" ]]; then - # Check this final line too before leaving - : - fi - - # Check for name field within pedigree.metadata or pedigree directly - if [[ "$line" =~ ^[[:space:]]+name[[:space:]]*= ]]; then - has_pedigree_name=true - fi - - # Check for version field - if [[ "$line" =~ ^[[:space:]]+(version|schema_version)[[:space:]]*= ]]; then - has_pedigree_version=true - fi + # Pedigree construct, plain dialect: top-level metadata:/pedigree: block + if [[ "$dialect" == "plain" ]] \ + && [[ "$line" =~ ^(metadata|pedigree):[[:space:]]*$ ]]; then + has_pedigree=true + fi - # Check for security level (leash field) - if [[ "$line" =~ ^[[:space:]]+(leash|security_level)[[:space:]]*= ]]; then - has_security_level=true - security_level_value="$(normalise_level "$line")" - security_level_line=$line_num - fi + # Required fields, either separator (= Nickel, : plain) + if [[ "$line" =~ ^[[:space:]]*name[[:space:]]*[=:] ]]; then + has_pedigree_name=true + fi - # Check for signature fields - if [[ "$line" =~ ^[[:space:]]+(signature|signature_required)[[:space:]]*= ]]; then - has_signature_field=true - fi + if [[ "$line" =~ ^[[:space:]]*(version|schema_version)[[:space:]]*[=:] ]]; then + has_pedigree_version=true + fi - # End of pedigree block - if [[ $pedigree_depth -le 0 && "$has_pedigree" == "true" && "$line" == *"}"* ]]; then - in_pedigree=false - fi + # Security level (leash field) + if [[ "$line" =~ ^[[:space:]]*(leash|security_level)[[:space:]]*[=:] ]]; then + has_security_level=true + security_level_value="$(normalise_level "$line")" + security_level_line=$line_num fi - # Also check for signature fields outside pedigree (top-level) - if [[ "$line" =~ ^[[:space:]]*(signature)[[:space:]]*= ]]; then + # Signature fields + if [[ "$line" =~ ^[[:space:]]*(signature|signature_required)[[:space:]]*[=:] ]]; then has_signature_field=true fi done < "$file" if [[ "$has_pedigree" == "false" ]]; then report_issue "error" "$file" 1 \ - "Missing pedigree block. K9 files must contain a 'pedigree = { ... }' section" + "Missing pedigree. K9 files need a pedigree section: 'pedigree = { ... }', a pedigree-schema merge, or (plain dialect) a 'metadata:' block" else if [[ "$has_pedigree_name" == "false" ]]; then report_issue "error" "$file" 1 \ @@ -214,7 +257,14 @@ validate_k9() { fi # --- Check 4: Security level validation --- - if [[ "$has_security_level" == "true" ]]; then + if [[ "$has_security_level" == "true" && "$security_level_value" =~ ^\{\{.*\}\}$ ]]; then + # Scaffold file: the level is a template placeholder to be filled at + # instantiation time. Note it, but a template cannot validate as + # concrete and flagging it every run just trains people to ignore + # the gate. + annotate "notice" "$file" "$security_level_line" \ + "Security level is a template placeholder (${security_level_value}); skipping level validation" + elif [[ "$has_security_level" == "true" ]]; then local level_valid=false for valid in $VALID_LEVELS; do if [[ "$security_level_value" == "$valid" ]]; then diff --git a/lol/.github/workflows/secret-scanner.yml b/lol/.github/workflows/secret-scanner.yml index c9f823b7..f90cc8ec 100644 --- a/lol/.github/workflows/secret-scanner.yml +++ b/lol/.github/workflows/secret-scanner.yml @@ -10,19 +10,6 @@ on: permissions: read-all jobs: - trufflehog: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - with: - fetch-depth: 0 # Full history for scanning - - - name: TruffleHog Secret Scan - uses: trufflesecurity/trufflehog@8a8ef8526528d8a4ff3e2c90be08e25ef8efbd9b # v3 - with: - extra_args: --only-verified --fail - gitleaks: runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/meta-a2ml/.github/workflows/secret-scanner.yml b/meta-a2ml/.github/workflows/secret-scanner.yml index a3b4f1b6..ba422cc4 100644 --- a/meta-a2ml/.github/workflows/secret-scanner.yml +++ b/meta-a2ml/.github/workflows/secret-scanner.yml @@ -10,19 +10,6 @@ on: permissions: read-all jobs: - trufflehog: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 - with: - fetch-depth: 0 # Full history for scanning - - - name: TruffleHog Secret Scan - uses: trufflesecurity/trufflehog@116e7171542d2f1dad8810f00dcfacbe0b809183 # v3 - with: - extra_args: --only-verified --fail - gitleaks: runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/rhodium-standard-repositories/.github/workflows/secret-scanner.yml b/rhodium-standard-repositories/.github/workflows/secret-scanner.yml index c9f823b7..f90cc8ec 100644 --- a/rhodium-standard-repositories/.github/workflows/secret-scanner.yml +++ b/rhodium-standard-repositories/.github/workflows/secret-scanner.yml @@ -10,19 +10,6 @@ on: permissions: read-all jobs: - trufflehog: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - with: - fetch-depth: 0 # Full history for scanning - - - name: TruffleHog Secret Scan - uses: trufflesecurity/trufflehog@8a8ef8526528d8a4ff3e2c90be08e25ef8efbd9b # v3 - with: - extra_args: --only-verified --fail - gitleaks: runs-on: ubuntu-latest timeout-minutes: 20 diff --git a/scripts/apply-baseline.sh b/scripts/apply-baseline.sh index beb31ac8..c85d3bc9 100755 --- a/scripts/apply-baseline.sh +++ b/scripts/apply-baseline.sh @@ -19,9 +19,19 @@ # 0 = all unfiltered findings have severity below blocking threshold # (or advisory mode) # 1 = one or more unfiltered findings >= high in blocking mode -# 2 = invalid input (missing files, malformed JSON) +# 2 = invalid input (missing files, malformed JSON, or a baseline that +# violates the schema — see below) # -# Dependencies: bash, jq. Optional: ajv-cli for schema validation. +# Dependencies: bash, jq. +# +# Schema enforcement: the baseline is structurally validated here, in jq, +# mirroring .machine_readable/hypatia-baseline.schema.json (required keys, +# file XOR file_pattern, closed key set, severity enums, rule_module/type/ +# tracking_issue/expires_at shapes). This script is the single place every +# baseline passes through (both reusable workflows call it), so validating +# here needs no extra tooling or checkout — and a malformed baseline FAILS +# (exit 2) rather than silently matching nothing, which is how suppression +# bugs creep in. Keep this mirror in sync when the schema changes. set -euo pipefail @@ -60,6 +70,62 @@ echo "$BASELINE_JSON" | jq -e 'type == "array"' >/dev/null || { exit 2 } +# Structural validation against the baseline schema (see header). +SCHEMA_ERRORS="$(jq -r ' + def known: ["severity","rule_module","type","file","file_pattern", + "severity_override","expires_at","note","tracking_issue"]; + def sevs: ["critical","high","medium","low","info"]; + [ to_entries[] | .key as $i | .value as $e | + if ($e|type) != "object" then "entry[\($i)]: not an object" + else ( + (["severity","rule_module","type"][] + | select(($e[.]|type) != "string") + | "entry[\($i)]: required key \(.) missing or not a string"), + (if (($e|has("file")) == ($e|has("file_pattern"))) + then "entry[\($i)]: exactly one of file / file_pattern is required" + else empty end), + ($e | keys[] + | select(. as $k | known | index($k) | not) + | "entry[\($i)]: unknown key \(.)"), + (if ($e.severity|type) == "string" and ((sevs|index($e.severity))|not) + then "entry[\($i)]: invalid severity \($e.severity)" else empty end), + (if ($e.rule_module|type) == "string" + and (($e.rule_module|test("^[a-z][a-z0-9_]*$"))|not) + then "entry[\($i)]: rule_module fails pattern: \($e.rule_module)" + else empty end), + (if ($e.type|type) == "string" + and (($e.type|test("^([a-z][a-z0-9_]*|[A-Z]{2,3}[0-9]{3})$"))|not) + then "entry[\($i)]: type fails pattern: \($e.type)" else empty end), + (if ($e|has("file")) and ((($e.file|type) != "string") or ($e.file == "")) + then "entry[\($i)]: file must be a non-empty string" else empty end), + (if ($e|has("file_pattern")) + and ((($e.file_pattern|type) != "string") or ($e.file_pattern == "")) + then "entry[\($i)]: file_pattern must be a non-empty string" + else empty end), + (if ($e|has("severity_override")) + and (((sevs + ["advisory"])|index($e.severity_override))|not) + then "entry[\($i)]: invalid severity_override \($e.severity_override)" + else empty end), + (if ($e|has("expires_at")) + and ((($e.expires_at|type) != "string") + or (($e.expires_at|test("^[0-9]{4}-[0-9]{2}-[0-9]{2}$"))|not)) + then "entry[\($i)]: expires_at must be an ISO date (YYYY-MM-DD)" + else empty end), + (if ($e|has("tracking_issue")) + and ((($e.tracking_issue|type) != "string") + or (($e.tracking_issue|test("^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+#[0-9]+$"))|not)) + then "entry[\($i)]: tracking_issue must look like owner/repo#N" + else empty end) + ) end + ] | .[] +' <<<"$BASELINE_JSON")" + +if [[ -n "$SCHEMA_ERRORS" ]]; then + echo "error: baseline violates hypatia-baseline.schema.json:" >&2 + echo "$SCHEMA_ERRORS" >&2 + exit 2 +fi + # Pre-filter baseline: drop expired entries (>=today). ACTIVE_BASELINE="$(jq --arg today "$TODAY" ' map(select((.expires_at // "9999-12-31") >= $today))