CI / go test -fuzz
- Job go test -fuzz ./... green in 12s
- Annotation SYS-REQ-100:denial_of_service_resistant:fuzz on TestParse
- Credit obligation marked covered
Topic · Fuzz testing
Gist
Fuzz testing feeds invalid, unexpected, or random data into a program. Proof fails proof audit --check fuzz_evidence_carrier_valid when a :fuzz annotation sits on a plain Test* function instead of a real Fuzz* or LLVMFuzzerTestOneInput entry point.
proof audit --check fuzz_evidence_carrier_valid
Keep go test -fuzz, libFuzzer, or AFL if you already mutate inputs. Keep Jama if you author shalls. Proof will not count a green unit test as a fuzz run. Jama still authors.
01 · The annotation is not the fuzzer
The suite still has a size-cap test. The obligation still claims denial of service is bounded. The annotated symbol never accepted a fuzzer.
Wikipedia, OWASP, GitLab, and the Go tutorial all define the same act: generate invalid or unexpected inputs and watch the program. That is the fuzzer's job. Proof does not replace that job. It asks a narrower question: if you claimed fuzz evidence on a requirement, is the annotated symbol a language-shaped fuzz entry point?
Before this check, a :fuzz triple on func TestParse_RejectsOversizedInput(t *testing.T) credited the same as one on func FuzzParse(f *testing.F). The first pins one boundary. The second is what go test -fuzz will actually mutate. Annotation-only credit is how a parser-family
denial_of_service_resistant obligation went green without a harness.
The command is deterministic. It does not call a model. It does not start AFL. It refuses a silent pass when the carrier is the wrong shape.
proof audit --check fuzz_evidence_carrier_valid
go test -run='^$' -fuzz=^FuzzParse$ -fuzztime=30s ./...
proof audit --fuzz
The first command is the shape gate. The second is the Go fuzzer on that target. The third is the opt-in fuzz lane. A real Fuzz* with no recorded run still passes the carrier check and stays uncovered in
obligation_evidence_complete. That split is honest: shape is not provenance.
02 · The exhibit
The CI log still says fuzz. The annotation still sits on TestParse. Click the tabs.
CI / go test -fuzz
This entry point
TestParse takes *testing.T. go test -fuzz will not mutate it. The size-cap case is one input.
Not a fuzz functionCI / go test -fuzz
Still the job. Still a green log. Keep the fuzzer.
Keep the runnerProof
Same parse. A green fuzz job, or this carrier. Click the tabs.
| What the record lists | What Proof actually does | What a green cell is not |
|---|---|---|
| Fuzzer | Keep go test -fuzz, libFuzzer, AFL, Honggfuzz | Not a mutation engine. Proof does not generate the bytes. |
| Carrier | fuzz_evidence_carrier_valid requires FuzzName(*testing.F) in Go, or LLVMFuzzerTestOneInput in C/C++ |
Not a Test* with one oversize pin. |
| Unchecked languages | Rust, JS/TS, Python :fuzz annotations pass as unchecked until a plugin lands |
Not a claim that cargo-fuzz or atheris was run. |
| Provenance | obligation_evidence_complete wants a fresh passing run bound to that artifact |
Not this check. Shape is not a recorded fuzztime. |
| CI | Default-on warning. Fuzz execution stays opt-in | Not a signed V&V certificate. Jama still authors. |
The carrier looks like this on a test file:
// Verifies: SYS-REQ-100
// SYS-REQ-100:denial_of_service_resistant:fuzz
func FuzzParse(f *testing.F) {
f.Add([]byte(""))
f.Fuzz(func(t *testing.T, data []byte) {
_, _ = Parse(data)
})
}
Keep the specific Test* pin next to it. Move the :fuzz triple onto the Fuzz* target. If no harness exists yet, delete the triple rather than credit a unit test. The obligation then re-surfaces as an evidence gap. That is the correct signal.
We have not run AFL, libFuzzer, and Proof on the same frozen corpus, and we have not scored this carrier as a proof of the Go. The loss is named, not scored.
03 · The honest loss
The command refuses a silent pass. Proof will not pretend that YAML is a crash, or that a warning is an error.
go test -fuzz still owns the mutations. libFuzzer, AFL, and Honggfuzz still own C/C++ campaigns. OWASP still owns the glossary. Jama still authors the shall. Proof stores the :fuzz triple in the repo and fails a silent pass when the annotated symbol is not a fuzz entry point. It does not independently prove the obligation is the right obligation. It does not execute the fuzzer unless you ran one. Default-on is a warning. Unrecognised languages pass as
unchecked.
Proof is not AFL. It is not ClusterFuzz. It is not a coverage-guided mutation engine. Jama still authors. VectorCAST or LDRA still win at a qualified C toolchain.
Two implementations on the same input still live on differential testing. Authored properties as fixtures still live on property based testing. A crash the suite missed still lives on a crash the whole suite missed.
04 · Nearby questions
What is fuzzing? Same URL. Fuzzing and fuzz testing are one cluster. The software qualifier
software fuzzing is this page, not a twin.
How do I start fuzzing in Go? The Go tutorial still wins that setup.
proof audit --check fuzz_evidence_carrier_valid is the shape gate after the harness exists.
Is Proof an alternative to AFL or libFuzzer? No. Keep the runner. Proof checks that the claimed evidence sits on a real entry point. Jama still authors.
Does a green go test -fuzz job count? The job counts as a run. It does not count as this carrier if the :fuzz triple is on a Test* function.
What is differential testing? That H1 lives on
differential testing.
proof differential fuzz hunts disagreement between two shims. It is not this page.
What is property based testing? That H1 lives on property based testing.
What is negative testing? That H1 lives on negative testing. Fuzzing mutates. The reject-path witness is a named invalid case on a security class.