Security Scanning
Security scans in CI catch vulnerabilities, leaked secrets, and misconfigurations before they ship. SAST (CodeQL, Semgrep), SCA (Trivy, OSV, Snyk), secret scanners (gitleaks, TruffleHog), container scans, IaC scans (tfsec, Checkov), and DAST (ZAP) compose into a defense pipeline.
SAST, SCA, secrets, containers, IaC
EXAMPLE
# 1) The complete security pipeline — one workflow
# .github/workflows/security.yml
name: security
on:
push:
pull_request:
schedule: [{ cron: '0 6 * * *' }]
jobs:
# ─── 1. SECRETS — catch before commit ────────────────────
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: gitleaks/gitleaks-action@v2
- uses: trufflesecurity/trufflehog@main
with: { extra_args: --only-verified }
# ─── 2. SAST — static analysis on source code ─────────────
codeql:
runs-on: ubuntu-latest
permissions: { security-events: write }
strategy: { matrix: { language: [ javascript, python ] } }
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality
- uses: github/codeql-action/analyze@v3
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: returntocorp/semgrep-action@v1
with: { config: 'p/owasp-top-ten p/security-audit p/secrets' }
# ─── 3. SCA — dependency CVE scan ────────────────────────
deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npm audit --audit-level=high --omit=dev
- uses: google/osv-scanner-action@v1
with:
scan-args: |-
-r
--skip-git
--lockfile=./package-lock.json
# ─── 4. SBOM — bill of materials per build ────────────────
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anchore/sbom-action@v0
with: { format: cyclonedx-json, output-file: sbom.json }
- uses: actions/upload-artifact@v4
with: { name: sbom, path: sbom.json }
# ─── 5. CONTAINER scan ───────────────────────────────────
container:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp:$GITHUB_SHA .
- uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
severity: CRITICAL,HIGH
exit-code: '1'
ignore-unfixed: true
# Or for SBOM-based scan:
- uses: aquasecurity/trivy-action@master
with:
scan-type: 'sbom'
scan-ref: 'sbom.json'
severity: HIGH,CRITICAL
exit-code: '1'
# ─── 6. INFRASTRUCTURE-as-Code scan ──────────────────────
iac:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aquasecurity/tfsec-action@v1.0.3
- uses: bridgecrewio/checkov-action@master
with: { directory: ./terraform }
- uses: stackrox/kube-linter-action@v1.0.4
with: { directory: ./k8s }
# ─── 7. LICENSE compliance ────────────────────────────────
licenses:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
npx license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD-3-Clause;BSD-2-Clause' > licenses.txt
- if: failure()
uses: actions/upload-artifact@v4
with: { name: licenses, path: licenses.txt }
# ─── 8. DAST — dynamic scan against staging (scheduled) ──
dast:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: zaproxy/action-baseline@v0.12.0
with: { target: https://staging.example.com }
# 2) Severity policy — when to fail builds
# • CRITICAL / HIGH — fail the build, block merge
# • MEDIUM — file a ticket, allow merge
# • LOW / INFO — record, no action
#
# Adjust per repo / per ecosystem.
# 3) GitHub Advanced Security
# • CodeQL — included in public repos; paid for private
# • Secret scanning — included in public repos; addon for private + push protection
# • Dependency review action — diff-aware dep check on PRs
# • Code scanning — SARIF upload from any tool
- uses: actions/dependency-review-action@v4
with: { fail-on-severity: high }
# 4) Renovate / Dependabot — automated patch PRs
# .github/dependabot.yml
version: 2
updates:
- { package-ecosystem: npm, directory: '/', schedule: { interval: daily } }
- { package-ecosystem: docker, directory: '/', schedule: { interval: weekly } }
- { package-ecosystem: 'github-actions', directory: '/', schedule: { interval: weekly } }
- { package-ecosystem: terraform, directory: '/terraform', schedule: { interval: weekly } }
# 5) Per-language linters with security rules
# Python — pip-audit, bandit
# Ruby — bundler-audit, brakeman (Rails)
# Go — govulncheck, gosec
# Java — Maven / Gradle dependency-check, SpotBugs + FindSecBugs
# Rust — cargo audit, cargo deny
# PHP — security-advisories (Composer)
# 6) Container hardening checklist
# • Pin base image by digest (not just tag)
# • Multi-stage build — minimal runtime stage
# • USER non-root
# • COPY --chown
# • Read-only filesystem in K8s securityContext
# • Drop ALL capabilities, add only what's needed
# • No 'latest' tag in production
# • Sign + verify images (Cosign / Sigstore)
# 7) Secret hygiene
# • Pre-commit gitleaks hook
# • Push protection (GitHub Secret Scanning)
# • Rotate on detection — automated where possible
# • Use OIDC for cloud auth (no static AWS keys in CI)
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111122223333:role/github-actions-deploy
aws-region: ap-southeast-2
# 8) Reporting + dashboards
# • GitHub Security tab — central view of code scanning alerts + Dependabot
# • Sonar / Snyk / Mend / Veracode — commercial dashboards if needed
# • SOC 2 / ISO 27001: keep run history; export reports periodically
# 9) Triage workflow
# • Auto-assign owners via CODEOWNERS
# • Apply SLA labels (critical-7d, high-30d)
# • Suppress false positives with INLINE comments + explanation
# • Re-evaluate suppressions quarterly
# 10) Performance — keep scans fast
# • Cache dep trees + scanner DBs
# • Run only relevant scans on path changes (paths-filter)
# • Parallelise jobs
# • Use scheduled deeper scans nightly; PR scans are lighter
# 11) Common bugs / pitfalls
# • Disabling scanner due to noise — tune rules instead
# • Suppressing findings without explanation
# • Required check not enforced on protected branches — make it a required check
# • Token with too-broad permissions in CI — minimize
# • Forgetting that secrets are visible to PRs from forks (use 'pull_request_target' carefully)
# • Container scan only on master — vulnerabilities reach prod
# • SARIF upload too large — split or filter
# • SBOM not stored anywhere — generate but never reference
# • False positive fatigue — measure + tune monthly
Why it matters
A complete CI security pipeline runs SAST (CodeQL/Semgrep), SCA (npm audit/OSV), secret scanning (gitleaks), container scan (Trivy), IaC scan (tfsec/Checkov), and licensing checks on every PR, plus DAST on staging nightly. Fail the build on critical/high, file tickets on medium, and use OIDC for cloud auth so no long-lived secrets sit in CI variables.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
# Vulnerabilities, secrets, IAM - uses: github/codeql-action/analyze@v3 - run: npm audit --omit=dev - uses: trufflesecurity/trufflehog@v3Try it Yourself »
Discussion
Loading…