name: Fuzz
on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
inputs:
duration:
description: 'Fuzzing duration in seconds per target'
required: false
default: '300'
type: string
targets:
description: 'Comma-separated list of fuzz targets to run (empty for all)'
required: false
default: ''
type: string
env:
CARGO_TERM_COLOR: always
jobs:
fuzz:
name: Fuzz Targets
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [constant_time, edge_cases, crypto_ops]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- name: Install cargo-fuzz
run: cargo install cargo-fuzz
- name: Cache dependencies
uses: Swatinem/rust-cache@v2
- name: Build fuzz targets
run: cargo fuzz build ${{ matrix.target }}
- name: Run fuzzing
run: |
# Determine fuzzing duration
DURATION="${{ github.event.inputs.duration || '300' }}"
# Determine if this target should run
TARGETS="${{ github.event.inputs.targets || '' }}"
if [ -n "$TARGETS" ]; then
# Check if this target is in the requested list
if ! echo "$TARGETS" | grep -q "${{ matrix.target }}"; then
echo "Skipping ${{ matrix.target }} (not in requested targets: $TARGETS)"
exit 0
fi
fi
echo "Running ${{ matrix.target }} for ${DURATION} seconds"
# Run fuzzing with timeout
timeout ${DURATION} cargo fuzz run ${{ matrix.target }} -- -max_total_time=${DURATION} || true
- name: Minimize corpus (on schedule only)
if: github.event_name == 'schedule'
run: |
echo "Minimizing fuzz corpus for ${{ matrix.target }}"
cargo fuzz cmin ${{ matrix.target }} || true
- name: Upload crash artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-crash-${{ matrix.target }}
path: |
fuzz/artifacts/${{ matrix.target }}/
fuzz/corpus/${{ matrix.target }}/
retention-days: 30
regression-test:
name: Regression Test
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- name: Install cargo-fuzz
run: cargo install cargo-fuzz
- name: Cache dependencies
uses: Swatinem/rust-cache@v2
- name: Build all fuzz targets
run: |
for target in constant_time edge_cases crypto_ops; do
echo "Building $target"
cargo fuzz build $target
done
- name: Run regression tests
run: |
# Run each fuzz target for a short time to catch immediate crashes
for target in constant_time edge_cases crypto_ops; do
echo "Testing $target for regressions"
timeout 30 cargo fuzz run $target -- -runs=10000 || {
echo "Regression detected in $target"
exit 1
}
done
coverage:
name: Fuzz Coverage
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: llvm-tools-preview
- name: Install cargo-fuzz
run: cargo install cargo-fuzz
- name: Install cargo-llvm-cov
run: cargo install cargo-llvm-cov --version 0.6.0
- name: Cache dependencies
uses: Swatinem/rust-cache@v2
- name: Build fuzz targets with coverage
run: |
export RUSTFLAGS="-Cinstrument-coverage"
for target in constant_time edge_cases crypto_ops; do
echo "Building $target with coverage"
cargo fuzz build $target
done
- name: Generate coverage report
run: |
# Run short fuzzing sessions to generate coverage
export LLVM_PROFILE_FILE="fuzz-%p-%m.profraw"
for target in constant_time edge_cases crypto_ops; do
echo "Generating coverage for $target"
timeout 60 cargo fuzz run $target -- -runs=5000 || true
done
# Generate coverage report
cargo llvm-cov report --lcov --output-path fuzz-coverage.lcov
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: fuzz-coverage.lcov
flags: fuzz
name: fuzz-coverage
fail_ci_if_error: false