chore(audit): weekly cloud audit — fix 4 fail-open / data-loss defects#115
Draft
tzone85 wants to merge 1 commit into
Draft
chore(audit): weekly cloud audit — fix 4 fail-open / data-loss defects#115tzone85 wants to merge 1 commit into
tzone85 wants to merge 1 commit into
Conversation
Weekly security/quality audit. Four verified defects fixed, each with a
regression test; three consecutive audit rounds (the last dry).
1. security/scanners.go · parseGovulncheck — fail-open dependency scan.
Text-mode govulncheck emits no "Vulnerability #" line on a network/build/
timeout error, so a failed scan was indistinguishable from a clean one and
routed to `ran` (clean) instead of `failed` — defeating even
require_scanners. Switched to `govulncheck -json` with exit-code + config-
handshake detection so a failed run is reported failed.
2. web/auth.go · RequireToken — fail-open auth. RequireToken("") set
AllowUnauthenticated, silently serving an unauthenticated dashboard,
contradicting its own doc, NewAuthMiddleware's panic contract, and CLAUDE.md.
Now inherits the panic-on-empty-token guarantee.
3. engine/conflict_resolver.go · handleBinaryConflict — silent data loss. Small
binary conflicts used `git checkout --ours`, but during a rebase --ours is
the base, so the story's binary asset change was discarded whenever the base
also touched the file. Changed to --theirs (story side), matching the text/
JSON deterministic paths.
4. engine/verification_loop.go · checkTests/parseGoTestJSON — completion gate
fail-open. A Go test binary that fails to COMPILE emits only package-level
events (empty Test) and go build ignores test files, so a non-compiling
suite parsed as (0,0,0)=clean → REQ_COMPLETED on cross-story drift. Now
counts package build-fail events and treats a non-zero runner exit with no
per-test failure as red.
Docs: corrected the CLAUDE.md binary-policy line and conflict-resolver comments
to match the fixed --theirs semantics.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LFb5kxYoibXzSz3YGnmJrU
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Weekly automated security/quality audit (cloud). Fan-out sub-agents by category (concurrency, silent-failures, event-sourcing wiring, security, correctness), each finding adversarially verified before fixing. Three rounds run; the third was dry (two consecutive dry categories). Every fix ships a Go regression test.
Findings fixed
1.
internal/security/scanners.go·parseGovulncheck→runGovulncheck/parseGovulncheckJSONReal bug — fail-open dependency scan. The four JSON scanners (gosec, gitleaks, semgrep, npm-audit) get failure detection for free: a crash emits non-JSON,
json.Unmarshalfails, andRunScannersroutes them tofailed. govulncheck was the exception — its text mode emits noVulnerability #line on a network/build/timeout error, so a failed run was indistinguishable from a clean one and landed inran(clean). This defeated evensecurity.require_scanners: true, which only inspectsskipped/failed. It is the exact false-open the surrounding code documents itself as preventing.Fix: switch to
govulncheck -json. In JSON mode govulncheck exits non-zero only on a tool error (findings do not set a non-zero code) and always emits a leadingconfighandshake, so a non-zero exit, a garbled/truncated stream, or a missing handshake are all surfaced as errors → routed tofailed. The parser also now reports only symbol-level (called) vulnerabilities, matching the prior intent. (This failure mode is live in the audit environment itself —vuln.go.devis network-blocked, which the old code would have reported as scanned-clean.)2.
internal/web/auth.go·RequireTokenReal bug — fail-open auth.
RequireToken(token, next)setAllowUnauthenticated: token == "", soRequireToken("")silently served a fully unauthenticated dashboard — directly contradicting its own doc comment,NewAuthMiddleware's documented panic contract, and CLAUDE.md's stated guarantee thatRequireToken("")PANICS. Currently uncalled, but a loaded footgun for any future caller / port whose token is empty by mistake.Fix: pass only
Token: tokenso an empty token panics (fail-closed), matching the contract. The fail-open path remains reachable only via an explicitAllowUnauthenticated: true.3.
internal/engine/conflict_resolver.go·handleBinaryConflictReal bug — silent data loss. Small (kept) binary conflicts resolved with
git checkout --ours. ButRebaseWithResolutionruns inside agit rebase, where the sides are inverted —--oursis the base/upstream,--theirsis the replayed story commit (pinned bygit.TestConflictSides_OursIsBaseTheirsIsStory, and matching the text/JSON deterministic paths which all keep--theirs). So when the base also touched a small binary asset, the story's change was silently reverted to the base version and the requirement merged with the story's work lost. Verified empirically with a real rebase (--ours→base,--theirs→story).Fix: use
git checkout --theirsso the story's binary change wins, as documented. Event message + CLAUDE.md corrected.4.
internal/engine/verification_loop.go·checkTests/parseGoTestJSONReal bug — completion gate fail-open. A Go test binary that fails to compile (e.g. a merged story's
_test.goreferences a symbol another story renamed — exactly the cross-story drift this gate exists to catch) emits only package-levelgo test -jsonevents with an emptyTestfield (build-fail,fail). All were skipped, so a non-compiling suite parsed as(0,0,0)= clean;go build ./...doesn't compile test files, socheckBuildalso passed; the discarded exit code hid the rest →ShouldRunFixCyclereturned false →REQ_COMPLETEDon a tree whose tests don't even compile. Verified empirically.Fix: count package-level
build-failevents as failures, and treat a non-zero test-runner exit with zero attributable per-test failures (compile error, panic, vet failure, or a jest/vitest runner that failed to launch) as red. Fails closed.Verification
go build ./...— cleango vet ./...— cleango test ./... -count=1— all packages pass except four pre-existing, environment-only failures unrelated to this diff, each confirmed failing on cleanmainviagit stash:internal/cliTestApproveStory_*/TestApproveAll_*/TestRunApprove_*—ghCLI not installed in the sandbox.internal/engineTestWriteLockFile_InvalidPath/TestWriteMetadata_InvalidDir— run as root, which can write to the "invalid" paths the tests expect to be rejected.internal/figmaTestResolveToken_UnreadableFileIsDistinguished— root bypasses the 0600 unreadable-file check.internal/repolearnTestCountFilesInDir_NonexistentDir—/nonexistent/pathactually exists in this container image.internal/engineconflict-resolver rebase tests also fail on a bare checkout becausegit initdefaults tomaster; they pass withgit config init.defaultBranch main, as CI sets.)make doc-coverage(TestDocCoverage_*) — pass. No new CLI commands, config fields, or event types were added.TestParseGovulncheckJSON,TestParseGovulncheckJSON_FailedRunIsNotClean,TestParseGovulncheckJSON_ImportOnlyFindingsNotCalled,TestRunScanners_GovulncheckFailureNotReportedClean,TestRequireToken_EmptyTokenPanics,TestBinaryConflict_SmallBinaryKeepsStoryVersion,TestParseGoTestJSON_BuildFailIsCounted,TestCheckTests_BrokenTestFileIsNotClean.Tooling limits (fallback per audit policy):
make lint(golangci-lint) could not run — the latest golangci-lint release still embeds Go 1.25 analysis packages and refuses this module'sgo 1.26.4directive.make vuln(govulncheck) could not complete — the environment's network policy blocksvuln.go.dev(403 CONNECT). Fell back to build + vet + test + doc-coverage as specified. Notably, finding #1 hardens VXD's own govulncheck integration against exactly the blocked-DB failure this environment exhibits.🤖 Generated with Claude Code
https://claude.ai/code/session_01LFb5kxYoibXzSz3YGnmJrU
Generated by Claude Code