Skip to content

Security

Security #151

Workflow file for this run

# ReasonKit Core - Security Scanning
# Dependency audits, license checks, SAST, secret scanning
name: Security
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# Run daily at 00:00 UTC
- cron: "0 0 * * *"
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
# Override .cargo/config.toml target-cpu=native to prevent SIGILL on different runners
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS: ""
jobs:
# ═══════════════════════════════════════════════════════════════════════════
# Dependency Vulnerability Scanning
# ═══════════════════════════════════════════════════════════════════════════
cargo-audit:
name: "Cargo Audit"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable
with:
toolchain: stable
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Run security audit
run: cargo audit --deny warnings
- name: Generate audit report
if: always()
run: |
cargo audit --json > audit-report.json || true
- name: Upload audit report
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4
with:
name: security-audit-report
path: audit-report.json
# ═══════════════════════════════════════════════════════════════════════════
# Supply Chain Security with cargo-deny
# ═══════════════════════════════════════════════════════════════════════════
cargo-deny:
name: "Cargo Deny"
runs-on: ubuntu-latest
strategy:
matrix:
checks:
- advisories
- licenses
- bans
- sources
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Run cargo-deny (${{ matrix.checks }})
uses: EmbarkStudios/cargo-deny-action@3f4a782664881cf5725d0ffd23969fcce89fd868 # v1
with:
log-level: warn
command: check ${{ matrix.checks }}
# ═══════════════════════════════════════════════════════════════════════════
# License Compliance Check
# ═══════════════════════════════════════════════════════════════════════════
license-check:
name: "License Check"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable
with:
toolchain: stable
- name: Install cargo-license
run: cargo install cargo-license
- name: Generate license report
run: |
cargo license --json > licenses.json
cargo license --tsv > licenses.tsv
- name: Check for GPL licenses
run: |
if grep -i "GPL" licenses.tsv; then
echo "❌ GPL licenses found (incompatible with Apache 2.0)"
exit 1
else
echo "✅ No GPL licenses found"
fi
- name: Upload license report
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4
with:
name: license-report
path: |
licenses.json
licenses.tsv
# ═══════════════════════════════════════════════════════════════════════════
# Secret Scanning with Gitleaks
# ═══════════════════════════════════════════════════════════════════════════
gitleaks:
name: "Secret Scanning"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
with:
fetch-depth: 0
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
# ═══════════════════════════════════════════════════════════════════════════
# Static Application Security Testing (SAST)
# ═══════════════════════════════════════════════════════════════════════════
clippy-security:
name: "Clippy Security Lints"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable
with:
toolchain: stable
components: clippy
- name: Cache cargo
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-security-${{ hashFiles('**/Cargo.lock') }}
- name: Run security-focused Clippy lints
run: |
cargo clippy --all-features -- \
-W clippy::suspicious \
-W clippy::perf \
-W clippy::correctness \
-W clippy::unwrap_used \
-W clippy::expect_used \
-W clippy::panic \
-W clippy::todo \
-W clippy::unimplemented
# ═══════════════════════════════════════════════════════════════════════════
# Semgrep SAST Scanning
# ═══════════════════════════════════════════════════════════════════════════
semgrep:
name: "Semgrep SAST"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Run Semgrep
uses: returntocorp/semgrep-action@713efdd345f3035192eaa63f56867b88e63e4e5d # v1
with:
config: >-
p/rust
p/security-audit
p/secrets
continue-on-error: true
# ═══════════════════════════════════════════════════════════════════════════
# SBOM Generation
# ═══════════════════════════════════════════════════════════════════════════
sbom:
name: "SBOM Generation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable
with:
toolchain: stable
- name: Install cargo-sbom
run: cargo install cargo-sbom
- name: Generate SBOM (CycloneDX)
run: cargo sbom --output-format cyclone_dx_json_1_4 > sbom-cyclonedx.json
- name: Generate SBOM (SPDX)
run: cargo sbom --output-format spdx_json_2_3 > sbom-spdx.json
- name: Upload SBOM
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4
with:
name: sbom
path: |
sbom-cyclonedx.json
sbom-spdx.json
- name: Attach SBOM to release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@26994186c0ac3ef5cae75ac16aa32e8153525f77 # v1
with:
files: |
sbom-cyclonedx.json
sbom-spdx.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ═══════════════════════════════════════════════════════════════════════════
# Dependency Review (PR only)
# ═══════════════════════════════════════════════════════════════════════════
dependency-review:
name: "Dependency Review"
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Dependency Review
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4
with:
fail-on-severity: moderate
deny-licenses: GPL-3.0, AGPL-3.0
# ═══════════════════════════════════════════════════════════════════════════
# Security Summary
# ═══════════════════════════════════════════════════════════════════════════
security-summary:
name: "Security Summary"
runs-on: ubuntu-latest
needs: [cargo-audit, cargo-deny, license-check, gitleaks, clippy-security]
if: always()
steps:
- name: Generate summary
run: |
echo "## 🔒 Security Scan Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Cargo Audit | ${{ needs.cargo-audit.result == 'success' && '✅ Pass' || '❌ Fail' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Cargo Deny | ${{ needs.cargo-deny.result == 'success' && '✅ Pass' || '❌ Fail' }} |" >> $GITHUB_STEP_SUMMARY
echo "| License Check | ${{ needs.license-check.result == 'success' && '✅ Pass' || '❌ Fail' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Secret Scanning | ${{ needs.gitleaks.result == 'success' && '✅ Pass' || '❌ Fail' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Clippy Security | ${{ needs.clippy-security.result == 'success' && '✅ Pass' || '❌ Fail' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.cargo-audit.result }}" = "success" ] && \
[ "${{ needs.cargo-deny.result }}" = "success" ] && \
[ "${{ needs.license-check.result }}" = "success" ] && \
[ "${{ needs.gitleaks.result }}" = "success" ]; then
echo "✅ **All security checks passed!**" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ **Some security checks failed. Review above.**" >> $GITHUB_STEP_SUMMARY
fi