{"record":{"id":"4a4a505d10dc34eb","repo":"Hmbown/CodeWhale","slug":"reviewed-plugin-mcp-endpoint-must-not-contain-user","errorCode":null,"errorMessage":"reviewed plugin MCP endpoint must not contain user information","messagePattern":"reviewed plugin MCP endpoint must not contain user information","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":1111,"sourceCode":"        .context(\"open reviewed launch file without following links\")\n}\n\n#[cfg(windows)]\nfn open_reviewed_launch_file(path: &Path) -> Result<fs::File> {\n    crate::plugins::manifest::open_bundle_file(path)\n        .context(\"open reviewed launch file without links, hard links, or write/delete sharing\")\n}\n\n#[cfg(all(not(unix), not(windows)))]\nfn open_reviewed_launch_file(path: &Path) -> Result<fs::File> {\n    fs::File::open(path).context(\"open reviewed launch file\")\n}\n\nfn reviewed_remote_endpoint_identity(endpoint: &str) -> Result<(String, String)> {\n    let endpoint =\n        reqwest::Url::parse(endpoint).context(\"reviewed plugin MCP endpoint is invalid\")?;\n    if !endpoint.username().is_empty() || endpoint.password().is_some() {\n        anyhow::bail!(\"reviewed plugin MCP endpoint must not contain user information\");\n    }\n    if endpoint.query().is_some() || endpoint.fragment().is_some() {\n        anyhow::bail!(\"reviewed plugin MCP endpoint must not contain a query or fragment\");\n    }\n    let origin = reviewed_remote_origin(&endpoint)\n        .ok_or_else(|| anyhow::anyhow!(\"reviewed plugin MCP endpoint has an unsafe origin\"))?;\n    Ok((endpoint.to_string(), origin))\n}\n\nfn reviewed_remote_origin(endpoint: &reqwest::Url) -> Option<String> {\n    if !endpoint.username().is_empty() || endpoint.password().is_some() {\n        return None;\n    }\n    let host = endpoint.host_str()?;\n    let allowed_scheme = endpoint.scheme() == \"https\"\n        || (endpoint.scheme() == \"http\"\n            && (host.eq_ignore_ascii_case(\"localhost\")\n                || host","sourceCodeStart":1093,"sourceCodeEnd":1129,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp.rs#L1093-L1129","documentation":"reviewed_remote_endpoint_identity parses the plugin's remote MCP endpoint URL and rejects any URL carrying user information (username or password). Credentials embedded in an approved endpoint could leak through review records or logs and enable credential smuggling, so such URLs are refused before review comparison.","triggerScenarios":"Thrown when endpoint.username() is non-empty or endpoint.password() is Some — i.e. the URL looks like https://user:pass@host/mcp — while normalizing the endpoint for review/approval comparison.","commonSituations":"Developer pasted a URL with basic-auth credentials copied from a curl example; a config template kept a user:pass prefix; a reverse-proxy URL included a token in the userinfo position.","solutions":["Remove the username/password from the endpoint URL and authenticate via headers or an OAuth flow instead","If the token must travel with the request, put it in a query-free header configured separately, not in the URL userinfo","Move credentials into an environment-backed secret the MCP client injects at request time"],"exampleFix":"// before\n\"endpoint\": \"https://alice:secret@api.example.com/mcp\"\n// after\n\"endpoint\": \"https://api.example.com/mcp\"  // auth via headers/token store","handlingStrategy":"validation","validationCode":"import re\n\ndef endpoint_has_no_userinfo(endpoint: str) -> bool:\n    m = re.match(r'^[a-z][a-z0-9+.-]*://', endpoint)\n    if not m:\n        return False\n    scheme_end = m.end()\n    at = endpoint.find('@', scheme_end)\n    authority = endpoint[scheme_end:endpoint.find('/', scheme_end)]\n    return not (at != -1 and at < scheme_end + len(authority))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never paste URLs with user:pass@ prefixes into plugin configs","Keep credentials in env vars/secret stores referenced by config, not inline in URLs","Lint plugin configs for the '://'+'@' userinfo pattern in endpoints"],"tags":["security","url","credentials","mcp"],"backgroundTag":"invalid-url-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}