diff --git a/.github/workflows/fuzzing-and-coverage.yml b/.github/workflows/fuzzing-and-coverage.yml new file mode 100644 index 0000000..09be5de --- /dev/null +++ b/.github/workflows/fuzzing-and-coverage.yml @@ -0,0 +1,132 @@ +name: Continuous Fuzzing and Coverage + +on: + push: + branches: [main] + pull_request: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + +defaults: + run: + working-directory: AURUM_full_bundle_v1_with_golden + shell: bash + +jobs: + fuzz-matrix: + name: Fuzz smoke (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + + - name: Install stable Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Install nightly for fuzzing + run: rustup toolchain install nightly --profile minimal + + - name: Install cargo-fuzz + run: cargo +nightly install cargo-fuzz + + - name: Run smoke fuzzers + run: ../scripts/ci/run_cargo_fuzz.sh + env: + RUST_FUZZ_TOOLCHAIN: nightly + FUZZ_RUNS: 2500 + + coverage: + name: Coverage (ubuntu-latest) + runs-on: ubuntu-latest + timeout-minutes: 40 + needs: fuzz-matrix + steps: + - uses: actions/checkout@v4 + + - name: Install stable Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Install coverage tooling + run: | + rustup component add llvm-tools-preview + cargo install cargo-llvm-cov + + - name: Generate coverage reports + run: | + cargo llvm-cov clean --workspace + cargo llvm-cov --workspace --all-features --lcov --output-path target/coverage/lcov.info --html + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: coverage-${{ github.sha }} + path: | + AURUM_full_bundle_v1_with_golden/target/coverage/lcov.info + AURUM_full_bundle_v1_with_golden/target/llvm-cov/html + + golden-vectors: + name: Validate golden vectors + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: fuzz-matrix + steps: + - uses: actions/checkout@v4 + + - name: Install stable Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Ensure GOLDEN.json matches generated vectors + run: ../scripts/ci/check_golden_vectors.sh + + nightly-longrun: + name: Nightly long-run fuzzers + if: github.event_name == 'schedule' + runs-on: ubuntu-latest + timeout-minutes: 240 + steps: + - uses: actions/checkout@v4 + + - name: Install stable Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Install nightly for fuzzing + run: rustup toolchain install nightly --profile minimal + + - name: Install cargo-fuzz + run: cargo +nightly install cargo-fuzz + + - name: Run long fuzzers + run: ../scripts/ci/run_cargo_fuzz.sh + env: + RUST_FUZZ_TOOLCHAIN: nightly + FUZZ_MODE: nightly + FUZZ_MAX_TOTAL_TIME: 7200 + + - name: Upload nightly fuzz artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: nightly-fuzz-artifacts-${{ github.run_id }} + path: | + AURUM_full_bundle_v1_with_golden/**/fuzz/artifacts + AURUM_full_bundle_v1_with_golden/**/fuzz/corpus + if-no-files-found: ignore diff --git a/scripts/ci/check_golden_vectors.sh b/scripts/ci/check_golden_vectors.sh new file mode 100755 index 0000000..e7039f7 --- /dev/null +++ b/scripts/ci/check_golden_vectors.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +WORKSPACE_DIR="$REPO_ROOT/AURUM_full_bundle_v1_with_golden" +GOLDEN_FILE="AURUM_full_bundle_v1_with_golden/GOLDEN.json" + +if [ ! -f "$WORKSPACE_DIR/Cargo.toml" ]; then + echo "Workspace manifest not found at $WORKSPACE_DIR/Cargo.toml." + exit 1 +fi + +pushd "$WORKSPACE_DIR" >/dev/null +cargo run -p aurum-pentest --bin gen_golden +popd >/dev/null + +if ! git -C "$REPO_ROOT" diff --quiet -- "$GOLDEN_FILE"; then + git -C "$REPO_ROOT" diff --stat -- "$GOLDEN_FILE" + echo "Golden vectors are stale. Run 'cargo run -p aurum-pentest --bin gen_golden' and commit the changes." >&2 + exit 1 +fi + +echo "Golden vectors are up-to-date." diff --git a/scripts/ci/run_cargo_fuzz.sh b/scripts/ci/run_cargo_fuzz.sh new file mode 100755 index 0000000..027f033 --- /dev/null +++ b/scripts/ci/run_cargo_fuzz.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +WORKSPACE_DIR="$REPO_ROOT/AURUM_full_bundle_v1_with_golden" + +if [ ! -f "$WORKSPACE_DIR/Cargo.toml" ]; then + echo "Workspace manifest not found at $WORKSPACE_DIR/Cargo.toml." + exit 0 +fi + +cd "$WORKSPACE_DIR" + +FUZZ_MANIFESTS=() +while IFS= read -r manifest; do + FUZZ_MANIFESTS+=("$manifest") +done < <(find . -path './target' -prune -o -path '*/fuzz/Cargo.toml' -print) + +if [ "${#FUZZ_MANIFESTS[@]}" -eq 0 ]; then + echo "No cargo-fuzz manifests found; skipping fuzzing step." + exit 0 +fi + +TOOLCHAIN="${RUST_FUZZ_TOOLCHAIN:-nightly}" +CARGO_CMD=(cargo "+${TOOLCHAIN}") + +if ! command -v cargo &>/dev/null; then + echo "cargo command not available in PATH." + exit 1 +fi + +if [ "${FUZZ_MODE:-smoke}" = "nightly" ]; then + MAX_TIME="${FUZZ_MAX_TOTAL_TIME:-7200}" + FUZZ_ARGS=(-- "-max_total_time=${MAX_TIME}") + MODE_DESC="nightly long-run" +else + RUNS="${FUZZ_RUNS:-2000}" + FUZZ_ARGS=(-- "-runs=${RUNS}") + MODE_DESC="smoke" +fi + +echo "Detected ${#FUZZ_MANIFESTS[@]} fuzz package(s); running in ${MODE_DESC} mode using toolchain '${TOOLCHAIN}'." + +for manifest in "${FUZZ_MANIFESTS[@]}"; do + crate_dir="$(dirname "$manifest")" + pushd "$crate_dir" >/dev/null + + TARGETS=() + while IFS= read -r raw_target; do + raw_target="$(echo "$raw_target" | tr -d '\r')" + raw_target="$(echo "$raw_target" | xargs)" + [ -z "$raw_target" ] && continue + TARGETS+=("$raw_target") + done < <("${CARGO_CMD[@]}" fuzz list) + + if [ "${#TARGETS[@]}" -eq 0 ]; then + echo "No fuzz targets defined in $crate_dir; skipping." + popd >/dev/null + continue + fi + + for target in "${TARGETS[@]}"; do + echo "Running fuzz target '$target' in $crate_dir" + "${CARGO_CMD[@]}" fuzz run "$target" "${FUZZ_ARGS[@]}" + done + + popd >/dev/null + +done