{"record":{"id":"4d947aa0c0a90800","repo":"DIYgod/RSSHub","slug":"invalid-username","errorCode":null,"errorMessage":"Invalid username","messagePattern":"Invalid username","errorType":"validation","errorClass":"InvalidParameterError","httpStatus":null,"severity":"error","filePath":"lib/routes/gumroad/index.tsx","lineNumber":45,"sourceCode":"};\n\nconst renderDescription = (img, productsName, price, desc, stack) =>\n    renderToString(\n        <>\n            <img src={img} />\n            <h1>{productsName}</h1>\n            <p style=\"color: red;\">{price}</p>\n            {desc ? <>{raw(desc)}</> : null}\n            <hr />\n            {stack ? <>{raw(stack)}</> : null}\n        </>\n    );\n\nasync function handler(ctx) {\n    const username = ctx.req.param('username');\n    const products = ctx.req.param('products');\n    if (!isValidHost(username)) {\n        throw new InvalidParameterError('Invalid username');\n    }\n    const url = `https://${username}.gumroad.com/l/${products}`;\n\n    const response = await got(url);\n    const $ = load(response.data);\n    const title = $('section.product-content.product-content__row > header > h1').text();\n    const userFullName = $('section.product-content.product-content__row > section.details > a').text();\n\n    const item = [\n        {\n            title,\n            link: url,\n            description: renderDescription(\n                response.data.match(/data-preview-url=\"(.*?)\"/)[1],\n                title,\n                $('div.price').text(),\n                $('section.product-content.product-content__row > section:nth-child(3) > div').html(),\n                $('div.product-info').find('ul.stack').html()","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/gumroad/index.tsx#L27-L63","documentation":"The Gumroad route builds `https://${username}.gumroad.com/l/${products}` and validates `username` with isValidHost before requesting. If the username is not a valid hostname label (alphanumeric + hyphens), it throws InvalidParameterError. This protects against malformed subdomains and URL injection.","triggerScenarios":"`/gumroad/<username>/<products>` where <username> contains invalid hostname characters (dots, slashes, spaces, punctuation) or is empty.","commonSituations":"Passing a full URL or email instead of the bare username; including `gumroad.com` in the username; typos that introduce punctuation.","solutions":["Pass the bare Gumroad subdomain username (lowercase letters, digits, hyphens only).","Do not include the protocol or `gumroad.com` — the handler adds them.","Confirm the username by opening `<username>.gumroad.com` in a browser first."],"exampleFix":"// before\n//   /gumroad/my.shop.name/myproduct\n// after\n//   /gumroad/myshopname/myproduct","handlingStrategy":"validation","validationCode":"import isValidHost from '@/utils/valid-host';\nfunction isValidGumroadUser(username: string): boolean {\n  return /^[a-z0-9-]+$/.test(username) && isValidHost(username);\n}","typeGuard":"function isValidGumroadUser(username: string): username is string {\n  return /^[a-z0-9-]+$/.test(username) && isValidHost(username);\n}","tryCatchPattern":null,"preventionTips":["Reject anything that is not a bare subdomain label before building the URL.","Never accept a full URL or email in the username slot."],"tags":["validation","hostname"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}