{"record":{"id":"03d2ec8eed77eab5","repo":"musistudio/claude-code-router","slug":"provider-manifest-url-cannot-include-credentials","errorCode":null,"errorMessage":"Provider manifest URL cannot include credentials.","messagePattern":"Provider manifest URL cannot include credentials\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/contracts/deep-link.ts","lineNumber":484,"sourceCode":"}\n\nfunction validateProviderBaseUrl(value: string): void {\n  const url = new URL(providerUrlWithDefaultScheme(value));\n  if (![\"http:\", \"https:\"].includes(url.protocol)) {\n    throw new Error(\"Provider Base URL must use http or https.\");\n  }\n  if (!url.hostname) {\n    throw new Error(\"Provider Base URL is invalid.\");\n  }\n}\n\nfunction validateManifestUrl(value: string): void {\n  const url = new URL(value);\n  if (url.protocol !== \"https:\") {\n    throw new Error(\"Provider manifest URL must use https.\");\n  }\n  if (url.username || url.password) {\n    throw new Error(\"Provider manifest URL cannot include credentials.\");\n  }\n  if (!url.hostname) {\n    throw new Error(\"Provider manifest URL is invalid.\");\n  }\n}\n\nfunction normalizeProviderProtocol(value: string | undefined): GatewayProviderProtocol | undefined {\n  if (!value) {\n    return undefined;\n  }\n  const protocol = value.trim();\n  if (!providerProtocols.has(protocol as GatewayProviderProtocol)) {\n    throw new Error(`Unsupported provider protocol: ${value}`);\n  }\n  return protocol as GatewayProviderProtocol;\n}\n\nfunction readDeepLinkModels(params: URLSearchParams, payload: Record<string, unknown> | undefined): string[] {","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/contracts/deep-link.ts#L466-L502","documentation":"validateManifestUrl rejects manifest URLs containing userinfo (url.username or url.password), e.g. https://user:pass@example.com/m.json. Credentials embedded in the URL would leak into logs and the deep link itself, so they are explicitly forbidden.","triggerScenarios":"Passing a manifest URL with embedded basic-auth credentials in the ccr:// deep link's manifest param.","commonSituations":"Copying an authenticated URL from a browser devtools/network tab; private registries that put tokens in the URL; shareable links accidentally containing personal tokens.","solutions":["Remove credentials from the URL; use a pre-signed https URL without userinfo if auth is needed","Move the manifest to a public (or token-in-path) endpoint that needs no userinfo","Strip url.username/url.password when sanitizing user-supplied URLs"],"exampleFix":"// before\nhttps://user:pass@example.com/m.json\n// after\nhttps://example.com/m.json?token=pre-signed-token","handlingStrategy":"validation","validationCode":"const u = new URL(manifestUrl); if (u.username || u.password) manifestUrl = u.origin + u.pathname + u.search; // strip userinfo","typeGuard":"const urlHasNoCredentials = (u: string) => { try { const p = new URL(u); return !p.username && !p.password; } catch { return false; } };","tryCatchPattern":"try { validateManifestUrl(url); } catch (e) { if (e instanceof Error && e.message.includes(\"cannot include credentials\")) return stripUserinfoAndRetry(url); throw e; }","preventionTips":["Sanitize pasted URLs to remove userinfo","Use pre-signed URLs instead of basic auth for private manifests"],"tags":["manifest","url-credentials","security"],"backgroundTag":"credentials-in-url-rejected","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}