ci: harden CI matrix, static analysis, supply-chain, and example/doc gates#194
Open
dgenio wants to merge 6 commits into
Open
ci: harden CI matrix, static analysis, supply-chain, and example/doc gates#194dgenio wants to merge 6 commits into
dgenio wants to merge 6 commits into
Conversation
…gates Bundles a coherent set of CI and repository-automation issues that all live in .github/ (plus their Makefile/scripts/doc support) so the shared ci.yml is restructured once instead of conflicting across separate PRs. - #187: trigger CI once per change (push scoped to main; pull_request for branches) and add a concurrency group that cancels superseded runs. - #109: run the test suite on a Linux/macOS/Windows matrix with the race detector; coverage stays single-source on Linux. Windows now exercises the engine's path-safety logic. - #110: add golangci-lint (.golangci.yml) and gosec gates + `make lint`/`make golangci`/`make sec`. Intentional gosec findings (operator-specified file reads, the proxy launching the operator's MCP server, forwarding to the configured upstream, non-secret 0o644 files) are annotated inline with `// #nosec <rule> -- <reason>`; fixed the two real G112 findings by setting ReadHeaderTimeout on the HTTP servers. - #177: add Dependabot for gomod + github-actions and a govulncheck CI job (`make vuln`); documented the supply-chain posture in SECURITY.md. - #181: add scripts/check-examples.sh + `make examples` and a CI job that runs every bundled example through the binary so examples/ cannot drift. - #165: add scripts/check-doc-claims.py + `make doc-check` and a CI job that verifies documented commands exist and internal doc links resolve (with a self-test that proves the checks fire). - #188: auto-label PRs by changed path via actions/labeler (.github/labeler.yml, existing labels only) and flag non-Conventional-Commit titles (advisory). CONTRIBUTING.md documents the new targets and auto-labeling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSZXR9R2Ejmw5FreniZ5yM
First CI run surfaced three environment/portability failures (not logic):
- lint: golangci-lint v2.5.0 (built with Go 1.25) panics type-checking
packages loaded by `go-version: stable` (1.26) — "file requires newer Go
version go1.26". Pin the lint job to Go 1.24.
- test (macos-latest): Go 1.22's linker omits the macOS LC_UUID load command,
so `-race` test binaries abort under the current macos-latest dyld
("missing LC_UUID load command" / "signal: abort trap"). Bump the test
matrix to Go 1.24, which builds run cleanly on all three runners.
- test (windows-latest): TestAuditKeygenCreatesUsableKeys asserted the private
key file mode is 0600, but Windows has no POSIX permission bits (os.Stat
reports 0666). Skip the mode assertion on Windows; keygen still requests
0600 and the key-load check still runs everywhere.
ubuntu test, security (gosec+govulncheck), examples, docs, goreleaser-check,
and docker-build all passed on the first run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSZXR9R2Ejmw5FreniZ5yM
There was a problem hiding this comment.
Pull request overview
This PR consolidates several CI and repository-automation improvements centered around .github/, adding cross-platform test coverage, new static-analysis/security gates, and automated checks to prevent example/docs drift—plus supporting Makefile targets, scripts, and documentation updates.
Changes:
- Restructures CI to avoid duplicate runs, add concurrency cancellation, and run tests on a Linux/macOS/Windows matrix (with single-source coverage from Linux).
- Adds dedicated CI jobs for linting, security scanning (gosec + govulncheck), examples validation, and docs claim/link validation.
- Adds Dependabot configuration and PR triage automation (path-based auto-labeling + advisory Conventional Commit title warning), plus targeted gosec-related code hardening/annotations.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
SECURITY.md |
Documents supply-chain posture (Dependabot, scanners, signing/SBOM references). |
scripts/check-examples.sh |
New hermetic script to exercise bundled examples end-to-end against the built binary. |
scripts/check-doc-claims.py |
New script to verify documented CLI commands exist and relative doc links resolve (with selftest). |
Makefile |
Adds lint/security/vuln/examples/doc-check targets and tool overrides. |
internal/proxy/proxy.go |
Adds inline gosec suppression for intentionally operator-specified command execution. |
internal/policy/policy.go |
Adds inline gosec suppression for operator-specified file reads. |
internal/metrics/metrics.go |
Adds inline gosec suppression for an intentional/guarded integer conversion case. |
internal/httpproxy/httpproxy.go |
Hardens HTTP server with ReadHeaderTimeout and annotates intentional proxy forwarding findings. |
internal/audit/signing.go |
Adds inline gosec suppressions for operator-supplied key paths and intentional public-key perms. |
CONTRIBUTING.md |
Updates contributor guidance for new CI gates, local targets, and PR triage behavior. |
cmd/agentfence/main.go |
Adds ReadHeaderTimeout for metrics server; adjusts Close defers and annotates intentional file perms/reads. |
.golangci.yml |
Introduces golangci-lint configuration (pragmatic linter set + errcheck exceptions). |
.github/workflows/pull-request-triage.yml |
Adds PR auto-labeling via labeler + advisory Conventional Commit title check. |
.github/workflows/ci.yml |
Restructures CI triggers/concurrency; adds matrix test + new lint/security/examples/docs jobs. |
.github/labeler.yml |
Maps changed paths to existing repo labels for auto-labeling. |
.github/dependabot.yml |
Enables grouped weekly updates for Go modules and GitHub Actions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- CONTRIBUTING: the lint-tools install snippet was labeled golangci-lint but only installed gosec; add the golangci-lint installer and label both. - CONTRIBUTING: correct the pre-push paragraph — the cross-platform `test` job runs `go test -race` directly (coverage on Linux) and `fmt-check` runs in the `lint` job, rather than `make ci` verbatim. - ci.yml: pin the security job to Go 1.24 instead of 'stable' so a new Go release can't break it unannounced (go.mod's 1.22 floor is too old to `go install` the latest gosec/govulncheck). The lint job was already pinned. - check-doc-claims.py: write the selftest fixture via a context manager so the handle is flushed/closed before the checks read it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSZXR9R2Ejmw5FreniZ5yM
…are real Pinning the security job to Go 1.24 (from review feedback) made govulncheck fail with 9 standard-library CVEs present in the go1.24.13 toolchain but already fixed upstream (e.g. os/net/url, "Fixed in: go1.25.8"). govulncheck reports stdlib vulnerabilities against the toolchain it runs on, so it must use the latest stable Go — otherwise it flags the pinned release's own unpatched stdlib. Revert the security job to 'stable' with a comment explaining why it differs from the (correctly pinned) lint job. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSZXR9R2Ejmw5FreniZ5yM
…per job Audit follow-up on PR #194. - internal/httpproxy/httpproxy.go: the two upstream-request calls were annotated `// #nosec G704`, but gosec has no G7xx rules, so the suppression matched nothing. The rule that applies to an HTTP request built from a variable URL is G107; annotate the NewRequestWithContext call with G107 and drop the annotation on client.Do (which raises no gosec rule). This preserves the "annotate, never globally disable" invariant so a genuinely new finding still fails the build. - .github/workflows/pull-request-triage.yml: move `pull-requests: write` from the workflow level to the `label` job only. The advisory `title` job merely emits a ::warning and needs no write scope; under pull_request_target, keeping the elevated token scoped to the one job that needs it is least-privilege. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEcWEtCkPD8z9xwFPZjkwL
Follow-up to acd90ed. gosec (v2.22+/latest, used by the security CI job) does have a G704 rule — "SSRF via taint analysis" — and raises it on both upstream-request calls in the HTTP proxy. acd90ed reannotated one call as `#nosec G107` and dropped the annotation on client.Do on the assumption that "gosec has no G7xx rules", so neither call was suppressed and the security job failed with 2 G704 findings. Annotate both the NewRequestWithContext and client.Do calls with `#nosec G107 G704` so the intentional operator-configured proxy forwarding is suppressed regardless of which rule ID the installed gosec version emits, while still failing on any genuinely new finding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSZXR9R2Ejmw5FreniZ5yM
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.
Summary
Implements a coherent group of CI / repository-automation issues in one PR.
They all center on
.github/(with supporting Makefile targets, scripts, anddocs), so the shared
ci.ymlis restructured once instead of conflictingacross seven separate PRs.
Related issues
Fixes #109
Fixes #110
Fixes #165
Fixes #177
Fixes #181
Fixes #187
Fixes #188
What changed
.github/workflows/ci.yml— restructured:pushscoped tomain,pull_requestfor branches (no moredouble runs), plus a
concurrencygroup withcancel-in-progress.testjob now runs aubuntu/macos/windowsmatrix with the racedetector (Go-native invocation so Windows needs no
make/POSIX shell);coverage is produced and uploaded only from Linux.
lintjob ([CI] Add gosec and golangci-lint static-analysis gate #110):make fmt-check+golangci-lint(v2.5.0).securityjob ([CI] Add gosec and golangci-lint static-analysis gate #110/Strengthen supply-chain hygiene with dependency pinning and update automation #177):gosec+govulncheck.examplesjob (Add an examples-validation CI job that runs every bundled example end to end #181) anddocsjob (Add a CI doc-claim check that ties README/doc status to actual commands #165).Static analysis (#110)
.golangci.ymlwith a pragmatic linter set;make lint/make golangci/make sec.ReadHeaderTimeouton the HTTP proxy and metricsservers (gosec G112).
// #nosec <rule> -- <reason>(operator-specified file reads, the proxy launching the operator's MCP server,
forwarding to the configured upstream, non-secret
0o644files) — no globalrule disables, so new findings still fail.
Supply-chain (#177) —
.github/dependabot.yml(gomod + github-actions,grouped), a
govulncheckjob +make vuln, and a SECURITY.md posture section.Examples (#181) —
scripts/check-examples.sh+make examplesrun everybundled example (validate / policy test / check / audit verify+summarize+export
/ the demo) through the binary. Hermetic.
Docs (#165) —
scripts/check-doc-claims.py+make doc-checkverify everyagentfence <subcommand>in the docs is a real command and that local doclinks resolve, with a
--selftestthat proves the checks fire.PR triage (#188) —
actions/labeler(.github/labeler.yml, existinglabels only) auto-labels PRs by changed path; an advisory check flags
non-Conventional-Commit titles. Documented in CONTRIBUTING.md.
How verified
Locally (Go 1.24, golangci-lint v2.5.0, gosec, govulncheck):
gofmt -l— cleango build ./...— okgolangci-lint run ./...—0 issuesgosec -quiet ./...— exit 0 (all findings annotated)go test -race ./...— all packages passmake examples— all examples validated end-to-endmake doc-check—11 files: commands + links resolve;--selftestPASSci.yml,pull-request-triage.yml,dependabot.yml,labeler.yml,.golangci.yml) parse cleanly.Could not run locally:
govulncheck(the sandbox blocksvuln.go.dev);it will run in the new
securityCI job, which has network access.Risks / notes
(its purpose) — that would be a real finding to address, not workflow noise.
golangci-lint-action@v8is pinned to golangci-lintv2.5.0(matches theconfig schema used locally).
gosec/govulncheckinstall viago install @latestin CI so scanners usecurrent rules/advisories; Dependabot keeps Actions pinned.
Checklist
suite green
govulncheckruns in CI🤖 Generated with Claude Code
https://claude.ai/code/session_01QSZXR9R2Ejmw5FreniZ5yM
Generated by Claude Code