Skip to content

Add comprehensive test coverage for scanner, enrichment, and CLI#2

Merged
beejak merged 9 commits into
mainfrom
claude/repo-testing-strategy-rygscm
Jul 1, 2026
Merged

Add comprehensive test coverage for scanner, enrichment, and CLI#2
beejak merged 9 commits into
mainfrom
claude/repo-testing-strategy-rygscm

Conversation

@beejak

@beejak beejak commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

This PR adds extensive test coverage across the scanner, enrichment, report generation, and CLI components. The changes include unit tests, integration tests, and end-to-end tests that verify core functionality without requiring external services (via mocking).

Key Changes

New Test Files

  • pkg/kev/kev_test.go: Unit tests for CISA KEV catalog loading, caching, HTTP error handling, concurrent safety, and case normalization
  • pkg/remediate/enrich_integration_test.go: Integration tests for enrichment pipeline with mocked KEV server, verifying exploit flagging, severity upgrades, and ransomware detection
  • pkg/report/report_test.go (additions): SARIF 2.1 structural validation tests covering required fields, tool driver metadata, result formatting, and rule deduplication
  • pkg/scanner/parse_test.go: Fixture-based tests for Trivy JSON parsing, finding conversion, severity distribution, and remediation link population
  • cmd/cli/main_test.go: CLI exit-code tests using fake Trivy binary, verifying policy enforcement (--fail-on-severity, --fail-on-count), and error handling
  • cmd/server/main_test.go: HTTP handler tests for /health, /api/scan, and summary building logic
  • cmd/baseline/main_test.go: Tests for image loading, CSV escaping, and markdown report generation
  • cmd/mcp-server/main_test.go: Tests for severity parsing and summary formatting
  • pkg/runc/runc_test.go (additions): Tests for HostVersion() with mocked Docker/runc binaries

Test Infrastructure

  • pkg/scanner/testdata/trivy-fixture.json: Minimal Trivy JSON fixture (5 vulnerabilities across severity levels) for reproducible parsing tests
  • Mock HTTP servers: KEV and Trivy endpoints mocked in tests to avoid network dependencies
  • Fake binary helpers: Scripts for testing CLI integration with Trivy and runc version detection
  • Cache reset utilities: Test helpers to isolate KEV and runc state between tests

Documentation Updates

  • docs/testing.md: Reorganized test matrix with current status; added CLI exit-code, enrichment integration, and server tests
  • docs/architecture-diagrams.md: Updated flowcharts to include rootfs/LXC scanning, runc advisory, KEV/OSV enrichment, and new output formats (CSV, SBOM)
  • .github/workflows/ci.yml: New CI pipeline with go vet, race-detector unit tests, and conditional integration tests

Source Code Modifications

  • pkg/kev/kev.go: Changed cisaKEVURL from const to var to allow test mocking; added SetURLForTest() and ResetForTest() helper functions
  • cmd/cli/main.go: Fixed runScan() to return exit code (was discarded); now properly propagates policy violations to os.Exit()
  • go.mod: Updated Go version to 1.25.0 and MCP SDK to v1.6.1

Notable Implementation Details

  • Tests use httptest.NewServer() to mock external APIs without network calls
  • KEV and runc tests include concurrent safety verification with -race detector
  • CLI tests use shell scripts as fake Trivy binaries to simulate real execution paths
  • SARIF tests validate GitHub/Azure import requirements (schema, version, tool metadata)
  • Enrichment integration tests verify the full pipeline: mock KEV → exploit flag → severity upgrade → policy gate
  • All tests are self-contained; no external fixtures or live API dependencies

Testing Strategy

  • Unit tests cover individual components (parsing, enrichment, policy, KEV client)
  • Integration tests wire together real functions with mocked HTTP servers
  • CLI tests verify exit codes and policy enforcement with fake Trivy
  • Race detector enabled on all unit tests to catch concurrency issues

https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t

Greptile Summary

