{"record":{"id":"bf52ff8adeaa516a","repo":"DIYgod/RSSHub","slug":"this-rss-is-disabled-unless-allow-user-supply-uns-bf52ff","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":"exception","errorClass":"ConfigNotFoundError","httpStatus":null,"severity":"error","filePath":"lib/routes/btzj/index.tsx","lineNumber":84,"sourceCode":"\n| 求助                 | 音轨字幕             |\n| -------------------- | -------------------- |\n| forum-index-fid-1187 | forum-index-fid-1191 |\n\n::: tip\nBT 之家的域名会变更，本路由以 \\`https://www.btbtt20.com\\` 为默认域名，若该域名无法访问，可以通过在路由后方加上 \\`?domain=<域名>\\` 指定路由访问的域名。如指定域名为 \\`https://www.btbtt15.com\\`，则在 \\`/btzj\\` 后加上 \\`?domain=btbtt15.com\\` 即可，此时路由为 [\\`/btzj?domain=btbtt15.com\\`](https://rsshub.app/btzj?domain=btbtt15.com)\n\n如果加入了分类参数，直接在分类参数后加入 \\`?domain=<域名>\\` 即可。如指定分类 [剧集](https://www.btbtt20.com/forum-index-fid-950.htm) \\`https://www.btbtt20.com/forum-index-fid-950.htm\\` 并指定域名为 \\`https://www.btbtt15.com\\`，即在 \\`/btzj/forum-index-fid-950\\` 后加上 \\`?domain=btbtt15.com\\`，此时路由为 [\\`/btzj/forum-index-fid-950?domain=btbtt15.com\\`](https://rsshub.app/btzj/forum-index-fid-950?domain=btbtt15.com)\n\n目前，你可以选择的域名有 \\`btbtt10-20.com\\` 共 10 个，或 \\`88btbbt.com\\`，该站也提供了专用网址查询工具。详见 [此贴](https://www.btbtt20.com/thread-index-fid-2-tid-4550191.htm)\n:::`,\n};\n\nasync function handler(ctx) {\n    let category = ctx.req.param('category') ?? '';\n    let domain = ctx.req.query('domain') ?? 'btbtt15.com';\n    if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(new URL(`http://${domain}/`).hostname)) {\n        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);\n    }\n\n    if (category === 'base') {\n        category = '';\n        domain = '88btbtt.com';\n    } else if (category === 'govern') {\n        category = '';\n        domain = '2btjia.com';\n    }\n\n    const rootUrl = `https://www.${domain}`;\n    const currentUrl = `${rootUrl}${category ? `/${category}.htm` : ''}`;\n\n    const response = await got({\n        method: 'get',\n        url: currentUrl,\n    });\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/btzj/index.tsx#L66-L102","documentation":"The BT之家 (btzj) route lets a caller override the mirror domain via the `?domain=` query string. Because letting a user steer RSSHub to an arbitrary host is an SSRF risk, RSSHub gates this behind an allowlist (`allowDomain` = 2btjia.com, 88btbtt.com, btbtt15.com, btbtt20.com) plus a global feature flag `config.feature.allow_user_supply_unsafe_domain`. A ConfigNotFoundError is thrown only when the supplied hostname is NOT in the allowlist AND the flag is disabled.","triggerScenarios":"Requesting `/btzj?domain=<x>` (or `/btzj/<category>?domain=<x>`) where `new URL('http://<x>/').hostname` is not one of the four allowlisted hosts, on an instance where ALLOW_USER_SUPPLY_UNSAFE_DOMAIN is unset/false.","commonSituations":"Public rsshub.app users copying a domain from the btbtt domain-finder thread that isn't hardcoded; self-hosters who did not set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN; supplying a bare TLD or a URL that normalises to a non-allowlisted hostname.","solutions":["Use one of the allowlisted domains: btbtt15.com (default), btbtt20.com, 88btbtt.com, or 2btjia.com.","If you self-host, set the env var ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true and restart RSSHub.","Omit `?domain=` entirely to fall back to the default btbtt15.com.","If a legitimate new btbtt mirror is needed on a public instance, open a PR adding it to the `allowDomain` Set at lib/routes/btzj/index.tsx:12."],"exampleFix":"// before\n//   /btzj?domain=mybtreddit.com\n// after\n//   /btzj?domain=btbtt15.com   (or omit ?domain=)","handlingStrategy":"validation","validationCode":"import { config } from '@/config';\nconst allowDomain = new Set(['2btjia.com', '88btbtt.com', 'btbtt15.com', 'btbtt20.com']);\nfunction isDomainAllowed(domain: string): boolean {\n  return config.feature.allow_user_supply_unsafe_domain || allowDomain.has(new URL(`http://${domain}/`).hostname);\n}\n// call before building the route URL\nif (!isDomainAllowed(domain)) { /* surface a 4xx to the user instead of crashing */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat the domain query param as untrusted input; normalise via `new URL` before checking the allowlist.","Surface allowlist violations as a 400 to the client rather than letting the handler throw an internal ConfigNotFoundError.","Document the four allowed domains prominently in the route description."],"tags":["config","security","ssrf","domain-allowlist"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}