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;