Skip to content

feat: the standard's first position on third-party components - #40

Merged
MCKRUZ merged 1 commit into
mainfrom
feat/dependency-axis
Aug 1, 2026
Merged

feat: the standard's first position on third-party components#40
MCKRUZ merged 1 commit into
mainfrom
feat/dependency-axis

Conversation

@MCKRUZ

@MCKRUZ MCKRUZ commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Closes #11 (with #39, which this stacks on — review that first).

Stacked PR. Base is feat/deploy-promotion-rollback so the diff shows only this change. Retarget to main once #39 merges.

The repo had zero mentions of dependabot, CVEs, or vulnerability scanning. Every other risk in the standard had a written rule; this one had silence — and it is the first thing a client security review asks about.

Three parts

What Blocks?
dependency-gate (job in ci.yml) what a change introduces yes, on High/Critical
dependency-scan.yml the standing stock, weekly no — raises an issue
dependabot.yml upgrade PRs rides the full merge bar

Diff-scoping is the load-bearing decision. Every other gate measures the diff; a vulnerability scan naturally measures the repo. Blocking that way means a CVE published overnight against untouched code reddens every open PR the next morning — nobody caused it, nobody can fix it in their branch, and within a fortnight red is ambient rather than a signal. That costs not just this gate but the credibility of every other one.

The seam follows eval_gate: the stack pack declares a command, the CI/CD packs run it. The contract is one TSV finding per line, so the packs never learn three JSON schemas.

Verified against real advisories, not documentation

Two of three findings changed the implementation:

  • dotnet list package --vulnerable exits 0 with a High advisory present. A step trusting the exit code passes green while shipping a known-vulnerable package — the same trap already documented in this repo for dotnet test --filter matching zero tests.
  • npm audit --json exits 1 when it finds anything, aborting the step under set -euo pipefail before jq runs. Two traps, opposite directions; the contract normalizes both.
  • Severity casing differs (High vs high), so case-insensitive comparison is a verified necessity, not defensive habit.

--output-version 1 is pinned because Microsoft documents that a new JSON version becomes the default when it ships, which would silently change every parsed field.

Two places this is honestly weaker, and says so

  • Python: pip-audit reports no severity, so that profile blocks on any newly introduced vulnerability rather than inventing a threshold from description text. It is also the one profile not empirically verified — written from documented schema and marked unproven in-file until the drill runs.
  • Azure DevOps: no first-party Dependabot. It is a marketplace extension, and installing one with repo write access is the client's decision. The first two parts hold; the third is manual until they choose. Written down rather than shipping a pipeline that quietly does less than its twin.

This gate fails differently from the others

Misconfigured, it reports nothing — and nothing is indistinguishable from clean. It fails silent and green where every other rail fails loudly. The shakedown drill (plant a known-vulnerable package, watch it go red) is the only proof it is wired, and all three operator guides say so in those words.

Test plan

  • python scripts/check_standard.py — no drift; dependency-gate registered in both platforms' policy files
  • python -m pytest scripts/tests -q — 46 passing
  • Both scan commands executed end-to-end against real published advisories (System.Net.Http 4.3.0; lodash 4.17.15 + minimist 1.2.0), producing the contract TSV
  • Also reconciles the required-check lists in the operator guides, which had already drifted — they named five contexts where the ruleset requires nine

🤖 Generated with Claude Code

The repo had zero mentions of dependabot, CVEs, or vulnerability scanning. Every other
risk in the standard had a written rule; this one had silence — and it is the first
thing a client security review asks about.

Three parts, and the split between the first two is the design:

  dependency-gate (ci.yml)   blocks what a change INTRODUCES — diff-scoped
  dependency-scan.yml        reports the standing stock weekly, as an issue
  dependabot.yml             opens the upgrade PRs, which ride the full merge bar

Diff-scoping is the load-bearing decision. Every other gate measures the diff; a
vulnerability scan naturally measures the repo, so a CVE published overnight against
untouched code would redden every open PR the next morning. Nobody caused it, nobody
can fix it in their branch, and within a fortnight red is an ambient condition rather
than a signal — which costs us not just this gate but the credibility of every other
one. So the gate compares this branch against the target branch and blocks on the
difference; the standing stock becomes an ordinary spec with a human deciding priority.

The seam follows eval_gate: the stack pack declares a command, the CI/CD packs run it.
The contract is one TSV finding per line, so the packs never learn three JSON schemas.

Verified empirically against real published advisories rather than from documentation,
and two of the three findings changed the implementation:

  - `dotnet list package --vulnerable` EXITS 0 with a High advisory present. A step
    trusting the exit code passes green while shipping a known-vulnerable package —
    the same trap already documented for `dotnet test --filter` matching zero tests.
  - `npm audit --json` EXITS 1 when it finds anything, aborting the step under
    `set -euo pipefail` before jq runs. Two traps, opposite directions; the contract
    normalizes both.
  - Severity casing differs (`High` vs `high`), so the comparison must be
    case-insensitive. Verified necessity, not defensive habit.

`--output-version 1` is pinned because Microsoft documents that a new JSON version
becomes the default when it ships, which would silently change every parsed field.

Python is honestly weaker and says so: pip-audit reports no severity, so that profile
blocks on any newly introduced vulnerability rather than inventing a threshold. Azure
DevOps has no first-party Dependabot — it is a marketplace extension, and installing
one with repo write access is the client's call. Both gaps are written down rather
than papered over.

This gate has a failure mode the others do not: misconfigured, it reports nothing, and
nothing is indistinguishable from clean. It fails silent and green. The shakedown drill
(plant a known-vulnerable package, watch it go red) is the only proof it is wired, and
the operator guides say so in those words.

Also reconciles the required-status-check lists in the operator guides, which had
already drifted — they named five contexts where the ruleset requires nine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MCKRUZ
MCKRUZ changed the base branch from feat/deploy-promotion-rollback to main August 1, 2026 21:05
@MCKRUZ
MCKRUZ merged commit 67c026f into main Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: deploy promotion + rollback workflows, and a dependency-update axis

1 participant