name: Continuous Fuzzing

on:
  push:
    branches: [main, master, staging, try]
  pull_request:
    branches: [main, master]
  schedule:
    - cron: "0 0 * * *" # Daily sanity check

permissions:
  contents: read

jobs:
  fuzz:
    name: Fuzz ${{ matrix.target }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        target: [fuzz_target_1]

    steps:
      - name: Harden the runner (Audit all outbound calls)
        uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
        with:
          egress-policy: audit

      - name: Checkout repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

      - name: Install Rust nightly
        uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly

      - name: Cache cargo bin
        id: cache-cargo-bin
        uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: ~/.cargo/bin/cargo-fuzz
          key: ${{ runner.os }}-cargo-fuzz-bin

      - name: Cache Fuzzing Corpus
        uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: fuzz/corpus/${{ matrix.target }}
          key: ${{ runner.os }}-fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
          restore-keys: |
            ${{ runner.os }}-fuzz-corpus-${{ matrix.target }}-

      - name: Cache build artifacts
        uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            fuzz/target/
            target/
          key: ${{ runner.os }}-fuzz-target-${{ matrix.target }}-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-fuzz-target-${{ matrix.target }}-

      - name: Run Fuzzer
        run: |
          if [[ "${{ github.event_name }}" == "pull_request" ]]; then
            # Run the fuzzer 5 times, each for 1 hour (3600 seconds) instead of 1 time for 5 hours.
            # Long single fuzzer runs can hit memory limits or cause CPU starvation in shared CI runners.
            for i in {1..5}; do
              echo "Starting PR fuzzer iteration $i of 5..."
              RUSTFLAGS="-Zsanitizer=address" cargo +nightly build --manifest-path ./fuzz/Cargo.toml --target x86_64-unknown-linux-gnu --release -Z build-std=std,panic_abort --config 'profile.release.debug = "line-tables-only"' --config 'profile.release.lto = false'
              ./target/x86_64-unknown-linux-gnu/release/${{ matrix.target }} -max_total_time=3600
            done
          else
            echo "Starting standard fuzzer iteration (1 hour)..."
            RUSTFLAGS="-Zsanitizer=address" cargo +nightly build --manifest-path ./fuzz/Cargo.toml --target x86_64-unknown-linux-gnu --release -Z build-std=std,panic_abort --config 'profile.release.debug = "line-tables-only"' --config 'profile.release.lto = false'
            ./target/x86_64-unknown-linux-gnu/release/${{ matrix.target }} -max_total_time=3600
          fi

      - name: Upload Crash Artifacts
        if: failure()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: fuzzer-artifacts-${{ matrix.target }}
          path: fuzz/artifacts/${{ matrix.target }}
          if-no-files-found: ignore
