-
Notifications
You must be signed in to change notification settings - Fork 9
chore: add govulncheck security scanning to CI #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
em-redhat
wants to merge
2
commits into
unbound-force:main
Choose a base branch
from
em-redhat:opsx/ci-govulncheck
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| 2026/07/07 12:58:52 INFO tool call tool=forge_worktree_list duration=4.536739ms success=true | ||
| 2026/07/07 13:04:12 INFO tool call tool=hivemind_store duration=811.57µs success=true | ||
| 2026/07/07 13:04:17 INFO tool call tool=hivemind_store duration=646.586µs success=true |
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| schema: unbound-force | ||
| created: 2026-07-07 |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| ## Context | ||
|
|
||
| Replicator's CI pipeline (`.github/workflows/ci.yml`) runs `go vet`, `go test`, | ||
| and `go build` but has no security vulnerability scanning. Issue #23 tracks | ||
| adding `govulncheck` -- the official Go vulnerability scanner maintained by the | ||
| Go security team -- to CI. | ||
|
|
||
| The release preflight (`release.yml`, added by the `ci-release-preflight` change) | ||
| was explicitly designed with an extensible `REQUIRED_CHECKS` array. Decision D5 | ||
| in that change's design document notes: "When govulncheck is added (issue #23), | ||
| the preflight can be extended to verify it." This change fulfills that plan. | ||
|
|
||
| The canonical org reference (`unbound-force/unbound-force`) runs security scans | ||
| via a separate `ci_security.yml` workflow. For replicator's current scale | ||
| (single binary, modest dependency tree), adding `govulncheck` as a step within | ||
| the existing `ci.yml` is sufficient. A separate workflow can be introduced later | ||
| if additional scanners (OSV-Scanner, Trivy) are added. | ||
|
|
||
| ## Goals / Non-Goals | ||
|
|
||
| ### Goals | ||
|
|
||
| - Add a `govulncheck` step to `ci.yml` that fails CI when known vulnerabilities | ||
| are found in dependencies. | ||
| - Document the check name so the release preflight can reference it. | ||
| - Extend the release preflight's `REQUIRED_CHECKS` to include the security | ||
| scan check, gating releases on vulnerability-free dependencies. | ||
|
|
||
| ### Non-Goals | ||
|
|
||
| - Adding OSV-Scanner or Trivy source scans -- those can follow if needed. | ||
| - Creating a separate `ci_security.yml` workflow -- not warranted for a single | ||
| scanner at current scale. | ||
| - Adding `govulncheck` to the `Makefile` -- CI is the enforcement point; local | ||
| usage is optional and left to developers. | ||
| - Modifying Go source code, tests, or the replicator binary. | ||
|
|
||
| ## Decisions | ||
|
|
||
| ### D1: Add govulncheck as a step in ci.yml, not a separate workflow | ||
|
|
||
| The canonical reference uses a dedicated `ci_security.yml` for security scans. | ||
| However, replicator has a single CI workflow with one job (`Build and Test`). | ||
| Adding `govulncheck` as a step within this job keeps CI simple and avoids a | ||
| separate workflow that would need its own `actions/checkout` and | ||
| `actions/setup-go` steps (duplicating setup time). | ||
|
|
||
| The step runs after `Vet` and before `Test`: | ||
|
|
||
| ```yaml | ||
| - name: Govulncheck | ||
| run: | | ||
| go install golang.org/x/vuln/cmd/govulncheck@latest | ||
| govulncheck ./... | ||
| ``` | ||
|
|
||
| This ordering means vulnerabilities are caught early (before the slower test | ||
| suite runs), while still benefiting from the Go toolchain already being set up. | ||
|
|
||
| **Trade-off**: If a separate `Security` check name is later needed (e.g., for | ||
| branch protection or release preflight granularity), the step would need to be | ||
| extracted into its own job. This is a straightforward refactor. | ||
|
|
||
| ### D2: Install govulncheck via go install at latest | ||
|
|
||
| Using `go install golang.org/x/vuln/cmd/govulncheck@latest` ensures the | ||
| scanner always uses the most current vulnerability database definitions. | ||
| Unlike application dependencies, pinning the scanner version provides no | ||
| reproducibility benefit -- the vulnerability database is inherently | ||
| time-varying. | ||
|
|
||
| **Alternative considered**: Using the `golang/govulncheck-action` GitHub | ||
| Action. Rejected because: | ||
| - It adds another third-party action to pin and maintain. | ||
| - The `go install` + `govulncheck` approach is simpler and uses the same | ||
| Go toolchain already set up by `actions/setup-go`. | ||
| - Direct invocation gives clearer control over flags and output. | ||
|
|
||
| ### D3: Extend release preflight REQUIRED_CHECKS | ||
|
|
||
| The release preflight in `release.yml` verifies CI passed before allowing a | ||
| release. The `REQUIRED_CHECKS` array currently contains only `"Build and Test"`. | ||
| Since `govulncheck` runs as a step within the `Build and Test` job (not a | ||
| separate job), the check name remains `"Build and Test"` -- no change is needed | ||
| to the preflight's `REQUIRED_CHECKS` array. | ||
|
|
||
| If `govulncheck` fails, the `Build and Test` job fails, and the preflight | ||
| already blocks the release. This is the simplest integration path. | ||
|
|
||
| **Consequence**: The preflight does not distinguish between a test failure and a | ||
| vulnerability failure. Both block the release equally, which is the desired | ||
| behavior. | ||
|
|
||
| ### D4: No Makefile changes | ||
|
|
||
| The `Makefile` currently defines `make check` as `vet + test`. Adding | ||
| `govulncheck` to the Makefile is tempting for local developer convenience, but: | ||
| - CI is the enforcement point for security scanning. | ||
| - Developers can run `govulncheck ./...` directly when needed. | ||
| - Adding it to `make check` would slow down the local development loop for a | ||
| check that primarily matters at PR/release time. | ||
|
|
||
| If local scanning is later desired, a separate `make vuln` target can be added. | ||
|
|
||
| ## Risks / Trade-offs | ||
|
|
||
| - **CI time increase**: `go install govulncheck@latest` adds ~5-10 seconds for | ||
| download/install, plus ~5-15 seconds for scanning. Total CI impact is modest | ||
| (~10-25 seconds) since the Go module cache is warm from `actions/setup-go`. | ||
|
|
||
| - **False positives from transitive dependencies**: `govulncheck` analyzes call | ||
| graphs and only reports vulnerabilities in code paths actually used by the | ||
| binary. This significantly reduces false positives compared to dependency-only | ||
| scanners. However, if a transitive dependency has a vulnerability in a code | ||
| path replicator uses, CI will fail even if the vulnerability is not | ||
| exploitable in practice. The fix is to update the dependency or, as a last | ||
| resort, document the exception. | ||
|
|
||
| - **Network dependency**: `go install govulncheck@latest` requires network | ||
| access to download the tool and vulnerability database. GitHub-hosted runners | ||
| have reliable network access, so this is low risk. If a network issue causes | ||
| intermittent failures, retrying the CI job resolves it. | ||
|
|
||
| - **No version pinning**: Using `@latest` means the scanner version can change | ||
| between CI runs. This is intentional -- newer versions have better detection. | ||
| If a specific version introduces a regression, it can be pinned temporarily. | ||
|
|
||
| - **Gatekeeping note**: This change modifies CI configuration, which falls under | ||
| the Gatekeeping Value Protection constraint in AGENTS.md. Adding a new | ||
| security check (govulncheck) is an improvement to CI gates, not a relaxation. | ||
| The proposal's constitution alignment confirms this is PASS for Observable | ||
| Quality (machine-parseable vulnerability output with clear pass/fail status). |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| ## Why | ||
|
|
||
| Replicator has no security scanning in its CI pipeline. Issue #15 identified | ||
| this gap, and issue #23 specifically tracks adding `govulncheck` to scan for | ||
| known vulnerabilities in Go dependencies. The existing CI workflow | ||
| (`.github/workflows/ci.yml`) runs `go vet`, `go test`, and `go build` but | ||
| performs no vulnerability analysis. | ||
|
|
||
| The canonical org reference (`unbound-force/unbound-force`) runs OSV-Scanner | ||
| and Trivy source scans via `ci_security.yml`. At minimum, replicator should | ||
| run `govulncheck` -- the official Go vulnerability scanner maintained by the | ||
| Go team -- as part of CI. | ||
|
|
||
| The release preflight (added in the `ci-release-preflight` change) was | ||
| explicitly designed to be extended with a security scan check once one exists. | ||
| Adding `govulncheck` to CI closes that gap. | ||
|
|
||
| Fixes: https://github.com/unbound-force/replicator/issues/23 | ||
| Parent: https://github.com/unbound-force/replicator/issues/15 | ||
|
|
||
| ## What Changes | ||
|
|
||
| 1. **`.github/workflows/ci.yml`**: Add a `govulncheck` step that runs | ||
| `govulncheck ./...` after the existing `Vet` step. The step installs | ||
| `govulncheck` via `go install golang.org/x/vuln/cmd/govulncheck@latest` | ||
| and scans all packages. CI fails if known vulnerabilities are found. | ||
|
|
||
| 2. **Release gating**: Since `govulncheck` runs as a step within the existing | ||
| `Build and Test` job, no changes to `release.yml` are needed. If | ||
| `govulncheck` fails, the `Build and Test` job fails, and the release | ||
| preflight already blocks the release through its existing | ||
| `REQUIRED_CHECKS` array. | ||
|
|
||
| 3. **Documentation**: Update `AGENTS.md` to document that the `Build and Test` | ||
| CI check now includes `govulncheck` vulnerability scanning. | ||
|
|
||
| ## Capabilities | ||
|
|
||
| ### New Capabilities | ||
|
|
||
| - `ci/govulncheck`: Automated vulnerability scanning of Go dependencies | ||
| using the official `govulncheck` tool, integrated into the CI pipeline. | ||
|
|
||
| ### Modified Capabilities | ||
|
|
||
| - `release/preflight`: No direct changes needed. The preflight already | ||
| gates releases on the `Build and Test` check, which now includes | ||
| `govulncheck` as a step. Vulnerability failures block releases | ||
| through the existing mechanism. | ||
|
|
||
| ### Removed Capabilities | ||
|
|
||
| - None | ||
|
|
||
| ## Impact | ||
|
|
||
| - `.github/workflows/ci.yml` -- new `govulncheck` step added | ||
| - `.github/workflows/release.yml` -- no changes needed; the existing | ||
| preflight already gates on `Build and Test` | ||
| - CI will now fail on PRs and pushes to main if `govulncheck` detects | ||
| known vulnerabilities in dependencies | ||
| - Releases will be blocked if the security scan has not passed on HEAD | ||
|
|
||
| ## Constitution Alignment | ||
|
|
||
| Assessed against the Replicator constitution (`.specify/memory/constitution.md`), | ||
| which extends the Unbound Force org constitution v1.1.0. | ||
|
|
||
| ### I. Autonomous Collaboration | ||
|
|
||
| **Assessment**: N/A | ||
|
|
||
| This change modifies CI/CD workflow files only. No MCP tools, inter-agent | ||
| communication, or tool outputs are affected. The change is purely | ||
| infrastructure-level and does not alter how heroes collaborate through | ||
| artifacts. | ||
|
|
||
| ### II. Composability First | ||
|
|
||
| **Assessment**: PASS | ||
|
|
||
| The binary remains independently installable and usable without any external | ||
| services. `govulncheck` is a CI-only tool that does not affect the standalone | ||
| functionality of the replicator binary. Dewey integration and graceful | ||
| degradation are unaffected. | ||
|
|
||
| ### III. Observable Quality | ||
|
|
||
| **Assessment**: PASS | ||
|
|
||
| `govulncheck` produces structured output identifying specific CVEs, affected | ||
| packages, and call stacks. CI step output is visible in GitHub Actions logs | ||
| with clear pass/fail status. The check name is documented so the release | ||
| preflight can query it via the Checks API, maintaining machine-parseable | ||
| quality signals. | ||
|
|
||
| ### IV. Testability | ||
|
|
||
| **Assessment**: N/A | ||
|
|
||
| This change modifies GitHub Actions workflow files which cannot be tested in | ||
| isolation (they require the GitHub Actions runtime). No Go source code, tests, | ||
| or testable components are modified. Post-merge verification will be performed | ||
| by observing the new `govulncheck` step in CI runs. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| ## ADDED Requirements | ||
|
|
||
| ### Requirement: CI vulnerability scanning | ||
|
|
||
| The CI pipeline MUST run `govulncheck ./...` on every push to `main` and every | ||
| pull request targeting `main`. The CI job MUST fail if `govulncheck` reports any | ||
| known vulnerabilities in dependencies reachable from the binary's call graph. | ||
|
|
||
| #### Scenario: Clean dependency tree | ||
|
|
||
| - **GIVEN** all Go dependencies are free of known vulnerabilities | ||
| - **WHEN** CI runs `govulncheck ./...` | ||
| - **THEN** the step exits with code 0 and CI proceeds to subsequent steps | ||
|
|
||
| #### Scenario: Vulnerable dependency detected | ||
|
|
||
| - **GIVEN** a Go dependency has a known vulnerability in a code path used by | ||
| replicator | ||
| - **WHEN** CI runs `govulncheck ./...` | ||
| - **THEN** the step exits with a non-zero code, the `Build and Test` job fails, | ||
| and the vulnerability details are visible in the CI log output | ||
|
|
||
| #### Scenario: Vulnerability in unused code path | ||
|
|
||
| - **GIVEN** a Go dependency has a known vulnerability but replicator does not | ||
| call any affected functions | ||
| - **WHEN** CI runs `govulncheck ./...` | ||
| - **THEN** `govulncheck` reports the vulnerability as informational but does | ||
| NOT fail the step (govulncheck's default behavior is call-graph-aware) | ||
|
|
||
| ### Requirement: Govulncheck step ordering | ||
|
|
||
| The `govulncheck` step MUST run after `Vet` and before `Test` in the CI job. | ||
| This ensures vulnerabilities are detected early without delaying faster static | ||
| analysis checks. | ||
|
|
||
| #### Scenario: Step execution order | ||
|
|
||
| - **GIVEN** the CI workflow is triggered | ||
| - **WHEN** the `Build and Test` job executes | ||
| - **THEN** steps run in order: Checkout, Setup Go, Vet, Govulncheck, Test, Build | ||
|
|
||
| ### Requirement: Release gated on vulnerability scan | ||
|
|
||
| The release preflight MUST NOT allow a release if the `Build and Test` CI check | ||
| has not passed. Since `govulncheck` is a step within the `Build and Test` job, | ||
| a vulnerability failure blocks the release through the existing preflight | ||
| mechanism. | ||
|
|
||
| #### Scenario: Release blocked by vulnerability | ||
|
|
||
| - **GIVEN** the most recent CI run on HEAD failed due to a `govulncheck` finding | ||
| - **WHEN** a release is triggered via `workflow_dispatch` | ||
| - **THEN** the preflight job fails with an error indicating the `Build and Test` | ||
| check has not passed | ||
|
|
||
| #### Scenario: Release proceeds after vulnerability is resolved | ||
|
|
||
| - **GIVEN** a vulnerability was detected, the dependency was updated, and CI | ||
| passed on the fix commit | ||
| - **WHEN** a release is triggered via `workflow_dispatch` | ||
| - **THEN** the preflight job succeeds and the release proceeds | ||
|
|
||
| ### Requirement: Check name documentation | ||
|
|
||
| The CI check name (`Build and Test`) MUST be documented in `AGENTS.md` so that | ||
| agents and the release preflight can reference it consistently. | ||
|
|
||
| #### Scenario: Documented check name matches CI | ||
|
|
||
| - **GIVEN** the CI workflow defines a job named `Build and Test` | ||
| - **WHEN** an agent or the release preflight queries the check name | ||
| - **THEN** the documented name in `AGENTS.md` matches the actual CI job name | ||
|
|
||
| ## MODIFIED Requirements | ||
|
|
||
| None. This change adds new CI capabilities without modifying existing | ||
| requirements. | ||
|
|
||
| ## REMOVED Requirements | ||
|
|
||
| None. No existing requirements are removed by this change. |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@em-redhat Minor. Consider pinning to a commit hash instead of @latest (reproducibility, supply chain hygiene). Will approve once its done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the planning mistakenly equating the binary and database. The binary can be pinned to a specific version and will still pull the latest vuln db. Would it be more in line with standard go ecosystem behaviour to pin to a version tag instead of a hash? Either can be done it just seems version tag would have a bit less friction