{"record":{"id":"286389376a4e08fb","repo":"diegosouzapw/OmniRoute","slug":"subscription-url-is-not-allowed-scheme-or-host-bl","errorCode":null,"errorMessage":"Subscription URL is not allowed (scheme or host blocked)","messagePattern":"Subscription URL is not allowed \\(scheme or host blocked\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/proxySubscription/subscriptionService.ts","lineNumber":299,"sourceCode":"\n// ───────────────────────────── Sync + apply ─────────────────────────────\n\n/**\n * Refuse to fetch a subscription URL unless it is http/https to an allowed\n * host. IP literals are checked structurally; hostnames are resolved and the\n * resolved addresses are re-checked (fail closed on resolution errors).\n *\n * Local-first (#10158): loopback/private fetch targets are ALLOWED when\n * `areLocalProviderUrlsAllowed()` is on (default ON — same local-first policy\n * already used for provider validation, and consistent with\n * `coreEndpoint.ts` already permitting a loopback routing core). Cloud\n * metadata / link-local (169.254.0.0/16, incl. 169.254.169.254 IMDS) is\n * blocked UNCONDITIONALLY regardless of that flag.\n */\nasync function assertSafeFetchTarget(url: string): Promise<void> {\n  const guardOpts: FetchGuardOptions = { allowLocal: areLocalProviderUrlsAllowed() };\n  if (!isSubscriptionFetchUrlAllowed(url, guardOpts)) {\n    throw new Error(\"Subscription URL is not allowed (scheme or host blocked)\");\n  }\n  const host = new URL(url).hostname.toLowerCase();\n  const bare = host.startsWith(\"[\") && host.endsWith(\"]\") ? host.slice(1, -1) : host;\n  if (!isIpLiteral(bare)) {\n    // Hostname: resolve ALL records and refuse if ANY address is internal\n    // (fail closed). A hostname can resolve to multiple records; checking only\n    // the first would let an internal IP slip through if a public record also\n    // exists. `lookup(..., { all: true })` returns every A/AAAA record.\n    try {\n      const dns = await import(\"node:dns\");\n      const addrs = await dns.promises.lookup(bare, { all: true });\n      if (isAnyResolvedAddressBlocked(addrs, guardOpts)) {\n        throw new Error(\"Subscription host resolves to a blocked (internal) address\");\n      }\n    } catch (e) {\n      if (e instanceof Error && e.message.includes(\"blocked\")) throw e;\n      throw new Error(`Subscription host resolution failed: ${e instanceof Error ? e.message : e}`);\n    }","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/diegosouzapw/OmniRoute/blob/a179ffed5bb2e0b883b9ae7214ce8717b2a94c4d/src/lib/proxySubscription/subscriptionService.ts#L281-L317","documentation":"Error \"Subscription URL is not allowed (scheme or host blocked)\" thrown in diegosouzapw/OmniRoute.","triggerScenarios":"Thrown at src/lib/proxySubscription/subscriptionService.ts:299 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"a179ffed5bb2e0b883b9ae7214ce8717b2a94c4d","analyzedAt":"2026-08-25T18:35:09.898Z","schemaVersion":2},"datasetVersion":"2026-08-25T21:54:21.419Z"}