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 (D-004) # nuclei template-based vuln scan against the same stack # schemathesis OpenAPI fuzz against the running server # testssl.sh TLS configuration audit (D-005) # race detector x10 full -count=10 race run on the entire test suite (D-002) # gosec Go security static analysis (slow first run) # go-mutesting mutation testing on crypto cluster (D-003) # semgrep p/react-security frontend XSS / dangerouslySetInnerHTML / target=_blank ruleset (D-007) # # Each step is best-effort — failures are uploaded as artefacts but do # NOT block the workflow. Triage happens via the Bundle-7 receipt # the project's comprehensive-audit tool-output directory. 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 # --- Mutation testing on crypto cluster (D-003) --- # # Operator runbook: docs/testing-strategy.md::Mutation testing. # Tool: go-mutesting (https://github.com/zimmski/go-mutesting). Each # package is mutated independently; the per-package summary line # (`The mutation score is X.YZ`) is grep-extracted into the receipt. # Acceptance threshold: ≥80% kill ratio per package; surviving # mutants get triaged in the project's comprehensive-audit notes/ # d003-mutation-results.md (per-mutant action item or # equivalent-mutation justification). - name: Install go-mutesting run: go install github.com/zimmski/go-mutesting/cmd/go-mutesting@latest continue-on-error: true - name: go-mutesting (crypto cluster) run: | : > go-mutesting.txt for pkg in ./internal/crypto/... ./internal/pkcs7/... ./internal/connector/issuer/local/...; do echo "=== $pkg ===" | tee -a go-mutesting.txt $(go env GOPATH)/bin/go-mutesting "$pkg" 2>&1 | tee -a go-mutesting.txt || true done continue-on-error: true # --- 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() # --- Frontend XSS / unsafe-link ruleset (D-007) --- # # Operator runbook: docs/testing-strategy.md::Frontend semgrep. # Bundle 8 already verified `dangerouslySetInnerHTML` count at # zero and the `target="_blank"` rel-noopener pin via grep # guards in ci.yml — semgrep p/react-security adds defence in # depth (it catches escape patterns the grep guards don't see, # e.g., href={user_input}, eval, document.write). - name: semgrep p/react-security (frontend) run: | docker run --rm -v "$PWD":/src returntocorp/semgrep:latest \ semgrep --config=p/react-security --json /src/web/src \ > semgrep-react.json 2>semgrep-react.stderr || true continue-on-error: true # --- 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 go-mutesting.txt trivy.json syft.cyclonedx.json nuclei.json testssl.json semgrep-react.json semgrep-react.stderr retention-days: 30