Skip to content
Back to skills

Fuzz

ASecurity

Runs the repository's `go test -fuzz` targets, adds new ones, and triages crashers. CI runs `.github/workflows/fuzz.yml`: 30s per target on PRs, 10m per target nightly, sharded.

  • 36 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 29, 2026
developmentgobashnodegit

Works with

  • cursor

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add luthersystems/elps --skill fuzz --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Fuzz?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Fuzz
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/luthersystems-fuzz/badge)](https://www.skillsdirectory.com/skills/luthersystems-fuzz)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# /fuzz — Native Go Fuzzing Skill

Runs the repository's `go test -fuzz` targets, adds new ones, and triages
crashers. CI runs `.github/workflows/fuzz.yml`: 30s per target on PRs, 10m per
target nightly, sharded.

## Trigger

Use when asked to fuzz, add or change a fuzz target, investigate a fuzz CI
failure, or triage a file under some package's `testdata/fuzz/<Target>/`.

## Run

```bash
make fuzz-list                                  # authoritative list of targets (discovered with go test -list)
make fuzz                                       # every target, 30s each (the PR budget)
make fuzz FUZZTIME=10m                          # the nightly budget
make fuzz FUZZ_PKGS=./parser/rdparser/...       # one package
go test -run='^$' -fuzz='^FuzzEval$' -fuzztime=60s ./lisp/   # one target by hand
```

`scripts/fuzz.sh` refuses to run without a parsable `FUZZTIME` and caps every
invocation with a hard `-timeout`; never write a `-fuzz` invocation without
`-fuzztime` (CI checks for that). `FUZZ_TAGS=elpscheck` fuzzes the tagged
build, whose targets are otherwise invisible.

Plain `go test ./...` (and so `make test`) replays every committed seed and
crasher in `testdata/fuzz/` as a regression case.

## Triage a crasher

1. Find it: `go test -fuzz` writes `testdata/fuzz/<Target>/<hash>` in the
   target's package; CI uploads the same file as an artifact.
2. Reproduce without fuzzing: `go test -run='^<Target>$/^<hash>$' ./<pkg>/`.
3. Classify: a real defect in the code under test, a harness defect (budget
   too small for a terminating program, a watchdog on wall clock), or a
   toolchain race. `make fuzz-classify-test` covers how `scripts/fuzz.sh`
   reports each; read its output before assuming the product is at fault.
4. Fix the root cause with a focused regression test, and **commit the
   crasher file** so `go test` keeps replaying it.

## Add a target

1. Put it in the package's `fuzz_test.go` (or `<area>_fuzz_test.go`), named
   `FuzzXxx`. Discovery is automatic — there is no registration table.
2. Seed from `internal/fuzzseed` (`All()`, `LispSources()`, `Adversarial()`,
   `Pathological()`) plus target-specific adversarial input.
3. Assert a property with teeth (round-trip equality, no internal panic,
   termination), not merely "did not crash".
4. Any termination watchdog must use `internal/fuzzwatch` (scheduled-time
   budget, with a floor enforced by its call-site guard test), not a bare
   `time.After`.
5. Run `make fuzz-budget-check`: each new target adds a `FUZZTIME` to the
   nightly sweep, and the check derives whether `fuzz.yml`'s
   `timeout-minutes` still holds. Follow its printed options rather than
   raising the timeout by hand.
6. After touching `scripts/fuzz.sh` or `fuzz.yml`, run `make ci-gates-test`
   and `make fuzz-classify-test`.

## Hazards that are load-bearing

Read the header comment of the target before changing it; these are the
condensed reasons.

- **Evaluator (`lisp.FuzzEval`, `lisp/eval_fuzz_test.go`)** — evaluation is
  Turing-complete and `go test -fuzz` has no per-input deadline, so every
  evaluation runs under explicit budgets (`WithMaxSteps`,
  `WithMaxTailIterations`, `WithMaximumPhysicalStackHeight`, `WithMaxAlloc`, a
  context deadline) plus a watchdog. `env.eval` recovers Go panics into an
  ordinary-looking `*LVal`, so the real assertion is
  **`lisp.IsInternalPanic(result) == false`**. Eval seeds are executed, so
  `internal/fuzzseed/evalseed.go` is hand-written and does **not** seed from
  `LispSources()`. Seeds are split: `EvalRunaway` must error (a budget
  stopping an infinite loop is correct) and `EvalTerminating` must complete
  without error (a bounded program tripping a budget is a defect).
- **Debugger (`lisp/x/debugger.FuzzDebugEval`)** — attaching a debugger
  disables tail-call optimisation and stamps `MacroExpansionInfo` onto
  macro-expanded nodes, so it is a different eval path from `FuzzEval`'s.
- **Language server (`lsp.FuzzLSPSession`, `lsp/lsp_fuzz_test.go`)** — a
  fuzzer-chosen sequence of requests against fuzzer-chosen documents and
  cursor positions; the only target that reaches `analysis/` and `lint/`. The
  server has four blanket `recover()`s, so "no crash" means nothing until they
  are accounted for; the header documents which are neutralised, which is
  detected, and which is not covered.

## Checklist

- [ ] Target asserts a real property, with budgets and a `fuzzwatch` watchdog if it evaluates
- [ ] Seeds come from `internal/fuzzseed`
- [ ] `make fuzz-budget-check` passes after adding a target
- [ ] Crasher fixed at the root cause and its `testdata/fuzz/` file committed
- [ ] `make test` passes (replays the corpus)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.