{"record":{"id":"498db34aea4ccf19","repo":"DIYgod/RSSHub","slug":"invalid-domain-498db3","errorCode":null,"errorMessage":"Invalid domain","messagePattern":"Invalid domain","errorType":"validation","errorClass":"InvalidParameterError","httpStatus":null,"severity":"error","filePath":"lib/routes/gamme/category.ts","lineNumber":26,"sourceCode":"import { isValidHost } from '@/utils/valid-host';\n\nexport const route: Route = {\n    path: '/:domain/:category?',\n    categories: ['new-media'],\n    example: '/gamme/news',\n    parameters: {\n        domain: '網站，`news` 為宅宅新聞，`sexynews` 為西斯新聞',\n        category: '分類名，可在 URL 找到，預設為全部',\n    },\n    name: '分類',\n    maintainers: ['TonyRL'],\n    handler,\n};\n\nasync function handler(ctx) {\n    const { domain = 'news', category } = ctx.req.param();\n    if (!isValidHost(domain)) {\n        throw new InvalidParameterError('Invalid domain');\n    }\n    const baseUrl = `https://${domain}.gamme.com.tw`;\n    const feed = await parser.parseURL(`${baseUrl + (category ? `/category/${category}` : '')}/feed`);\n\n    const items = await Promise.all(\n        feed.items.map((item) =>\n            cache.tryGet(item.link!, async () => {\n                const { data } = await got(item.link);\n                const $ = load(data);\n\n                $('.entry img').each((_, img) => {\n                    if (!(img.attribs['data-original'] || img.attribs['data-src'])) {\n                        return;\n                    }\n\n                    img.attribs.src = img.attribs['data-original'] || img.attribs['data-src'];\n                    delete img.attribs['data-original'];\n                    delete img.attribs['data-src'];","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/gamme/category.ts#L8-L44","documentation":"The Gamme category route builds a subdomain from the user-supplied domain parameter (e.g. news, sexynews) and validates it with isValidHost before constructing https://{domain}.gamme.com.tw. If the host is not whitelisted/valid, it throws InvalidParameterError('Invalid domain') — a 400-class error that tells the user the parameter is rejected, not the server.","triggerScenarios":"Passing a domain value that is neither 'news' nor 'sexynews' (the two known Gamme subdomains), or a value with characters that fail isValidHost's check. Because the domain is interpolated directly into a URL, invalid input could cause a malformed request, hence the guard.","commonSituations":"Users typing the full URL instead of the subdomain label; typos like 'new'; attempting a subdomain that Gamme does not operate; injection-style input.","solutions":["Use only 'news' (宅宅新聞) or 'sexynews' (西斯新聞) as the domain parameter.","If Gamme adds a new subdomain, extend the isValidHost whitelist.","Omit the parameter to accept the default 'news'."],"exampleFix":"// before\nif (!isValidHost(domain)) {\n    throw new InvalidParameterError('Invalid domain');\n}\n\n// after\nconst validDomains = ['news', 'sexynews'];\nif (!validDomains.includes(domain)) {\n    throw new InvalidParameterError(`Invalid domain \"${domain}\". Valid: ${validDomains.join(', ')}`);\n}","handlingStrategy":"validation","validationCode":"const validDomains = ['news', 'sexynews'];\nif (!validDomains.includes(domain)) {\n  throw new InvalidParameterError(`Invalid domain. Valid: ${validDomains.join(', ')}`);\n}","typeGuard":"const isValidDomain = (d: string): d is 'news' | 'sexynews' => d === 'news' || d === 'sexynews';","tryCatchPattern":null,"preventionTips":["Whitelist the exact subdomain labels rather than a loose host check.\nDefault to 'news' when the parameter is omitted.\nNever interpolate raw user input into a URL without validation."],"tags":["validation","user-input","url-injection"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}