{"schema":"threatline.record/2","record":{"id":"GHSA-j843-8p47-rhxp","aliases":["GHSA-j843-8p47-rhxp"],"summary":"ReDoS via untrusted regex patterns freezes the Docker server","details":"### Summary\r\n\r\nThe Docker API server lets an untrusted request body supply regular expressions in three places: `RegexExtractionStrategy.custom`, the `regex` field type in a `JsonCssExtractionStrategy` / `JsonXPathExtractionStrategy` schema, and `RegexChunking.patterns`. An authenticated API client can send a pattern with catastrophic backtracking and a page that triggers it. Python's `re` holds the interpreter lock while it matches, so one request stops the whole server, for all users, for as long as the match runs.\r\n\r\n### Details\r\n\r\n`RegexExtractionStrategy` and `RegexChunking` are in `UNTRUSTED_ALLOWED_TYPES` in `crawl4ai/async_configs.py` but have no entry in `UNTRUSTED_FIELD_ALLOWLIST`, so `_filter_untrusted_fields()` keeps their `custom` and `patterns` fields. `RegexExtractionStrategy.__init__()` compiles each custom pattern and `extract()` runs it with `finditer()` over the crawled content. `JsonCssExtractionStrategy` runs `re.search(pattern, value)` for every schema field of type `regex`. `RegexChunking.chunk()` runs `re.split(pattern, ...)`. None of these bound the pattern or the run time.\r\n\r\nExtraction runs in a worker thread, but Python's `re` engine does not release the GIL during a match, so the event loop cannot serve any other request. The crawl deadline (`asyncio.wait_for`) cannot interrupt the match either. With the pattern `(a+)+$` and a page containing `\"a\" * n + \"X\"`, the run time grows about 4x for every 2 more characters: about 1.5 s at n=24, and hours at n=40.\r\n\r\n### Proof of concept\r\n\r\nServer: crawl4ai 0.9.4 Docker image with default settings and a valid API token in `$TOKEN`.\r\n\r\nServe a page at `https://attacker.example/` whose text holds `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX`.\r\n\r\n```bash\r\ncurl -s http://localhost:11235/crawl -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' -d '{\r\n  \"urls\": [\"https://attacker.example/\"],\r\n  \"crawler_config\": {\"type\": \"CrawlerRunConfig\", \"params\": {\r\n    \"extraction_strategy\": {\"type\": \"RegexExtractionStrategy\", \"params\": {\"custom\": {\"evil\": \"(a+)+$\"}}}\r\n  }}\r\n}'\r\n```\r\n\r\nResult: the request is accepted. The match backtracks exponentially, and the whole server stops answering, including `/health`, because `re` holds the GIL. The crawl deadline does not stop it.\r\n\r\nThe same works with `RegexChunking` (`\"chunking_strategy\": {\"type\": \"RegexChunking\", \"params\": {\"patterns\": [\"(a+)+$\"]}}`) and with a `\"type\": \"regex\", \"pattern\": \"(a+)+$\"` field in a `JsonCssExtractionStrategy` schema.\r\n\r\nLibrary-level timing, no network: with 24 `a` characters each case takes 1 to 3 seconds, and every 2 more characters is about 4 times slower.\r\n\r\n### Impact\r\n\r\nAny authenticated API client can make the Docker server stop answering every request, including health checks, with one `/crawl` request. No optional extras are needed. Confidentiality and integrity are not affected.\r\n\r\nThe library used directly is not affected unless an application passes untrusted regular expressions into these strategies.\r\n\r\n### Patch\r\n\r\nUntrusted request bodies may no longer supply regular expressions: `RegexExtractionStrategy.custom`, `RegexChunking.patterns`, and schema fields of type `regex` in `JsonCssExtractionStrategy`, `JsonXPathExtractionStrategy` and `JsonLxmlExtractionStrategy` are refused. The built-in `RegexExtractionStrategy` patterns stay available. Fixed in 0.9.5.\r\n\r\n### Workaround\r\n\r\nAt your reverse proxy, reject `crawler_config` bodies that contain `RegexExtractionStrategy`, `RegexChunking`, or a schema field with `\"type\": \"regex\"`. Give API tokens only to trusted clients.\r\n\r\n### Credit\r\n\r\nReported by Zhixi \"Jace\" Sun ([manus-use](https://github.com/manus-use)), independent security researcher.","categories":["Unclassified"],"product":"crawl4ai","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/unclecode/crawl4ai/security/advisories/GHSA-j843-8p47-rhxp","source_modified_at":"2026-10-15T10:01:54Z","fetched_at":"2026-10-27T04:51:10.072820Z","package":{"ecosystem":"pip","name":"crawl4ai"},"range":">= 0.9.0, <= 0.9.4"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/unclecode/crawl4ai/security/advisories/GHSA-j843-8p47-rhxp","source_modified_at":"2026-10-15T10:01:54Z","fetched_at":"2026-10-27T04:51:10.072820Z","package":{"ecosystem":"pip","name":"crawl4ai"},"version":"0.9.5"}],"cvss":6.5,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-15T10:01:54Z","modified_at":"2026-10-15T10:01:54Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/unclecode/crawl4ai/security/advisories/GHSA-j843-8p47-rhxp","fetched_at":"2026-10-27T04:51:10.072820Z","source_modified_at":"2026-10-15T10:01:54Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-15T10:01:54Z","content_hash":"037d06f3f59a65d405bbfb36282ba0285b748fb18f827132b900a5dcf67d2c2c"}}
