Every request is an in-process WHATWG Request
Attack code, not people.
No sockets. Ever.
Crashtest drives a seeded adversarial suite against your real handlers - routing, guards, state, volume - through a fake transport. The output is a hardening ledger your CI fails on new EXPLOITED findings or a REFUSED->EXPLOITED regression. Same commit, same findings, forever.
# 60 seconds - point it at your handlers npx crashtest init # edit crashtest.config.js - routes list your handlers, env the fake d1/kv/secrets npx crashtest run # -> labs/hardening.md / labs/crashledger.json / exit 0 = hold, exit 1 = a finding regressed 🛡 EFF. auth/secret-watermark 🛡 guards/burst-wall 🛡 state/enumeration-oracle ⚡ guards/hygiene-no-store (2xx write with no Cache-Control - real findings look like this)
The pipeline
How a run turns handlers into evidence.
Request; the fake d1/kv/env mirrors your backend. Nothing leaves the process.packs: accepts any directory on disk - another repo's checkout, a vendored npm module - and a self-contained pack bundles its own imports, so adopting it is one config line.Why it exists
LLMs write programs fast. Programs that route, store, and spend budget need their own fire drill.
section Fail at cause, loudly
No swallowed errors, no phantom 200s - a handler that never ends its response is surfaced as a finding, not papered over.
section Deterministic attacks
Seeded RNG from commit to commit. A scratch-run today reproduces bit-for-bit in CI tomorrow.
section Cost-aware from day one
Every request is accounted in ops - amplification attacks get named per-request and bounded, not vibes.
section Fits CI, not just dev
Zero runtime deps, plain Node, one command. Plus a composite GitHub Action to gate other repos.
Questions people ask first
The short versions. Full answers on the FAQ page.
Is it safe on real code?
Yes - that is the whole design. No socket, no network, no live server: your handlers receive in-process Request objects backed by a fake D1/KV/env. Nothing leaves the process.
How is it different from SAST or pentest tools?
Scanners read code and never run it, so behavioral holes survive. Pentest tools need a live server and traffic. Crashtest runs the real handlers - in-process - before deploy.
What does CI actually gate on?
Exit 1 only on a NEW EXPLOITED finding or a REFUSED-to-EXPLOITED regression. Accepted risks with an owner + bound never re-alarm. Undeclared routes degrade to skipped-REFUSED - never a false positive.
Can I write my own attacks?
Yes - packs, and they travel. One ESM file matching a small contract in packs/public/ (tracked) or packs/local/ (gitignored); a self-contained pack's manifest can even bundle its own imports so adopting it takes zero config wiring. run --module-plan shows exactly what would fire.