{"record":{"id":"4a1703c57f2c899a","repo":"DIYgod/RSSHub","slug":"this-rss-is-disabled-unless-allow-user-supply-uns-4a1703","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":"error","filePath":"lib/routes/fediverse/timeline.ts","lineNumber":41,"sourceCode":"    },\n    name: 'Timeline',\n    maintainers: ['DIYgod', 'pseudoyu'],\n    handler,\n};\n\nconst allowedDomain = new Set(['mastodon.social', 'pawoo.net', config.mastodon.apiHost].filter(Boolean));\nconst activityPubTypes = new Set(['application/activity+json', 'application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"']);\n\nasync function handler(ctx) {\n    const account = ctx.req.param('account');\n    const domain = account.split('@', 2)[1];\n    const username = account.split('@', 1)[0];\n\n    if (!domain || !username) {\n        throw new InvalidParameterError('Invalid account');\n    }\n    if (!config.feature.allow_user_supply_unsafe_domain && !allowedDomain.has(domain.toLowerCase())) {\n        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);\n    }\n\n    const requestOptions = {\n        headers: {\n            Accept: 'application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"',\n        },\n    };\n\n    const acc = await ofetch(`https://${domain}/.well-known/webfinger?resource=acct:${account}`, {\n        headers: {\n            Accept: 'application/jrd+json',\n        },\n    });\n    const jsonLink = acc.links.find((link) => link.rel === 'self' && activityPubTypes.has(link.type))?.href;\n    const link = acc.links.find((link) => link.rel === 'http://webfinger.net/rel/profile-page')?.href;\n    const officialFeed = await parser.parseURL(`${link}.rss`);\n\n    if (officialFeed) {","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/fediverse/timeline.ts#L23-L59","documentation":"Thrown as a `ConfigNotFoundError` when the user-supplied Fediverse domain is not in the allowed-domain set AND the `ALLOW_USER_SUPPLY_UNSAFE_DOMAIN` feature flag is not enabled. This is an SSRF defense: without it, any user could make RSSHub fetch from arbitrary domains via the webfinger and ActivityPub endpoints. The allowed set includes `mastodon.social`, `pawoo.net`, and the configured `config.mastodon.apiHost`.","triggerScenarios":"A user requests `/fediverse/timeline/user@some-mastodon-instance.example` where the domain is not mastodon.social, pawoo.net, or the configured apiHost. The `config.feature.allow_user_supply_unsafe_domain` flag is false (the default), so the ConfigNotFoundError fires before any outbound request.","commonSituations":"Self-hosted RSSHub where the operator wants to follow users on a non-allowlisted instance (e.g., a private Mastodon, a GoToSocial, or an Akkoma server). The operator has not set `ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true` in the environment. A public RSSHub instance correctly rejects the request for security.","solutions":["Set the `ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true` environment variable if you trust your users and understand the SSRF risk.","Alternatively, add the desired domain to `config.mastodon.apiHost` or extend the `allowedDomain` Set in the route file.","Use an allowlisted domain (mastodon.social or pawoo.net) if you only need those instances.","If you are the end user on a public instance, ask the operator to allowlist your instance or self-host."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const ALLOWED_DOMAINS = new Set(['mastodon.social', 'pawoo.net', config.mastodon.apiHost].filter(Boolean));\n\nfunction validateFediverseDomain(domain: string): void {\n    if (!config.feature.allow_user_supply_unsafe_domain && !ALLOWED_DOMAINS.has(domain.toLowerCase())) {\n        throw new ConfigNotFoundError(`Domain '${domain}' is not allowed. Set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true or use an allowed instance.`);\n    }\n}","typeGuard":"function isAllowedDomain(domain: string): boolean {\n    return config.feature.allow_user_supply_unsafe_domain ||\n        ALLOWED_DOMAINS.has(domain.toLowerCase());\n}","tryCatchPattern":null,"preventionTips":["If you are the instance operator and trust your users, set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true.","Otherwise, use only allowlisted domains: mastodon.social, pawoo.net, or your configured apiHost.","Add commonly requested instances to the allowedDomain Set in the route file.","Understand this is an SSRF defense — removing it without thought is dangerous."],"tags":["ssrf-prevention","config-required","feature-flag","security","config-not-found-error","fediverse"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}