{"record":{"id":"cbe3a873d88cea18","repo":"googleapis/mcp-toolbox","slug":"url-scheme-must-be-https-got-q","errorCode":null,"errorMessage":"URL scheme must be https, got %q","messagePattern":"URL scheme must be https, got %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudhealthcare/cloud_healthcare.go","lineNumber":331,"sourceCode":"\tif v[1] < '1' || v[1] > '9' {\n\t\treturn false\n\t}\n\tfor i := 2; i < len(v); i++ {\n\t\tif !isAlphanumeric(v[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (s *Source) validateFHIRPageURL(pageURL string) (string, error) {\n\tparsed, err := url.Parse(pageURL)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid page URL: %w\", err)\n\t}\n\n\tif parsed.Scheme != \"https\" {\n\t\treturn \"\", fmt.Errorf(\"URL scheme must be https, got %q\", parsed.Scheme)\n\t}\n\n\tparsed.Host = strings.ToLower(parsed.Host)\n\thost := parsed.Host\n\tif h, _, err := net.SplitHostPort(host); err == nil {\n\t\thost = h\n\t}\n\tif _, ok := allowedFHIRHosts[host]; !ok {\n\t\treturn \"\", fmt.Errorf(\"URL host must be an allowed FHIR host, got %q\", host)\n\t}\n\n\t// Clean and split path\n\tcleanPath := path.Clean(parsed.Path)\n\t// Truncate leading and trailing slashes for easier splitting\n\ttrimmed := strings.Trim(cleanPath, \"/\")\n\tparts := strings.Split(trimmed, \"/\")\n\n\t// Page URL format Reference: https://docs.cloud.google.com/healthcare-api/docs/how-tos/fhir-search#using_the_search_method_with_get","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudhealthcare/cloud_healthcare.go#L313-L349","documentation":"After parsing, validateFHIRPageURL enforces that the pagination URL uses the https scheme. Any http:// (or other scheme) page URL is rejected to prevent SSRF and credential leakage over plaintext. The %q shows the offending scheme found in the URL.","triggerScenarios":"A caller supplies a page URL rewritten to http://healthcare.googleapis.com/... (downgraded by a proxy, manually edited, or reconstructed by an LLM from text), or a scheme-less string that parses with an unexpected scheme value.","commonSituations":"Reverse proxies configured with http backends rewriting Location headers; tests hitting local http mock servers against production validation; copy-pasting a link that had https stripped by a chat client or document.","solutions":["Always use the exact https page URL from the prior response's links.next","If a local test endpoint is needed, extend allowedFHIRHosts/scheme logic in a dev build rather than downgrading the scheme","Use https proxies (CONNECT) instead of rewriting to http","If you must test, run the validation with a test-only allowlist that permits http for localhost"],"exampleFix":"// before\npage := \"http://healthcare.googleapis.com/v1/projects/.../fhir/Patient?pageToken=x\"\n// after\npage := \"https://healthcare.googleapis.com/v1/projects/.../fhir/Patient?pageToken=x\"","handlingStrategy":"validation","validationCode":"function ensureHttps(u) { const p = new URL(u); if (p.protocol !== 'https:') throw new Error(`page URL must be https, got ${p.protocol}`); return p; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never downgrade googleapis.com URLs to http; keep the scheme from links.next","If a proxy rewrites Location/Link headers to http, fix the proxy or use an HTTPS CONNECT proxy","Hard-code https when constructing pagination URLs programmatically"],"tags":["https","security","ssrf","url-validation"],"backgroundTag":"insecure-http-scheme-rejected","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}