{"record":{"id":"0bfa8f915775bfc7","repo":"DIYgod/RSSHub","slug":"this-rss-is-disabled-unless-allow-user-supply-uns-0bfa8f","errorCode":null,"errorMessage":"This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.","messagePattern":"This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'\\.","errorType":"validation","errorClass":"ConfigNotFoundError","httpStatus":null,"severity":"error","filePath":"lib/routes/javbus/index.tsx","lineNumber":84,"sourceCode":"        path: {\n            description: 'Any path of list page on javbus',\n        },\n    },\n    features: {\n        nsfw: true,\n    },\n};\n\nasync function handler(ctx) {\n    const isWestern = getSubPath(ctx).startsWith('/western');\n    const domain = ctx.req.query('domain') ?? 'javbus.com';\n    const westernDomain = ctx.req.query('western_domain') ?? 'javbus.org';\n\n    const rootUrl = `https://www.${domain}`;\n    const westernUrl = `https://www.${westernDomain}`;\n\n    if (!config.feature.allow_user_supply_unsafe_domain && (!allowDomain.has(new URL(`https://${domain}/`).hostname) || !allowDomain.has(new URL(`https://${westernDomain}/`).hostname))) {\n        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);\n    }\n\n    const currentUrl = `${isWestern ? westernUrl : rootUrl}${getSubPath(ctx)\n        .replace(/^\\/western/, '')\n        .replace(/\\/home/, '')}`;\n\n    const headers = {\n        'accept-language': 'zh-CN',\n    };\n\n    const response = await got({\n        method: 'get',\n        url: currentUrl,\n        headers,\n    });\n\n    const $ = load(response.data);\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/javbus/index.tsx#L66-L102","documentation":"Thrown as a ConfigNotFoundError by the javbus route when a user supplies a custom domain (via ?domain= or ?western_domain= query params) that is not in the hardcoded allowlist (javbus.com, javbus.org, javsee.icu, javsee.one) AND the server-level feature flag ALLOW_USER_SUPPLY_UNSAFE_DOMAIN is not enabled. This is a deliberate security control to prevent SSRF — without it, any user could point RSSHub at an arbitrary domain.","triggerScenarios":"A request like /javbus?domain=mirror.example.com where mirror.example.com is not in the allowDomain set, and the deployment has not set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true. Also fires if only one of the two domains (domain vs western_domain) is non-allowlisted, since the check uses OR on the negation.","commonSituations":"A self-hoster wants to use a personal javbus mirror/proxy but has not set the env var. A user misunderstands the domain param as a free-form target. A shared/public RSSHub instance correctly blocks the request because enabling the flag on a public instance is an SSRF risk.","solutions":["If you operate the instance and trust the domain, set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true in your environment/config (only on private/self-hosted instances, never public ones).","If you cannot change config, use one of the allowlisted domains: javbus.com, javbus.org, javsee.icu, javsee.one.","To permanently support a new mirror safely, add its hostname to the allowDomain Set in lib/routes/javbus/index.tsx and rebuild.","Do NOT enable this flag on public/shared instances — it allows arbitrary outbound requests (SSRF)."],"exampleFix":"// before (caller passes arbitrary domain)\nconst domain = ctx.req.query('domain') ?? 'javbus.com';\n\n// after — restrict to allowlist before the config check even runs\nconst domain = ctx.req.query('domain') ?? 'javbus.com';\nif (!allowDomain.has(new URL(`https://${domain}/`).hostname)) {\n    throw new InvalidParameterError(`Domain ${domain} is not supported. Allowed: ${[...allowDomain].join(', ')}`);\n}","handlingStrategy":"validation","validationCode":"import { config } from '@/config';\nconst allowDomain = new Set(['javbus.com', 'javbus.org', 'javsee.icu', 'javsee.one']);\n\nfunction resolveDomain(input: string | undefined, fallback: string): string {\n    const d = input ?? fallback;\n    const hostname = new URL(`https://${d}/`).hostname;\n    if (!allowDomain.has(hostname) && !config.feature.allow_user_supply_unsafe_domain) {\n        throw new ConfigNotFoundError(`Domain '${hostname}' not allowed. Set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true or use: ${[...allowDomain].join(', ')}`);\n    }\n    return hostname;\n}","typeGuard":null,"tryCatchPattern":"try {\n    const domain = resolveDomain(ctx.req.query('domain'), 'javbus.com');\n    // ...proceed\n} catch (e) {\n    if (e instanceof ConfigNotFoundError) {\n        // surface config guidance to the operator\n    }\n    throw e;\n}","preventionTips":["Never enable ALLOW_USER_SUPPLY_UNSAFE_DOMAIN on public instances — it is an SSRF vector.","Prefer adding vetted mirrors to the allowDomain Set over enabling the global flag.","Normalize input (strip protocol/trailing slash) before the hostname check.","Document the allowlist in the route description so users know the supported domains."],"tags":["config","ssrf-protection","security","domain-allowlist","javbus"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}