threatflux-cache 0.1.8

A flexible async cache library for Rust with pluggable backends and serialization
Documentation
name: Security

on:
  push:
    branches: [ "main", "develop" ]
  pull_request:
    branches: [ "main", "develop" ]
  schedule:
    # Run security scans daily at 3 AM UTC
    - cron: '0 3 * * *'
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

permissions:
  contents: read
  security-events: write

jobs:
  audit:
    name: Security Audit
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable

    - name: Cache cargo
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
        key: ${{ runner.os }}-cargo-audit-${{ hashFiles('**/Cargo.lock') }}

    - name: Install cargo-audit
      run: cargo install cargo-audit

    - name: Run cargo audit
      run: |
        cargo audit --json > audit-report.json || AUDIT_EXIT_CODE=$?
        
        # Display results
        echo "📋 Security Audit Results:"
        cargo audit || true
        
        # Check for vulnerabilities
        if [ -s audit-report.json ]; then
          VULN_COUNT=$(jq '.vulnerabilities.count // 0' audit-report.json)
          if [ "$VULN_COUNT" -gt 0 ]; then
            echo "❌ Found $VULN_COUNT vulnerabilities"
            exit 1
          else
            echo "✅ No vulnerabilities found"
          fi
        else
          echo "âš ī¸  Audit report is empty or malformed"
        fi

    - name: Upload audit report
      uses: actions/upload-artifact@v4
      with:
        name: security-audit-report
        path: audit-report.json
      if: always()

  clippy-security:
    name: Clippy Security Lints
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable
      with:
        components: clippy

    - name: Cache cargo
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target
        key: ${{ runner.os }}-cargo-clippy-security-${{ hashFiles('**/Cargo.lock') }}

    - name: Run security-focused clippy lints
      run: |
        cargo clippy --all-targets --all-features -- \
          -W clippy::suspicious \
          -W clippy::security \
          -W clippy::panic \
          -W clippy::unwrap_used \
          -W clippy::expect_used \
          -W clippy::indexing_slicing \
          -W clippy::integer_arithmetic \
          -W clippy::float_arithmetic \
          -W clippy::unsafe_derive_deserialize \
          -W clippy::mem_forget \
          -W clippy::todo \
          -W clippy::unimplemented \
          -W clippy::unreachable \
          -W clippy::as_conversions \
          -W clippy::cast_possible_truncation \
          -W clippy::cast_possible_wrap \
          -W clippy::cast_precision_loss \
          -W clippy::cast_sign_loss \
          -W clippy::integer_division \
          -W clippy::lossy_float_literal \
          -W clippy::string_slice

  dependency-review:
    name: Dependency Review
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Dependency Review
      uses: actions/dependency-review-action@v4
      with:
        fail-on-severity: moderate
        comment-summary-in-pr: on-failure

  supply-chain-security:
    name: Supply Chain Security
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable

    - name: Cache cargo
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
        key: ${{ runner.os }}-cargo-supply-chain-${{ hashFiles('**/Cargo.lock') }}

    - name: Install cargo-deny
      run: cargo install cargo-deny

    - name: Create deny.toml if it doesn't exist
      run: |
        if [ ! -f deny.toml ]; then
          cat > deny.toml << 'EOF'
        [graph]
        targets = [
            { triple = "x86_64-unknown-linux-gnu" },
            { triple = "x86_64-unknown-linux-musl" },
            { triple = "x86_64-pc-windows-msvc" },
            { triple = "x86_64-apple-darwin" },
        ]

        [licenses]
        allow = [
            "MIT",
            "Apache-2.0",
            "Apache-2.0 WITH LLVM-exception",
            "BSD-2-Clause",
            "BSD-3-Clause",
            "ISC",
            "Unicode-DFS-2016",
            "CC0-1.0",
        ]
        confidence-threshold = 0.8
        exceptions = []

        [[licenses.clarify]]
        name = "ring"
        version = "*"
        expression = "MIT AND ISC AND OpenSSL"
        license-files = [
            { path = "LICENSE", hash = 0xbd0eed23 }
        ]

        [bans]
        multiple-versions = "warn"
        wildcards = "allow"
        highlight = "all"
        
        # Deny crates with known security issues
        deny = [
            { name = "openssl", version = "<0.10.55" },
            { name = "chrono", version = "<0.4.20" },
        ]

        skip = [
            { name = "windows-sys" },
            { name = "winapi" },
        ]

        skip-tree = [
            { name = "criterion" },
        ]

        [advisories]
        version = 2
        db-path = "~/.cargo/advisory-db"
        db-urls = ["https://github.com/rustsec/advisory-db"]
        unmaintained = "all"
        yanked = "deny"
        ignore = []

        [sources]
        unknown-registry = "warn"
        unknown-git = "warn"
        allow-registry = ["https://github.com/rust-lang/crates.io-index"]
        allow-git = []
        EOF
        fi

    - name: Check licenses
      run: cargo deny check licenses

    - name: Check advisories
      run: cargo deny check advisories

    - name: Check bans
      run: cargo deny check bans

    - name: Check sources
      run: cargo deny check sources

  secrets-scan:
    name: Secrets Scan
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Run TruffleHog OSS
      uses: trufflesecurity/trufflehog@main
      with:
        path: ./
        extra_args: --debug --only-verified

  code-ql:
    name: CodeQL Analysis
    runs-on: ubuntu-latest
    if: github.event_name == 'push' || github.event_name == 'schedule'

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: rust
        queries: security-extended,security-and-quality

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable

    - name: Cache cargo
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target
        key: ${{ runner.os }}-cargo-codeql-${{ hashFiles('**/Cargo.lock') }}

    - name: Build for CodeQL
      run: cargo build --all-features

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3
      with:
        category: "/language:rust"

  unsafe-code-review:
    name: Unsafe Code Review
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable

    - name: Check for unsafe code
      run: |
        echo "🔍 Scanning for unsafe code blocks..."
        UNSAFE_COUNT=$(find src -name "*.rs" -exec grep -l "unsafe" {} \; | wc -l)
        
        if [ $UNSAFE_COUNT -gt 0 ]; then
          echo "âš ī¸  Found unsafe code in $UNSAFE_COUNT files:"
          find src -name "*.rs" -exec grep -l "unsafe" {} \; | while read file; do
            echo "  - $file"
            grep -n "unsafe" "$file" | head -5
          done
          
          # Don't fail the build for unsafe code, just report it
          echo ""
          echo "â„šī¸  Unsafe code detected. Please ensure it's properly reviewed and documented."
        else
          echo "✅ No unsafe code found"
        fi

  security-report:
    name: Security Report
    runs-on: ubuntu-latest
    needs: [audit, clippy-security, supply-chain-security, unsafe-code-review]
    if: always()

    steps:
    - name: Download audit report
      uses: actions/download-artifact@v4
      with:
        name: security-audit-report
        path: ./reports
      continue-on-error: true

    - name: Generate security summary
      run: |
        echo "# 🔒 Security Analysis Report" > security-summary.md
        echo "" >> security-summary.md
        echo "## Job Results" >> security-summary.md
        echo "- **Audit**: ${{ needs.audit.result }}" >> security-summary.md
        echo "- **Clippy Security**: ${{ needs.clippy-security.result }}" >> security-summary.md
        echo "- **Supply Chain**: ${{ needs.supply-chain-security.result }}" >> security-summary.md
        echo "- **Unsafe Code Review**: ${{ needs.unsafe-code-review.result }}" >> security-summary.md
        echo "" >> security-summary.md
        
        if [ -f "./reports/audit-report.json" ]; then
          echo "## Vulnerability Summary" >> security-summary.md
          VULN_COUNT=$(jq '.vulnerabilities.count // 0' ./reports/audit-report.json)
          echo "- **Total Vulnerabilities**: $VULN_COUNT" >> security-summary.md
        fi
        
        echo "" >> security-summary.md
        echo "Report generated on: $(date)" >> security-summary.md

    - name: Upload security report
      uses: actions/upload-artifact@v4
      with:
        name: security-report
        path: security-summary.md

    - name: Check overall security status
      run: |
        if [ "${{ needs.audit.result }}" = "failure" ]; then
          echo "❌ Security audit failed - vulnerabilities detected"
          exit 1
        elif [ "${{ needs.clippy-security.result }}" = "failure" ]; then
          echo "❌ Security lints failed"
          exit 1
        elif [ "${{ needs.supply-chain-security.result }}" = "failure" ]; then
          echo "❌ Supply chain security checks failed"
          exit 1
        else
          echo "✅ All security checks passed"
        fi