From 2f9dd03de5b83ff5be269bd56322349375b079e0 Mon Sep 17 00:00:00 2001 From: zircote Date: Wed, 1 Jul 2026 12:45:57 -0400 Subject: [PATCH] docs: OSV-Scanner rollout ADR, reference, and runbook Adds ADR-012 (the decision to close the OSV-Scanner adoption gap), a new docs/reference/ entry for reusable-sca-osv.yml, and a tactical on-call runbook for a failed sca check or code-scanning alert. ADR-012 validated against structured-madr in both smadr strict mode and mif --level 3; the reference doc validated at mif --level 3; the runbook validated at mif --level 1, matching this repo's existing runbook convention. --- .../ADR-012-osv-scanner-rollout-completion.md | 266 ++++++++++++++++++ docs/reference/reusable-sca-osv.md | 128 +++++++++ docs/runbooks/osv-scanner-alert-runbook.md | 183 ++++++++++++ 3 files changed, 577 insertions(+) create mode 100644 docs/adr/ADR-012-osv-scanner-rollout-completion.md create mode 100644 docs/reference/reusable-sca-osv.md create mode 100644 docs/runbooks/osv-scanner-alert-runbook.md diff --git a/docs/adr/ADR-012-osv-scanner-rollout-completion.md b/docs/adr/ADR-012-osv-scanner-rollout-completion.md new file mode 100644 index 0000000..741fb3e --- /dev/null +++ b/docs/adr/ADR-012-osv-scanner-rollout-completion.md @@ -0,0 +1,266 @@ +--- +title: "OSV-Scanner Rollout Completion Across Org Repos" +description: "Closes an adoption gap in ADR-004's standard SCA gate by wiring reusable-sca-osv.yml into doc-site, mif-docs-plugin, and research-harness-template via one reviewed PR per repo; mnemonic-vscode and design-system remain tracked exceptions." +type: adr +conceptType: semantic +x-ontology: + id: mif-docs + version: "1.0.0" + entity_type: decision-record +category: security +tags: + - sca + - osv + - supply-chain + - ci + - security +status: accepted +created: 2026-07-01 +updated: 2026-07-01 +author: MIF Maintainers +project: modeled-information-format +technologies: + - github-actions + - osv-scanner +audience: + - developers + - architects + - maintainers +related: + - ADR-004-supply-chain-scanning.md + - ADR-010-plugin-catalog-hub.md +--- + +# ADR-012: OSV-Scanner Rollout Completion Across Org Repos + +## Status + +Accepted + +## Context + +### Background and Problem Statement + +ADR-004 established `reusable-sca-osv.yml` (OSV-Scanner plus the GitHub +`dependency-review` action) as the org's standard SCA gate: an independent +second opinion against the OSV vulnerability database, uploaded as SARIF to the +code-scanning hub, plus a PR merge gate on introduced vulnerable dependencies. +The reusable is SHA-pinned and free for every consuming repo to adopt as a thin +caller. + +An audit of every active repo under the org, conducted on 2026-07-01, found +that adoption had stalled. Only 5 of 12 active repos actually wired the +reusable into their CI: `MIF`, `claude-code-plugins`, `mif-repo-template`, +`ontologies`, and `structured-madr`. No commit in the prior 48 hours attempted +to extend adoption further, and no repo's CI showed a disabled or malformed +reference to the reusable — the gate was simply never added to the remaining +repos' workflow files. + +### Current Limitations Before This Decision + +Three repos carried a `package-lock.json` (a real dependency surface) with no +SCA gate at all: `doc-site`, `mif-docs-plugin`, and `research-harness-template`. +A fourth, `mnemonic-vscode`, also carries a `package-lock.json` and lacks the +gate, but was explicitly excluded from this rollout pass by operator decision +and remains a known, tracked gap rather than an oversight. `design-system` has +no `.github/workflows/` directory at all and needs baseline CI before any +quality gate — including this one — can be added. `mnemonic` (a pure Go module) +and `modeled-information-format.github.io` carry no dependency lockfile, so the +gate does not apply to either. + +## Decision Drivers + +### Primary Decision Drivers + +- WHEN a repo under the org carries a dependency lockfile, the repo SHALL run + the org's standard SCA gate (`reusable-sca-osv.yml`) on every push and pull + request against its default branch. +- WHEN a repo's SCA gate is added, the change SHALL be delivered as a reviewed + pull request, not a direct push to the repo's default branch. +- IF a repo has no CI workflow at all, THEN this decision SHALL NOT bundle + baseline CI setup with the SCA gate addition; baseline CI is a separate, + prerequisite decision. + +### Secondary Decision Drivers + +- Reuse the exact SHA already pinned by `structured-madr`, `ontologies`, and + `MIF` (`f83ee8058630235396f7242580570b26cf3617fa`) so no new commit-SHA trust + decision is introduced by this rollout. +- Keep each repo's unrelated CI jobs untouched; add the `sca` job as an + independent top-level job, not a modification of existing steps. + +## Considered Options + +### Option 1: Leave adoption as repo-by-repo opt-in on demand + +**Description**: No forcing function; each repo's maintainer adds the gate +whenever they get to it. + +**Advantages**: + +- No work required now; zero authoring cost today. + +**Disadvantages**: + +- This is the status quo that produced the gap in the first place; nothing + changes the outcome next time a repo's CI is scaffolded without checking + precedent. +- Coverage stays inconsistent for an indefinite, unbounded period. + +**Risk Assessment**: + +- **Technical Risk**: Low. No new surface introduced. +- **Schedule Risk**: N/A. No work scheduled. +- **Ecosystem Risk**: High. The gap persists and is likely to recur. + +### Option 2: A scheduled org-wide automation that force-adds the job to every repo with a lockfile + +**Description**: Extend a pattern similar to `plugin-catalog-update-hub.yml` +(ADR-010): a scheduled job across all org repos that detects a missing SCA +gate and opens an auto-merge PR. + +**Advantages**: + +- Self-healing: a future repo added without the gate would be caught + automatically rather than depending on another manual audit. + +**Disadvantages**: + +- A new cross-repo write surface, a new App or token scope, a new failure + mode to operate — as seen when `plugin-catalog-update-hub.yml`'s own + auto-merge needed a branch-protection bypass grant to actually land a PR. +- Building and testing a second cross-repo automation is real engineering + work for a one-time backlog of three repos. + +**Risk Assessment**: + +- **Technical Risk**: Medium. New automation surface with its own failure + modes to operate. +- **Schedule Risk**: Medium. Disproportionate build cost for today's backlog + size. +- **Ecosystem Risk**: Low once built, but the investment is not justified by + the scope of the actual problem today. + +### Option 3: Manual, audited one-time rollout PRs to the specific repos found missing the gate (chosen) + +**Description**: Add the `sca` job directly to `doc-site`, `mif-docs-plugin`, +and `research-harness-template`'s existing `ci.yml` files, matching the exact +pattern already proven in `structured-madr`/`ontologies`/`MIF`, and open one +PR per repo through each repo's normal review process. + +**Advantages**: + +- The pattern is already proven in five other repos; no new YAML shape, no + new pinned SHA to trust. +- Each repo's own branch protection and review process still gates the + change; no new automation surface to operate or maintain. + +**Disadvantages**: + +- Purely manual; does not prevent the same gap from recurring if a future + repo's CI is scaffolded without checking this precedent. + +**Risk Assessment**: + +- **Technical Risk**: Low. The pattern is already proven in five other repos. +- **Schedule Risk**: Low. Three small, independent, single-file PRs. +- **Ecosystem Risk**: Low. No new automation surface to operate or maintain. + +## Decision + +Wire `modeled-information-format/.github/.github/workflows/reusable-sca-osv.yml` +(pinned at `f83ee8058630235396f7242580570b26cf3617fa`, `fail-on-severity: high`) +into `doc-site`, `mif-docs-plugin`, and `research-harness-template` as a new +top-level `sca` job in each repo's existing `ci.yml`, using the identical +permissions block (`actions: read`, `contents: read`, `security-events: write`, +`pull-requests: write`) and `with:` input already used in `structured-madr`. +Each change lands as its own pull request against the target repo's default +branch; none is merged as part of this decision — each goes through that +repo's normal review and required-checks process. + +`mnemonic-vscode` is intentionally excluded from this rollout pass. +`design-system` is intentionally excluded pending baseline CI. + +## Consequences + +### Positive + +- Dependency vulnerability coverage now spans every active repo in the org + that carries a lockfile, except the two explicitly tracked exceptions. +- No new commit-SHA trust decision: all three rollout PRs reuse the exact pin + already trusted by three other repos. +- Each repo's own review process remains the merge gate; this decision adds no + new bypass or automation surface. + +### Negative + +- This was a manual, one-time catch-up. The same drift can recur if a new + repo's CI is scaffolded without checking this precedent — nothing in this + decision prevents that on its own. +- Three separate PRs must each independently pass their repo's existing CI + before merge; a repo-specific failure in one does not block the others, but + also is not resolved by this decision. + +### Neutral + +- `mnemonic-vscode` remains a known, deliberately deferred gap, not an + oversight, following an explicit operator decision to exclude it from this + pass. + +## Decision Outcome + +The decision meets its objective — closing the specific, audited gap — without +introducing new infrastructure. The identified follow-up risk (recurrence via +future un-scaffolded CI) is not mitigated here; it is a candidate for a future +decision, most plausibly by extending the `plugin-catalog-update-hub.yml` +pattern (ADR-010) or by adding this gate to a repo-scaffolding checklist, +should the same gap recur. + +## Related Decisions + +- [ADR-004](ADR-004-supply-chain-scanning.md) — established + `reusable-sca-osv.yml` as the org's standard SCA gate; this decision closes + a gap in its adoption, it does not change the gate itself. +- [ADR-010](ADR-010-plugin-catalog-hub.md) — the scheduled cross-repo + automation pattern considered and rejected as disproportionate for this + backlog (Option 2), noted here as the precedent to revisit if the gap + recurs. + +## Links + +- `modeled-information-format/.github/.github/workflows/reusable-sca-osv.yml` +- `modeled-information-format/doc-site` PR: ci: add OSV-Scanner SCA gate +- `modeled-information-format/mif-docs-plugin` PR: ci: add OSV-Scanner SCA gate +- `modeled-information-format/research-harness-template` PR: ci: add + OSV-Scanner SCA gate + +## More Information + +The rollout was triggered by a manual audit rather than a scheduled process; +this ADR itself is the record of that one-time catch-up, not of a recurring +mechanism. + +## Audit + +### 2026-07-01 + +**Status:** Partial + +**Findings:** + +| Finding | Files | Assessment | +|---------|-------|------------| +| `sca` job added to `doc-site/.github/workflows/ci.yml`, matching the structured-madr pattern | `doc-site` PR | compliant | +| `sca` job added to `mif-docs-plugin/.github/workflows/ci.yml`, matching the structured-madr pattern | `mif-docs-plugin` PR | compliant | +| `sca` job added to `research-harness-template/.github/workflows/ci.yml`, matching the structured-madr pattern | `research-harness-template` PR | compliant | +| `mnemonic-vscode` still missing the gate | (tracked gap) | non-compliant, deliberate | +| `design-system` has no CI workflow to add the gate to | (tracked gap) | non-compliant, deferred | + +**Summary:** Three of the four repos found missing the standard SCA gate now +carry it, each via an independent, unmerged pull request awaiting normal +review. `mnemonic-vscode` remains excluded by operator decision; +`design-system` remains blocked on baseline CI setup, out of scope here. + +**Action Required:** Review and merge the three open pull requests; decide +separately whether and when to bring `mnemonic-vscode` and `design-system` +into scope. diff --git a/docs/reference/reusable-sca-osv.md b/docs/reference/reusable-sca-osv.md new file mode 100644 index 0000000..b3ff215 --- /dev/null +++ b/docs/reference/reusable-sca-osv.md @@ -0,0 +1,128 @@ +--- +id: reference-reusable-sca-osv +type: semantic +created: 2026-07-01T00:00:00Z +modified: 2026-07-01T00:00:00Z +namespace: reference/reusable-workflows +title: "reusable-sca-osv.yml" +tags: + - sca + - osv + - supply-chain + - reusable-workflow + - github-actions +temporal: + '@type': TemporalMetadata + validFrom: '2026-07-01T00:00:00Z' + recordedAt: '2026-07-01T00:00:00Z' +provenance: + '@type': Provenance + sourceType: system_generated + trustLevel: verified + wasDerivedFrom: + '@id': 'urn:mif:workflow:reusable-sca-osv' + '@type': prov:Entity +citations: + - '@type': Citation + citationType: tool + citationRole: source + title: reusable-sca-osv.yml source + url: https://github.com/modeled-information-format/.github/blob/main/.github/workflows/reusable-sca-osv.yml + - '@type': Citation + citationType: specification + citationRole: methodology + title: 'ADR-004: Supply-Chain Scanning' + url: https://github.com/modeled-information-format/.github/blob/main/docs/adr/ADR-004-supply-chain-scanning.md +relationships: + - type: relates-to + target: /semantic/adr/ADR-004-supply-chain-scanning.md + - type: relates-to + target: /semantic/adr/ADR-012-osv-scanner-rollout-completion.md +--- + +# reusable-sca-osv.yml + +`modeled-information-format/.github/.github/workflows/reusable-sca-osv.yml` — a +`workflow_call` reusable workflow providing software composition analysis (SCA) +via OSV-Scanner and GitHub's `dependency-review-action`. + +## Synopsis + +```yaml +jobs: + sca: + permissions: + actions: read + contents: read + security-events: write + pull-requests: write + uses: modeled-information-format/.github/.github/workflows/reusable-sca-osv.yml@ + with: + fail-on-severity: high +``` + +## Trigger + +| Property | Value | +| --- | --- | +| Event | `workflow_call` | +| Direct invocation | Not supported. Called only from a consuming repo's own workflow file. | + +## Inputs + +| Name | Type | Default | Constraints | Description | +| --- | --- | --- | --- | --- | +| `fail-on-severity` | string | `high` | one of `low`, `moderate`, `high`, `critical` | The `dependency-review-action` threshold. A pull request introducing a dependency at or above this severity fails the check. | +| `scan-args` | string | `--recursive`\n`./` | any valid OSV-Scanner CLI arguments | Arguments passed to the OSV-Scanner invocation. | + +## Required permissions (calling job) + +| Permission | Level | Description | +| --- | --- | --- | +| `actions` | `read` | Read workflow run metadata. | +| `contents` | `read` | Read the repository checkout. | +| `security-events` | `write` | Upload OSV-Scanner SARIF output to the code-scanning hub. | +| `pull-requests` | `write` | Allow `dependency-review-action` to annotate the pull request. | + +## Layers provided + +| Layer | Tool | License | Output | +| --- | --- | --- | --- | +| Independent SCA scan | OSV-Scanner (Google) | Apache-2.0 | SARIF, uploaded to the consuming repo's code-scanning hub (Security tab) | +| PR merge gate | `dependency-review-action` (GitHub) | MIT | Pass/fail check on the pull request; fails when a PR introduces a dependency at or above `fail-on-severity`, or a disallowed license | + +Dependabot alerts are a third, complementary layer. They are configured at the +repo level and are outside this workflow. + +## Pinned SHA in active use + +| Field | Value | +| --- | --- | +| SHA | `f83ee8058630235396f7242580570b26cf3617fa` | + +## Consuming repos (as of 2026-07-01) + +| Repo | Status | +| --- | --- | +| `MIF` | Consuming | +| `claude-code-plugins` | Consuming | +| `mif-repo-template` | Consuming | +| `ontologies` | Consuming | +| `structured-madr` | Consuming | +| `doc-site` | Consuming (PR open, pending review) | +| `mif-docs-plugin` | Consuming (PR open, pending review) | +| `research-harness-template` | Consuming (PR open, pending review) | +| `mnemonic-vscode` | Not consuming — tracked, deliberately excluded from the 2026-07-01 rollout | +| `design-system` | Not consuming — blocked on baseline CI setup | +| `mnemonic` | Not applicable — pure Go module, no lockfile | +| `modeled-information-format.github.io` | Not applicable — no dependency lockfile | + +## Related + +- [ADR-004: Supply-Chain Scanning](../adr/ADR-004-supply-chain-scanning.md) — + established this reusable as the org's standard SCA gate. +- [ADR-012: OSV-Scanner Rollout Completion](../adr/ADR-012-osv-scanner-rollout-completion.md) + — the 2026-07-01 decision closing the adoption gap documented above. +- `reusable-sast-codeql.yml` — the org's SAST gate. +- `reusable-trivy.yml`, `reusable-checkov.yml`, `reusable-secrets.yml`, + `reusable-vex.yml` — the org's other supply-chain reusables. diff --git a/docs/runbooks/osv-scanner-alert-runbook.md b/docs/runbooks/osv-scanner-alert-runbook.md new file mode 100644 index 0000000..deff78e --- /dev/null +++ b/docs/runbooks/osv-scanner-alert-runbook.md @@ -0,0 +1,183 @@ +--- +id: 0a1eebf7-33fc-42ba-82fb-3bf8365bf95d +type: procedural +created: 2026-07-01T00:00:00-04:00 +namespace: _procedural/runbooks +title: "OSV-Scanner Alert Runbook" +tags: + - runbook + - sca + - osv + - supply-chain +--- + +# OSV-Scanner Alert Runbook — Failed SCA Check or Code-Scanning Alert + +This runbook covers a failed `sca` status check or a new OSV-Scanner +code-scanning alert in any repo consuming +`modeled-information-format/.github/.github/workflows/reusable-sca-osv.yml` +(currently: `MIF`, `claude-code-plugins`, `mif-repo-template`, `ontologies`, +`structured-madr`, `doc-site`, `mif-docs-plugin`, `research-harness-template`). +See [ADR-004](../adr/ADR-004-supply-chain-scanning.md) for why this gate +exists, [ADR-012](../adr/ADR-012-osv-scanner-rollout-completion.md) for its +rollout, and +[the reference doc](../reference/reusable-sca-osv.md) for the workflow's +inputs and consuming repos. + +## Overview + +The `sca` job runs two layers on every push and pull request: OSV-Scanner (an +independent scan against the OSV vulnerability database, uploaded as SARIF to +the repo's code-scanning hub) and `dependency-review-action` (the PR merge +gate, failing on a newly introduced dependency at or above the configured +`fail-on-severity`, default `high`). This runbook covers both failure modes: +a blocked PR and a standalone code-scanning alert. + +## Prerequisites & Access + +- Read access to the affected repo's **Security** tab (code-scanning alerts) + and **Pull requests** tab (the `dependency-review` check annotation). +- `gh` CLI authenticated against the org (`gh auth status`). +- For remediation: write access to open a branch and push a dependency bump. + +## Detection + +Two independent signals, from the two layers: + +1. **Blocked PR** — the `sca` / `dependency-review` status check on a pull + request shows failed. Confirm with: + + ```bash + gh pr checks --repo modeled-information-format/ + ``` + + A failing `dependency-review` line means the PR introduces a dependency at + or above `fail-on-severity`, or a disallowed license. + +2. **Code-scanning alert** — a new alert appears in the repo's **Security -> + Code scanning** tab, independent of any open PR (OSV-Scanner runs on every + push to the default branch too, not only PRs). Confirm the alert's tool is + OSV-Scanner, not a different scanner sharing the same hub (SAST/CodeQL, + Trivy, Checkov, Gitleaks/TruffleHog all upload SARIF to the same tab): + + ```bash + gh api repos/modeled-information-format//code-scanning/alerts \ + --jq '.[] | select(.tool.name == "osv-scanner") | {number, rule: .rule.id, severity: .rule.security_severity_level, state}' + ``` + +## Diagnosis + +1. **Read the `dependency-review` annotation** (blocked-PR case) or the + **alert detail** (code-scanning case) for the affected package name, + installed version, severity, and advisory id (GHSA-xxxx or OSV id): + + ```bash + gh api repos/modeled-information-format//code-scanning/alerts/ \ + --jq '{rule: .rule.id, severity: .rule.security_severity_level, description: .rule.description}' + ``` + +2. **Open the advisory** to read the affected version range and the patched + version: + + ```bash + gh api /advisories/ --jq '{summary, severity, vulnerabilities: [.vulnerabilities[] | {package: .package.name, vulnerable_range: .vulnerable_version_range, patched: .first_patched_version.identifier}]}' + ``` + +3. **Determine direct vs. transitive.** Check whether the package appears in + the repo's `package.json` `dependencies`/`devDependencies` (direct) or only + in `package-lock.json` (transitive): + + ```bash + grep -n "\"\"" package.json + ``` + + A hit in `package.json` means direct; no hit means transitive, pulled in by + one of the direct dependencies. + +## Remediation + +1. **Direct dependency, patch available** — bump to the patched version: + + ```bash + npm install @ + npm run lint --if-present && npm test --if-present + ``` + + Expected result: `package-lock.json` updates to the patched version; local + test/lint gates (if present) still pass. + +2. **Transitive dependency, patch available upstream** — check whether a + newer release of the direct dependency that pulls it in already exists: + + ```bash + npm ls + npm outdated + ``` + + If a newer direct-dependency release resolves the transitive vulnerability, + bump the direct dependency instead of overriding the transitive one + directly. Only use an `overrides` (npm) or `resolutions` (yarn) field if no + upstream release exists yet, and note the override with a comment linking + the advisory so it is not forgotten once a real fix ships. + +3. **No patch available yet** — do not silence the finding by disabling the + gate or lowering `fail-on-severity`. This org's supply-chain layer includes + an OpenVEX exploitability disposition step (see + [ADR-004](../adr/ADR-004-supply-chain-scanning.md)) for exactly this case: + disposition the specific advisory as `not_affected` (with a documented + justification, e.g. the vulnerable code path is unreachable in this repo's + usage) rather than suppressing the class of finding. Changing + `fail-on-severity` is a scope change to the gate itself and requires its + own decision (an ADR), not a per-incident workaround. + +4. **Commit the fix**: + + ```bash + git add package.json package-lock.json + git commit -m "fix(deps): bump to ()" + git push + ``` + +## Escalation + +Escalate to the repo maintainer, do not force a merge past the gate, when: + +- The advisory is disputed (e.g. the maintainer believes the CVE/GHSA is + mis-scored or does not apply to this repo's actual usage) — the VEX + disposition path in ADR-004 requires a maintainer's documented justification, + not a unilateral one. +- OSV-Scanner itself appears to misbehave (a clear false positive, a tool + crash, a SARIF upload failure unrelated to the dependency itself). +- The only available fix is a breaking major-version bump with application- + level compatibility risk that cannot be validated by CI alone. + +## Verification & Rollback + +**Verification**: after pushing the fix, re-run the check: + +```bash +gh pr checks --repo modeled-information-format/ --watch +``` + +Expected result: `dependency-review` and `sca` both report success. For a +standalone code-scanning alert (no open PR), confirm the alert closes on the +next scheduled or push-triggered scan: + +```bash +gh api repos/modeled-information-format//code-scanning/alerts/ --jq '.state' +``` + +Expected result: `state` is `fixed` or `dismissed` (with a documented VEX +disposition, if dispositioned rather than patched). + +**Rollback**: if the dependency bump breaks the build (test/lint/build +failure unrelated to the vulnerability fix), revert the single commit: + +```bash +git revert HEAD +git push +``` + +This restores the prior, vulnerable dependency version — the `sca` check will +fail again, which is the expected, safe state (blocked, not silently merged) +until a working patched version is found.