{"record":{"id":"ed04840fc1b2873f","repo":"DIYgod/RSSHub","slug":"invalid-country-ed0484","errorCode":null,"errorMessage":"Invalid country","messagePattern":"Invalid country","errorType":"exception","errorClass":null,"httpStatus":503,"severity":"warning","filePath":"lib/routes-deprecated/fashionnetwork/news.js","lineNumber":17,"sourceCode":"const got = require('@/utils/got');\nconst cheerio = require('cheerio');\nconst { isValidHost } = require('@/utils/valid-host');\n\nmodule.exports = async (ctx) => {\n    const country = ctx.params.country || 'ww';\n    let sectors = ctx.params.sectors || '';\n    let categories = ctx.params.categories || '';\n\n    sectors = sectors === 'all' ? '' : sectors;\n    categories = categories === 'all' ? '' : categories;\n\n    const sectorsUrl = sectors ? 'sectors%5B%5D=' + sectors.split(',').join('&sectors%5B%5D=') : '';\n    const categoriesUrl = categories ? 'categs%5B%5D=' + categories.split(',').join('&categs%5B%5D=') : '';\n\n    if (!isValidHost(country)) {\n        throw new Error('Invalid country');\n    }\n\n    const rootUrl = `https://${country}.fashionnetwork.com`;\n    const currentUrl = `${rootUrl}/news/s.jsonp?${sectorsUrl}&${categoriesUrl}`;\n    const response = await got({\n        method: 'get',\n        url: currentUrl,\n    });\n\n    const $ = cheerio.load(\n        unescape(response.data.match(/\"html\":\"(.*)\",\"relatedUrl\"/)[1].replaceAll(/\\\\(u[\\dA-Fa-f]{4})/gm, '%$1'))\n            .replaceAll('\\\\n', '')\n            .replaceAll('\\\\/', '/')\n    );\n\n    const list = $('.list-ui__title')\n        .slice(0, 10)\n        .map((_, item) => {","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes-deprecated/fashionnetwork/news.js#L1-L35","documentation":"Thrown by the fashionnetwork news route when the `country` path parameter (default 'ww') fails isValidHost, after sectors/categories are normalized. The value feeds `https://${country}.fashionnetwork.com`, so only a single safe DNS label is accepted.","triggerScenarios":"Request to /fashionnetwork/news/:country with a country containing invalid host characters (dots, slashes, protocol).","commonSituations":"Typo; passing the full hostname; traversal attempt; copy-pasting a URL fragment into the path.","solutions":["Use a valid country label (e.g. 'ww', 'fr', 'cn').","Omit the segment to default to 'ww'.","Keep sectors/categories values to comma-separated slugs only."],"exampleFix":"// before\n/fashionnetwork/news/fr/news\n// after\n/fashionnetwork/news/fr","handlingStrategy":"validation","validationCode":"import { isValidHost } from '@/utils/valid-host';\nconst country = params.country ?? 'ww';\nif (!isValidHost(country)) throw new Error('Invalid country');","typeGuard":"const isSafeCountry = (v: unknown): boolean =>\n  typeof v === 'string' && /^[a-z0-9-]+$/i.test(v) && !v.includes('.');","tryCatchPattern":null,"preventionTips":["Validate country before normalizing sectors/categories.","Reject values containing '.', '/', or ':' at the trust boundary."],"tags":["validation","routes","ssrf-guard"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}