{"record":{"id":"e695ea0d0a382abb","repo":"BigPizzaV3/CodexPlusPlus","slug":"cdp-websocket-host-must-be-loopback","errorCode":null,"errorMessage":"CDP WebSocket host must be loopback","messagePattern":"CDP WebSocket host must be loopback","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/cdp.rs","lineNumber":225,"sourceCode":"    }\n    Ok(targets)\n}\n\npub fn validate_cdp_websocket_url(url: &str, expected_port: u16) -> anyhow::Result<()> {\n    let parsed = reqwest::Url::parse(url).context(\"invalid CDP WebSocket URL\")?;\n    if !matches!(parsed.scheme(), \"ws\" | \"wss\") {\n        bail!(\"CDP WebSocket URL must use ws or wss\");\n    }\n    let host = parsed\n        .host_str()\n        .ok_or_else(|| anyhow::anyhow!(\"CDP WebSocket URL has no host\"))?;\n    let address = host\n        .trim_start_matches('[')\n        .trim_end_matches(']')\n        .parse::<IpAddr>()\n        .with_context(|| \"CDP WebSocket host must be a loopback IP address\")?;\n    if !address.is_loopback() {\n        bail!(\"CDP WebSocket host must be loopback\");\n    }\n    let port = parsed\n        .port()\n        .ok_or_else(|| anyhow::anyhow!(\"CDP WebSocket URL must include an explicit port\"))?;\n    if port != expected_port {\n        bail!(\"CDP WebSocket port {port} does not match debug port {expected_port}\");\n    }\n    Ok(())\n}\n\npub fn pick_page_target(targets: &[CdpTarget]) -> anyhow::Result<CdpTarget> {\n    let mut first_page = None;\n    for target in targets\n        .iter()\n        .filter(|target| is_injectable_page_target(target))\n    {\n        first_page.get_or_insert(target);\n        if is_primary_codex_page_target(target) {","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-core/src/cdp.rs#L207-L243","documentation":"Security guard in validate_cdp_websocket_url: the WebSocket host parsed from the CDP URL is not a loopback IP, so connecting would send the DevTools protocol off-machine — rejected to prevent SSRF/exfiltration. The offending input is a non-127.0.0.1/::1 host in the returned webSocketDebuggerUrl.","triggerScenarios":"Thrown at crates/codex-plus-core/src/cdp.rs:225 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Launch the browser with remote debugging bound to loopback only","Rewrite the CDP URL to 127.0.0.1/[::1] when accessing via a forwarded host","Never connect to a DevTools WebSocket on a non-loopback host"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}