{"record":{"id":"5aef06e365d16648","repo":"DIYgod/RSSHub","slug":"this-rss-is-disabled-unless-allow-user-supply-uns","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":503,"severity":"warning","filePath":"lib/routes/18comic/utils.ts","lineNumber":22,"sourceCode":"import { config } from '@/config';\nimport ConfigNotFoundError from '@/errors/types/config-not-found';\nimport type { DataItem } from '@/types';\nimport cache from '@/utils/cache';\nimport got from '@/utils/got';\nimport md5 from '@/utils/md5';\nimport { parseDate } from '@/utils/parse-date';\n\nimport { renderDescription } from './templates/description';\n\nconst defaultDomain = 'jmcomic1.me';\n// list of address: https://jmcomic2.bet\nconst allowDomain = new Set(['18comic.vip', '18comic.org', 'jmcomic.me', 'jmcomic1.me', 'jm-comic3.art', 'jm-comic.club', 'jm-comic2.ark']);\n\nconst apiDomain = 'www.cdnhth.cc';\n\nconst getRootUrl = (domain) => {\n    if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(domain)) {\n        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);\n    }\n\n    return `https://${domain}`;\n};\n\nconst apiMapCategory = (category) => {\n    switch (category) {\n        case 'another':\n            return '其他漫畫';\n        case 'doujin':\n            return '同人';\n        case 'hanman':\n            return '韓漫';\n        case 'meiman':\n            return '美漫';\n        case 'short':\n            return '短篇';\n        case 'single':","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/18comic/utils.ts#L4-L40","documentation":"18comic routes let the caller pick the source domain via a path parameter. Because the site rotates through many mirrors (some untrusted), the route only accepts a hard-coded allow-list of domains unless the operator explicitly opts in via config.feature.allow_user_supply_unsafe_domain. ConfigNotFoundError is thrown when an off-list domain is supplied and the flag is false.","triggerScenarios":"Calling any /18comic/... route with a `domain` parameter not in {18comic.vip, 18comic.org, jmcomic.me, jmcomic1.me, jm-comic3.art, jm-comic.club, jm-comic2.ark} while ALLOW_USER_SUPPLY_UNSAFE_DOMAIN is unset/false.","commonSituations":"Hardcoding a mirror found on a forum that isn't yet in the allow-list; the site rotated domains and the allow-list is stale; running a self-hosted RSSHub with the safe default and pasting the newest mirror.","solutions":["Use one of the allow-listed domains verbatim (see allowDomain in lib/routes/18comic/utils.ts).","If you run your own instance and accept the SSRF/trust risk, set environment variable ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true.","Open an issue / PR to add the new canonical domain to allowDomain so others benefit."],"exampleFix":"# before (env)\n# nothing set, calling /18comic/<untrusted-mirror>/...\n# after\nALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true\n# OR keep default and call:\n/18comic/jmcomic1.me/...","handlingStrategy":"validation","validationCode":"import { config } from '@/config';\nconst ALLOW_DOMAIN_18COMIC = new Set(['18comic.vip','18comic.org','jmcomic.me','jmcomic1.me','jm-comic3.art','jm-comic.club','jm-comic2.ark']);\nfunction safe18comicDomain(domain) {\n  return ALLOW_DOMAIN_18COMIC.has(domain) || config.feature.allow_user_supply_unsafe_domain === true;\n}","typeGuard":"function isAllowed18comicDomain(d, allowAny): boolean {\n  return allowAny === true || ALLOW_DOMAIN_18COMIC.has(d);\n}","tryCatchPattern":null,"preventionTips":["Resolve the mirror once and store it in config, not in ad-hoc URLs.","Document the SSRF/trust implication of ALLOW_USER_SUPPLY_UNSAFE_DOMAIN before enabling it.","Subscribe to the route's updates so allow-list additions propagate."],"tags":["config","security","domain-allowlist","rsshub","ssrf"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}