Skip to content

Security Audit

Security Audit #59

name: Security Audit
# PR/push cargo-audit for dependency changes is handled by ci.yml
# (`security-audit` job). This workflow keeps schedule + manual runs,
# cargo-deny, SBOM upload, and supply-chain/scorecard checks.
on:
schedule:
# Run security audit daily at 02:00 UTC
- cron: '0 2 * * *'
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
security-audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
cache-key: "ci-security-audit"
extra-tools: "cargo-deny cargo-audit cargo-outdated"
- name: Run cargo deny
run: cargo deny check
continue-on-error: false
- name: Run cargo audit
run: |
# rust-cache can leave a non-empty advisory-db dir that cargo-audit
# refuses to re-initialize ("Refusing to initialize the non-empty directory").
rm -rf "${CARGO_HOME:-$HOME/.cargo}/advisory-db"
cargo audit
continue-on-error: false
- name: Check for outdated dependencies
run: cargo outdated --exit-code 1 --root-deps-only
continue-on-error: true
- name: Generate SBOM
run: |
mkdir -p security/sbom
cargo tree --format "{p} {l}" > security/sbom/dependencies.txt
cargo deny list --format json > security/sbom/licenses.json 2>/dev/null || true
- name: Upload SBOM artifacts
uses: actions/upload-artifact@v4
with:
name: security-sbom-${{ github.sha }}
path: security/sbom/
retention-days: 90
- name: Security report summary
run: |
echo "## Security Audit Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Dependency Policy Check" >> $GITHUB_STEP_SUMMARY
if cargo deny check --format json > deny-report.json 2>/dev/null; then
echo "✅ All dependency policies satisfied" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Dependency policy violations found" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Vulnerability Scan" >> $GITHUB_STEP_SUMMARY
if cargo audit --quiet; then
echo "✅ No known vulnerabilities detected" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Security advisories found (check justified exceptions)" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Artifacts Generated" >> $GITHUB_STEP_SUMMARY
echo "- Software Bill of Materials (SBOM)" >> $GITHUB_STEP_SUMMARY
echo "- Dependency tree with licenses" >> $GITHUB_STEP_SUMMARY
supply-chain-security:
name: Supply Chain Security
runs-on: ubuntu-latest
# Runs on schedule / workflow_dispatch with this workflow (PR cargo-audit
# path lives in ci.yml).
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
cache-key: "ci-supply-chain"
extra-tools: "cargo-deny"
- name: Check for dependency drift vs previous day tip
run: |
echo "Scheduled/manual supply-chain pass — reviewing Cargo.toml licenses via cargo-deny."
git log -1 --oneline Cargo.toml Cargo.lock || true
- name: Validate dependency licenses
run: cargo deny check licenses
security-scorecard:
name: OpenSSF Scorecard
runs-on: ubuntu-latest
permissions:
security-events: write
id-token: write
contents: read
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@v2.4.0
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload SARIF results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif