{"record":{"id":"29002455166492ae","repo":"koala73/worldmonitor","slug":"url-protocol-not-allowed","errorCode":null,"errorMessage":"URL protocol not allowed","messagePattern":"URL protocol not allowed","errorType":"http","errorClass":"RssProxyPolicyError","httpStatus":400,"severity":"error","filePath":"api/rss-proxy.js","lineNumber":75,"sourceCode":"    }),\n  }, timeoutMs);\n}\n\n// Allowlist + match predicate live in api/_rss-allowed-domain-match.js\n// (shared with scripts/validate-rss-feeds.mjs --ci so the SSRF guard runs\n// identically in the Edge handler and the build-time validator).\n\nfunction isGoogleNewsFeedUrl(feedUrl) {\n  try {\n    return new URL(feedUrl).hostname === 'news.google.com';\n  } catch {\n    return false;\n  }\n}\n\nfunction assertHttpProtocol(url, message = 'URL protocol not allowed', status = 400) {\n  if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n    throw new RssProxyPolicyError(message, status);\n  }\n}\n\nfunction assertAllowedRedirect(url) {\n  assertHttpProtocol(url, 'Redirect protocol not allowed', 403);\n  // Apply the same www-normalization as the initial domain check so that\n  // canonical redirects (e.g. apex -> www) are not incorrectly rejected when\n  // only one form is in the allowlist.\n  if (!isAllowedDomain(url.hostname)) {\n    throw new RssProxyPolicyError('Redirect to disallowed domain');\n  }\n}\n\nexport default async function handler(req, ctx) {\n  const corsHeaders = getCorsHeaders(req, 'GET, OPTIONS');\n\n  if (isDisallowedOrigin(req)) {\n    return jsonResponse({ error: 'Origin not allowed' }, 403, corsHeaders);","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/api/rss-proxy.js#L57-L93","documentation":"Thrown by assertHttpProtocol() in the RSS proxy edge function when a feed URL's protocol is neither http: nor https: (e.g. ftp:, javascript:, file:, data:). This is a security guard preventing the proxy from being used as an SSRF vector. The error is an RssProxyPolicyError with HTTP status 400 (the default for the initial URL check).","triggerScenarios":"Calling GET /api/rss-proxy?url=<feedUrl> where the feedUrl parses to a non-HTTP protocol — e.g. url=ftp://feeds.example.com/rss, url=javascript:alert(1), or a malformed URL that the URL constructor parses with an unexpected protocol. Also fires on the redirect path with a different message ('Redirect protocol not allowed', status 403).","commonSituations":"A feed URL in the source registry or user input that uses a non-standard protocol; an attempted SSRF payload targeting internal protocols (file:///etc/passwd); a copy-paste error introducing a typo in the URL scheme; a feed URL that was valid but got mangled by URL encoding.","solutions":["Ensure the feed URL uses http:// or https:// — re-encode the url query parameter correctly.","If the URL looks correct, check for hidden characters or encoding issues in the url query parameter (e.g. url=http%3A%2F%2F... vs a raw ftp://).","For redirect-path instances (status 403, message 'Redirect protocol not allowed'), the initial URL was fine but a redirect Location header pointed to a non-HTTP URL — the source needs to fix its redirect.","Validate the URL client-side before calling the proxy."],"exampleFix":"// before\nGET /api/rss-proxy?url=ftp://feeds.example.com/news.xml\n// after\nGET /api/rss-proxy?url=https://feeds.example.com/news.xml","handlingStrategy":"validation","validationCode":"// Validate the feed URL protocol before calling the RSS proxy\nfunction isValidFeedUrl(urlStr) {\n  try {\n    const url = new URL(urlStr);\n    return url.protocol === 'http:' || url.protocol === 'https:';\n  } catch {\n    return false;\n  }\n}\nif (!isValidFeedUrl(feedUrl)) {\n  throw new Error(`Feed URL must use http or https protocol: ${feedUrl}`);\n}","typeGuard":"function isRssProxyPolicyError(e: unknown): e is { status: number } & Error {\n  return e instanceof Error && (e as any).name === 'RssProxyPolicyError';\n}","tryCatchPattern":null,"preventionTips":["Always validate feed URLs with new URL() and check protocol before passing to the proxy.","Reject non-HTTP(S) protocols early in feed-source registration/config.","URL-encode the url query parameter correctly to avoid protocol mangling."],"tags":["rss-proxy","security","ssrf","url-validation","edge-function"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}