{"record":{"id":"88bc93995dd189f9","repo":"DIYgod/RSSHub","slug":"this-rss-is-disabled-unless-allow-user-supply-uns-88bc93","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/lemmy/index.ts","lineNumber":73,"sourceCode":"        supportPodcast: false,\n        supportScihub: false,\n    },\n    name: 'Community',\n    maintainers: ['wb14123', 'pseudoyu'],\n    handler,\n};\n\nasync function handler(ctx) {\n    const sort = ctx.req.param('sort') ?? 'Active';\n    const community = ctx.req.param('community');\n    const communitySlices = community.split('@');\n    if (communitySlices.length !== 2) {\n        throw new InvalidParameterError(`Invalid community: ${community}`);\n    }\n    const instance = community.split('@', 2)[1];\n    const allowedDomain = ['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev'];\n    if (!config.feature.allow_user_supply_unsafe_domain && !allowedDomain.includes(new URL(`http://${instance}/`).hostname)) {\n        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);\n    }\n\n    const communityUrl = `https://${instance}/api/v3/community?name=${community}`;\n    const communityData = await cache.tryGet(communityUrl, async () => {\n        const result = await got({ method: 'get', url: communityUrl, headers: { 'Content-Type': 'application/json' } });\n        return result.data.community_view.community;\n    });\n\n    const postUrl = `https://${instance}/api/v3/post/list?type_=All&sort=${sort}&community_name=${community}&limit=50`;\n    const postData = await cache.tryGet(\n        postUrl,\n        async () => {\n            const result = await got({ method: 'get', url: postUrl, headers: { 'Content-Type': 'application/json' } });\n            return result.data;\n        },\n        config.cache.routeExpire,\n        false\n    );","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/lemmy/index.ts#L55-L91","documentation":"Thrown by the lemmy route handler as ConfigNotFoundError when the instance hostname extracted from the community param is not in the allowedDomain list ({lemmy.world, lemm.ee, lemmy.ml, sh.itjust.works, feddit.de, hexbear.net, beehaw.org, lemmynsfw.com, lemmy.ca, programming.dev}) and config.feature.allow_user_supply_unsafe_domain is falsy. This is the same SSRF-guard pattern as javdb: the route issues API calls to a user-influenced host, so only trusted instances are reachable by default.","triggerScenarios":"Request like /lemmy/<name>@<untrusted-instance>/Hot where the instance is not in allowedDomain and the feature flag is off. The handler builds `new URL(http://<instance>/)` and checks hostname membership; failure throws.","commonSituations":"User points at a smaller/newer Lemmy instance not in the allow-list; self-hoster without ALLOW_USER_SUPPLY_UNSAFE_DOMAIN; attempt to use the route as a server-side request proxy to an internal host.","solutions":["Use one of the allow-listed instances (lemmy.world, lemmey.ml, programming.dev, etc.)","For a trusted instance you run, add its hostname to allowedDomain in lib/routes/lemmy/index.ts:69","Set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true in config only if you accept the SSRF risk","Note the instance check uses exact hostname match — subdomains (e.g. ml.lemmy.world) are rejected"],"exampleFix":"// before\nconst allowedDomain = ['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev'];\n// after — add your trusted instance\nconst allowedDomain = ['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev', 'discuss.tchncs.de'];","handlingStrategy":"validation","validationCode":"import config from '@/utils/config';\nconst ALLOWED = new Set(['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev']);\nfunction assertAllowedInstance(instance: string): void {\n  const host = new URL(`http://${instance}/`).hostname;\n  if (!config.feature.allow_user_supply_unsafe_domain && !ALLOWED.has(host)) {\n    throw new ConfigNotFoundError(`instance '${host}' not allowed`);\n  }\n}","typeGuard":"const ALLOWED = new Set(['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev']);\nfunction isAllowedLemmyInstance(instance: string): boolean {\n  return ALLOWED.has(new URL(`http://${instance}/`).hostname);\n}","tryCatchPattern":"// validate before any API call\nconst { instance } = parseCommunity(community);\nif (!isAllowedLemmyInstance(instance) && !config.feature.allow_user_supply_unsafe_domain) {\n  return ctx.json({ error: 'instance not allowed', allowed: [...ALLOWED] }, 400);\n}\ntry { return await handler(ctx); }\ncatch (e) { if (e instanceof ConfigNotFoundError) return ctx.json({ error: e.message }, 403); throw e; }","preventionTips":["Whitelist exact hostnames; subdomains are not auto-allowed","Make the allow-list a shared constant referenced by route + tests","Gating the feature flag is an operator-level SSRF decision — document it","Reject before issuing any request to the user-influenced host"],"tags":["security","ssrf","config","validation","fediverse"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}