Files
orca/docs/security-scanning.md
T
Jon Chery df58bc25a3 docs(milestone): complete scheduling-streaming (v0.3)
---ci---
project: orca
phase: 3
milestone: v0.3
status: complete
requirements:
  covered: [REQ-022, REQ-030, REQ-032]
  partial: []
---/ci---

v0.3 milestone merged to main. Includes all v0.2 work (P08-P10) that
was previously on the milestone branch but not yet merged to main, plus
the v0.3 completion work (iter.Seq streaming + doctor network/db).

v0.2 phases included: P08 (mTLS), P09 (scheduling), P10 (security scan).
v0.3 phases: P0 (pre-execution), P1 (iter.Seq streaming), P2 (doctor),
P3 (final review+ship).

Total: 40 requirements, all complete. No new go.mod dependencies.
Full test suite passes under -race. gofmt + go vet clean.
2026-08-01 20:06:47 +00:00

6.0 KiB

Security Scanning in Orca

This document describes the three security scanning tools integrated in v0.2 P03 (Phases 10): gosec, govulncheck, and gitleaks. All three run in the .coreci.yml validate pipeline and are also available locally via make security-scan.

TL;DR

# Run all three tools locally (silently skips tools not on PATH).
make security-scan

# Strict mode: require all three to be installed.
./scripts/security_scan.sh --strict

The .coreci.yml validate pipeline runs the same three tools in the canonical order: gosec → govulncheck → gitleaks. A failure at any stage blocks merges to main.

Tools

gosec

gosec is a static analyzer for Go that catches common security smells: hardcoded credentials (G101), SQL injection (G201), weak random (G404), insecure TLS (G402), etc.

Configuration: gosec -fmt text -quiet ./... — text output, quiet mode (only summary + findings). The plan calls for an empty gosec.json baseline at the start; new G101 findings fail the build.

What gets caught:

  • G101: hardcoded credentials (e.g., apiKey := "abc123")
  • G102: bind to all interfaces (0.0.0.0)
  • G201/G202: SQL string concatenation
  • G404: weak random number generator (math/rand instead of crypto/rand)
  • G501-G505: weak crypto primitives

Exclusions: _test.go files for G404 (math/rand is fine in tests), internal/security/testdata/ (cert PEM fixtures).

govulncheck (offline mode, REQ-027)

govulncheck walks the dependency graph and reports known CVEs in modules you actually call. REQ-027 requires offline mode — the default invocation calls vuln.go.dev to fetch the latest vulnerability database. To honor offline-first:

  • GOFLAGS=-mod=mod forces module mode (avoids surprise network fetches during the build).
  • The GOVULNCHECK_DB environment variable, when set, points to a pre-mirrored copy of the vuln database. The CI image bundles a daily-mirrored DB at /var/lib/orca/vulndb/. Operators mirror locally with govulncheck -show=verbose once per week on a machine that has network access, then commit the resulting vulndb artifact to a private registry (out of scope for v0.2 OSS; documented as a follow-up).
  • Until the mirror is in place, govulncheck -mode binary ./... uses its bundled DB. The bundled DB is updated on every govulncheck release; in CI we pin to v1.1.3 for reproducibility.

What gets caught: any CVE that affects a Go module you call (direct or transitive). Output is the govulncall symbol + CVE ID.

gitleaks (REQ-039)

gitleaks scans the working tree (and git history, if asked) for hardcoded secrets: API keys, private keys, tokens, passwords. REQ-039 specifies a project-local .gitleaks.toml to allowlist -----BEGIN CERTIFICATE----- PEM blocks (which are not secrets) while still flagging -----BEGIN RSA PRIVATE KEY----- and similar.

Configuration:

  • .gitleaks.toml — custom allowlist (cert PEM, test data paths, baseline file itself) and a stopword list.
  • .gitleaks-baseline.json — REQ-029. Suppresses the pre-existing .env SHA-1 leak from v0.1 history (rotated forward; the baseline gates future re-leaks of the same SHA).
  • Pre-commit hook (.githooks/pre-commit) — runs gitleaks protect --staged on every commit. Commits are still allowed when gitleaks is not installed (the if command -v gate is in the hook).

Pipeline Integration

.coreci.yml validate pipeline:

- name: gosec
  image: golang:1.25
  commands:
    - go install github.com/securego/gosec/v2/cmd/gosec@v2.18.2
    - gosec -fmt text -quiet ./...

- name: govulncheck
  image: golang:1.25
  env:
    GOFLAGS: -mod=mod
  commands:
    - go install golang.org/x/vuln/cmd/govulncheck@v1.1.3
    - govulncheck -mode binary ./...

- name: gitleaks
  image: golang:1.25
  commands:
    - apk add --no-cache curl
    - sh -c "$(curl -fsSL https://github.com/gitleaks/gitleaks/releases/latest/download/install.sh)"
    - gitleaks detect --source . --config .gitleaks.toml --baseline-path .gitleaks-baseline.json --no-banner

The test pipeline runs with -race (REQ-031):

- name: test
  image: golang:1.25
  commands:
    - go test -race -coverprofile=coverage.out ./...
    - go tool cover -func=coverage.out | tail -1

Local development

# Install the three tools (one-time).
go install github.com/securego/gosec/v2/cmd/gosec@v2.18.2
go install golang.org/x/vuln/cmd/govulncheck@v1.1.3
# gitleaks: see https://github.com/gitleaks/gitleaks#installation

# Run all three.
make security-scan

# Run with strict mode (all three required).
./scripts/security_scan.sh --strict

Adding a baseline entry

If a new (intentional) finding appears:

  1. gosec: regenerate the baseline with gosec -fmt json -no-fail ./... > gosec.json. Inspect for false positives; document the suppression in the JSON's suppressions field.
  2. govulncheck: wait for the upstream fix; if you must pin a vulnerable dep, document the pin in a //nolint:govulncheck comment and create a tracking issue.
  3. gitleaks: add a fingerprint to .gitleaks-baseline.json with gitleaks detect --baseline-path .gitleaks-baseline.json --report-path new-findings.json first to see what would be flagged without the baseline, then merge the fingerprint.

Why offline mode matters

Default govulncheck calls vuln.go.dev on every run. That violates REQ-003 (offline-first). The fix in P03 is:

  1. GOFLAGS=-mod=mod ensures module mode (no surprise module downloads).
  2. The pre-mirrored DB mechanism is a follow-up; the bundled DB in the pinned govulncheck binary is the immediate fallback.
  3. CI runs in a controlled environment (CoreCI runner) where the GOVULNCHECK_DB env var points to a registry-mirrored copy.

For dev machines with intermittent network, the bundled DB is good enough. For air-gapped CI runners, set GOVULNCHECK_DB to a known-good DB file.