Skip to content

Security

Security #1006

Workflow file for this run

name: Security
on:
pull_request:
branches:
- main
paths:
- 'bun.lock'
- 'package.json'
- 'services/*/package.json'
# Dockerfile + dockerignore changes alter what trivy's misconfig
# scanner sees on the fs-scan path; .trivyignore.yaml changes can
# silently un-suppress findings. Round-2 R2-B11 found this branch
# added new Dockerfiles + a trivyignore without re-triggering the
# security scan — PRs went out blind.
- 'services/*/Dockerfile'
- 'services/*/Dockerfile.dockerignore'
- '.trivyignore.yaml'
- '.github/workflows/security.yml'
push:
branches:
- main
paths:
- 'bun.lock'
- 'package.json'
- 'services/*/package.json'
- 'services/*/Dockerfile'
- 'services/*/Dockerfile.dockerignore'
- '.trivyignore.yaml'
- '.github/workflows/security.yml'
schedule:
- cron: '0 3 * * 1' # Monday 03:00 UTC
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ---------------------------------------------------------------------------
# Audit JS dependencies for known advisories.
# Reports the full advisory list (all severities) in the job summary, then a
# gate fails the build on HIGH or CRITICAL production advisories. If an
# advisory has no fixed release, suppress it explicitly: a dated accept-risk
# entry in .trivyignore.yaml for the Trivy gate, and --ignore=<advisory-id>
# on the gate command below for bun audit.
# ---------------------------------------------------------------------------
bun-audit:
name: Bun audit
if: github.event.pull_request.draft != true
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.12'
- name: Run bun audit (report)
# Full advisory list for visibility; does not fail the build itself.
continue-on-error: true
run: bun audit --prod | tee bun-audit.txt
- name: Summary
if: always()
run: |
echo "## Bun audit" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
tail -n 50 bun-audit.txt >> "$GITHUB_STEP_SUMMARY" || true
echo '```' >> "$GITHUB_STEP_SUMMARY"
- name: Gate on high/critical advisories
# The blocking gate: fail on HIGH or CRITICAL production advisories.
# pipefail so tee does not mask a non-zero exit.
run: |
set -o pipefail
bun audit --prod --audit-level=high | tee bun-audit-gate.txt
# ---------------------------------------------------------------------------
# Scan source tree for known CVEs in dependencies + misconfigurations + secrets.
# Uploads SARIF to GitHub Security (all scanners, HIGH+CRITICAL, informational),
# then a vuln-only gate fails the build on FIXABLE HIGH or CRITICAL dependency
# CVEs. Secret/misconfig findings stay informational (SARIF) to avoid blocking
# on scanner noise. Unlike bun audit, this scans the whole tree (bun.lock,
# every workspace) with no prod/dev split; unfixable findings are suppressed
# via dated accept-risk entries in .trivyignore.yaml.
# ---------------------------------------------------------------------------
trivy-fs:
name: Trivy filesystem scan
if: github.event.pull_request.draft != true
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Run Trivy filesystem scan
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-fs.sarif'
severity: 'HIGH,CRITICAL'
exit-code: '0'
scanners: 'vuln,secret,misconfig'
ignore-unfixed: true
# Per-path misconfig suppressions live in .trivyignore.yaml; the
# plain .trivyignore is auto-detected but cannot scope by path.
trivyignores: '.trivyignore.yaml'
# Skip handlebars Dockerfile templates: handlebars syntax confuses
# the misconfig scanner. The generated Dockerfiles are scanned
# downstream when each service runs its own build.
skip-files: 'tools/plop/templates/**/Dockerfile.hbs'
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4
if: always()
with:
sarif_file: 'trivy-fs.sarif'
category: 'trivy-fs'
# Blocking gate: fail the build on FIXABLE high or critical dependency
# vulnerabilities. Runs vuln-only (secret/misconfig stay informational via
# the SARIF upload above) and honours the same path-scoped ignore file, so
# a real, patchable high/critical CVE cannot merge silently.
- name: Trivy vulnerability gate (HIGH/CRITICAL)
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: 'fs'
scan-ref: '.'
format: 'table'
severity: 'HIGH,CRITICAL'
exit-code: '1'
scanners: 'vuln'
ignore-unfixed: true
trivyignores: '.trivyignore.yaml'
skip-files: 'tools/plop/templates/**/Dockerfile.hbs'
- name: Summary
if: always()
run: |
echo "## Trivy filesystem scan complete" >> "$GITHUB_STEP_SUMMARY"
echo "Results uploaded to the Security tab under category \`trivy-fs\`." >> "$GITHUB_STEP_SUMMARY"