Skip to content

Commit

Permalink
feat: add URL validation (denoland#443)
Browse files Browse the repository at this point in the history
closes denoland#378.  

adds some naive url validations to the submit route.  
adds some tests for these naive validations.

these validations check that:
- the urls start w/ `http:` or `https:`
- the urls don't use local ip addresses and/or `localhost`

---------

Co-authored-by: Asher Gomez <[email protected]>
  • Loading branch information
mbhrznr and iuioiua authored Aug 22, 2023
1 parent 8b8febd commit 088c0ff
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 2 deletions.
6 changes: 4 additions & 2 deletions routes/submit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Head from "@/components/Head.tsx";
import IconCheckCircle from "tabler_icons_tsx/circle-check.tsx";
import IconCircleX from "tabler_icons_tsx/circle-x.tsx";
import { SignedInState } from "@/utils/middleware.ts";
import { isPublicUrl, isValidUrl } from "@/utils/url_validation.ts";

export const handler: Handlers<SignedInState, SignedInState> = {
async POST(req, ctx) {
Expand All @@ -24,8 +25,9 @@ export const handler: Handlers<SignedInState, SignedInState> = {
}

try {
// Throws if an invalid URL
new URL(url);
if (!isValidUrl(url) || !isPublicUrl(url)) {
return new Response(null, { status: 400 });
}
} catch {
return new Response(null, { status: 400 });
}
Expand Down
29 changes: 29 additions & 0 deletions utils/url_validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.

export function isValidUrl(string: string): boolean {
try {
const { protocol } = new URL(string);
return protocol.startsWith("http");
} catch {
return false;
}
}

export function isPublicUrl(string: string): boolean {
try {
const { hostname } = new URL(string);
const ranges = [
/^localhost$/,
/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
/^::1$/,
/^0:0:0:0:0:0:0:1$/,
/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
/^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}$/,
/^192\.168\.\d{1,3}\.\d{1,3}$/,
];

return !ranges.some((range) => range.test(hostname));
} catch (_) {
return false;
}
}
24 changes: 24 additions & 0 deletions utils/url_validation_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.

import { assertEquals } from "std/testing/asserts.ts";
import { isPublicUrl, isValidUrl } from "./url_validation.ts";

Deno.test("[url_validation] isValidUrl()", () => {
assertEquals(isValidUrl("https://hunt.deno.land/"), true);
assertEquals(isValidUrl("http://hunt.deno.land/"), true);
assertEquals(isValidUrl("ws://hunt.deno.land/"), false);
assertEquals(isValidUrl("wss://hunt.deno.land/"), false);
assertEquals(isValidUrl("invalidurl"), false);
});

Deno.test("[url_validation] isPublicUrl()", () => {
assertEquals(isPublicUrl("https://hunt.deno.land/"), true);
assertEquals(isPublicUrl("http://hunt.deno.land/"), true);
assertEquals(isPublicUrl("ws://hunt.deno.land/"), true);
assertEquals(isPublicUrl("http://localhost/"), false);
assertEquals(isPublicUrl("http://127.0.0.1/"), false);
assertEquals(isPublicUrl("http://::1/"), false);
assertEquals(isPublicUrl("http://10.0.0.0/"), false);
assertEquals(isPublicUrl("http://172.16.0.0/"), false);
assertEquals(isPublicUrl("http://192.168.0.0/"), false);
});

0 comments on commit 088c0ff

Please sign in to comment.