{"record":{"id":"f8edf7414a1efeaf","repo":"unclecode/crawl4ai","slug":"invalid-value-for-webhook-header-name","errorCode":null,"errorMessage":"invalid value for webhook header {name}","messagePattern":"invalid value for webhook header (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"deploy/docker/webhook.py","lineNumber":77,"sourceCode":"_MAX_WEBHOOK_HEADERS = 20\n_MAX_WEBHOOK_HEADER_VALUE = 2048\n\n\ndef sanitize_webhook_headers(headers: Optional[Dict[str, str]]) -> Dict[str, str]:\n    \"\"\"Validate user-supplied webhook headers; raise ValueError on any bad one.\"\"\"\n    if not headers:\n        return {}\n    if len(headers) > _MAX_WEBHOOK_HEADERS:\n        raise ValueError(\"too many webhook headers\")\n    clean: Dict[str, str] = {}\n    for name, value in headers.items():\n        if not isinstance(name, str) or not _WEBHOOK_HEADER_NAME.match(name):\n            raise ValueError(f\"invalid webhook header name: {name!r}\")\n        if name.lower() in _WEBHOOK_DENY_HEADERS:\n            raise ValueError(f\"webhook header not allowed: {name}\")\n        sval = str(value)\n        if len(sval) > _MAX_WEBHOOK_HEADER_VALUE or any(c in sval for c in \"\\r\\n\\x00\"):\n            raise ValueError(f\"invalid value for webhook header {name}\")\n        clean[name] = sval\n    return clean\n\n\nclass WebhookDeliveryService:\n    \"\"\"Handles webhook delivery with exponential backoff retry logic.\"\"\"\n\n    def __init__(self, config: Dict):\n        \"\"\"\n        Initialize the webhook delivery service.\n\n        Args:\n            config: Application configuration dictionary containing webhook settings\n        \"\"\"\n        self.config = config.get(\"webhooks\", {})\n        self.max_attempts = self.config.get(\"retry\", {}).get(\"max_attempts\", 5)\n        self.initial_delay = self.config.get(\"retry\", {}).get(\"initial_delay_ms\", 1000) / 1000\n        self.max_delay = self.config.get(\"retry\", {}).get(\"max_delay_ms\", 32000) / 1000","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/webhook.py#L59-L95","documentation":"ValueError from sanitize_webhook_headers when a header value is over 2048 chars (_MAX_WEBHOOK_HEADER_VALUE) or contains CR, LF, or NUL. The CR/LF check blocks header/response-splitting injection; the length check bounds request size.","triggerScenarios":"A webhook header value containing a literal newline or carriage return (multi-line values, stack traces, pretty-printed JSON), a NUL byte, or more than 2048 characters (long JWTs, base64 blobs, entire payloads stuffed into a header).","commonSituations":"Putting a signed payload or a long bearer/JWT token into a custom header; embedding multi-line error text or templates with newlines; binary data base64-encoded beyond the cap.","solutions":["Move large payloads out of headers into the webhook POST body","Strip/encode CR/LF: value.replace(chr(13), '').replace(chr(10), '') or URL/base64-encode the value","For long tokens, shorten (use an opaque ID the receiver resolves) or split across two custom headers"],"exampleFix":"# before\n\"headers\": {\"X-Payload\": json.dumps(big_obj)}  # newlines + >2048\n\n# after\n# big_obj travels in the webhook body; header only carries a short signature\n\"headers\": {\"X-Signature\": hmac_sha256_hex(secret, body)[:64]}","handlingStrategy":"validation","validationCode":"def header_values_ok(headers: dict) -> bool:\n    return all(isinstance(v, str) and len(v) <= 2048\n               and not any(c in v for c in chr(13) + chr(10) + chr(0))\n               for v in headers.values())","typeGuard":"def is_valid_webhook_value(v) -> bool:\n    return (isinstance(v, str) and 0 < len(v) <= 2048\n            and not any(c in v for c in chr(13) + chr(10) + chr(0)))","tryCatchPattern":"try:\n    sanitize_webhook_headers(headers)\nexcept ValueError as e:\n    if \"invalid value\" in str(e):\n        headers = {k: v.replace(chr(13), \"\").replace(chr(10), \"\")[:2048] for k, v in headers.items()}\n        sanitize_webhook_headers(headers)","preventionTips":["Never place multi-line text (JSON, stack traces) into a header without flattening/encoding","Keep tokens short: reference server-side secrets by ID instead of shipping full JWTs in headers"],"tags":["crawl4ai","webhook","validation","header-injection","limits"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}