mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 15:21:30 +02:00
Every place that accepts user-supplied YAML — engine import, an engine's stored yaml_source, nuclei template upload and report theme upload — could be handed a billion-laughs document. Measured on engine import: 379 bytes of nested aliases blocked the event loop for 36 seconds. A request issued alongside it timed out at 20s and the next took 13s, against a 4ms baseline. Any authenticated user could do it, repeatedly. The cost is not in the parser. PyYAML shares an aliased node rather than copying it, so safe_load returns in 2ms; the expansion happens in whatever walks the result as a tree, which for us is pydantic — it has no node cache, so nine levels of nine aliases is 43 million visits. Bounding the parser would have missed this entirely. load_document() now measures the document the way a consumer will walk it and stops at 100,000 values, which is four times the largest thing reNgine ships (a 24,133-node nuclei template, still indexed in 0.67s) and far below the bomb. All four call sites use it and each reports the limit in its own idiom — an HTTP 400, a template rejection reason, a theme error. Same payload after: 0.03s and refused, with API latency during the attack at 36ms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6LcH8uUddjJbKrahf34ys