{"record":{"id":"171647e0dcf91059","repo":"aaif-goose/goose","slug":"external-acp-backend-url-must-not-include-query-pa","errorCode":null,"errorMessage":"External ACP backend URL must not include query parameters or fragments","messagePattern":"External ACP backend URL must not include query parameters or fragments","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ui/desktop/src/acp/url.ts","lineNumber":50,"sourceCode":"    return false;\n  }\n\n  return octets.every((octet) => Number(octet) <= 255) && Number(octets[0]) === 127;\n}\n\nexport function normalizeAcpHttpBaseUrl(rawBaseUrl: string): string {\n  const trimmed = rawBaseUrl.trim();\n  if (!trimmed) {\n    throw new Error('External ACP backend URL is required');\n  }\n\n  const url = new URL(trimmed);\n  if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n    throw new Error(`External ACP backend URL must use http: or https:, got ${url.protocol}`);\n  }\n\n  if (url.search || url.hash) {\n    throw new Error('External ACP backend URL must not include query parameters or fragments');\n  }\n\n  const pathname = url.pathname.replace(/\\/+$/, '');\n  if (pathname.endsWith('/acp')) {\n    throw new Error('External ACP backend URL must be the base URL before /acp');\n  }\n\n  return `${url.origin}${pathname}`;\n}\n\nfunction httpEndpointUrlFromHttpBase(rawBaseUrl: string, endpoint: 'status' | 'acp'): string {\n  const baseUrl = normalizeAcpHttpBaseUrl(rawBaseUrl);\n  const url = new URL(baseUrl);\n  url.pathname = `${url.pathname.replace(/\\/+$/, '')}/${endpoint}`;\n  return url.toString();\n}\n\nexport function statusHttpUrlFromHttpBase(rawBaseUrl: string): string {","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/ui/desktop/src/acp/url.ts#L32-L68","documentation":"Thrown by normalizeAcpHttpBaseUrl when the parsed URL contains a query string (url.search) and/or a fragment (url.hash). The base URL is used verbatim to construct .../status and .../acp endpoints, so any ?params or #fragment would either corrupt those paths or be silently dropped; the function rejects them up front.","triggerScenarios":"Passing 'http://host:8080/?token=abc' or 'https://host/goose#section'; URLs copied from a browser address bar after navigating (fragments get appended); config values containing a token query parameter because the user tried to inline auth.","commonSituations":"Trying to embed an auth token in the URL (tokens belong elsewhere); pasting a URL with utm params or anchors; bookmarks adding fragments.","solutions":["Strip everything after the path: keep only scheme://host[:port]/path.","Pass tokens/credentials through the mechanism the backend actually supports, not URL query params.","If callers may paste decorated URLs, strip search and hash before calling normalizeAcpHttpBaseUrl."],"exampleFix":"// before\nif (url.search || url.hash) {\n  throw new Error('External ACP backend URL must not include query parameters or fragments');\n}\n\n// after (caller sanitizes the pasted URL first)\nconst raw = new URL(userInput);\nraw.search = '';\nraw.hash = '';\nconst baseUrl = normalizeAcpHttpBaseUrl(raw.toString());","handlingStrategy":"validation","validationCode":"// Strip query/fragment before validation\nfunction cleanBaseUrl(raw: string): string {\n  const url = new URL(raw.trim());\n  url.search = '';\n  url.hash = '';\n  return url.toString();\n}","typeGuard":"function isBareHttpUrl(value: string): boolean {\n  try {\n    const url = new URL(value.trim());\n    return (url.protocol === 'http:' || url.protocol === 'https:') && !url.search && !url.hash;\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  const base = normalizeAcpHttpBaseUrl(inputUrl);\n} catch (error) {\n  if (/query parameters or fragments/.test(String(error))) {\n    return normalizeAcpHttpBaseUrl(cleanBaseUrl(inputUrl)); // retry with sanitized input\n  }\n  throw error;\n}","preventionTips":["Sanitize pasted URLs (strip ? and #) at the input boundary.","Never inline tokens as query params in the base URL.","Use an input type/pattern that discourages decorated URLs."],"tags":["validation","url","query-string","configuration"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}