mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 19:41:30 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Executable
+57
@@ -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"
|
||||
@@ -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
|
||||
]
|
||||
Reference in New Issue
Block a user