diff --git a/api/package.json b/api/package.json index 4d99dd2..625d358 100644 --- a/api/package.json +++ b/api/package.json @@ -10,6 +10,7 @@ "dependencies": { "node-fetch": "^2.6.0", "npm-name": "^6.0.0", + "validator": "^13.1.0", "whois-json": "^2.0.4" }, "devDependencies": { diff --git a/api/services/existence.ts b/api/services/existence.ts index 45e0660..cbef18b 100644 --- a/api/services/existence.ts +++ b/api/services/existence.ts @@ -1,3 +1,4 @@ +import isURL from 'validator/lib/isURL'; import {send, sendError, fetch, NowRequest, NowResponse} from '../util/http'; export default async function handler( @@ -10,12 +11,8 @@ export default async function handler( return sendError(res, new Error('no query given')); } - if ( - !/^[(http(s)?)://(www.)?a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)$/.test( - query, - ) - ) { - return sendError(res, new Error('Invalid characters')); + if (!isURL(query)) { + return sendError(res, new Error('Invalid URL: ' + query)); } try { diff --git a/web/src/components/cards/index.tsx b/web/src/components/cards/index.tsx index 21000cc..2430570 100644 --- a/web/src/components/cards/index.tsx +++ b/web/src/components/cards/index.tsx @@ -21,7 +21,7 @@ import JsOrgCard from './providers/JsOrg'; import GithubSearchCard from './providers/GitHubSearch'; import AppStoreCard from './providers/AppStore'; import HerokuCard from './providers/Heroku'; -import NowCard from './providers/Now'; +import VercelCard from './providers/Vercel'; import NtaCard from './providers/Nta'; import NetlifyCard from './providers/Netlify'; import OcamlCard from './providers/Ocaml'; @@ -45,7 +45,7 @@ const Index: React.FC<{query: string}> = ({query}) => { - + diff --git a/web/src/components/cards/providers/Domains.tsx b/web/src/components/cards/providers/Domains.tsx index 5f24893..c09388a 100644 --- a/web/src/components/cards/providers/Domains.tsx +++ b/web/src/components/cards/providers/Domains.tsx @@ -7,7 +7,11 @@ import {zones} from '../../../util/zones'; const DomainCard: React.FC<{query: string}> = ({query}) => { const {t} = useTranslation(); - const lowerCase = query.toLowerCase(); + + const sanitizedQuery = query + .replace(/[^0-9a-zA-Z_-]/g, '') + .replace(/_/g, '-'); + const lowerCase = sanitizedQuery.toLowerCase(); const domainHackSuggestions = zones .map((zone) => new RegExp(`${zone}$`).exec(lowerCase.slice(1))) diff --git a/web/src/components/cards/providers/Heroku.tsx b/web/src/components/cards/providers/Heroku.tsx index 3c31aef..2374df1 100644 --- a/web/src/components/cards/providers/Heroku.tsx +++ b/web/src/components/cards/providers/Heroku.tsx @@ -6,7 +6,11 @@ import {Card, Repeater, DedicatedAvailability} from '../core'; const HerokuCard: React.FC<{query: string}> = ({query}) => { const {t} = useTranslation(); - const lowerCase = query.toLowerCase(); + + const sanitizedQuery = query + .replace(/[^0-9a-zA-Z_-]/g, '') + .replace(/_/g, '-'); + const lowerCase = sanitizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/web/src/components/cards/providers/JsOrg.tsx b/web/src/components/cards/providers/JsOrg.tsx index bd765fe..f850998 100644 --- a/web/src/components/cards/providers/JsOrg.tsx +++ b/web/src/components/cards/providers/JsOrg.tsx @@ -6,7 +6,11 @@ import {Card, Repeater, DedicatedAvailability} from '../core'; const JsOrgCard: React.FC<{query: string}> = ({query}) => { const {t} = useTranslation(); - const lowerCase = query.toLowerCase(); + + const sanitizedQuery = query + .replace(/[^0-9a-zA-Z_-]/g, '') + .replace(/_/g, '-'); + const lowerCase = sanitizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/web/src/components/cards/providers/Netlify.tsx b/web/src/components/cards/providers/Netlify.tsx index 070be50..aa7ce5a 100644 --- a/web/src/components/cards/providers/Netlify.tsx +++ b/web/src/components/cards/providers/Netlify.tsx @@ -6,7 +6,11 @@ import {Card, Repeater, DedicatedAvailability} from '../core'; const NetlifyCard: React.FC<{query: string}> = ({query}) => { const {t} = useTranslation(); - const lowerCase = query.toLowerCase(); + + const sanitizedQuery = query + .replace(/[^0-9a-zA-Z_-]/g, '') + .replace(/_/g, '-'); + const lowerCase = sanitizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/web/src/components/cards/providers/Npm.tsx b/web/src/components/cards/providers/Npm.tsx index 776decc..9a039d4 100644 --- a/web/src/components/cards/providers/Npm.tsx +++ b/web/src/components/cards/providers/Npm.tsx @@ -19,8 +19,8 @@ const NpmCard: React.FC<{query: string}> = ({query}) => { } diff --git a/web/src/components/cards/providers/S3.tsx b/web/src/components/cards/providers/S3.tsx index ee331d0..1b92285 100644 --- a/web/src/components/cards/providers/S3.tsx +++ b/web/src/components/cards/providers/S3.tsx @@ -6,7 +6,11 @@ import {Card, DedicatedAvailability, Repeater} from '../core'; const S3Card: React.FC<{query: string}> = ({query}) => { const {t} = useTranslation(); - const lowerCase = query.toLowerCase(); + + const sanitizedQuery = query + .replace(/[^0-9a-zA-Z_-]/g, '') + .replace(/_/g, '-'); + const lowerCase = sanitizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/web/src/components/cards/providers/Slack.tsx b/web/src/components/cards/providers/Slack.tsx index 37bdfb3..a19924f 100644 --- a/web/src/components/cards/providers/Slack.tsx +++ b/web/src/components/cards/providers/Slack.tsx @@ -6,7 +6,11 @@ import {Card, DedicatedAvailability, Repeater} from '../core'; const SlackCard: React.FC<{query: string}> = ({query}) => { const {t} = useTranslation(); - const lowerCase = query.toLowerCase(); + + const sanitizedQuery = query + .replace(/[^0-9a-zA-Z_-]/g, '') + .replace(/_/g, '-'); + const lowerCase = sanitizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/web/src/components/cards/providers/Now.tsx b/web/src/components/cards/providers/Vercel.tsx similarity index 72% rename from web/src/components/cards/providers/Now.tsx rename to web/src/components/cards/providers/Vercel.tsx index 7a79de7..6ae55d1 100644 --- a/web/src/components/cards/providers/Now.tsx +++ b/web/src/components/cards/providers/Vercel.tsx @@ -4,9 +4,13 @@ import {NowIcon} from '../../Icons'; import {Card, Repeater, DedicatedAvailability} from '../core'; -const NowCard: React.FC<{query: string}> = ({query}) => { +const VercelCard: React.FC<{query: string}> = ({query}) => { const {t} = useTranslation(); - const lowerCase = query.toLowerCase(); + + const sanitizedQuery = query + .replace(/[^0-9a-zA-Z_-]/g, '') + .replace(/_/g, '-'); + const lowerCase = sanitizedQuery.toLowerCase(); const names = [lowerCase]; @@ -27,4 +31,4 @@ const NowCard: React.FC<{query: string}> = ({query}) => { ); }; -export default NowCard; +export default VercelCard;