{"record":{"id":"b49c55ba36d3fdba","repo":"headroomlabs-ai/headroom","slug":"invalid-trusted-dashboard-client-cidrs-env-entry","errorCode":null,"errorMessage":"Invalid {TRUSTED_DASHBOARD_CLIENT_CIDRS_ENV} entry: {exc}","messagePattern":"Invalid (.+?) entry: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/proxy/forwarded_headers.py","lineNumber":130,"sourceCode":"    the process environment. A malformed entry raises\n    :class:`ValueError` — let it propagate so the failure is loud at\n    startup instead of silently disabling the gate.\n    \"\"\"\n    if raw is None:\n        raw = os.environ.get(TRUSTED_GATEWAY_CIDRS_ENV, \"\")\n    return _parse_cidr_list(raw)\n\n\ndef load_trusted_dashboard_client_cidrs(\n    raw: str | None = None,\n) -> tuple[ipaddress.IPv4Network | ipaddress.IPv6Network, ...]:\n    \"\"\"Parse the Dashboard client CIDR allow-list from its environment variable.\"\"\"\n    if raw is None:\n        raw = os.environ.get(TRUSTED_DASHBOARD_CLIENT_CIDRS_ENV, \"\")\n    try:\n        return _parse_cidr_list(raw)\n    except ValueError as exc:\n        raise ValueError(f\"Invalid {TRUSTED_DASHBOARD_CLIENT_CIDRS_ENV} entry: {exc}\") from exc\n\n\ndef _normalize_ip(\n    host: str,\n) -> ipaddress.IPv4Address | ipaddress.IPv6Address | None:\n    \"\"\"Parse ``host`` into an IPv4/IPv6 address, unmapping ``::ffff:*``.\n\n    IPv4-mapped IPv6 addresses (``::ffff:10.0.0.1``) — emitted by Linux\n    dual-stack sockets — are normalized to their underlying IPv4 form\n    so a CIDR allow-list of ``10.0.0.0/8`` matches them naturally.\n    Returns ``None`` on malformed input; callers treat that as \"not a\n    trusted gateway\".\n    \"\"\"\n    return normalize_ip(host)\n\n\ndef _peer_host(request: Any) -> str | None:\n    \"\"\"Pull ``request.client.host`` defensively (TestClient may omit).\"\"\"","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/proxy/forwarded_headers.py#L112-L148","documentation":"load_trusted_dashboard_client_cidrs() parses HEADROOM_PROXY_TRUSTED_DASHBOARD_CLIENT_CIDRS (a comma-separated CIDR list identifying gateways whose X-Forwarded-For may be trusted) via ipaddress.ip_network(). Any entry the ip module rejects — bad mask, host bits set without strict=False, malformed address — is re-raised as this ValueError with the underlying ipaddress message chained. The check runs at request-handling/config load time, so a bad list can break trust resolution.","triggerScenarios":"Setting HEADROOM_PROXY_TRUSTED_DASHBOARD_CLIENT_CIDRS to a value containing an invalid CIDR: '10.0.0.1/8' (host bits set — strict networks required), '10.0.0.0/33' (bad mask), 'not-an-ip/8', '10.0.0.0/8,,' (empty segment from a trailing comma may or may not be filtered — invalid segments are rejected).","commonSituations":"Entering a single host IP where a network is expected (10.0.0.1 instead of 10.0.0.1/32); IPv6 zone identifiers or compressed forms that ipaddress rejects; copy-pasting a list with a trailing comma or whitespace-only entry; typos in the mask.","solutions":["Use well-formed CIDR networks: 10.0.0.0/8, 192.168.0.0/16, 2001:db8::/32; write single hosts as a.b.c.d/32.","Validate the list before deploying: python -c \"import ipaddress,sys; [ipaddress.ip_network(s.strip()) for s in sys.argv[1].split(',') if s.strip()]\" \"$VALUE\".","Remove trailing commas and stray empty entries from comma-separated lists."],"exampleFix":"# before\nexport HEADROOM_PROXY_TRUSTED_DASHBOARD_CLIENT_CIDRS=\"10.0.0.1/8\"\n\n# after\nexport HEADROOM_PROXY_TRUSTED_DASHBOARD_CLIENT_CIDRS=\"10.0.0.0/8\"","handlingStrategy":"validation","validationCode":"import ipaddress\n\ndef cidr_list_ok(raw: str) -> str | None:\n    try:\n        nets = [ipaddress.ip_network(s.strip()) for s in raw.split(\",\") if s.strip()]\n        return None\n    except ValueError as exc:\n        return str(exc)","typeGuard":null,"tryCatchPattern":"try:\n    cidrs = load_trusted_dashboard_client_cidrs(raw)\nexcept ValueError as exc:\n    raise ConfigError(f\"fix {TRUSTED_DASHBOARD_CLIENT_CIDRS_ENV}: {exc}\") from exc","preventionTips":["Always write networks, not hosts: 10.0.0.0/8, and /32 for a single address.","Add a pre-deploy check that parses every CIDR env var with ipaddress.ip_network.","Avoid trailing commas/whitespace-only entries in comma-separated lists."],"tags":["configuration","network","cidr","validation","security"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}