Use when a stateful REST API (resources created, read, changed and deleted in sequence) needs fuzzing that finds multi-step bugs stateless fuzzers miss. Runs Microsoft RESTler from an OpenAPI spec (compile, test, fuzz-lean, fuzz) and reports 5xx errors, resource leaks and hierarchy violations.
The canonical home for this skill is testland/restler-fuzzing
RESTler is a stateful REST API fuzzer that finds security and reliability bugs
(restler-readme). Its differentiator vs. stateless fuzzers like
schemathesis-fuzzing is that it infers producer-consumer dependencies from
the OpenAPI spec - if POST /resources returns an id and GET /resources/{id}
accepts that id, RESTler sequences them in that order to reach deeper state.
If the API is stateless (search endpoints, calculator endpoints, report-generation endpoints), Schemathesis is sufficient and lighter to operate. RESTler shines on resource lifecycle APIs.
Prerequisites per restler-readme: Python 3.12.8 and .NET 8.0.
git clone https://github.com/microsoft/restler-fuzzer.git
cd restler-fuzzer
mkdir restler_bin
python ./build-restler.py --dest_dir "$(pwd)/restler_bin"(Per restler-readme.)
docker build -t restler .For one-off CI runs:
docker run --rm -v "$PWD/output:/output" restler ...Per restler-readme:
Generate a RESTler grammar from the OpenAPI spec. The grammar captures the producer-consumer dependencies RESTler will exploit during fuzzing.
restler compile --api_spec openapi.jsonOutput: Compile/grammar.py plus Compile/dict.json (a starter
dictionary RESTler uses to seed parameter values).
Run a single end-to-end pass to verify the spec, auth, and target URL are wired correctly. Measures endpoint coverage - what fraction of the API RESTler can reach with the current grammar / dictionary.
restler test --grammar_file Compile/grammar.py \
--dictionary_file Compile/dict.json \
--target_ip <api-host> \
--target_port 443 \
--use_sslIf endpoint coverage is below the team's threshold (often 80%+), stop and amend the dictionary or grammar before continuing.
One pass through every endpoint with default checkers active - fast bug discovery focused on the obvious failure modes.
restler fuzz-lean --grammar_file Compile/grammar.py \
--dictionary_file Compile/dict.json \
--target_ip <api-host> \
--target_port 443 \
--use_sslAggressive breadth-first exploration. Run for a fixed time budget (hours to days for a comprehensive run).
restler fuzz --grammar_file Compile/grammar.py \
--dictionary_file Compile/dict.json \
--target_ip <api-host> \
--target_port 443 \
--use_ssl \
--time_budget 8.0 # hoursPer restler-readme, RESTler reports two bug categories:
| Category | Trigger |
|---|---|
| 5xx errors | Any 5xx response is a bug; RESTler triages by URL pattern. |
| Checker violations | Targeted sequences look for resource leaks, hierarchy violations (e.g. accessing a resource after deletion), and use-after-free patterns. |
Each bug appears in RestlerResults/.../bug_buckets/ with a replay
log - the exact sequence of requests that triggered it. Replay
logs are deterministic - the bug reproduces on demand.
RESTler doesn't bake in an auth strategy; for tokens, the canonical pattern is:
restler_custom_payload_header for
the Authorization slot.TOKEN=$(curl -s ... | jq -r .access_token)
echo "{\"restler_custom_payload_header\": {\"Authorization\": [\"Bearer $TOKEN\"]}}" > auth-dict.json
restler fuzz-lean ... --dictionary_file auth-dict.jsonFor OAuth flows that rotate tokens during a long fuzz run, supply a
refresh script RESTler invokes periodically. See restler-readme
for the --token_refresh_command flag and cadence settings.
Results land under RestlerResults/ with Compile/, Test/, FuzzLean/, and
Fuzz/ subtrees; each unique bug gets a bug_buckets/Bug_N/ folder holding a
bug_replay_log.txt and bug_request.txt. The full directory layout is in
references/triage-and-ci.md.
Per-bug triage:
bug_replay_log.txt - the deterministic sequence.restler replay --replay_log <path>.RESTler is not a per-PR tool by default - fuzz-lean takes 5-30 minutes typically; deep fuzz is hours. The canonical cadence:
| Cadence | Stage | Time budget | Trigger |
|---|---|---|---|
| Per-PR | Test only | <1 minute | Schema-validation smoke; fail fast. |
| Nightly | Fuzz-lean | 30-60 minutes | New endpoint regression scan. |
| Weekly | Fuzz | 4-12 hours | Deep state-machine exploration. |
| Pre-release | Fuzz | 24-72 hours | Final security / reliability gate. |
The nightly cadence runs on a schedule: cron trigger: build the RESTler
Docker image, compile the grammar from openapi.json, run fuzz-lean against
staging, upload RestlerResults/ as an artifact (if: always()), then fail the
job when any bug_replay_log.txt exists (find ... | wc -l). The complete
GitHub Actions workflow is in
references/triage-and-ci.md.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Running RESTler on production | Generated requests mutate live data; 5xx alerts spam oncall. | Always target staging; production is for runtime monitoring, not fuzz traffic. |
| Skipping Stage 2 (Test) | Spec-vs-impl drift is invisible until Stage 4 wastes hours. | Always run Test first; check coverage before fuzz-lean. |
| Stage 4 (Fuzz) on every PR | Hours-long PR CI; no team accepts that. | Fuzz is nightly / weekly only. |
| Triaging bugs without replay-log confirmation | Some bugs are environmental (test DB state); confirm reproducibility. | Always run restler replay on each bug before opening a ticket. |
| Letting bug counts grow unbounded | Backlog of unfixed bugs becomes noise; team learns to ignore RESTler reports. | Treat each bug as a P1 / P2 ticket; fix or document waiver per the team's escape-defect policy. |
schemathesis-fuzzing.progress/ directory for manual checkpointing.schemathesis-fuzzing -
stateless complement; lower setup cost; cover happy / boundary on
PRs.testland/qa
aa16752
Canonical home
since Jul 23, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.