{"record":{"id":"9a416c197afa3f6b","repo":"Hmbown/CodeWhale","slug":"mcp-http-redirect-must-not-contain-credentials","errorCode":null,"errorMessage":"MCP HTTP redirect must not contain credentials","messagePattern":"MCP HTTP redirect must not contain credentials","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/http_client.rs","lineNumber":131,"sourceCode":"            let next_request = request\n                .try_clone()\n                .context(\"MCP request body cannot be replayed\")?;\n            let response = client.execute(request).await?;\n            if !follow_redirects\n                || !matches!(response.status().as_u16(), 301 | 302 | 303 | 307 | 308)\n            {\n                return Ok(response);\n            }\n            let Some(location) = response.headers().get(header::LOCATION) else {\n                return Ok(response);\n            };\n            if redirect_count == 5 {\n                bail!(\"MCP HTTP redirect limit exceeded\");\n            }\n            let next_url = url.join(location.to_str().context(\"invalid MCP redirect Location\")?)?;\n            validate_url(&next_url)?;\n            if url_has_credentials(&next_url) {\n                bail!(\"MCP HTTP redirect must not contain credentials\");\n            }\n            if url.scheme() == \"https\" && next_url.scheme() != \"https\" {\n                bail!(\"MCP HTTP redirect would downgrade HTTPS\");\n            }\n            request = next_request;\n            if (matches!(response.status().as_u16(), 301 | 302) && request.method() == Method::POST)\n                || (response.status().as_u16() == 303 && request.method() != Method::HEAD)\n            {\n                *request.method_mut() = Method::GET;\n                *request.body_mut() = None;\n                request.headers_mut().remove(header::CONTENT_TYPE);\n                request.headers_mut().remove(header::CONTENT_LENGTH);\n                request.headers_mut().remove(header::TRANSFER_ENCODING);\n            }\n            if next_url.origin() != url.origin() {\n                // Custom headers can contain credentials under arbitrary names;\n                // retaining just Authorization/ Cookie exclusions is insufficient.\n                let mut headers = header::HeaderMap::new();","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp/http_client.rs#L113-L149","documentation":"When following an MCP HTTP redirect, the target Location URL is checked for embedded credentials. If a server redirects to a URL containing user:password@, the client refuses with this error to prevent credential handling on untrusted redirect targets.","triggerScenarios":"execute_inner follows a 3xx response whose Location header parses (after url.join) to a URL with a non-empty username or a password, i.e. url_has_credentials(&next_url) is true.","commonSituations":"A misbehaving or compromised server redirecting to an auth-embedded URL; a staging gateway that bakes basic-auth into redirect targets; a hand-written Location header copied from a curl example.","solutions":["Fix the server to redirect to a credential-free URL and authenticate via headers at the target","Redirect to the bare origin/path and send the Authorization header on the follow-up request","If you control the hop, remove userinfo from the Location header"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn redirect_target_is_clean(base: &Url, location: &str) -> bool {\n    base.join(location).ok()\n        .map(|u| !(!u.username().is_empty() || u.password().is_some()))\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = client.execute(req).await {\n    if e.to_string().contains(\"redirect must not contain credentials\") {\n        // server-side fix required: strip userinfo from Location\n    }\n}","preventionTips":["Audit server redirect headers (curl -I) for embedded userinfo","Never copy curl-style authenticated URLs into redirect configs","Authenticate at the target with headers, not the URL"],"tags":["mcp","http","redirect","credentials","security"],"backgroundTag":"invalid-url","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}