diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 30d2b3ea3..9ebaca328 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -25120,3 +25120,41 @@ The harm path is not "a reader is mildly misinformed". It is specific and it run ### Closing act One docstring edit in `scripts/asvs/apply.py`, stating that the listed keys are the ones the writer ORDERS and that any other key is carried verbatim. Deliberately **no count** of the carried fields: a tally in a docstring goes stale silently, which is the same defect wearing a number. + + +## 1485. docs/SECURITY.md files the built gitleaks and SBOM jobs under Planned CI additions + +> ๐Ÿšง **Filed 2026-09-07 -- the doc fix ships in this PR.** Value **4/10** ยท Difficulty **1/10** ยท _quick win_. The "Supply-chain & CI security" section listed the `gitleaks` secret scan and the CycloneDX SBOM build under **Planned CI additions**. Both are built, and `gitleaks (secret scan)` is a required context. The same two bullets carried three further stale claims, each independently checkable and each corrected here. Per `CLOSING_SEAT["code"]` the banner flip on merge is the LANDER's. +> Verdict: build +> Research: none +> Closing-act: code + +**Cluster:** documentation honesty / security prose. **Priority:** P3. **Verdict:** build. +**Severity:** no deployment axis (sec. 0). Nothing here is engine behaviour, a shipped artifact, or PHI, so no deploying site would meet it. The cost falls on a reader of the security record: they are told a built control is planned, told the wrong scope for a required gate, and told a local hook does not exist when it does. + +**Measured 2026-09-07** at engine `4c68c28eb`, which was `origin/main`'s tip in the same run. + +### Five claims, and what the tree says + +| the claim, as it stood | what the tree says | +|---|---| +| **SBOM** is a planned CI addition | `security.yml`'s `sbom` job builds CycloneDX bills of materials for the Python engine and the VS Code extension, scores them with `sbomqs`, and uploads them; the `trivy` job adds the container image. ADR 0149 and `docs/SUPPLY-CHAIN.md` document it. | +| **Secret-history scan** is a planned CI addition | `security.yml` job `gitleaks`, `name: gitleaks (secret scan)`, and that exact string is listed in `.github/required-contexts.txt`. | +| the scan is "Kept in CI rather than a per-author pre-commit hook" | `.pre-commit-config.yaml` pins `repo: https://github.com/gitleaks/gitleaks`, `rev: v8.18.4`, hook id `gitleaks`. Both exist. | +| the scan runs "over the **full git history**" | BACKLOG #1479 scoped it with `--log-opts HEAD`. The reason lives once, on the scan step's own comment, and this ledger row does not restate it either. | +| **pip-audit** and **bandit** are advisory | Both job comments read `BLOCKING`, neither declares `continue-on-error`, and both context strings are in `.github/required-contexts.txt`. | + +A sixth line was stale in a different direction: the section told the reader to turn **CodeQL** on through GitHub Advanced Security "on a private repo". `.github/workflows/codeql.yml` has run here for months and this repository is public, so no licence is involved. `security.yml`'s own header already records that correction. + +### The fix + +`docs/SECURITY.md` now carries one list of what CI runs, with `gitleaks` and the SBOM job in it, and no "Planned CI additions" block. It states the scan's scope **nowhere**: it names the step that holds it and stops, because a second copy of that fact is what went false last time. The section closes with a short paragraph naming what it used to say, so a reader who absorbed the old text can recognise the shape rather than be silently overwritten. + +### One dependent edit, and why it was not left to drift + +`tests/test_cutover_slug_rot.py`'s triage taxonomy cited the removed sentence by name, as a KEEP example ("a true statement about GitHub's pricing"). Removing the sentence without that edit would have left a docstring citing a line that no longer exists. The retirement is recorded in its place. The ratchet count falls 39 to 38 against a ceiling of 41; `test_the_ratchet_is_not_slack` allows up to 8 slack, so the ceiling is deliberately left alone. + +### Not taken here, and it is the same fact + +`docs/Secure_Build_Scorecard_MEFOR.md` says "gitleaks full-history" in three places (lines 31, 58 and 93), one of them the evidence for signal 5 graded **Built -- Strong**. That evidence is now overstated by exactly the scope BACKLOG #1479 removed. It is **not edited here**: that file is a dated scoring snapshot ("Scored 2026-07-14, against HEAD") whose own convention is that re-scoring is an owner act, and it already carries a precedent blockquote flagging a correction for the next re-sign rather than folding it silently. Naming the three lines is the handoff; whoever re-signs the scorecard folds them. + diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 03e90e603..662eadbfd 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -2054,26 +2054,41 @@ All knobs live in the `[auth]` section of `messagefoundry.toml` (the AD bind pas ## Supply-chain & CI security -Automated security scanning runs in CI (`.github/workflows/security.yml`), so it lives there -rather than in the per-author `messagefoundry check` gate: +Automated security scanning runs in CI ([`.github/workflows/security.yml`](../.github/workflows/security.yml)), +so the enforced set lives there rather than in the per-author `messagefoundry check` gate. Read that +workflow for what each job does and whether it blocks; +[`.github/required-contexts.txt`](../.github/required-contexts.txt) is the authority on which checks +branch protection requires. At least these run: - **pip-audit** โ€” audits the **committed lockfile** (`requirements.lock`) for known-CVE dependencies, - so the audit is reproducible rather than auditing a fresh latest-resolve (advisory for now). -- **bandit** โ€” Python SAST over `messagefoundry/` (advisory). + so the audit is reproducible rather than auditing a fresh latest-resolve. +- **bandit** โ€” Python SAST over `messagefoundry/`. +- **gitleaks** โ€” the secret scan, in the job named `gitleaks (secret scan)`. Its step is *Scan the ref + under test for secrets*, and that step's comment states the scan's scope and the reason for it. Read + it there; this page does not restate it. A `gitleaks` hook in + [`.pre-commit-config.yaml`](../.pre-commit-config.yaml) runs the same tool before a commit. That hook + is a local aid, not a second gate: a fresh clone lacks it until `pre-commit install` runs, and + `git commit --no-verify` skips it. +- **SBOMs** โ€” CycloneDX bills of materials for the Python engine, the VS Code extension, and the + container image, kept as build artifacts, so "are we exposed to CVE-X?" is answerable from a recorded + bill of materials rather than a fresh resolve. Advisory, and generated on a cron rather than per pull + request. How they are built, scored, and used: [SUPPLY-CHAIN.md](SUPPLY-CHAIN.md) and + [ADR 0149](adr/0149-multi-ecosystem-sbom-vex-and-sbom-quality-gate.md). - **Dependabot** (`.github/dependabot.yml`) โ€” weekly PRs for `pip` and `github-actions` updates. - A private vulnerability-disclosure policy lives at [`.github/SECURITY.md`](../.github/SECURITY.md). -Enable via **GitHub Advanced Security** in repo settings (they need GHAS on a private repo, so they -can't be added by file alone): **CodeQL** code scanning and **secret scanning** + push protection. - -**Planned CI additions:** - -- **SBOM** โ€” generate a CycloneDX SBOM (e.g. `cyclonedx-py`) from the committed lockfile in CI and keep it - as a build artifact, so "are we exposed to CVE-X?" is answerable from a recorded bill of materials rather - than a fresh resolve. -- **Secret-history scan** โ€” a `gitleaks` (or trufflehog) job over the **full git history** in CI, to - complement GHAS secret scanning above. Kept in CI rather than a per-author pre-commit hook, to match the - pip-audit/bandit stance (one enforced gate, not optional local tooling). +**CodeQL** runs from [`.github/workflows/codeql.yml`](../.github/workflows/codeql.yml). This repository +is public, so CodeQL is free here and needs no GitHub Advanced Security licence. GitHub's own **secret +scanning** and push protection are repository settings rather than files in the tree. Read the settings +for their current state; this page does not track them. + +**What this section used to say, named so a reader who believed it can recognise the shape.** Until +2026-09-07 it filed the gitleaks and SBOM jobs under "Planned CI additions", called pip-audit and bandit +advisory, and told the reader to turn CodeQL on through Advanced Security. Every job it named was already +built, and pip-audit and bandit had stopped being advisory. The gitleaks entry also said the scan was kept +in CI "rather than a per-author pre-commit hook", while that hook sat pinned in the same tree, and it +described the scan as covering the full git history, which BACKLOG #1479 changed. Corrected under +BACKLOG #1485. ### Dependency lockfile (DEP-1) diff --git a/tests/test_cutover_slug_rot.py b/tests/test_cutover_slug_rot.py index 13a711b78..eb0210241 100644 --- a/tests/test_cutover_slug_rot.py +++ b/tests/test_cutover_slug_rot.py @@ -29,7 +29,9 @@ test_threat_model_doc_drift, test_ui_csp_canary), and the "pre-cutover this said X" annotations added when the guards above were fixed. These describe the past accurately. * KEEP โ€” not about this repo: `docs/INSTALL-GUIDE.md` / `docs/VERSION-CONTROL.md` mean the ADOPTER's - own private repo; `docs/SECURITY.md`'s GHAS note is a true statement about GitHub's pricing. + own private repo. This list also named `docs/SECURITY.md`'s GHAS note, which was retired on + 2026-09-07 (BACKLOG #1485) because its ADVICE had gone stale on a public repo, not because the + pricing claim in it was rot. Its removal is why the count fell by one. * KEEP โ€” idiom: "the mirror image of" in PLAN-PHASE4-GROUP-COMMIT and test_ledger_check. * FIX โ€” present-tense claims that this repo is a mirror, or that a private source repo runs CI.