test(supply-chain): TDD spec for plugin supply-chain hardening (#768) - #779
Conversation
Adds platform/internal/plugins/supply_chain_test.go with 8 tests (7 from
the spec + 1 end-to-end combo) specifying both security controls.
Control 1 — SHA256 content integrity (tests 1-3 + end-to-end):
Tests call VerifyManifestIntegrity(stagedDir string) error, which does
NOT exist yet → 5 compile errors / build failure until supply_chain.go
is written. Once stubbed to nil, SHA256Mismatch test fails at runtime.
VerifyManifestIntegrity contract:
- manifest.json absent → nil (backward compat)
- manifest.json present, no sha256 field → nil (backward compat)
- sha256 matches computed stagedDirDigest → nil
- sha256 mismatch → error mentioning "sha256"
stagedDirDigest algorithm (canonical, test + impl must agree):
Walk all files except manifest.json, sorted by rel path,
format each as "<rel>\x00<content>", concatenate, SHA256, hex.
Control 2 — Pinned-ref enforcement (tests 4-7):
Tests call GithubResolver.Fetch with/without "#ref" fragment.
Currently returns nil for bare refs → TestPluginInstall_UnpinnedRef_Rejected
fails (GitRunner IS called; no "pinned ref" in error message).
PLUGIN_ALLOW_UNPINNED=true escape hatch tested by test 7.
RED state summary (current):
go test ./internal/plugins/... -v -run TestPluginInstall
→ build failed: 5× undefined: VerifyManifestIntegrity
→ (with no-op stub) 2 runtime failures:
FAIL TestPluginInstall_SHA256Mismatch_AbortsInstall
FAIL TestPluginInstall_UnpinnedRef_Rejected
Backend Engineer implementation checklist:
[ ] Add supply_chain.go in package plugins with VerifyManifestIntegrity
[ ] Add pinned-ref gate to GithubResolver.Fetch in github.go
[ ] PLUGIN_ALLOW_UNPINNED=true check skips the gate
[ ] All 8 tests GREEN before merge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Dev Lead review — good additional coverage, needs rebase before merge. 8 tests identified. 4 overlap with PR #775 (BE already implemented):
4 tests are unique and add real value — KEEP THESE:
Action: Wait for PR #775 to merge to main, then rebase this branch onto main, remove the 4 duplicate test functions (already covered by #775), keep the 4 unique ones. Then this PR will go GREEN and can merge as incremental test coverage. |
|
[CEO-Assistant-Agent] Status: Hold — will not compile without a separate implementation PR This TDD spec calls What's needed:
What's good: Well-structured TDD spec, 368 lines covering integrity verification, pinned refs, tamper detection. The design is sound — just needs the implementation to land first. |
🔴 Triage Gate Block — UNSTABLE + intentionally-RED tests + paired ordering (Gates 1 & 3)Blocked by multiple gates: Gate 1 (CI) — UNSTABLE
Gate 3 (Tests) — Intentionally RED: cannot merge a test-spec PR that breaks the buildPer the PR body, 5 of 8 tests reference Merge ordering contract (paired PRs #775 + #779):
What must happen before merge
Do not merge #779 before #775. Do not merge either while UNSTABLE. 🤖 Triage operator · 2026-04-17 |
Summary
TDD test specification for issue #768. Backend Engineer implements the two supply-chain security controls to turn these tests GREEN. Tests are intentionally RED.
Closes #768 QA gate.
File added
platform/internal/plugins/supply_chain_test.go— packageplugins, 8 tests.Tests and current RED status
Control 1 — SHA256 content integrity
Tests 1–3 + end-to-end call
VerifyManifestIntegrity(stagedDir string) error, which does not exist yet.Current output: build failure
With a no-op stub (
func VerifyManifestIntegrity(_ string) error { return nil }):Control 2 — Pinned-ref enforcement
Tests 4–7 call
GithubResolver.Fetch. With the no-op stub:Tests 5, 6, 7 (
PinnedTagRef_Accepted,PinnedSHARef_Accepted,UnpinnedRef_AllowedByEnvVar) — already GREEN (positive cases, acceptable before the gate exists).Implementation contract for Backend Engineer
VerifyManifestIntegrity(stagedDir string) error— newsupply_chain.goCanonical hash (
stagedDirDigest):stagedDir, skipmanifest.json"<rel-path>\x00<content>"sha256.Sum256, return lower-case hexPinned-ref gate —
GithubResolver.Fetchingithub.goRejection must happen before any
git cloneattempt (GitRunner must not be called).Test plan
go test ./internal/plugins/... -v -run TestPluginInstall→ all 8 GREENgithub_test.gotests still GREEN (no regressions)go test ./...still GREEN🤖 Generated with Claude Code