name: Security
on:
push:
branches: [main]
paths-ignore:
- "**.md"
- "docs/**"
- "assets/**"
pull_request:
branches: [main]
paths-ignore:
- "**.md"
- "docs/**"
- "assets/**"
schedule:
- cron: "30 1 * * 1"
permissions:
contents: read
jobs:
codeql:
name: CodeQL SAST
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Initialize CodeQL
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 with:
languages: rust
queries: security-extended
- name: Build for CodeQL analysis
run: cargo build 2>&1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 with:
category: /language:rust
cargo-audit:
name: Dependency CVE Audit
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Run cargo-audit
id: audit
continue-on-error: true
run: |
cargo audit --json 2>/dev/null > audit-results.json || true
cargo audit 2>&1 | tee audit-summary.txt || true
- name: Check for vulnerabilities
run: |
# Fail the build on any CRITICAL or HIGH vulnerability in production dependencies
VULNS=$(cargo audit --json 2>/dev/null | \
jq '[.vulnerabilities.list[]? | select(.advisory.cvss != null)] | length' 2>/dev/null || echo 0)
echo "Vulnerabilities found: $VULNS"
if [ "${VULNS:-0}" -gt 0 ]; then
echo "❌ Found $VULNS known vulnerabilities - review audit-summary.txt"
cat audit-summary.txt
exit 1
fi
echo "✅ No known CVEs in Cargo dependencies"
- name: Upload audit results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with:
name: cargo-audit-results
path: |
audit-results.json
audit-summary.txt
retention-days: 30
- name: Comment PR with audit results
if: github.event_name == 'pull_request' && steps.audit.outcome == 'failure'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 with:
script: |
const fs = require('fs');
const summary = fs.existsSync('audit-summary.txt')
? fs.readFileSync('audit-summary.txt', 'utf8').slice(0, 3000)
: 'See workflow logs for details.';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🚨 cargo-audit: Vulnerable Dependencies\n\n\`\`\`\n${summary}\n\`\`\`\n\nPlease update the affected crates before merging.`
});
- name: Security summary
if: always()
run: |
echo "## 📦 Dependency Security (cargo-audit)" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- **Database**: [RustSec Advisory Database](https://rustsec.org/)" >> "$GITHUB_STEP_SUMMARY"
echo "- **Status**: ${{ steps.audit.outcome }}" >> "$GITHUB_STEP_SUMMARY"
if [ -f audit-summary.txt ]; then
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "\`\`\`" >> "$GITHUB_STEP_SUMMARY"
head -50 audit-summary.txt >> "$GITHUB_STEP_SUMMARY"
echo "\`\`\`" >> "$GITHUB_STEP_SUMMARY"
fi
trivy:
name: Trivy Vulnerability Scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
statuses: write
pull-requests: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with:
fetch-depth: 0
- name: Run Trivy filesystem scan (SARIF)
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 with:
scan-type: fs
scan-ref: .
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: "0"
- name: Run Trivy filesystem scan (table - for PR summary)
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 id: trivy-table
with:
scan-type: fs
scan-ref: .
format: table
severity: CRITICAL,HIGH
exit-code: "0"
- name: Upload Trivy SARIF to GitHub Security tab
if: always()
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 with:
sarif_file: trivy-results.sarif
category: Trivy-FS
- name: Fail on CRITICAL vulnerabilities
run: |
# Re-run with strict exit code only for CRITICAL severity
trivy fs . --exit-code 1 --severity CRITICAL --format table 2>&1 || {
echo "❌ CRITICAL vulnerabilities found - review Trivy results in the Security tab"
exit 1
}
echo "✅ No CRITICAL vulnerabilities in repository"
- name: Comment PR with Trivy results
if: github.event_name == 'pull_request' && steps.trivy-table.outcome == 'failure'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## 🔍 Trivy Security Scan\n\n⚠️ Vulnerabilities detected. Check the **Security** tab and the workflow logs for details.'
});
secrets-scan:
name: TruffleHog Secrets Scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
statuses: write
pull-requests: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with:
fetch-depth: 0
- name: TruffleHog - scan git history (verified only)
id: trufflehog
uses: trufflesecurity/trufflehog@bcfcf73aaf4759d4dadc2783177c245a02792318 continue-on-error: true
with:
path: ./
extra_args: --json --only-verified
- name: Comment PR on secrets detected
if: github.event_name == 'pull_request' && steps.trufflehog.outcome == 'failure'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## ⚠️ TruffleHog Alert\n\nVerified secrets detected in this PR.\n\n**Action required**: Remove sensitive data, revoke and rotate any exposed credentials immediately before merging.'
});
core.setFailed('Verified secrets detected by TruffleHog');
- name: Security report
if: always()
run: |
echo "## 🔐 Secrets Scan (TruffleHog)" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
if [ "${{ steps.trufflehog.outcome }}" = "failure" ]; then
echo "❌ **ALERT**: Verified secrets detected - immediate action required." >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "1. Identify and remove the secret from code" >> "$GITHUB_STEP_SUMMARY"
echo "2. Revoke and rotate the exposed credential" >> "$GITHUB_STEP_SUMMARY"
echo "3. If in git history, rewrite history with \`git filter-repo\`" >> "$GITHUB_STEP_SUMMARY"
else
echo "✅ No verified secrets detected." >> "$GITHUB_STEP_SUMMARY"
fi