harden against PI and Comment attacks #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Fuzzing on Linux (libFuzzer). Uses fuzz/mathml.dict for structured MathML tokens. | |
| # Corpus is cached between runs so libFuzzer can accumulate interesting inputs over time. | |
| # Crash artifacts are uploaded on failure for reproduction and regression test generation. | |
| name: Fuzz | |
| on: | |
| push: | |
| pull_request: | |
| workflow_dispatch: | |
| schedule: | |
| # Weekly long run (1 hour per configuration); corpus is cached between runs. | |
| - cron: "0 12 * * 1" | |
| # Paths are relative to the repo root (cache / mkdir). The fuzz crate lives in fuzz/. | |
| env: | |
| FUZZ_CORPUS_ROOT: fuzz/corpus/fuzz_target_1 | |
| jobs: | |
| fuzz: | |
| name: Build and fuzz (with MathML dict + corpus cache) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Ensure corpus directory exists | |
| run: mkdir -p "${{ env.FUZZ_CORPUS_ROOT }}" | |
| # Restore latest corpus; unique key each run so we always save an updated snapshot after fuzzing. | |
| - name: Restore fuzz corpus | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ env.FUZZ_CORPUS_ROOT }} | |
| key: fuzz-corpus-fuzz_target_1-${{ runner.os }}-${{ github.run_id }} | |
| restore-keys: | | |
| fuzz-corpus-fuzz_target_1-${{ runner.os }}- | |
| - uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| # rust-src: cargo-fuzz defaults to -Z build-std for sanitizer builds | |
| # llvm-tools-preview: needed for llvm-cov coverage reports | |
| components: rust-src, llvm-tools-preview | |
| - name: Install cargo-fuzz | |
| run: cargo install cargo-fuzz | |
| - name: Set fuzz duration (weekly = 1h each, else smoke = 60s) | |
| id: fuzztime | |
| run: | | |
| if [ "${{ github.event_name }}" = "schedule" ]; then | |
| echo "seconds=3600" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "seconds=60" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Build fuzz target (default) | |
| working-directory: fuzz | |
| run: cargo fuzz build fuzz_target_1 | |
| - name: Build fuzz target (no-unsafe) | |
| working-directory: fuzz | |
| run: cargo fuzz build --features no-unsafe fuzz_target_1 | |
| # Corpus path is relative to the fuzz crate directory (matches default cargo-fuzz layout). | |
| - name: Fuzz (default, dictionary + corpus) | |
| working-directory: fuzz | |
| run: >- | |
| cargo fuzz run fuzz_target_1 corpus/fuzz_target_1 -- | |
| -max_total_time=${{ steps.fuzztime.outputs.seconds }} | |
| -dict=mathml.dict | |
| - name: Fuzz (no-unsafe, dictionary + corpus) | |
| working-directory: fuzz | |
| run: >- | |
| cargo fuzz run --features no-unsafe fuzz_target_1 corpus/fuzz_target_1 -- | |
| -max_total_time=${{ steps.fuzztime.outputs.seconds }} | |
| -dict=mathml.dict | |
| # --- Crash artifact upload (on failure) --- | |
| - name: Upload crash artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fuzz-crash-artifacts | |
| path: fuzz/artifacts/ | |
| retention-days: 90 | |
| if-no-files-found: ignore | |
| # --- Coverage report (weekly only, after successful fuzzing) --- | |
| - name: Generate fuzz coverage data | |
| if: github.event_name == 'schedule' | |
| working-directory: fuzz | |
| run: cargo fuzz coverage fuzz_target_1 corpus/fuzz_target_1 | |
| - name: Produce HTML coverage report | |
| if: github.event_name == 'schedule' | |
| working-directory: fuzz | |
| run: | | |
| # Locate the coverage binary and profdata produced by cargo-fuzz. | |
| TARGET_DIR="target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release" | |
| PROFDATA="coverage/fuzz_target_1/coverage.profdata" | |
| BINARY=$(find "$TARGET_DIR" -name fuzz_target_1 -type f | head -1) | |
| if [ -z "$BINARY" ] || [ ! -f "$PROFDATA" ]; then | |
| echo "::warning::Could not locate coverage binary or profdata; skipping report." | |
| exit 0 | |
| fi | |
| # Use the llvm-cov / llvm-profdata shipped with the nightly toolchain. | |
| LLVM_COV=$(find "$(rustc --print sysroot)" -name llvm-cov -type f | head -1) | |
| if [ -z "$LLVM_COV" ]; then | |
| echo "::warning::llvm-cov not found in toolchain; skipping HTML report." | |
| exit 0 | |
| fi | |
| LLVM_COV_DIR=$(dirname "$LLVM_COV") | |
| mkdir -p coverage-report | |
| "$LLVM_COV_DIR/llvm-cov" show "$BINARY" \ | |
| --instr-profile="$PROFDATA" \ | |
| --format=html \ | |
| --output-dir=coverage-report \ | |
| --ignore-filename-regex='(\.cargo|fuzz_targets|rustc)' \ | |
| --show-instantiations=false | |
| - name: Upload coverage report | |
| if: github.event_name == 'schedule' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fuzz-coverage-report | |
| path: fuzz/coverage-report/ | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| # --- Corpus minimization (weekly only, after successful fuzzing) --- | |
| - name: Minimize corpus | |
| if: github.event_name == 'schedule' | |
| working-directory: fuzz | |
| run: | | |
| cargo fuzz cmin fuzz_target_1 corpus/fuzz_target_1 -- -dict=mathml.dict |