Skip to content

feat(supply-chain): Track S quick wins — govulncheck + readonly + dependabot + detect-secrets#27

Open
remyluslosius wants to merge 1 commit into
mainfrom
feat/supply-chain-hardening-quick-wins
Open

feat(supply-chain): Track S quick wins — govulncheck + readonly + dependabot + detect-secrets#27
remyluslosius wants to merge 1 commit into
mainfrom
feat/supply-chain-hardening-quick-wins

Conversation

@remyluslosius
Copy link
Copy Markdown
Contributor

Lands the four parallel-safe Track S items (S-005 / S-006 / S-012 / S-014) plus the go.mod toolchain bump 1.26.1 → 1.26.3 that the new govulncheck gate immediately demanded on first run.

Closes the worst of the supply-chain governance gaps surfaced by the OpenWatch-team parity audit (2026-05-29): the repo had no .github/dependabot.yml at all, no govulncheck wiring, no -mod=readonly enforcement, no go mod tidy drift check, and no detect-secrets CI gate.

Four quick wins + one CVE fix

S-012 — .github/dependabot.yml (new — was missing entirely)

Two ecosystem entries (gomod + github-actions), weekly Monday cadence, minor+patch grouped per dep, max 10 PRs (gomod) / 5 PRs (github-actions). The github-actions half catches the Node.js 20 deprecation warnings we already see in CI logs.

S-005 — govulncheck merge gate (new)

  • make vuln target reads go.mod's go directive to pin GOTOOLCHAIN explicitly (otherwise go run pkg@latest picks go1.25.x which can't typecheck go1.26+ source). Auto-tracks future go.mod bumps.
  • Vulnerability Scan CI job calls make vuln. Blocks merge on CALLED vulnerabilities (uncalled stdlib/dep vulns reported but not failing).

First run of this gate found 6 stdlib CVEs called by kensa code. All resolved by the go.mod toolchain bump in this same PR (see below).

S-014 — -mod=readonly + go mod tidy no-op gate (new)

  • Top-level GOFLAGS: '-mod=readonly' in ci.yml — any go step that would mutate go.mod/go.sum fails loudly. Three jobs (Vulnerability Scan, Module drift check, Release pipeline) unset it per-step where they legitimately need write access.
  • make mod-tidy-check target. Saves pre-tidy state → runs tidy → diffs against saved state (NOT against HEAD — would false-fail on intentional in-flight go.mod edits during development). Clear one-line fix message points contributor at go mod tidy.
  • Module drift check CI job calls make mod-tidy-check.

S-006 — detect-secrets baseline + CI scan (new)

  • .secrets.baseline initial scan captures 3 confirmed false-positive findings:
    • internal/evidence/signer.go:178 — "Secret Keyword" hit on the error message containing "private key"
    • pkg/kensa/kensa_test.go:29 — test-fixture Ed25519 private key (existing detect-private-key exclude already covers this; detect-secrets is a separate detector)
    • specs/cli/password.spec.yaml:10 — the literal word "password" in the cli-password-prompt spec
  • Secret scan (detect-secrets) CI job runs the baseline scan on every PR; drift (new findings not in baseline) fails the job.
  • .pre-commit-config.yaml gains the detect-secrets hook alongside existing hooks, default-stage so contributors see findings before pushing.

go.mod toolchain bump 1.26.1 → 1.26.3 (CVE-driven)

Resolves the 6 stdlib CVEs the new govulncheck gate found:

