{"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":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":996,"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":978,"sourceCodeEnd":1014,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L978-L1014","documentation":"reviewed_remote_endpoint_identity parses the remote MCP endpoint URL of a reviewed plugin and rejects any URL carrying user information, i.e. a `user:pass@host` (or bare `user@`) component (crates/tui/src/mcp.rs:995-997). Reviewed plugins are trusted as byte-exact bundles with pinned origins; credentials embedded in the URL would leak into reviews and logs and diverge from the approved origin, so validation fails closed.","triggerScenarios":"A reviewed plugin manifest declares an MCP endpoint like https://user:pass@api.example.com/mcp or https://pat@example.com/mcp.","commonSituations":"Copying an endpoint from a provider dashboard that embeds an API key as basic-auth userinfo; migrating older configs that used URL-embedded credentials.","solutions":["Strip the userinfo from the URL: use https://api.example.com/mcp.","Move credentials to the supported channels: env_headers (e.g. Authorization: Bearer sourced from an env var) or the server's oauth config.","Re-trust the plugin after fixing the manifest (/plugin reload, then /plugin trust <name> <token>)."],"exampleFix":"# before\nurl = \"https://apikey:x-oauth-basic@company.example.com/mcp\"\n# after: credentials supplied via env_headers, not the URL\nurl = \"https://company.example.com/mcp\"\n[env_headers]\nAuthorization = \"Bearer ${COMPANY_MCP_TOKEN}\"","handlingStrategy":"validation","validationCode":"fn endpoint_has_userinfo(endpoint: &str) -> bool {\n    reqwest::Url::parse(endpoint)\n        .map(|u| !u.username().is_empty() || u.password().is_some())\n        .unwrap_or(true) // unparseable endpoints fail later anyway; treat as invalid\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never embed basic-auth userinfo in plugin MCP endpoint URLs.","Route credentials through env_headers or the oauth config block.","Lint manifests for '@' before the host portion before submitting plugins for review."],"tags":["url","security","plugin","mcp","validation"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}