Safeguard
Developer Tools•

Fuzzing in CI: short runs on pull requests, long runs overnight

How to add fuzzing to CI without slowing every build: corpus replay on each commit, a bounded run on pull requests, and longer batch runs on a schedule.

Safeguard Security Team
4 min read

Split fuzzing into three jobs. Replay the saved corpus on every commit, which takes seconds. Run a short bounded fuzz session on pull requests that touch fuzzed code. Run long batch sessions on a schedule and feed new inputs back into the corpus. Each job has a different goal and cost.

Job 1: corpus replay

This is the cheapest and most valuable. Run every fuzz target over the saved corpus as a normal test, using sanitizers. The OSS-Fuzz ideal integration guidance describes exactly this: run each target with a standalone driver over the seed corpus as part of regular tests, with ASan, UBSan and MSan, and note that continuous fuzzing is not required at this stage.

In Go this happens automatically in go test. Jazzer's JUnit regression mode does the same on the JVM.

Job 2: short fuzzing on pull requests

ClusterFuzzLite is designed for this. Its documentation describes code change fuzzing that fuzzes pull requests to catch bugs before they land, and downloads crashing test cases. It runs on GitHub Actions, GitLab, Google Cloud Build and Prow, with libFuzzer as the engine and ASan, MSan and UBSan as sanitizers.

If you use another tool, the pattern is simple: bound the run with a time flag such as libFuzzer's -max_total_time, and only run targets for code the pull request touched. A five minute run will not find deep bugs, but it often catches a regression that the change introduced.

Job 3: batch fuzzing

Longer runs, hours or days, happen on a schedule or a dedicated machine. ClusterFuzzLite calls this batch fuzzing: longer running, asynchronous, finds deeper bugs, and builds a corpus that the pull request job can use. Store the corpus somewhere persistent so each run starts where the last one ended.

Coverage reports

Add a coverage job. ClusterFuzzLite lists coverage reports as a feature to show which parts of the code are fuzzed. A target that is running but reaches little code is a false comfort.

Make failures actionable

  1. The failure message should say which target failed and attach the crashing input.
  2. Developers should replay it with one command.
  3. The fix should add the input to the corpus.

A red build with no reproducer gets ignored fast.

What to gate on

Fail the pull request on a crash from corpus replay, since that is a regression. Be more careful gating on new findings from a short fuzz run: they may be old bugs that were simply never found. Many teams report these as issues instead of blocking, unless the crash is in code the pull request changed.

Try it yourself

Open the demo and watch how a pull request gets blocked by a failing check. Think of a corpus replay as one more check that can fail a change.

Click through it at your own pace, or open it full screen.

Where Safeguard fits

For dependencies, Safeguard offers policies and gates that can block a pull request on risky packages, plus SBOM and dependency scanning. It is separate from your fuzz jobs. You can try a free scan at gold.safeguard.sh/scan.

Quick check

How long should a pull request fuzz run be?

Short enough that developers do not skip it, often a few minutes. Treat it as a regression check, and leave deep hunting to scheduled runs.

Do I need continuous fuzzing to start?

No. Replaying a saved corpus under sanitizers in normal tests is a useful first step.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.