ID Component Severity
GO-2026-4866 crypto/x509 — case-sensitive excludedSubtrees → auth bypass high
GO-2026-4870 crypto/tls — unauthenticated TLS 1.3 KeyUpdate DoS high
GO-2026-4918 net/http — HTTP/2 infinite loop on bad SETTINGS_MAX_FRAME_SIZE medium
GO-2026-4946 crypto/x509 — inefficient policy validation medium
GO-2026-4947 crypto/x509 — unexpected work during chain building medium
GO-2026-4971 net — NUL byte panic in Dial on Windows (kensa doesn't run on Windows) low

Bump is one line in go.mod; Go's module system handles the rest via the toolchain directive (matching toolchain auto-downloads). Post-bump make vuln reports 0 called vulnerabilities.

Local validation

Check Result
go test ./... 69 packages green
make build && kensa version reports 0.2.1
gofmt -l . clean
make vuln 0 called vulnerabilities
make mod-tidy-check no-op
detect-secrets scan --baseline ... stable
goreleaser check 1 config validated

What this PR does NOT do (next on Track S)

  • S-010system-supply-chain.spec.yaml (Tier 1 foundation spec). Needs proper SDD treatment in its own PR.
  • S-011 — depguard with the 10-dep starting allowlist. Gated on S-010.
  • S-013 — SBOM (CycloneDX) on release tag. Gated on S-001 (FIPS) for release.yml ordering.
  • The FIPS chain (S-001..S-004).

§7 failure-mode analysis

Touches CI configuration + go.mod toolchain directive — no engine / handler / rollback code change.

  1. What could this do wrong in production? The go.mod bump 1.26.1 → 1.26.3 changes which Go stdlib code links into the binary. The stdlib fixes in 1.26.3 are all security patches in crypto/x509, crypto/tls, net/http, net — paths kensa uses through TLS handshake and HTTP (PDF gen), x509 cert verification (agent framing). The semantics of these APIs don't change in a patch release; only their bug fixes do. Local tests + goreleaser snapshot pass post-bump.
  2. Rollback sufficient? Not applicable — no engine state. Revert commit if needed.
  3. Edge case NOT safe for? A CI environment without Go 1.26.3 toolchain available would fail to fetch it. GitHub Actions runners reliably have all recent Go versions; air-gapped CI mirrors might need a manual cache update.

🤖 Generated with Claude Code

Four parallel-safe Track S items + the toolchain bump that the
govulncheck gate immediately demands. Closes the worst of the
supply-chain governance gaps that the OpenWatch-team audit surfaced.

## Lands

### S-012 — Dependabot Go ecosystem (was missing entirely)

New .github/dependabot.yml with TWO ecosystem entries:
- gomod (weekly Monday, minor + patch grouped, max 10 open PRs)
- github-actions (weekly Monday, minor + patch grouped, max 5 open PRs)

The repo previously had NO dependabot config at all. The github-actions
half catches the Node.js 20 deprecation warnings we already see in CI
logs (actions/checkout@v4 + actions/setup-go@v5).

### S-005 — govulncheck merge gate (was not wired)

- Makefile: new `vuln` target reading go.mod's `go` directive to pin
  GOTOOLCHAIN explicitly. Without that pin, `go run pkg@latest` picks
  go1.25.x which can't typecheck go1.26+ source. The pin auto-tracks
  future go.mod bumps.
- ci.yml: new Vulnerability Scan job calling `make vuln`. Blocks merge
  on CALLED vulnerabilities (uncalled stdlib/dep vulns reported but
  not failing).

The very first run of this gate found 6 stdlib CVEs (crypto/x509,
crypto/tls, net/http) called by kensa code. All resolved by the
go.mod toolchain bump 1.26.1 → 1.26.3 in this same commit — see
below.

### S-014 — read-only modules + tidy no-op drift gate (was not wired)

- ci.yml: top-level env adds GOFLAGS: '-mod=readonly' so any go step
  that would mutate go.mod/go.sum fails loudly. A few jobs unset it
  per-step where they legitimately need write access (the new
  Vulnerability Scan, Module drift check, and the existing release
  pipeline which needs `go mod tidy` from goreleaser's before-hooks).
- Makefile: new `mod-tidy-check` target. Saves pre-tidy state,
  runs `go mod tidy`, diffs against saved state (NOT against HEAD —
  comparing to HEAD would false-fail on intentional in-flight go.mod
  edits during development). One-line fix message points contributor
  at `go mod tidy`.
- ci.yml: new Module drift check job calling `make mod-tidy-check`.

### S-006 — detect-secrets baseline + CI scan (was partial)

- .secrets.baseline: initial scan captures 3 known-false-positive
  findings:
  - internal/evidence/signer.go:178 — "Secret Keyword" hit on the
    error message string containing "private key"
  - pkg/kensa/kensa_test.go:29 — test-fixture Ed25519 private key
    (already excluded from the existing detect-private-key hook;
    detect-secrets is a separate detector)
  - specs/cli/password.spec.yaml:10 — the literal word "password"
    in the cli-password-prompt spec's description
  All three are confirmed not-actually-secrets.
- ci.yml: new Secret scan (detect-secrets) job runs the baseline scan
  on every PR. Drift (new findings not in baseline) fails the job.
- .pre-commit-config.yaml: detect-secrets hook added alongside the
  existing hooks. Default-stage so contributors see new findings
  before they push.

### go.mod toolchain bump (govulncheck-driven)

go 1.26.1 → 1.26.3. Resolves the 6 stdlib CVEs the new govulncheck gate
found on its very first run:

  GO-2026-4866 — case-sensitive excludedSubtrees in crypto/x509 (auth bypass)
  GO-2026-4870 — unauthenticated TLS 1.3 KeyUpdate DoS in crypto/tls
  GO-2026-4918 — infinite loop in HTTP/2 transport (golang.org/x/net)
  GO-2026-4946 — inefficient policy validation in crypto/x509
  GO-2026-4947 — unexpected work during chain building in crypto/x509
  GO-2026-4971 — NUL byte panic in net.Dial on Windows

The bump itself is one line in go.mod; go's module system handles the
rest via the toolchain directive (the matching toolchain auto-downloads).
Post-bump govulncheck reports 0 called vulnerabilities.

## Local validation

  go test ./...                 69 packages green
  make build && kensa version   reports 0.2.1
  gofmt -l .                    clean
  make vuln                     0 called vulnerabilities
  make mod-tidy-check           no-op (tidy is idempotent)
  detect-secrets scan ...       baseline stable
  goreleaser check              1 config validated

## What this PR does NOT do (next on Track S)

- S-010 system-supply-chain.spec.yaml (Tier 1 foundation spec) — needs
  proper SDD treatment in its own PR
- S-011 depguard with the 10-dep starting allowlist — gated on S-010
- S-013 SBOM (CycloneDX) on release tag — gated on S-001 (FIPS) for
  release.yml ordering
- The FIPS chain (S-001..S-004)

## §7 failure-mode analysis

Touches CI configuration + go.mod toolchain directive — no engine /
handler / rollback code change.

1. **What could this do wrong in production?** The go.mod bump 1.26.1
   → 1.26.3 changes which Go stdlib code links into the binary. The
   stdlib fixes in 1.26.3 are all security patches in crypto/x509,
   crypto/tls, net/http, net — paths kensa uses through TLS handshake
   and HTTP (PDF gen), x509 cert verification (agent framing). The
   semantics of these APIs don't change in a patch release; only their
   bug fixes do. Local tests + goreleaser snapshot pass post-bump.
2. **Rollback sufficient?** Not applicable — no engine state. Revert
   commit if needed.
3. **Edge case NOT safe for?** A CI environment without Go 1.26.3
   toolchain available would fail to fetch it. GitHub Actions runners
   reliably have all recent Go versions; air-gapped CI mirrors might
   need a manual cache update.

## SDD

No new specs — these are CI/config additions, not behaviour changes.
S-010 will land the proper supply-chain spec as a follow-up PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant