Testland
Browse all skills & agents

restler-fuzzing

Runs stateful REST API fuzzing using Microsoft's RESTler - infers producer-consumer dependencies from an OpenAPI spec, drives sequences of requests (POST → GET → DELETE chains), and reports 5xx errors, resource leaks, and hierarchy violations. Wraps the canonical 4-stage workflow (compile → test → fuzz-lean → fuzz). Use when the API is stateful (resources are created, queried, modified, deleted) and Schemathesis's stateless fuzzing is missing the multi-step bugs.

Install with skills.sh (any agent)

npx skills add testland/qa --skill restler-fuzzing
View source

restler-fuzzing

Overview

RESTler is a stateful REST API fuzzer that finds security and reliability bugs (restler-readme (opens in new window)). 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.

When to use

  • The API is stateful: resources are created, queried, modified, deleted, and bugs lurk in the dependency chains.
  • A team is on Schemathesis already but suspects multi-step bugs aren't being reached. RESTler is the next layer down.
  • A new service is launching and the team wants the broadest possible auto-coverage before releasing - particularly for cloud services.
  • The API exposes a complete OpenAPI 2.0 / 3.x specification (RESTler consumes JSON or YAML).

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.

Install

Prerequisites per restler-readme (opens in new window): Python 3.12.8 and .NET 8.0.

From source

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 (opens in new window).)

Via Docker (preferred for CI)

docker build -t restler .

For one-off CI runs:

docker run --rm -v "$PWD/output:/output" restler ...

The four-stage workflow

Per restler-readme (opens in new window):

Stage 1 - Compile

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.json

Output: Compile/grammar.py plus Compile/dict.json (a starter dictionary RESTler uses to seed parameter values).

Stage 2 - Test (smoke)

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_ssl

If endpoint coverage is below the team's threshold (often 80%+), stop and amend the dictionary or grammar before continuing.

Stage 3 - Fuzz-lean

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_ssl

Stage 4 - Fuzz (deep)

Aggressive 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   # hours

Bug detection

Per restler-readme (opens in new window), RESTler reports two bug categories:

CategoryTrigger
5xx errorsAny 5xx response is a bug; RESTler triages by URL pattern.
Checker violationsTargeted 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.

Authentication

RESTler doesn't bake in an auth strategy; for tokens, the canonical pattern is:

  1. Obtain the token with a short pre-script.
  2. Pass it via the dictionary's 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.json

For OAuth flows that rotate tokens during a long fuzz run, supply a refresh script RESTler invokes periodically. See restler-readme (opens in new window) for the --token_refresh_command flag and cadence settings.

Output and triage

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:

  1. Read bug_replay_log.txt - the deterministic sequence.
  2. Confirm the bug locally with restler replay --replay_log <path>.
  3. File a ticket with the replay log attached; the replay log can be converted into a project-specific failing regression test.

CI integration

RESTler is not a per-PR tool by default - fuzz-lean takes 5-30 minutes typically; deep fuzz is hours. The canonical cadence:

CadenceStageTime budgetTrigger
Per-PRTest only<1 minuteSchema-validation smoke; fail fast.
NightlyFuzz-lean30-60 minutesNew endpoint regression scan.
WeeklyFuzz4-12 hoursDeep state-machine exploration.
Pre-releaseFuzz24-72 hoursFinal 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-patterns

Anti-patternWhy it failsFix
Running RESTler on productionGenerated 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 PRHours-long PR CI; no team accepts that.Fuzz is nightly / weekly only.
Triaging bugs without replay-log confirmationSome bugs are environmental (test DB state); confirm reproducibility.Always run restler replay on each bug before opening a ticket.
Letting bug counts grow unboundedBacklog 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.

Limitations

  • OpenAPI-driven only. No GraphQL support; for GraphQL fuzzing, use schemathesis-fuzzing.
  • Heavy infra. Python 3.12 + .NET 8 prereqs are non-trivial; Docker is the practical install path.
  • Run-time cost. Deep fuzz is hours-to-days; budget accordingly. Fuzz-lean is the cost-effective sweet spot for nightly runs.
  • No native parallelism. RESTler runs single-threaded; for parallelism, shard at the endpoint level via multiple invocations with different grammar subsets.
  • State across runs. Each fuzz run is independent - there's no built-in mechanism to remember "we've explored these states already." Use the progress/ directory for manual checkpointing.

References

  • restler-readme (opens in new window) - main repo: install, 4-stage workflow, bug categories, authentication patterns.
  • schemathesis-fuzzing - stateless complement; lower setup cost; cover happy / boundary on PRs.

RESTler output tree and nightly CI workflow

View source (opens in new window)

RESTler output tree and nightly CI workflow

