HKUDS/nanobot · error · ValueError

assertion_header must identify a proxy-generated…

Error message

assertion_header must identify a proxy-generated authentication assertion, not a routing or client metadata header

What it means

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.

Solutions

  1. Configure a dedicated proxy-injected auth header (e.g. X-Nanobot-Auth) with a strong secret
  2. Never point assertion_header at X-Forwarded-* or client metadata headers
  3. Have the reverse proxy explicitly set and overwrite the assertion header
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at nanobot/channels/websocket/runtime.py:163 when the library encounters an invalid state.

Common situations: See trigger scenarios.

Understand the failure class


AI-assisted analysis of HKUDS/nanobot@42f37dc4c0 (2026-08-26). Data as JSON: /api/errors/490473308287a797. Report an issue: GitHub.

Appendix: source

Thrown at nanobot/channels/websocket/runtime.py:163

                raise ValueError(f"invalid trusted proxy CIDR: {value!r}") from exc
            if network.prefixlen == 0:
                raise ValueError("universal trusted proxy CIDRs are not allowed")
            if isinstance(network, ipaddress.IPv6Network):
                mapped_start = ipaddress.IPv6Address("::ffff:0:0")
                mapped_end = ipaddress.IPv6Address("::ffff:ffff:ffff")
                if mapped_start in network and mapped_end in network:
                    raise ValueError("trusted proxy CIDRs must not cover all IPv4-mapped addresses")
            normalized.append(network.with_prefixlen)
        return normalized

    @field_validator("assertion_header")
    @classmethod
    def validate_assertion_header(cls, value: str) -> str:
        value = value.strip()
        if not value or any(char.isspace() or ord(char) < 0x21 for char in value):
            raise ValueError("assertion_header must be a valid HTTP header name")
        if _is_routing_assertion_header(value):
            raise ValueError(
                "assertion_header must identify a proxy-generated authentication assertion, "
                "not a routing or client metadata header"
            )
        return value

    @model_validator(mode="after")
    def compile_trusted_peer_networks(self) -> Self:
        self._trusted_peer_networks = tuple(
            ipaddress.ip_network(value, strict=False) for value in self.trusted_peer_cidrs
        )
        return self


class WebSocketConfig(Base):
    """WebSocket server channel configuration.

    Clients connect with URLs like ``ws://{host}:{port}{path}?client_id=...&token=...``.
    - ``client_id``: Used for ``allow_from`` authorization; if omitted, a value is generated and logged.

View on GitHub (pinned to 42f37dc4c0)