diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 759c3445..584b8cb3 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -67,7 +67,7 @@ workflows: - 'actions/upload-artifact@v7.0.1' - 'actions/upload-pages-artifact@v5.0.0' - 'dtolnay/rust-toolchain@master' - - 'editorconfig-checker/action-editorconfig-checker@v2.2.0' + - 'editorconfig-checker/action-editorconfig-checker@v3.0.0' - 'ruby/setup-ruby@v1.321.0' '.github/workflows/release.yml': - 'actions/attest-build-provenance@v4.2.2' @@ -254,9 +254,9 @@ dependencies: commit: 'sha1-6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' owner_id: 1940490 repo_id: 260749683 - 'editorconfig-checker/action-editorconfig-checker@v2.2.0': - ref: 'v2.2.0' - commit: 'sha1-840e866d93b8e032123c23bac69dece044d4d84c' + 'editorconfig-checker/action-editorconfig-checker@v3.0.0': + ref: 'v3.0.0' + commit: 'sha1-51f63319f592f97930c73d9c46184d20bd206393' owner_id: 26415196 repo_id: 297874902 'erlef/setup-beam@v1.24.1': @@ -301,9 +301,14 @@ dependencies: repo_id: 1275649586 'hyperpolymath/k9-ecosystem@main': ref: 'main' - commit: 'sha1-3f250fba42e432c7ff47b48f59525bec3357136b' + commit: 'sha1-c74f04c77a36247a813493945e803178b93d170f' owner_id: 6759885 repo_id: 1275650185 + 'hyperpolymath/smtp-notify-action@v0.2.0': + ref: 'v0.2.0' + commit: 'sha1-ede1191ef6ff3ac02c4f4d9efdf837ee517e11d7' + owner_id: 6759885 + repo_id: 1352485172 'ruby/setup-ruby@v1.321.0': ref: 'v1.321.0' commit: 'sha1-95ef2b042f9d7a56d8268cba8559e2842e2ad01b' diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index bfe74e6a..bc4453cf 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -106,6 +106,23 @@ jobs: echo "::warning::Found $CONFIG_COUNT config files but no K9 contracts. Run k9iser to generate contracts." fi + # QUARANTINE PIN -- see .github/workflows/actions.lock. + # This action is referenced at the MOVING `@main` ref, so the lockfile is + # what actually selects the commit. k9-ecosystem main (3f250fba, + # 2026-08-28) carries a dangling symlink: + # editors/vscode/container/.gatekeeper.yaml + # -> ../../../../_shared/container/.gatekeeper.yaml + # which is one `..` too many and escapes the repository; no `_shared/` + # directory exists. GitHub extracts the WHOLE action repo during + # "Prepare all required actions", BEFORE any step `if:` is evaluated, so + # the unresolvable link killed this job at setup regardless of how many + # K9 files the caller had, and the `k9_count > 0` guard below could not + # protect it: + # ##[error]Could not find file '.../editors/vscode/container/.gatekeeper.yaml' + # The lockfile therefore pins @main back to c74f04c7 (2026-08-24), the + # last commit whose tree contains no symlinks at all. Restore the pin to + # main HEAD once k9-ecosystem carries _shared/container/.gatekeeper.yaml + # across, or replaces its two symlinks with real files. - name: Validate K9 contracts if: steps.detect.outputs.k9_count > 0 uses: hyperpolymath/k9-ecosystem/validate-action@main diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index c5349a6e..3a381d43 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -336,7 +336,7 @@ jobs: find . -type f -size +1M -not -path "./.git/*" | head -10 - name: EditorConfig check - uses: editorconfig-checker/action-editorconfig-checker@v2.2.0 + uses: editorconfig-checker/action-editorconfig-checker@v3.0.0 docs: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56d6946d..3c48b5ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,16 @@ name: Release & Container on: - push: null + # Was `push: null`, which fires on EVERY branch push. The release + # pipeline then ran on feature branches, where `${GITHUB_REF#refs/tags/v}` + # cannot strip its prefix and returns the ref VERBATIM -- so the archive + # name became `hypatia-refs/heads/-.tar.gz`, whose + # embedded slashes are directories that do not exist, and tar died with + # `Cannot write: Broken pipe` on all five targets. Releases are cut from + # tags, so the trigger now says so. + push: + tags: + - 'v*' workflow_dispatch: inputs: version: @@ -48,8 +57,14 @@ jobs: run: | if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ inputs.version }}" ]]; then VERSION="${{ inputs.version }}" - else + elif [[ "$GITHUB_REF" == refs/tags/v* ]]; then VERSION="${GITHUB_REF#refs/tags/v}" + else + # `${VAR#prefix}` returns VAR UNCHANGED when the prefix is absent. + # Relying on it to parse a tag meant a non-tag ref silently became + # the version string. Fail loudly instead of building a nonsense name. + echo "::error::Release requires a refs/tags/v* ref or an explicit version input; got '$GITHUB_REF'" + exit 1 fi echo "version=$VERSION" >> $GITHUB_OUTPUT diff --git a/.github/workflows/security-policy.yml b/.github/workflows/security-policy.yml index 239c5ad8..92e5e7a7 100644 --- a/.github/workflows/security-policy.yml +++ b/.github/workflows/security-policy.yml @@ -98,6 +98,36 @@ jobs: head: HEAD extra_args: --only-verified + # `.gitleaks.toml` carries `[extend] path = ".gitleaks-estate.toml"`, and + # gitleaks resolves that against the PROCESS CWD. Only standards' + # secret-scanner-reusable.yml staged that file, so these two hand-rolled + # jobs died before scanning a single byte: + # FTL failed to load extended config ... no such file or directory + # the action then crashed on the results.sarif it never wrote, and the + # audit summary reported a CRASHED SCANNER as "possible secrets in + # repository" -- a fake red in the worst possible direction for a + # security gate. Pinned to the same standards sha that + # .github/workflows/secret-scanner.yml already consumes. + - name: Fetch estate gitleaks baseline + uses: actions/checkout@v7.0.1 + with: + repository: hyperpolymath/standards + ref: 571cc734cd69fb846032ec77a662aa8ee4fc32cd + path: .standards-gitleaks + sparse-checkout: | + config/gitleaks/estate-baseline.toml + sparse-checkout-cone-mode: false + persist-credentials: false + + - name: Stage estate gitleaks baseline + run: | + set -euo pipefail + cp .standards-gitleaks/config/gitleaks/estate-baseline.toml .gitleaks-estate.toml + # Delete the tooling checkout BEFORE scanning, or the scan walks + # standards' own tree and reports ITS files as hypatia's. + rm -rf .standards-gitleaks + test -s .gitleaks-estate.toml + - name: Gitleaks Secret Scan uses: gitleaks/gitleaks-action@v3.0.0 env: @@ -147,20 +177,20 @@ jobs: toolchain: stable - name: Install cargo-cyclonedx - run: cargo install cargo-cyclonedx --locked + run: cargo install cargo-cyclonedx --locked --version 0.5.9 - name: Generate CycloneDX SBOM run: | - cargo cyclonedx --format json --output-cdx rust-sbom.cdx.json - cargo cyclonedx --format xml --output-cdx rust-sbom.cdx.xml + cargo cyclonedx --format json --override-filename rust-sbom.cdx + cargo cyclonedx --format xml --override-filename rust-sbom.cdx - name: Upload SBOM artifacts uses: actions/upload-artifact@v7.0.1 with: name: rust-sbom path: | - rust-sbom.cdx.json - rust-sbom.cdx.xml + **/rust-sbom.cdx.json + **/rust-sbom.cdx.xml retention-days: 90 # ============================================================================ @@ -477,6 +507,36 @@ jobs: EOF fi + # `.gitleaks.toml` carries `[extend] path = ".gitleaks-estate.toml"`, and + # gitleaks resolves that against the PROCESS CWD. Only standards' + # secret-scanner-reusable.yml staged that file, so these two hand-rolled + # jobs died before scanning a single byte: + # FTL failed to load extended config ... no such file or directory + # the action then crashed on the results.sarif it never wrote, and the + # audit summary reported a CRASHED SCANNER as "possible secrets in + # repository" -- a fake red in the worst possible direction for a + # security gate. Pinned to the same standards sha that + # .github/workflows/secret-scanner.yml already consumes. + - name: Fetch estate gitleaks baseline + uses: actions/checkout@v7.0.1 + with: + repository: hyperpolymath/standards + ref: 571cc734cd69fb846032ec77a662aa8ee4fc32cd + path: .standards-gitleaks + sparse-checkout: | + config/gitleaks/estate-baseline.toml + sparse-checkout-cone-mode: false + persist-credentials: false + + - name: Stage estate gitleaks baseline + run: | + set -euo pipefail + cp .standards-gitleaks/config/gitleaks/estate-baseline.toml .gitleaks-estate.toml + # Delete the tooling checkout BEFORE scanning, or the scan walks + # standards' own tree and reports ITS files as hypatia's. + rm -rf .standards-gitleaks + test -s .gitleaks-estate.toml + - name: Run Gitleaks uses: gitleaks/gitleaks-action@v3.0.0 env: @@ -623,12 +683,12 @@ jobs: toolchain: stable - name: Install cargo-cyclonedx - run: cargo install cargo-cyclonedx --locked + run: cargo install cargo-cyclonedx --locked --version 0.5.9 - name: Generate Rust SBOM run: | - cargo cyclonedx --format json --output-cdx sbom-rust.cdx.json || true - cargo cyclonedx --format xml --output-cdx sbom-rust.cdx.xml || true + cargo cyclonedx --format json --override-filename sbom-rust.cdx || true + cargo cyclonedx --format xml --override-filename sbom-rust.cdx || true - name: Upload SBOM artifacts @@ -636,8 +696,8 @@ jobs: with: name: sbom path: | - sbom-rust.cdx.json - sbom-rust.cdx.xml + **/sbom-rust.cdx.json + **/sbom-rust.cdx.xml retention-days: 90 # ============================================================================ diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ca53e18e..39415eb0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -86,7 +86,14 @@ jobs: - name: E2E — Scan fixture repo run: | + rm -rf /tmp/test-repo mkdir -p /tmp/test-repo/.github/workflows /tmp/test-repo/src + # The Rust CLI opens the target with git2 (Repository::open, + # cli/src/commands/mod.rs:38) and exits 1 on a plain directory. + # The fixture was only ever mkdir'd, so this step could never have + # passed -- the wrong binary name (exit 127) masked it. No commit + # is needed; an empty repository is enough. + git init -q /tmp/test-repo echo 'name: CI' > /tmp/test-repo/.github/workflows/ci.yml echo 'on: push' >> /tmp/test-repo/.github/workflows/ci.yml echo 'jobs:' >> /tmp/test-repo/.github/workflows/ci.yml @@ -94,7 +101,11 @@ jobs: echo ' runs-on: ubuntu-latest' >> /tmp/test-repo/.github/workflows/ci.yml echo ' steps:' >> /tmp/test-repo/.github/workflows/ci.yml echo ' - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.2.2' >> /tmp/test-repo/.github/workflows/ci.yml - ./target/release/hypatia scan /tmp/test-repo 2>&1 + # The workspace binary is `hyper` (cli/Cargo.toml [[bin]] name), + # not `hypatia` -- the crate is `hypatia-cli`. Invoking the crate + # name gave `No such file or directory` and exit 127, which reads + # like a missing build rather than a wrong filename. + ./target/release/hyper scan /tmp/test-repo 2>&1 echo "PASS: Rust CLI scan completed" aspect-tests: diff --git a/0-AI-MANIFEST.a2ml b/0-AI-MANIFEST.a2ml index 97748a35..bdf5b2ab 100644 --- a/0-AI-MANIFEST.a2ml +++ b/0-AI-MANIFEST.a2ml @@ -21,7 +21,7 @@ seambot, finishbot, panicbot) via the safety triangle pipeline (Eliminate > Substitute > Control). 8 neural networks on a shared blackboard, VCL queries over verisim-data, Bayesian confidence updating, - and 33 Elixir rule modules for pattern detection.") + and 35 Elixir rule modules for pattern detection.") ;; =================================================================== ;; CONTEXT TIERS @@ -62,7 +62,7 @@ (description "Safety triangle pipeline with neural dispatch") (layers (elixir-pipeline "lib/" "133 modules — pattern analysis, dispatch, learning") - (rules "lib/rules/" "33 rule modules; catalogue at docs/rules/catalogue.md") + (rules "lib/rules/" "35 rule modules; catalogue at docs/rules/catalogue.adoc") (neural "lib/neural/" "8 networks on a blackboard + coordinator GenServer") (vcl "lib/vcl/" "VCL parser, file executor, query cache, federation") (merge-orch "lib/merge_orchestration/" "12 modules — leases, ledger, ticker") diff --git a/README.adoc b/README.adoc index 4826f996..fc787b64 100644 --- a/README.adoc +++ b/README.adoc @@ -19,8 +19,8 @@ toc::[] *Hypatia* is the CI/CD governance scanner for the hyperpolymath estate (~400 repositories). It combines: -* *Symbolic reasoning* — 33 Elixir rule modules producing fast, deterministic findings - (link:docs/rules/catalogue.md[full rule catalogue]) +* *Symbolic reasoning* — 35 Elixir rule modules producing fast, deterministic findings + (link:docs/rules/catalogue.adoc[full rule catalogue]) * *Neural learning* — 8 networks on a shared blackboard, learning which fixes actually stick * *Bayesian confidence* — every recipe carries a Beta-distributed confidence updated from real outcomes, which gates whether a fix is applied automatically, proposed as a PR, or merely reported @@ -68,7 +68,7 @@ PatternRegistry ──> TriangleRouter ──> FleetDispatcher ──> DispatchM [cols="1,3"] |=== | `lib/` | 133 Elixir modules — pipeline, rules, VCL, neural, safety, web, merge orchestration -| `lib/rules/` | 33 rule modules; see link:docs/rules/catalogue.md[the catalogue] +| `lib/rules/` | 35 rule modules; see link:docs/rules/catalogue.adoc[the catalogue] | `lib/neural/` | 8 networks + blackboard, coordinator, persistence, rebalancer | `lib/vcl/` | VCL query layer over verisim-data: parser, file executor, cache, federation | `lib/merge_orchestration/` | 12 modules — leases, ledger, ticker @@ -141,7 +141,7 @@ by any dispatch path, and the CI blockage currently stopping the Rust lane. == Documentation * link:docs/README.adoc[docs/README.adoc] — the documentation index -* link:docs/rules/catalogue.md[Rule catalogue] — every rule module and ID family +* link:docs/rules/catalogue.adoc[Rule catalogue] — every rule module and ID family * link:docs/DEBT-REGISTER.md[Debt register] — what is broken and what is not gated * https://github.com/hyperpolymath/hypatia/wiki[Wiki] — getting started, architecture, operations, FAQ * link:PALIMPSEST.adoc[Palimpsest Philosophy] — philosophical underpinnings diff --git a/docs/rules/catalogue.adoc b/docs/rules/catalogue.adoc index 9d29a85e..de57dbaa 100644 --- a/docs/rules/catalogue.adoc +++ b/docs/rules/catalogue.adoc @@ -1,122 +1,54 @@ -== Hypatia rule catalogue +// SPDX-License-Identifier: CC-BY-SA-4.0 +// GENERATED by scripts/gen-rule-catalogue.sh — do not edit by hand. -Generated from `+lib/rules/*.ex+` on 2026-08-07. Regenerate with: += Hypatia rule catalogue + +Generated from `+lib/rules/*.ex+` on 2026-09-08. Regenerate with: [source,bash] ---- ./scripts/gen-rule-catalogue.sh ---- -33 rule modules. IDs are extracted from `+rule:+`/`+id:+` literals; a -blank ID column means the module emits atom-keyed findings rather than -prefixed IDs. +35 rule modules. IDs are extracted from `+rule:+`/`+id:+` literals; +a blank ID column means the module emits atom-keyed findings rather than prefixed IDs. -[width="100%",cols="34%,33%,33%",options="header",] +[cols="34,33,33",options="header"] |=== |Module |Rule IDs |Purpose -|`+admin_merge_eligibility+` |DBA002 |Recognises PR shapes that are safe -to admin-merge in bulk when the - -|`+baseline_health+` |BH001 BH002 BH003 BH004 BH005 BH006 BH007 BH008 -|Detects degraded `+main+`-branch baseline conditions that allow silent -rot. - -|`+branch_protection+` |BP001 BP002 BP003 BP004 BP005 BP006 BP007 BP008 -BP009 |Branch-protection hygiene rules drawn from the CIS GitHub -Benchmark, - -|`+build_system_rules+` |— |Build-tool configuration anti-patterns that -block estate CI but slip past - -|`+cicd_rules+` |— |Primary Elixir CI/CD policy rules (migrated from -legacy Logtalk engine). - -|`+code_safety+` |— |Primary Elixir rules for code safety (migrated from -legacy Logtalk engine). - -|`+code_scanning_alerts+` |CSA001 CSA002 CSA003 CSA004 |GitHub Code -Scanning alert querying (CodeQL + third-party SARIF). - -|`+dependabot_alerts+` |DA001 DA002 DA003 DA004 |GitHub Dependabot alert -querying and risk assessment. - -|`+disambiguation_rules+` |DR001 |Cross-reference / -language-disambiguation rules. - -|`+dogfooding+` |HYP-DOG-001 HYP-DOG-003 HYP-DOG-004 HYP-DOG-005 -HYP-DOG-006 HYP-DOG-007 HYP-DOG-008 HYP-DOG-009 HYP-DOG-010 |Dogfooding -compliance rules – HYP-DOG-001 through HYP-DOG-010. - -|`+forge_adapters+` |— |Primary Elixir forge adapter rules (migrated -from legacy Logtalk engine). - -|`+git_state+` |GS001 GS002 GS003 GS004 GS005 GS006 GS007 |Git -repository synchronisation state checks. - -|`+green_web+` |— |Green Web Foundation certification checks for -hyperpolymath repositories. - -|`+honest_completion+` |— |Honest completion audit – cross-references -claimed completion percentages - -|`+learning+` |— |Learning engine rules absorbed from Logtalk -engine/rules/learning.lgt - -|`+migration_rules+` |— |Primary Elixir ReScript migration rules -(migrated from legacy Logtalk engine). - +|`+actions_lock+` |— |Strict local validation for machine-generated `gh actions-lock` v0.0.2 files. +|`+admin_merge_eligibility+` |DBA002 |Recognises PR shapes that are safe to admin-merge in bulk when the +|`+baseline_health+` |BH001 BH002 BH003 BH004 BH005 BH006 BH007 BH008 |Detects degraded `main`-branch baseline conditions that allow silent rot. +|`+branch_protection+` |BP001 BP002 BP003 BP004 BP005 BP006 BP007 BP008 BP009 |Branch-protection hygiene rules drawn from the CIS GitHub Benchmark, +|`+build_system_rules+` |— |Build-tool configuration anti-patterns that block estate CI but slip past +|`+cicd_rules+` |— |Primary Elixir CI/CD policy rules (migrated from legacy Logtalk engine). +|`+code_safety+` |— |Primary Elixir rules for code safety (migrated from legacy Logtalk engine). +|`+code_scanning_alerts+` |CSA001 CSA002 CSA003 CSA004 |GitHub Code Scanning alert querying (CodeQL + third-party SARIF). +|`+dependabot_alerts+` |DA001 DA002 DA003 DA004 |GitHub Dependabot alert querying and risk assessment. +|`+disambiguation_rules+` |DR001 |Cross-reference / language-disambiguation rules. +|`+dogfooding+` |HYP-DOG-001 HYP-DOG-003 HYP-DOG-004 HYP-DOG-005 HYP-DOG-006 HYP-DOG-007 HYP-DOG-008 HYP-DOG-009 HYP-DOG-010 |Dogfooding compliance rules -- HYP-DOG-001 through HYP-DOG-010. +|`+forge_adapters+` |— |Primary Elixir forge adapter rules (migrated from legacy Logtalk engine). +|`+git_state+` |GS001 GS002 GS003 GS004 GS005 GS006 GS007 |Git repository synchronisation state checks. +|`+green_web+` |— |Green Web Foundation certification checks for hyperpolymath repositories. +|`+honest_completion+` |— |Honest completion audit -- cross-references claimed completion percentages +|`+implementation_inside_canon+` |— |Executable evaluator for the standards-authored HYP-S009 rule. +|`+learning+` |— |Learning engine rules absorbed from Logtalk engine/rules/learning.lgt +|`+migration_rules+` |— |Primary Elixir ReScript migration rules (migrated from legacy Logtalk engine). |`+proof_obligation+` |— |ProofObligation recipe type for Hypatia. - -|`+proof_strategy_selection+` |— |Prover-strategy selection from -VeriSimDB historical outcomes. - -|`+research_extensions+` |RE001 RE002 RE003 RE004 RE005 RE006 RE007 -RE008 RE009 RE010 |Static detection of GitHub Actions workflow defects -sourced from - -|`+root_hygiene+` |ERR-GIT-001 |Root directory hygiene enforcement -rules. - -|`+rsr_conformance+` |— |The RSR v2.0 conformance *oracle* (increment 3 -of the HYP-S family): - -|`+rsr_criteria+` |— |Loads the *RSR v2.0 criteria SSOT* -(`+rsr-criteria-v2.a2ml+` from - -|`+rule_loader+` |— |Loads standards-authored rule definitions -(`+.a2ml+` files) into structured - -|`+rules+` |ERR-LIC-001 |Facade module for all Hypatia rules, absorbed -from the Logtalk engine. - -|`+scorecard_compliance+` |— |Enforces OpenSSF Scorecard best practices -as automated Elixir rules. - -|`+secret_scanning_alerts+` |SSA001 SSA002 SSA003 SSA004 |GitHub Secret -Scanning alert querying. - -|`+security_errors+` |— |Primary Elixir security error database -(migrated from legacy Logtalk engine). - -|`+sha_bump_propagation+` |— |Detection: an estate-wide reusable -workflow has been bumped to a new SHA - -|`+strategy_drift+` |— |N4: Detects strategy-shift events and flags -previously-failed attempts - -|`+structural_drift+` |SD001 SD002 SD003 SD004 SD005 SD006 SD007 SD008 -SD009 SD010 SD011 SD013 SD014 SD021 SD022 SD023 |Structural drift -detection rules. - -|`+supply_chain+` |SC001 SC002 SC003 SC004 SC005 SC006 SC007 SC008 SC009 -SC010 SC011 SC012 SC013 |Supply-chain integrity rules drawn from OSSF -Scorecard, SLSA, OWASP - -|`+workflow_audit+` |ERR-WF-013 ERR-WF-014 WF013 WF014 WF015 WF016 WF017 -WF018 WF019 WF020 WF021 WF022 WF023 WF024 WF-025 WF025 |CI/CD workflow -audit rules. - -|`+workflow_hardening+` |WH001 WH002 WH003 WH004 WH005 WH006 WH007 WH008 -WH009 WH010 WH011 WH012 |Static detection of dangerous GitHub Actions -workflow patterns. +|`+proof_strategy_selection+` |— |Prover-strategy selection from VeriSimDB historical outcomes. +|`+research_extensions+` |RE001 RE002 RE003 RE004 RE005 RE006 RE007 RE008 RE009 RE010 |Static detection of GitHub Actions workflow defects sourced from +|`+root_hygiene+` |ERR-GIT-001 |Root directory hygiene enforcement rules. +|`+rsr_conformance+` |— |The RSR v2.0 conformance **oracle** (increment 3 of the HYP-S family): +|`+rsr_criteria+` |— |Loads the **RSR v2.0 criteria SSOT** (`rsr-criteria-v2.a2ml` from +|`+rule_loader+` |— |Loads standards-authored rule definitions (`.a2ml` files) into structured +|`+rules+` |ERR-LIC-001 |Facade module for all Hypatia rules, absorbed from the Logtalk engine. +|`+scorecard_compliance+` |— |Enforces OpenSSF Scorecard best practices as automated Elixir rules. +|`+secret_scanning_alerts+` |SSA001 SSA002 SSA003 SSA004 |GitHub Secret Scanning alert querying. +|`+security_errors+` |— |Primary Elixir security error database (migrated from legacy Logtalk engine). +|`+sha_bump_propagation+` |— |Detection: an estate-wide reusable workflow has been bumped to a new SHA +|`+strategy_drift+` |— |N4: Detects strategy-shift events and flags previously-failed attempts +|`+structural_drift+` |SD001 SD002 SD003 SD004 SD005 SD006 SD007 SD008 SD009 SD010 SD011 SD013 SD014 SD021 SD022 SD023 |Structural drift detection rules. +|`+supply_chain+` |SC001 SC002 SC003 SC004 SC005 SC006 SC007 SC008 SC009 SC010 SC011 SC012 SC013 |Supply-chain integrity rules drawn from OSSF Scorecard, SLSA, OWASP +|`+workflow_audit+` |ERR-WF-013 ERR-WF-014 WF013 WF014 WF015 WF016 WF017 WF018 WF019 WF020 WF021 WF022 WF023 WF024 WF-025 WF025 |CI/CD workflow audit rules. +|`+workflow_hardening+` |WH001 WH002 WH003 WH004 WH005 WH006 WH007 WH008 WH009 WH010 WH011 WH012 |Static detection of dangerous GitHub Actions workflow patterns. |=== diff --git a/k9iser.toml b/k9iser.toml index 297b2821..7f052182 100644 --- a/k9iser.toml +++ b/k9iser.toml @@ -18,11 +18,6 @@ path = "mix.exs" type = "elixir" output = "generated/k9iser/elixir-manifest.k9" -[[source]] -path = "deno.json" -type = "deno" -output = "generated/k9iser/deno-manifest.k9" - [[source]] path = "Justfile" type = "justfile" @@ -37,10 +32,6 @@ output = "generated/k9iser/ci-security.k9" rule = "build.dependencies has no banned_packages" severity = "error" -[[constraint]] -rule = "deno.imports has no npm: specifiers" -severity = "error" - [[constraint]] rule = "workflows includes hypatia-scan" severity = "error" diff --git a/lib/hypatia/cli.ex b/lib/hypatia/cli.ex index 8857ca00..704238c1 100644 --- a/lib/hypatia/cli.ex +++ b/lib/hypatia/cli.ex @@ -1302,7 +1302,7 @@ defmodule Hypatia.CLI do git_state,dependabot_alerts, secret_scanning_alerts,code_scanning_alerts, structural_drift,implementation_inside_canon - --format, -f Output format: json (default), text, github, sarif, sarif + --format, -f Output format: json (default), text, github, sarif --severity, -s Minimum severity: critical, high, medium (default), low --path, -p Path to scan (alternative to positional arg) --exit-zero Always exit 0 after a successful scan, even when diff --git a/scripts/gen-rule-catalogue.sh b/scripts/gen-rule-catalogue.sh index 9ab4baa2..087fa98c 100755 --- a/scripts/gen-rule-catalogue.sh +++ b/scripts/gen-rule-catalogue.sh @@ -1,28 +1,40 @@ #!/usr/bin/env bash # SPDX-License-Identifier: MPL-2.0 -# gen-rule-catalogue.sh — regenerate docs/rules/catalogue.md from lib/rules/*.ex +# gen-rule-catalogue.sh — regenerate docs/rules/catalogue.adoc from lib/rules/*.ex # so the published rule reference cannot drift from the code. +# +# Emits AsciiDoc natively. The catalogue was converted .md -> .adoc by the +# estate documentation sweep, but this generator kept writing the .md that +# no longer exists -- so "regenerate" silently produced an orphan file beside +# the real one and the catalogue drifted unnoticed. The conversion also +# dropped the SPDX header (C7 requires it on line 1); emitted here again. +# +# Monospace cells use the `+...+` passthrough form: rule modules are +# snake_case and AsciiDoc would otherwise treat a leading/trailing `_` as +# italic markup. set -euo pipefail cd "$(dirname "$0")/.." -out="docs/rules/catalogue.md" +out="docs/rules/catalogue.adoc" mkdir -p docs/rules { - echo "" - echo "" + echo "// SPDX-License-Identifier: CC-BY-SA-4.0" + echo "// GENERATED by scripts/gen-rule-catalogue.sh — do not edit by hand." echo - echo "# Hypatia rule catalogue" + echo "= Hypatia rule catalogue" echo - echo "Generated from \`lib/rules/*.ex\` on $(date -u +%Y-%m-%d). Regenerate with:" + echo "Generated from \`+lib/rules/*.ex+\` on $(date -u +%Y-%m-%d). Regenerate with:" echo - echo '```bash' + echo "[source,bash]" + echo "----" echo "./scripts/gen-rule-catalogue.sh" - echo '```' + echo "----" echo - echo "$(find lib/rules -maxdepth 1 -name '*.ex' | wc -l) rule modules. IDs are extracted from \`rule:\`/\`id:\` literals;" + echo "$(find lib/rules -maxdepth 1 -name '*.ex' | wc -l | tr -d ' ') rule modules. IDs are extracted from \`+rule:+\`/\`+id:+\` literals;" echo "a blank ID column means the module emits atom-keyed findings rather than prefixed IDs." echo - echo "| Module | Rule IDs | Purpose |" - echo "|---|---|---|" + echo '[cols="34,33,33",options="header"]' + echo "|===" + echo "|Module |Rule IDs |Purpose" for f in lib/rules/*.ex; do m=$(basename "$f" .ex) # `|| true` here is a no-match, not a discarded failure: many modules emit @@ -32,7 +44,8 @@ mkdir -p docs/rules doc=$(awk '/@moduledoc """/{f=1;next} f&&/"""/{exit} f{gsub(/^[ \t]+/,""); if($0!="") {print; exit}}' "$f" \ | sed 's/|/\\|/g' | cut -c1-140) # shellcheck disable=SC2016 # printf format string: literal by design - printf '| `%s` | %s | %s |\n' "$m" "${ids:-—}" "${doc:-—}" + printf '|`+%s+` |%s |%s\n' "$m" "${ids:-—}" "${doc:-—}" done + echo "|===" } > "$out" echo "wrote $out"