Merge branch 'fix/bundle-7-tool-suite-execution' (Bundle 7: Verification & Tool Suite Execution, ~5 audit findings closed + 4 new IDs)

This commit is contained in:
Shankar
2026-04-26 14:37:36 +00:00
5 changed files with 308 additions and 0 deletions
+46
View File
@@ -42,8 +42,31 @@ jobs:
run: go install golang.org/x/vuln/cmd/govulncheck@latest
- name: Run govulncheck
# Bundle-7 / D-001 partial: govulncheck distinguishes called-vs-uncalled
# advisories. Default exit code is non-zero only when YOUR code calls
# the vulnerable function — deferred-call advisories show up in the
# output but don't fail the gate. See .govulnignore for the
# suppression contract if a triaged false-positive needs to be muted.
run: govulncheck ./...
- name: Install staticcheck (Bundle-7 / D-001)
run: go install honnef.co/go/tools/cmd/staticcheck@latest
- name: Run staticcheck
# Bundle-7 / D-001: Go static analysis additive to vet. Suppressed
# rules live in staticcheck.conf with documented justifications;
# adding a new entry requires an explicit security review.
#
# SOFT gate (continue-on-error: true) until M-028 closes the 6
# remaining SA1019 deprecated-API sites:
# - cmd/server/main_test.go × 3: middleware.NewAuth → NewAuthWithNamedKeys
# - internal/api/handler/scep.go: csr.Attributes → Extensions
# - internal/connector/issuer/local/local.go: elliptic.Marshal → crypto/ecdh
# When M-028 ships, flip continue-on-error to false to make this
# a hard gate. Until then, the step still annotates findings on PRs.
continue-on-error: true
run: staticcheck ./...
- name: Forbidden auth-type literal regression guard (G-1)
# G-1 closed the JWT silent auth downgrade by removing "jwt" from the
# accepted CERTCTL_AUTH_TYPE values. This step grep-fails the build
@@ -590,6 +613,17 @@ jobs:
CRYPTO_COV=$(go tool cover -func=coverage.out | grep 'internal/crypto' | awk '{print $NF}' | sed 's/%//' | awk '{sum+=$1; n++} END {if(n>0) printf "%.1f", sum/n; else print "0"}')
echo "Crypto package coverage: ${CRYPTO_COV}%"
# Bundle-7 / Audit H-005 — extended crypto-cluster gates per CLAUDE.md.
# internal/pkcs7/ is at 100% at HEAD (encoder-only, exhaustively tested
# via Bundle-4 fuzz targets + unit tests). internal/connector/issuer/local/
# is at 68.3% at HEAD; H-010 tracks the gap and will lift this floor
# to 85% once the missing CSR-validation + CA-cert-loading tests land.
PKCS7_COV=$(go tool cover -func=coverage.out | grep 'internal/pkcs7' | awk '{print $NF}' | sed 's/%//' | awk '{sum+=$1; n++} END {if(n>0) printf "%.1f", sum/n; else print "0"}')
echo "PKCS7 package coverage: ${PKCS7_COV}%"
LOCAL_ISSUER_COV=$(go tool cover -func=coverage.out | grep 'internal/connector/issuer/local' | awk '{print $NF}' | sed 's/%//' | awk '{sum+=$1; n++} END {if(n>0) printf "%.1f", sum/n; else print "0"}')
echo "Local-issuer coverage: ${LOCAL_ISSUER_COV}%"
# Fail if thresholds not met
if [ "$(echo "$SERVICE_COV < 55" | bc -l)" -eq 1 ]; then
echo "::error::Service layer coverage ${SERVICE_COV}% is below 55% threshold"
@@ -611,6 +645,18 @@ jobs:
echo "::error::Crypto package coverage ${CRYPTO_COV}% is below 85% threshold"
exit 1
fi
# Bundle-7 / H-005: pkcs7 hard gate (currently 100% — protects regressions).
if [ "$(echo "$PKCS7_COV < 85" | bc -l)" -eq 1 ]; then
echo "::error::PKCS7 package coverage ${PKCS7_COV}% is below 85% threshold"
exit 1
fi
# Bundle-7 / H-005 / H-010: local-issuer SOFT gate at 65% — H-010
# tracks the gap from 68.3% (HEAD) → 85% (CLAUDE.md target). Once
# H-010's missing test cases land, raise this floor to 85.
if [ "$(echo "$LOCAL_ISSUER_COV < 65" | bc -l)" -eq 1 ]; then
echo "::error::Local-issuer coverage ${LOCAL_ISSUER_COV}% is below 65% transitional floor (H-010 will raise to 85%)"
exit 1
fi
echo "Coverage thresholds passed!"
- name: Upload Coverage Report
+149
View File
@@ -0,0 +1,149 @@
name: security-deep-scan
# Bundle-7 / Audit D-001..D-007:
# Slow / containerized scans on a daily schedule + manual dispatch.
# Per-PR fast gates live in ci.yml; this workflow runs the heavyweight
# tools that need docker, network egress to scanner registries, or
# longer wall-clock budgets than a per-PR check tolerates.
#
# Scope:
# trivy image container CVE + secret scan
# syft SBOM CycloneDX SBOM artefact upload
# ZAP baseline DAST baseline against a live deploy_test stack
# nuclei template-based vuln scan against the same stack
# schemathesis OpenAPI fuzz against the running server
# testssl.sh TLS configuration audit
# race detector x10 full -count=10 race run on the entire test suite
# gosec Go security static analysis (slow first run)
#
# Each step is best-effort — failures are uploaded as artefacts but do
# NOT block the workflow. Triage happens via the Bundle-7 receipt
# directory under cowork/comprehensive-audit-2026-04-25/tool-output/.
on:
schedule:
- cron: '0 6 * * *' # daily 06:00 UTC
workflow_dispatch: {}
permissions:
contents: read
security-events: write # SARIF upload to GitHub code scanning
jobs:
deep-scan:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25'
- name: Install Go-based tools
run: bash scripts/install-security-tools.sh
continue-on-error: true
# --- Static analysis (slow paths) ---
- name: gosec
run: |
$(go env GOPATH)/bin/gosec -fmt sarif -out gosec.sarif ./... || true
continue-on-error: true
- name: osv-scanner (multi-ecosystem CVE)
run: |
$(go env GOPATH)/bin/osv-scanner -r --format json --output osv-scanner.json . || true
continue-on-error: true
# --- Race detector at -count=10 (D-002) ---
- name: go test -race -count=10 (full suite)
run: |
go test -race -count=10 -short ./... 2>&1 | tee go-test-race.txt
continue-on-error: true
# --- Coverage receipts for crypto cluster (H-005) ---
- name: go test -cover (crypto cluster)
run: |
go test -cover -covermode=atomic \
./internal/crypto/... \
./internal/pkcs7/... \
./internal/connector/issuer/local/... \
2>&1 | tee go-test-cover.txt
# --- Container + supply chain (D-001 partial, D-006 partial) ---
- name: Build certctl image
run: docker build -t certctl:deep-scan .
continue-on-error: true
- name: trivy image scan
run: |
docker run --rm -v "$PWD":/src aquasec/trivy:latest image \
--format json --output /src/trivy.json certctl:deep-scan || true
continue-on-error: true
- name: syft SBOM
run: |
docker run --rm -v "$PWD":/src anchore/syft:latest dir:/src \
-o cyclonedx-json > syft.cyclonedx.json || true
continue-on-error: true
# --- DAST against a live stack (D-004) ---
- name: docker compose up (test stack)
run: |
docker compose -f deploy/docker-compose.yml up -d
sleep 20
continue-on-error: true
- name: ZAP baseline
uses: zaproxy/action-baseline@v0.10.0
with:
target: 'https://localhost:8443'
continue-on-error: true
- name: schemathesis (OpenAPI fuzz)
run: |
pip install schemathesis
schemathesis run --base-url https://localhost:8443 \
--hypothesis-max-examples=50 api/openapi.yaml || true
continue-on-error: true
- name: nuclei
run: |
docker run --rm --network host projectdiscovery/nuclei:latest \
-u https://localhost:8443 -j -o nuclei.json || true
continue-on-error: true
# --- TLS audit (D-005) ---
- name: testssl.sh
run: |
docker run --rm -v "$PWD":/data drwetter/testssl.sh:latest \
--jsonfile /data/testssl.json https://localhost:8443 || true
continue-on-error: true
- name: docker compose down
run: docker compose -f deploy/docker-compose.yml down || true
if: always()
# --- Upload everything as artefacts ---
- name: Upload deep-scan receipts
uses: actions/upload-artifact@v4
if: always()
with:
name: security-deep-scan-${{ github.run_id }}
path: |
gosec.sarif
osv-scanner.json
go-test-race.txt
go-test-cover.txt
trivy.json
syft.cyclonedx.json
nuclei.json
testssl.json
retention-days: 30
+21
View File
@@ -0,0 +1,21 @@
# Bundle-7 / Audit D-001 / govulncheck suppressions.
#
# Format: one OSV ID per line, with a comment justifying the suppression.
# Every entry needs:
# - the OSV ID (GO-YYYY-NNNN)
# - one-line "what is it"
# - one-line "why we're not affected" (must reference call-graph evidence)
# - "review-by" date (YYYY-MM-DD) — re-triage on/after this date
#
# Triage rule: only suppress an advisory if `govulncheck ./...` (NOT
# verbose) reports it as a deferred-call vulnerability ("packages you
# import" or "modules you require", not "Your code is affected by").
#
# At Bundle-7 time (2026-04-26): the 5 advisories surfaced are all in
# transitive deps and govulncheck confirms our code does not call them.
# Documented here for tracking; no entries needed because the default
# fail-on-non-zero gate already passes (govulncheck distinguishes
# called vs uncalled and only exits non-zero when the latter calls in).
#
# Example (do not enable unless the advisory becomes call-affected):
# GO-2026-4441 # transitive: golang.org/x/crypto pre-v0.40 — net/ssh terrapin downgrade; we don't use net/ssh; review 2026-07-01
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# Bundle-7 / Audit D-001:
# Idempotent installer for the §12 mandatory tool suite. Used locally
# during a Bundle-7-style run and by the CI workflows
# (.github/workflows/ci.yml + .github/workflows/security-deep-scan.yml).
#
# Tools installed via go install (host-Go, no docker required):
# govulncheck Go module CVE scan
# staticcheck Go static-analysis (additive to vet)
# errcheck unchecked-error finder
# ineffassign dead-assignment finder
# gosec Go security static-analysis (best-effort; large download)
# osv-scanner multi-ecosystem CVE scan
#
# Tools NOT installed by this script (containerized in CI workflows):
# semgrep, hadolint, trivy, syft, checkov, kube-score,
# schemathesis, OWASP ZAP, nuclei, testssl.sh
#
# Usage:
# bash scripts/install-security-tools.sh # default GOBIN
# GOBIN=/tmp/gobin bash scripts/install-security-tools.sh
#
# Exit codes:
# 0 every tool installed
# 1 one or more installs failed (script continues but reports at end)
set -uo pipefail
if ! command -v go >/dev/null 2>&1; then
echo "ERROR: go toolchain not on PATH" >&2
exit 1
fi
FAIL=0
install_tool() {
local pkg="$1"
local name
name="$(basename "${pkg%@*}")"
echo "=> install $name ($pkg)"
if ! go install "$pkg" 2>&1; then
echo "WARN: failed to install $name" >&2
FAIL=$((FAIL + 1))
fi
}
install_tool golang.org/x/vuln/cmd/govulncheck@latest
install_tool honnef.co/go/tools/cmd/staticcheck@latest
install_tool github.com/kisielk/errcheck@latest
install_tool github.com/gordonklaus/ineffassign@latest
install_tool github.com/securego/gosec/v2/cmd/gosec@latest
install_tool github.com/google/osv-scanner/cmd/osv-scanner@latest
if [ "$FAIL" -ne 0 ]; then
echo "WARNING: $FAIL tool(s) failed to install — partial coverage only" >&2
exit 1
fi
echo "OK: all 6 tools installed"
+35
View File
@@ -0,0 +1,35 @@
# Bundle-7 / Audit D-001 / staticcheck suppressions.
#
# Bundle-7 first-run dispositions:
#
# ST1005 (90 hits — error strings should not be capitalized)
# Style-only. Would require updating ~90 error strings across
# connectors/discovery for a cosmetic effect. Tracked for a future
# style-sweep bundle, not security-relevant. Suppressed below.
#
# S1009 (15 hits — should omit nil check; len() handles nil)
# Style-only. The redundant nil checks make the intent explicit
# for new-Go-engineer readers. Tracked for the same future sweep.
#
# S1011 (4 hits — could use append spread)
# Style-only.
#
# SA1019 (6 hits — deprecated API)
# Real engineering issue, NOT suppressed. Tracked as new audit
# finding M-028 for explicit migration:
# - cmd/server/main_test.go × 4: middleware.NewAuth → NewAuthWithNamedKeys
# - internal/api/handler/scep.go: csr.Attributes → Extensions
# - one more SA1019 hit elsewhere — see staticcheck.txt receipt.
#
# Configuration format: TOML.
checks = [
"all",
# Bundle-7 first-run dispositions:
"-ST1005", # error string capitalization — style-only (90 hits), future sweep
"-ST1000", # package comment — already-documented packages (126 hits), low value
"-ST1003", # naming convention — pre-existing, future style sweep (10 hits)
"-S1009", # redundant nil check — explicit-intent style (15 hits)
"-S1011", # append-spread — style-only (4 hits)
"-SA9003", # empty branch — guarded sites with `// no-op (...)` comments
]