Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ jobs:
- name: Vet
run: go vet ./...

- name: Govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest

@sonupreetam sonupreetam Jul 13, 2026

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

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

govulncheck ./...

- name: Test
run: go test ./... -count=1 -race

Expand Down
3 changes: 3 additions & 0 deletions .uf/replicator/replicator.log
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
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ non-negotiable.
- **CI Parity Gate**: Before marking any task complete, run
the CI-equivalent checks locally. Read
`.github/workflows/` for the exact commands -- do not
rely on memory. Any failure blocks the task.
rely on memory. Any failure blocks the task. The CI
`Build and Test` check includes `govulncheck` for
vulnerability scanning of Go dependencies.
- **Intent Drift Detection**: Implementation must faithfully
capture the spec's intent. The parity test suite verifies
response shapes match the TypeScript version.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/unbound-force/replicator

go 1.25.7
go 1.25.11

require (
github.com/charmbracelet/lipgloss v1.1.0
Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/ci-govulncheck/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: unbound-force
created: 2026-07-07
132 changes: 132 additions & 0 deletions openspec/changes/ci-govulncheck/design.md
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).
104 changes: 104 additions & 0 deletions openspec/changes/ci-govulncheck/proposal.md
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.
82 changes: 82 additions & 0 deletions openspec/changes/ci-govulncheck/specs/ci-govulncheck.md
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.
Loading