@greymoth/api-robustness-lab
v0.1.0
Published
Safe-by-default API robustness & fuzz harness for APIs you own. Generates mutation cases from OpenAPI, detects 5xx/schema drift, and uniquely runs a multi-token authorization oracle (BOLA/BFLA). Refuses non-localhost targets unless explicitly allowlisted
Downloads
61
Maintainers
Readme
api-robustness-lab (arl)
A safe-by-default robustness & fuzz harness for HTTP APIs you own. It reads an OpenAPI 3 spec, generates mutation cases, and reports where your API crashes (5xx), leaks internals, or drifts from its declared response schema — and uniquely runs a multi-token authorization oracle to catch broken object/function-level authorization (OWASP API1 BOLA / API5 BFLA), the gap that crash-fuzzers miss.
This tool tests your own / consented assets. It refuses non-localhost targets unless you explicitly allowlist and confirm them. It does not discover, enumerate, or attack third-party APIs.
Why it exists
Prior art (Schemathesis, RESTler, CATS, Dredd — see ../GitRepo/REFERENCES.md)
is strong at crash/schema fuzzing but does not automatically detect broken
authorization, because that needs a multi-identity oracle. arl fills exactly
that gap, behind a hard safety boundary. It does not try to beat those tools
on raw fuzzing depth or speed (it won't — see docs/AUDIT.md).
Safety model (the load-bearing feature)
- Default target host allowlist:
localhost/127.0.0.1only. - Any other host must be
--allow-host <host>and--confirm. - Wildcards rejected. Non-http(s) rejected. Fail-closed: any doubt → refuse,
before a single request is sent.
--dry-rungenerates cases without sending. --max-rpsthrottles to protect the target you own.
Requirements
Node >= 22.18 (native TypeScript type-stripping — no build step).
npm install only needed for npm run typecheck.
Use
# 1. run the bundled fragile mock API (demo target)
node fixtures/serve.ts 4581
# 2. fuzz it + run the authorization oracle
node src/cli.ts run \
--spec fixtures/openapi.json \
--target http://127.0.0.1:4581 \
--auth fixtures/auth.json \
--json report.jsonExit code is 1 when any high/critical finding exists (CI gate), 0 when clean,
3 when the target is refused by the safety guard.
Authorization oracle config (--auth)
{
"profiles": [
{ "name": "alice", "headers": { "Authorization": "Bearer alice" } },
{ "name": "bob", "headers": { "Authorization": "Bearer bob" } },
{ "name": "admin", "headers": { "Authorization": "Bearer admin" }, "privileged": true }
],
"probes": [
{ "path": "/users/2", "kind": "object", "owner": "bob" },
{ "path": "/admin/stats", "kind": "function" }
]
}For an object probe, any non-owner profile that gets a 2xx is a BOLA
finding. For a function probe, any non-privileged profile that gets a 2xx is a
BFLA finding.
What it detects
| Finding | Meaning |
|---|---|
| unexpected_5xx | a generated input crashed the endpoint |
| error_leak | the error response exposed a stack trace / internal detail |
| schema_violation | response body did not match the declared response schema |
| valid_request_rejected | a well-formed request was rejected (≥400) |
| bola | broken object-level authorization (OWASP API1) |
| bfla | broken function-level authorization (OWASP API5) |
Test
npm test # 23 tests: safety guard, schema, generator, integration, auto-auth, stateful chains
npm run typecheckStateful sequences (--auto-stateful)
Single-shot fuzzing can't reach bugs that only exist after a resource is created.
arl infers producer→consumer chains from the spec — a POST returning an id
paired with a GET <collection>/{id} — runs them in order, threads the captured
id, and checks each response. In the bundled mock this surfaces a schema_violation
on GET /orders/{id} (a total returned as a string) that the single-shot pass
never reaches, because GET /orders/{sampleId} hits an empty store and 404s. You
can also declare scenarios explicitly via RunConfig.scenarios.
The integration test spins up the bundled fragile mock on localhost and asserts
every planted bug is found while /health stays clean.
Honest scope
MVP. The fuzzer is deterministic and shallow (no stateful sequences, no
property-based shrinking); the auth oracle needs you to declare probes (it does
not auto-discover object-id endpoints). See docs/AUDIT.md for the full
limitations and where it should not be oversold. Production callers wanting
deep schema handling should pair this with ajv / Schemathesis.
License
MIT. No code copied from surveyed tools (AGPL/GPL ones referenced only).
Threat-intel & prior-art provenance in ../GitRepo/REFERENCES.md.
