{"record":{"id":"eee9fb16be1e77fe","repo":"different-ai/openwork","slug":"embedded-url-credentials-are-not-allowed","errorCode":null,"errorMessage":"embedded URL credentials are not allowed","messagePattern":"embedded URL credentials are not allowed","errorType":"exception","errorClass":"LocalManagedMcpPrivateUrlError","httpStatus":null,"severity":"error","filePath":"apps/server/src/local-managed-mcp-url-guard.ts","lineNumber":112,"sourceCode":"export function isLocalManagedMcpPrivateAddress(address: string): boolean {\n  const version = isIP(address);\n  if (version === 4) return isPrivateIpv4(address);\n  if (version === 6) return isPrivateIpv6(address);\n  return true;\n}\n\nfunction parseHttpUrl(rawUrl: string): URL {\n  let url: URL;\n  try {\n    url = new URL(rawUrl);\n  } catch {\n    throw new LocalManagedMcpPrivateUrlError(rawUrl, \"not a valid URL\");\n  }\n  if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n    throw new LocalManagedMcpPrivateUrlError(rawUrl, `protocol \"${url.protocol}\" is not allowed`);\n  }\n  if (url.username || url.password) {\n    throw new LocalManagedMcpPrivateUrlError(rawUrl, \"embedded URL credentials are not allowed\");\n  }\n  return url;\n}\n\nfunction allowPrivateUrls(): boolean {\n  return process.env.OPENWORK_DEV_MODE === \"1\" || process.env.OPENWORK_ALLOW_PRIVATE_MCP_URLS === \"1\";\n}\n\ntype ResolveAddresses = (hostname: string, options: LookupAllOptions) => Promise<LookupAddress[]>;\n\nconst resolveAddresses: ResolveAddresses = (hostname, options) => lookup(hostname, options);\n\nfunction validateResolvedAddresses(hostname: string, addresses: LookupAddress[]): void {\n  if (addresses.length === 0) {\n    throw new LocalManagedMcpPrivateUrlError(`https://${hostname}/`, \"the hostname does not resolve\");\n  }\n  if (allowPrivateUrls()) return;\n  for (const { address } of addresses) {","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/local-managed-mcp-url-guard.ts#L94-L130","documentation":"LocalManagedMcpPrivateUrlError thrown by parseHttpUrl when the configured MCP URL embeds userinfo credentials (username or password, e.g. https://user:pass@host). Embedding credentials in URLs is rejected to prevent credential leakage into logs, error messages, and downstream fetches.","triggerScenarios":"A local managed MCP server URL of the form scheme://user:pass@host/... is parsed: url.username or url.password is non-empty after new URL(rawUrl).","commonSituations":"Copy-pasting a URL with basic-auth credentials from another tool; putting API keys in the URL instead of headers; legacy configs that predate header-based auth.","solutions":["Remove user:pass@ from the URL and pass credentials via HTTP headers (e.g. Authorization) in the server config","If the MCP server requires basic auth, use its header/config option instead of URL userinfo","Rotate the credential if it was embedded in a URL — URLs get logged and shared","Store the secret in an env var and reference it in the config rather than inline"],"exampleFix":"// before\n{ \"url\": \"https://user:hunter2@mcp.example.com/sse\" }\n// after\n{ \"url\": \"https://mcp.example.com/sse\", \"headers\": { \"Authorization\": \"Basic ...\" } }","handlingStrategy":"validation","validationCode":"function assertNoUrlCredentials(raw: string): void {\n  const u = new URL(raw);\n  if (u.username || u.password) throw new Error(\"move credentials out of the URL into headers\");\n}","typeGuard":"function hasEmbeddedCredentials(raw: string): boolean {\n  try { const u = new URL(raw); return u.username !== \"\" || u.password !== \"\"; } catch { return false; }\n}","tryCatchPattern":"try {\n  await mcp.addServer({ url: rawUrl });\n} catch (e) {\n  if (e instanceof LocalManagedMcpPrivateUrlError && e.message.includes(\"credentials\")) {\n    // strip userinfo, reconfigure auth via headers, rotate the leaked secret\n  } else throw e;\n}","preventionTips":["Never put user:pass@ or API keys in URLs — use headers","Lint/scan configs for the @userinfo pattern before deploy","Rotate any credential that was ever embedded in a URL"],"tags":["url","security","mcp","credentials"],"backgroundTag":"url-embedded-credentials","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}