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

170 lines
6.0 KiB
Markdown

# 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
```bash
# 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](https://github.com/securego/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](https://golang.org/x/vuln) 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](https://github.com/gitleaks/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:
```yaml
- 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):
```yaml
- name: test
image: golang:1.25
commands:
- go test -race -coverprofile=coverage.out ./...
- go tool cover -func=coverage.out | tail -1
```
## Local development
```bash
# 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.