Skip to content

harden(security): close RCE gate gap + supply-chain/scan hardening#26

Merged
alxxjohn merged 1 commit into
mainfrom
clean-harden
Jul 2, 2026
Merged

harden(security): close RCE gate gap + supply-chain/scan hardening#26
alxxjohn merged 1 commit into
mainfrom
clean-harden

Conversation

@alxxjohn

@alxxjohn alxxjohn commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Remediation of a full-repo SOC 3 / OWASP security audit. Closes one confirmed critical RCE plus supply-chain, path-traversal, DoS, and audit-trail gaps. Each fix ships with a regression test.

Changes

🔴 Critical

  • RCE gate gap fixedsecurity_rules.govulncheck_command was passed straight to exec with no trust gate and is default-on for Go targets, so an untrusted PR editing codeguard.yaml could run arbitrary commands in CI. govulncheck.Run now routes a config-supplied override through trust.GuardConfigCommand; the built-in govulncheck binary (resolved from PATH, a static analyzer that doesn't execute scanned code) stays exempt so default auto mode keeps working.

🟠 Supply chain (OWASP A08 / SOC 3 change management)

  • All third-party GitHub Actions SHA-pinned across the four workflows, including the previously branch-pinned Homebrew/actions/setup-homebrew@main. The SLSA generator remains version-tag-pinned by design (documented inline).
  • release.yml hardened: untrusted tag input moved out of run: into env:; auto-tag creation gated behind a new create_missing_tag input that only the trusted cd.yml callers set (a manual dispatch can no longer mint an arbitrary tag from an arbitrary SHA); top-level permissions reduced to contents: read with per-job elevation; secrets: inherit replaced with explicit RELEASE_PLEASE_TOKEN passing.
  • Dockerfiles: digest-pin base images and add a non-root USER.

🟡 Governance / robustness

  • Add SECURITY.md (private disclosure policy + SLAs) and .github/CODEOWNERS.
  • Symlink-safe path containmentconfig.containedPath now canonicalizes symlinks (base + deepest existing ancestor), closing a committed-symlink escape of the baseline/cache/ai.cache path guarantee. Adds the previously-missing cache.path/ai.cache.path coverage.
  • Scan file-size capsWalkFiles skips files > 32 MiB and reads go through a bounded reader, preventing a giant file in an untrusted PR from OOMing the in-memory corpus.
  • SARIF audit metadata — emits driver.version/informationUri + an invocations record so a CI consumer can attribute results to a specific run (A09 / monitoring).

Not included (deliberate)

  • Branch protection left unchanged per maintainer decision. Note it is currently weak on main (0 required approvals, no required status checks, no code-owner review); tightening it is a separate policy call.

Test plan

  • go build ./..., go vet ./..., gofmt -l — clean
  • go test ./...505 pass (new tests: govulncheck gate, cache/ai-cache + symlink containment, scan file cap, SARIF invocation)
  • golangci-lint run (incl. gosec) — 0 issues
  • self-scan make codeguard-ci0 fails (updated .codeguard/codeguard.yaml required-workflow marker to match the SHA-pinned action)
  • Docker image build/run not exercised locally — sandbox keychain blocks image pulls; base-image digests verified live against the registry (HTTP 200).

Knowledge

Updated .claude/knowledge/architecture-boundaries.md: corrected two stale entries (one claimed govulncheck was "not gated" — that was the bug) and documented the new corpus-walk size cap.

🤖 Generated with Claude Code

SOC 3 / OWASP gap remediation from a full-repo security audit.

- fix(security): gate config-supplied govulncheck_command through
  trust.GuardConfigCommand. It was exec'd ungated and default-on for Go
  targets, an RCE for untrusted PRs. Built-in default binary stays exempt.
- ci: SHA-pin every third-party GitHub Action (incl. setup-homebrew@main);
  SLSA generator stays tag-pinned by design.
- ci(release): move untrusted tag input into env:, gate auto-tag creation
  behind a create_missing_tag input set only by trusted callers, scope
  job permissions to least privilege, pass secrets explicitly.
- build(docker): digest-pin base images and run as non-root USER.
- docs: add SECURITY.md (disclosure policy) and .github/CODEOWNERS.
- fix(config): make artifact-path containment symlink-aware (EvalSymlinks
  on base + deepest existing ancestor) to close a committed-symlink escape.
- perf(scan): skip files over 32 MiB in the walk and read via a bounded
  reader to prevent memory exhaustion on untrusted repos.
- feat(report): emit SARIF tool version + invocations for run attribution.

Adds regression tests for each. Verified: build, vet, gofmt, go test
(505), golangci-lint (0 issues), self-scan `make codeguard-ci` (0 fails).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alxxjohn alxxjohn merged commit 8e885cf into main Jul 2, 2026
17 of 18 checks passed
@alxxjohn alxxjohn deleted the clean-harden branch July 2, 2026 17:39
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