Skip to content

Merge pull request #224 from MerlinTheWhiz/fix/stale-accrual-before-b… #194

Merge pull request #224 from MerlinTheWhiz/fix/stale-accrual-before-b…

Merge pull request #224 from MerlinTheWhiz/fix/stale-accrual-before-b… #194

Workflow file for this run

# Credence-Contracts Security Scanning
# Automated security analysis for smart contracts
# Runs on push and PR to main/develop branches
name: Security Scanning
on:
push:
branches: [main, master, develop]
pull_request:
branches: [main, master, develop]
# Allow manual trigger for ad-hoc scans
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
cargo-audit:
name: Dependency Vulnerability Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-audit
run: cargo install cargo-audit --version 0.22.0 --locked
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-audit-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-audit-
- name: Run cargo audit
run: |
cargo audit --json > audit-report.json || true
cargo audit
- name: Check for critical vulnerabilities
run: |
CRITICAL_COUNT=$(jq '[.vulnerabilities.list[] | select(.advisory.severity == "critical")] | length' audit-report.json)
echo "Critical vulnerabilities found: $CRITICAL_COUNT"
if [ "$CRITICAL_COUNT" -gt 0 ]; then
echo "❌ CRITICAL vulnerabilities detected - failing pipeline"
exit 1
fi
echo "✅ No critical vulnerabilities found"
- name: Upload audit report
uses: actions/upload-artifact@v4
if: always()
with:
name: cargo-audit-report
path: audit-report.json
retention-days: 30
clippy-security:
name: Static Analysis (Clippy Security Lints)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-clippy-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-clippy-
- name: Run Clippy with security lints
run: |
cargo clippy --all-targets --message-format=json -- \
-W clippy::integer_arithmetic \
-W clippy::unwrap_used \
-W clippy::expect_used \
-W clippy::panic \
-W clippy::todo \
-W clippy::unimplemented \
-W clippy::indexing_slicing \
-W clippy::cast_possible_truncation \
-W clippy::cast_sign_loss \
-D warnings \
2>&1 | tee clippy-output.json
- name: Parse Clippy results
if: always()
run: |
# Extract warnings and errors from JSON output
echo "## Clippy Security Analysis Summary" > clippy-summary.txt
echo "" >> clippy-summary.txt
# Count issues by severity
ERRORS=$(grep -c '"level":"error"' clippy-output.json || echo "0")
WARNINGS=$(grep -c '"level":"warning"' clippy-output.json || echo "0")
echo "- Errors: $ERRORS" >> clippy-summary.txt
echo "- Warnings: $WARNINGS" >> clippy-summary.txt
echo "" >> clippy-summary.txt
cat clippy-summary.txt
# Generate HTML report
echo "<html><head><title>Clippy Security Report</title></head><body>" > clippy-report.html
echo "<h1>Clippy Security Analysis</h1>" >> clippy-report.html
echo "<pre>" >> clippy-report.html
cat clippy-summary.txt >> clippy-report.html
echo "</pre>" >> clippy-report.html
echo "<h2>Detailed Output</h2><pre>" >> clippy-report.html
cat clippy-output.json >> clippy-report.html
echo "</pre></body></html>" >> clippy-report.html
- name: Upload Clippy report
uses: actions/upload-artifact@v4
if: always()
with:
name: clippy-security-report
path: |
clippy-report.html
clippy-output.json
clippy-summary.txt
retention-days: 30
cargo-geiger:
name: Unsafe Code Detection
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-geiger
run: cargo install cargo-geiger --version 0.12.0 --locked
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-geiger-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-geiger-
- name: Run cargo-geiger on contracts
run: |
# Get list of workspace members
MEMBERS=$(cargo metadata --no-deps --format-version 1 | jq -r '.workspace_members[]' | cut -d' ' -f1)
echo "# Cargo Geiger Report" > geiger-report.md
echo "" >> geiger-report.md
# Run cargo geiger for each workspace member
for member in $MEMBERS; do
echo "## Package: $member" >> geiger-report.md
echo "" >> geiger-report.md
cargo geiger -p "$member" --output-format GitHubMarkdown >> geiger-report.md 2>&1 || true
echo "" >> geiger-report.md
done
# Also run a summary scan
echo "## Summary" >> geiger-report.md
cargo geiger 2>&1 || true
- name: Check for unsafe code in contracts
run: |
# Get list of workspace members
MEMBERS=$(cargo metadata --no-deps --format-version 1 | jq -r '.workspace_members[]' | cut -d' ' -f1)
TOTAL_UNSAFE=0
# Check each contract package
for member in $MEMBERS; do
if [[ "$member" == credence_* ]]; then
UNSAFE_COUNT=$(cargo geiger -p "$member" --output-format Json 2>/dev/null | jq '[.packages[] | select(.name | startswith("credence_")) | .unsafety.used.functions + .unsafety.used.exprs + .unsafety.used.impls + .unsafety.used.traits + .unsafety.used.methods] | add // 0' || echo "0")
echo "Unsafe code in $member: $UNSAFE_COUNT"
TOTAL_UNSAFE=$((TOTAL_UNSAFE + UNSAFE_COUNT))
fi
done
echo "Total unsafe code blocks in contracts: $TOTAL_UNSAFE"
if [ "$TOTAL_UNSAFE" -gt 0 ]; then
echo "⚠️ WARNING: Unsafe code detected in contracts - review required"
echo "This is a warning only - pipeline will continue"
else
echo "✅ No unsafe code in contracts"
fi
- name: Upload geiger report
uses: actions/upload-artifact@v4
if: always()
with:
name: cargo-geiger-report
path: geiger-report.md
retention-days: 30
security-summary:
name: Security Scan Summary
runs-on: ubuntu-latest
needs: [cargo-audit, clippy-security, cargo-geiger]
if: always()
steps:
- name: Download all reports
uses: actions/download-artifact@v4
with:
path: security-reports
- name: Generate summary
run: |
echo "# Security Scan Summary" > summary.md
echo "" >> summary.md
echo "## Scan Results" >> summary.md
echo "" >> summary.md
# Check job statuses
AUDIT_STATUS="${{ needs.cargo-audit.result }}"
CLIPPY_STATUS="${{ needs.clippy-security.result }}"
GEIGER_STATUS="${{ needs.cargo-geiger.result }}"
echo "- Dependency Audit: $AUDIT_STATUS" >> summary.md
echo "- Clippy Security Lints: $CLIPPY_STATUS" >> summary.md
echo "- Unsafe Code Detection: $GEIGER_STATUS" >> summary.md
echo "" >> summary.md
if [ "$AUDIT_STATUS" = "failure" ]; then
echo "❌ **CRITICAL**: Dependency vulnerabilities detected" >> summary.md
elif [ "$CLIPPY_STATUS" = "failure" ]; then
echo "❌ **CRITICAL**: Security lint violations detected" >> summary.md
else
echo "✅ All security scans passed" >> summary.md
fi
cat summary.md
- name: Upload summary
uses: actions/upload-artifact@v4
with:
name: security-summary
path: summary.md
retention-days: 30