Companion to the restler-fuzzing skill: the full results directory layout and the complete nightly GitHub Actions workflow. Sourced from restler-readme (opens in new window).

Output tree

RestlerResults/
  Compile/
    grammar.py
    dict.json
  Test/
    coverage_failures_to_investigate.txt
    bug_buckets/
  FuzzLean/
    bug_buckets/                   # one folder per unique bug pattern
      Bug_1/
        bug_replay_log.txt
        bug_request.txt
  Fuzz/
    bug_buckets/
    progress/

Nightly CI workflow (GitHub Actions)

# .github/workflows/restler-nightly.yml
name: restler-nightly

on:
  schedule:
    - cron: '0 2 * * *'   # nightly at 02:00 UTC
  workflow_dispatch:

jobs:
  fuzz-lean:
    runs-on: ubuntu-latest
    timeout-minutes: 90
    steps:
      - uses: actions/checkout@v5

      - name: Build RESTler image
        run: docker build -t restler ./.restler/

      - name: Compile grammar
        run: |
          docker run --rm -v "$PWD:/work" restler \
            compile --api_spec /work/openapi.json

      - name: Fuzz-lean
        env:
          API_TOKEN: ${{ secrets.STAGING_API_TOKEN }}
        run: |
          docker run --rm -v "$PWD:/work" \
            -e API_TOKEN="$API_TOKEN" \
            restler fuzz-lean \
              --grammar_file /work/Compile/grammar.py \
              --dictionary_file /work/Compile/dict.json \
              --target_ip staging.example.com \
              --target_port 443 \
              --use_ssl

      - name: Upload bug buckets
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: restler-results
          path: |
            RestlerResults/
          retention-days: 30

      - name: Fail if bugs found
        run: |
          BUG_COUNT=$(find RestlerResults -name 'bug_replay_log.txt' | wc -l)
          if [ "$BUG_COUNT" -gt 0 ]; then
            echo "::error::RESTler found $BUG_COUNT bug(s) - see artifacts"
            exit 1
          fi

Related skills

api-chaos-runner

Runs the project's existing API tests under injected network chaos - latency, timeouts, dropped connections, bandwidth caps, packet loss - via Toxiproxy (notes on Pumba / Gremlin / LitmusChaos). Builds a per-scenario chaos matrix and reports which assertions break under which conditions, verifying resilience patterns (retry, circuit-breaker, timeout, fallback). Unlike schemathesis-fuzzing and restler-fuzzing, which generate new tests from a schema, this drives your EXISTING example-based suite.

karate-testing

Authors Karate `.feature` files using its Gherkin-flavored DSL for HTTP API tests, leverages the `match` keyword with fuzzy validators (#number / #string / #regex / contains / arrays), runs the suite via JUnit 5 plus Maven Surefire, and produces JUnit XML for CI gating. Use when the project is on the JVM and prefers a feature-file authoring flow over Java-DSL fluent chains; for those fluent chains use restassured-testing, for the same YAML-style flow on a Python/pytest stack use tavern-testing.

postman-collections

Repairs Postman and Newman runs in CI - a reporter that never writes the HTML file the pipeline expects, a nightly job that fires requests as fast as it can until a partner API rate-limits it, or a report with one row per request when the collection asserts a dozen things between them. Authors Postman collections (requests, tests, variables, environments), runs them headless via the Newman CLI, configures reporters (cli / json / junit / html) for CI artifact upload, and drives data-driven runs from JSON / CSV iteration files. Use when HTTP API tests are authored in Postman and need to run, pace themselves, and report correctly in CI.

restassured-testing

Strengthens and speeds up JVM API test suites - assertions so loose that an empty `200 OK` passed for three days, a 220-test suite spending fourteen minutes mostly waiting on sequential HTTP calls, or endpoints exercised through a hand-rolled JDK HTTP client with JSON parsed by hand. Authors REST Assured (Java) tests in the given().when().then() BDD-style DSL: status code and JSON / XML path assertions, authentication (Basic, OAuth2, API key), Maven / Gradle dependencies, JUnit 5 execution, and Surefire / JaCoCo reports for CI gating. Use when the project is on the JVM and its API tests miss real assertions, run too slowly, or are written by hand.

schemathesis-fuzzing

Generates property-based API tests automatically from an OpenAPI 2/3.x or GraphQL schema using Schemathesis, runs them via the `schemathesis run` CLI or as a pytest decorator, configures the canonical checks (status_code_conformance, response_schema_conformance, content_type_conformance, response_headers_conformance, not_a_server_error), and gates CI on schema-conformance failures plus 5xx detection. Use when the project ships an OpenAPI or GraphQL schema and the team wants schema-driven coverage that scales as the API evolves.