This PR adds comprehensive test coverage across all pkg/* and cmd/* packages, refactors runScan() to return an exit code instead of calling os.Exit() directly, and introduces a new GitHub Actions CI pipeline along with CI templates for CircleCI, AWS CodeBuild, Google Cloud Build, Bitbucket Pipelines, and Tekton. The Go module is bumped to 1.25.0 to satisfy the MCP SDK dependency.

  • Test coverage: Unit, integration, and CLI exit-code tests added for pkg/kev, pkg/runc, pkg/scanner, pkg/remediate, pkg/report, cmd/cli, cmd/server, cmd/mcp-server, and cmd/baseline; all use mock HTTP servers or fake binaries and avoid live network dependencies.
  • CLI fix: runScan() now returns an int instead of calling os.Exit(), making exit-code behavior fully testable and correctly propagating policy violations.
  • CI templates: Two of the five new platform templates have defects \u2014 the CircleCI exit-code capture pattern is broken under set -e, and the CodeBuild spec pins Go 1.21 while go.mod requires 1.25.0.

Confidence Score: 4/5

Safe to merge for the Go source and test changes; two new CI templates for CircleCI and AWS CodeBuild have defects that would affect any team adopting them.

The core Go changes (runScan refactor, kev test helpers, test files) are correct and well-tested. The CodeBuild template fails to compile on the first run due to a Go version mismatch, and the CircleCI template silently loses the security gate if a user works around the failing step.

ci/circleci/config.example.yml (broken exit-code capture) and ci/aws-codebuild/buildspec.yml (Go version mismatch with go.mod).

Important Files Changed

Filename Overview
cmd/cli/main.go Fixed runScan() to return an int exit code instead of calling os.Exit() directly, making the function testable and correctly propagating policy violations.
pkg/kev/kev.go Changed cisaKEVURL from const to var and added exported SetURLForTest/ResetForTest helpers for cross-package mock injection; HTTP status check is still absent (previously flagged).
.github/workflows/ci.yml New CI pipeline adding go vet, race-detector tests for ./pkg/... ./cmd/..., and a conditional integration job; previously flagged bugs (cmd/... missing, changed_files integer) are both fixed here.
ci/circleci/config.example.yml New CircleCI template with a broken exit-code capture pattern: echo $? after docker run is dead code under CircleCI's default set -e, so the propagation step always exits 0 if users work around the failing step.
ci/aws-codebuild/buildspec.yml New CodeBuild template hardcodes golang: 1.21 but go.mod now requires 1.25.0; the from-source build path fails immediately with a toolchain version error.
pkg/kev/kev_test.go 13 tests covering cache TTL, concurrent safety, case normalization, and HTTP error paths using mock HTTP servers; thorough and race-detector clean.
cmd/cli/main_test.go 7 exit-code tests using fake Trivy binaries in PATH; covers policy enforcement, Trivy errors, offline KEV skip, and SBOM/Dockerfile merge paths.
pkg/remediate/enrich_integration_test.go 9 integration tests wiring real Enrich() to a mock KEV server; verifies exploit flagging, severity upgrades, ransomware detection, and policy gating end-to-end.
cmd/server/main_test.go 8 HTTP handler tests covering health endpoint, SSE content-type, input validation, concurrency guard, and summary counting.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant Test as Test
  participant CLI as runScan()
  participant Scanner as pkg/scanner
  participant KEV as pkg/kev
  participant Enricher as pkg/remediate
  participant Policy as pkg/policy
  participant Report as pkg/report

  Test->>CLI: runScan(ctx, opts)
  CLI->>Scanner: Scan(ctx, ScanOptions)
  Scanner-->>CLI: []Finding
  alt "offline=false"
    CLI->>Enricher: Enrich(findings, false)
    Enricher->>KEV: Load() mock HTTP
    KEV-->>Enricher: CVE map
    Enricher-->>CLI: enriched
  else "offline=true"
    CLI->>Enricher: Enrich(findings, true)
    Enricher-->>CLI: enriched
  end
  CLI->>Report: Generate(enriched)
  Report-->>CLI: files written
  CLI->>Policy: EvaluateFailPolicy
  alt violated
    Policy-->>CLI: "shouldFail=true"
    CLI-->>Test: return 1
  else ok
    Policy-->>CLI: "shouldFail=false"
    CLI-->>Test: return 0
  end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
  participant Test as Test
  participant CLI as runScan()
  participant Scanner as pkg/scanner
  participant KEV as pkg/kev
  participant Enricher as pkg/remediate
  participant Policy as pkg/policy
  participant Report as pkg/report

  Test->>CLI: runScan(ctx, opts)
  CLI->>Scanner: Scan(ctx, ScanOptions)
  Scanner-->>CLI: []Finding
  alt "offline=false"
    CLI->>Enricher: Enrich(findings, false)
    Enricher->>KEV: Load() mock HTTP
    KEV-->>Enricher: CVE map
    Enricher-->>CLI: enriched
  else "offline=true"
    CLI->>Enricher: Enrich(findings, true)
    Enricher-->>CLI: enriched
  end
  CLI->>Report: Generate(enriched)
  Report-->>CLI: files written
  CLI->>Policy: EvaluateFailPolicy
  alt violated
    Policy-->>CLI: "shouldFail=true"
    CLI-->>Test: return 1
  else ok
    Policy-->>CLI: "shouldFail=false"
    CLI-->>Test: return 0
  end
Loading

Comments Outside Diff (3)

  1. .github/workflows/ci.yml, line 49-51 (link)

    P2 Trivy install script fetched from floating main branch

    The install script URL uses main/contrib/install.sh, which is a moving target — any future commit to that branch can change the script's behavior. Only the Trivy binary version (v0.69.1) is pinned. Pinning the script to a specific release tag or commit hash (e.g. https://raw.githubusercontent.com/aquasecurity/trivy/v0.69.1/contrib/install.sh) is a simple safeguard against unexpected upstream changes.

    Fix in Claude Code Fix in Cursor

  2. pkg/kev/kev.go, line 72-79 (link)

    P1 Missing HTTP status check — silent empty-catalog caching

    Load() never inspects resp.StatusCode. If the CISA endpoint returns a 4xx/5xx with a parseable JSON body — e.g. a CDN that returns {} or {"vulnerabilities":[]} on error — json.Decode succeeds, knownExploited is set to a non-nil empty map, and lastFetch is stamped. On every subsequent call for the next 24 hours the cache-hit guard (knownExploited != nil && …) returns immediately, so all CVEs are silently treated as "not exploited" with no error surfaced to the caller.

    Add a status check immediately after resp.Body.Close deferral:

    if resp.StatusCode != http.StatusOK {
        return fmt.Errorf("KEV endpoint returned HTTP %d", resp.StatusCode)
    }

    Fix in Claude Code Fix in Cursor

  3. ci/aws-codebuild/buildspec.yml, line 183-186 (link)

    P1 Go runtime version 1.21 is incompatible with go.mod requiring 1.25.0

    go.mod was bumped to go 1.25.0 in this PR (required by the MCP SDK). When CodeBuild provisions golang: 1.21 and then runs go build ./cmd/scanner, the toolchain rejects the module with an error like note: module requires Go >= 1.25.0. The build fails immediately. This line should be updated to match the minimum version in go.mod.

    Fix in Claude Code Fix in Cursor

Fix All in Claude Code Fix All in Cursor

Reviews (6): Last reviewed commit: "Update COMPARISON and README to reflect ..." | Re-trigger Greptile

claude added 4 commits June 30, 2026 17:43
…sion, CI

- pkg/kev: add kev_test.go (13 tests) covering Load, cache TTL,
  concurrent safety, IsKnownExploited case normalisation, GetInfo,
  HTTP errors, and malformed JSON. Change cisaKEVURL from const to var
  so tests can inject a mock server (same pattern as pkg/osv).

- pkg/scanner: add oracle fixture (testdata/trivy-fixture.json) pinned
  to alpine:3.10 with 5 known CVEs across severity levels. Add
  parse_test.go (9 tests) exercising JSON parsing, severity distribution,
  FilePath resolution (PkgPath vs target fallback), remediation links,
  severity filtering, and boundary error cases (rootfs-not-dir,
  rootfs-nonexistent, SBOM-requires-image) — all without needing Trivy.

- pkg/runc: extend runc_test.go with 5 HostVersion tests using the
  fake-binary-in-PATH pattern — covers Server.Components JSON, top-level
  Components JSON, runc fallback, malformed docker JSON fallback, and the
  neither-available graceful empty-return path.

- .github/workflows/ci.yml: add CI pipeline — go vet + race-detected
  unit tests + build on every push/PR; integration scan on push to main.

All 8 pkg/* test packages pass with -race -count=1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
… pipeline

pkg/kev: export SetURLForTest and ResetForTest so cross-package tests can
inject a mock KEV server and clear cache state without duplicating
package internals.

pkg/report/report_test.go: add 10 new tests —
  SARIF structural validation (required fields: $schema, version, runs,
  tool.driver name/version/informationUri, result ruleId/level/message),
  location inclusion/omission based on FilePath, rule deduplication with
  multi-finding same CVE, empty-findings SARIF validity.
  WriteFindingsCSVWithImage: header row, image-as-first-column, RFC 4180
  quote escaping for embedded quotes/commas, empty-input guard.

pkg/remediate/enrich_integration_test.go: new file with 9 tests wiring
real Enrich() against a mock KEV HTTP server —
  KEV hit → Exploitable="yes" and ExploitInfo populated from shortDesc,
  KEV hit → severity upgraded to CRITICAL regardless of original severity,
  KEV miss → Exploitable="no",
  ransomware=Known → ExploitInfo contains "ransomware" text,
  offline=true → Exploitable="unknown" without touching KEV.
  Policy+enrichment pipeline: CRITICAL findings trigger fail-on-severity,
  zero findings do not trigger, fail-on-count threshold arithmetic,
  combined severity+count policy evaluated correctly.

All 8 pkg/* packages pass with -race -count=1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
…ng docs

cmd/cli/main.go: refactor runScan to return int exit code instead of
calling os.Exit directly. main() now calls os.Exit(runScan(...)). This
minimal change makes the entire CLI orchestration layer testable without
subprocess overhead or exec.Command tricks.

cmd/cli/main_test.go: 7 new tests using a fake trivy binary in PATH —
  fail-on-severity=CRITICAL + CRITICAL finding → exit 1
  fail-on-severity=CRITICAL + no findings → exit 0
  Trivy exits non-zero → exit 1
  fail-on-count=CRITICAL:1 with 1 finding → exit 1
  fail-on-count=CRITICAL:5 with 1 finding → exit 0
  reports written to disk (sarif, markdown, html)
  validLXCName path-traversal rejection

docs/architecture-diagrams.md: full rewrite —
  Architecture diagram: adds runc advisory, KEV/OSV enrichment nodes,
  CSV/SBOM outputs, MCP server topology, air-gapped topology.
  Scan workflow sequence diagram: adds runc advisory, KEV/OSV calls,
  offline mode branch, severity upgrade in enricher.
  NEW Network diagram: external services (registry, Trivy DB, CISA KEV,
  OSV.dev, NVD) with online vs offline annotation.
  Deployment topologies: adds MCP/AI assistant and air-gapped topologies.
  NEW Test coverage map: diagrams which test tier covers which component
  and what remains untested (server, mcp-server, baseline, IDE).

docs/testing.md: full rewrite — documents all test types (including new
  CLI exit-code and enrichment integration tiers), oracle fixture, race
  detector usage, per-package test coverage table, complete command
  reference, CI pipeline details.

docs/sanity.md: updated — adds CLI test step (7), build server step (5),
  CI check note, SARIF import / Web UI / MCP manual steps.

docs/LESSONS-LEARNED.md: appends testing strategy session entry.

All 9 test packages (8 pkg/* + cmd/cli) pass with -race -count=1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
…r mcp-sdk

- cmd/server/main_test.go: 8 tests covering handleHealth, buildSummary
  (empty + count accuracy), SSE input validation (missing params, both
  image+fs, invalid image ref, relative fs path, concurrency guard),
  and SSE Content-Type header — all via httptest.NewRecorder, no network.

- cmd/mcp-server/main_test.go: 6 tests for parseSeverities (empty
  default, custom input, case normalisation) and formatSummary (empty,
  count accuracy, severity order, unknown/empty severity bucket).
  Tests pure helper functions only — no MCP SDK import in test file.

- cmd/baseline/main_test.go: 7 tests for loadImages (strips comments
  and blanks, missing file, empty file), csvEscape (RFC-4180 quoting),
  writeFindingsMarkdown (content + empty case), writeDashboardHTML (HTML
  output + image name present). All use t.TempDir() — no leftover files.

- go.mod / go.sum: run `go get github.com/modelcontextprotocol/go-sdk/mcp`
  to resolve pre-existing missing go.sum entry; upgrades Go toolchain
  requirement to 1.25 (SDK minimum). All existing tests remain green.

- docs: update test-coverage map in architecture-diagrams.md (three
  "not yet" nodes promoted to "tested"), add three new rows to
  testing.md test-types table, append lessons-learned entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml
claude added 2 commits July 1, 2026 04:04
1. Add ./cmd/... to the unit-test step so cmd/cli, cmd/server,
   cmd/mcp-server, and cmd/baseline tests actually run in CI.
   Previously only ./pkg/... was covered, leaving all new cmd/*
   test files unexecuted in automation.

2. Fix integration job's if: condition — github.event.pull_request
   .changed_files is an integer count, not a path list, so
   contains(..., 'pkg/scanner') always evaluated false on PRs.
   Simplified to push-to-main only; a path-scoped PR trigger
   requires tj-actions/changed-files.

3. Pin Trivy install script URL to the tagged release (v0.69.1)
   instead of the floating main branch, so the script version
   matches the pinned binary version.

Also adds a clarifying comment on SetURLForTest/ResetForTest in
pkg/kev/kev.go explaining the ForTest naming convention for
cross-package test injection (these can't move to _test.go since
pkg/remediate's integration tests call them externally).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
…coverage

- README: bump Go badge and version references from 1.21+ to 1.25+;
  add CI status badge; note cmd/* test coverage in testing table
- docs/getting-started.md, docs/HELP.md: Go 1.21 → 1.25
- docs/sanity.md: consolidate pkg/... and cmd/... into a single test
  step; update CI check note to match
- docs/testing.md: update all test commands to include ./cmd/...;
  fix CI pipeline code block to match actual ci.yml
- docs/architecture-diagrams.md: update integration job label to
  "push-to-main" (was "nightly"); promote cmd/server, cmd/mcp-server,
  cmd/baseline from "not yet" to tested subgraph
- docs/LESSONS-LEARNED.md: append code-review entry (PR #2 CI bugs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

claude added 3 commits July 1, 2026 05:04
Phase 1 tests (cmd/cli/main_test.go):
- TestRunScan_dockerfileFindingsMerged: verifies --dockerfile scan path
  merges DS002 misconfig into report.md (was completely untested)
- TestRunScan_sbomWritten: verifies --sbom produces report.cdx.json with
  CycloneDX content (was untested)
- TestRunScan_offlineSkipsKEV: verifies --offline=true produces zero HTTP
  hits to the CISA KEV endpoint (uses httptest recording server +
  kev.SetURLForTest injection)

CI/CD integrations (ci/ + docs/ci/):
- CircleCI: two-job workflow (build → scan), workspace file passing,
  artifacts; docs with variable table and PR comment tip
- AWS CodeBuild: four-phase buildspec.yml with ECR push/pull, S3 artifact
  upload, Security Hub SARIF import snippet; docs with IAM policy
- Google Cloud Build: cloudbuild.yaml with Artifact Registry, GCS artifact
  upload, substitution variable reference; docs with API enablement steps
- Bitbucket Pipelines: Docker service, default + custom on-demand pipeline,
  artifacts; docs with Repository Variables table and manual trigger curl
- Tekton: v1 Task + Pipeline CRDs (Kaniko build → scan → notify),
  PVC workspace, registry Secret injection; docs with tkn CLI log commands

docs/ci/README.md updated with all 9 platforms grouped by type.
docs/architecture-diagrams.md gains CI/CD ecosystem flowchart showing all
supported platforms feeding into the scanner and report outputs.

All 12 packages pass: go test -race -count=1 ./pkg/... ./cmd/...

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
pkg/runc: pre-release versions like 1.2.8-rc1 are now correctly flagged
as vulnerable. Per semver, X.Y.Z-pre < X.Y.Z, so a host on an RC of the
fixed version still needs patching. Detected by checking for '-' immediately
after the semver match in the version string.

cmd/cli: remove custom min() helper — shadowed the built-in added in Go 1.21;
redundant since go.mod now requires Go 1.25.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
COMPARISON.md: three places that said "Azure, GitHub, GitLab, Jenkins"
now list all nine supported platforms (added CircleCI, AWS CodeBuild,
GCB, Bitbucket Pipelines, Tekton). Feature table row, Strengths section,
and Summary table all updated.

README.md: Added "More platforms" table in the CI/CD Integration section
linking to all five new templates and guides. Updated repo directory
listing to include all nine platforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189QVFiKNFT5MEsskeEi19t
@beejak beejak merged commit c9c5d64 into main Jul 1, 2026
3 checks passed
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.

2 participants