{"record":{"id":"490473308287a797","repo":"HKUDS/nanobot","slug":"assertion-header-must-identify-a-proxy-generated-a","errorCode":null,"errorMessage":"assertion_header must identify a proxy-generated authentication assertion, not a routing or client metadata header","messagePattern":"assertion_header must identify a proxy-generated authentication assertion, not a routing or client metadata header","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"nanobot/channels/websocket/runtime.py","lineNumber":163,"sourceCode":"                raise ValueError(f\"invalid trusted proxy CIDR: {value!r}\") from exc\n            if network.prefixlen == 0:\n                raise ValueError(\"universal trusted proxy CIDRs are not allowed\")\n            if isinstance(network, ipaddress.IPv6Network):\n                mapped_start = ipaddress.IPv6Address(\"::ffff:0:0\")\n                mapped_end = ipaddress.IPv6Address(\"::ffff:ffff:ffff\")\n                if mapped_start in network and mapped_end in network:\n                    raise ValueError(\"trusted proxy CIDRs must not cover all IPv4-mapped addresses\")\n            normalized.append(network.with_prefixlen)\n        return normalized\n\n    @field_validator(\"assertion_header\")\n    @classmethod\n    def validate_assertion_header(cls, value: str) -> str:\n        value = value.strip()\n        if not value or any(char.isspace() or ord(char) < 0x21 for char in value):\n            raise ValueError(\"assertion_header must be a valid HTTP header name\")\n        if _is_routing_assertion_header(value):\n            raise ValueError(\n                \"assertion_header must identify a proxy-generated authentication assertion, \"\n                \"not a routing or client metadata header\"\n            )\n        return value\n\n    @model_validator(mode=\"after\")\n    def compile_trusted_peer_networks(self) -> Self:\n        self._trusted_peer_networks = tuple(\n            ipaddress.ip_network(value, strict=False) for value in self.trusted_peer_cidrs\n        )\n        return self\n\n\nclass WebSocketConfig(Base):\n    \"\"\"WebSocket server channel configuration.\n\n    Clients connect with URLs like ``ws://{host}:{port}{path}?client_id=...&token=...``.\n    - ``client_id``: Used for ``allow_from`` authorization; if omitted, a value is generated and logged.","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/HKUDS/nanobot/blob/42f37dc4c0e409eac7818a82d165c5f2757bc075/nanobot/channels/websocket/runtime.py#L145-L181","documentation":"In validate_assertion_header, the configured assertion_header names a routing or client-metadata header (matched by _is_routing_assertion_header, e.g. X-Forwarded-For-class headers). Trusting such a header as an authentication assertion would let any client forge identity via headers proxies set from request data. The input at fault is the unsafe header name.","triggerScenarios":"Thrown at nanobot/channels/websocket/runtime.py:163 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Configure a dedicated proxy-injected auth header (e.g. X-Nanobot-Auth) with a strong secret","Never point assertion_header at X-Forwarded-* or client metadata headers","Have the reverse proxy explicitly set and overwrite the assertion header"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"42f37dc4c0e409eac7818a82d165c5f2757bc075","analyzedAt":"2026-08-26T00:18:52.276Z","contentChangedAt":"2026-08-26T00:18:52.276Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}