Skip to content

Commit dbb7c4e

Browse files
committed
Implement code validation and sanitization
1 parent 5ccff4a commit dbb7c4e

File tree

4 files changed

+323
-8
lines changed

4 files changed

+323
-8
lines changed

app/api/code/submit/route.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NextResponse } from "next/server";
22
import { xata } from "@/lib/xata_client";
33
import axios from "axios";
4+
import { validateHTML } from "./submit-helpers";
45

56
export async function GET() {
67
const record = await xata.db.Site.filter({
@@ -33,13 +34,23 @@ On a successful OpenAI call, call the email generator (api)
3334

3435
export async function POST(req: Request) {
3536
try {
37+
const body = await req.json();
38+
const { code } = body;
39+
if (!validateHTML(code)) {
40+
return NextResponse.json(
41+
{
42+
message:
43+
"We've detected some disallowed code. Only HTML with Tailwind and no JS allowed!",
44+
},
45+
{ status: 400 }
46+
);
47+
}
3648
// Extract the host and protocol from the incoming request
3749
const url = new URL(req.url);
3850
const baseUrl = `${url.protocol}//${url.host}`;
3951

4052
// Use the base URL for Axios requests
4153
axios.put(`${baseUrl}/api/increment/submit`, {});
42-
const body = await req.json();
4354

4455
axios.post(`${baseUrl}/api/code/score`, body);
4556

app/api/code/submit/submit-helpers.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import createDOMPurify from "dompurify";
2+
import { JSDOM } from "jsdom";
3+
import isHtml from "is-html";
4+
5+
const window = new JSDOM("").window;
6+
const DOMPurify = createDOMPurify(window);
7+
8+
// Sanitizer function
9+
export const validateHTML = (html: string): boolean => {
10+
if (!isHtml(html)) {
11+
return false;
12+
}
13+
14+
const cleanHTML = DOMPurify.sanitize(html, {
15+
ALLOWED_ATTR: ["href", "src", "alt", "title", "style"],
16+
FORBID_TAGS: [
17+
"script",
18+
"iframe",
19+
"object",
20+
"embed",
21+
"link",
22+
"style",
23+
"form",
24+
],
25+
FORBID_ATTR: [
26+
"onerror",
27+
"onload",
28+
"onclick",
29+
"onmouseover",
30+
"onfocus",
31+
"onblur",
32+
"onchange",
33+
"onsubmit",
34+
],
35+
});
36+
37+
if (cleanHTML !== html) {
38+
return false;
39+
}
40+
41+
return isHtml(cleanHTML);
42+
};

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333
"axios": "^1.5.1",
3434
"class-variance-authority": "^0.7.0",
3535
"clsx": "^2.0.0",
36+
"dompurify": "^3.0.6",
3637
"encoding": "^0.1.13",
3738
"framer-motion": "^10.16.4",
39+
"is-html": "^3.0.0",
3840
"js-beautify": "^1.14.9",
41+
"jsdom": "^23.0.1",
3942
"lucide-react": "^0.279.0",
4043
"next": "13.4.19",
4144
"next-themes": "^0.2.1",
@@ -56,7 +59,10 @@
5659
"devDependencies": {
5760
"@tanstack/eslint-plugin-query": "^4.34.1",
5861
"@tanstack/react-query-devtools": "^4.35.3",
62+
"@types/dompurify": "^3.0.5",
63+
"@types/is-html": "^2.0.2",
5964
"@types/js-beautify": "^1.14.1",
65+
"@types/jsdom": "^21.1.6",
6066
"@types/node": "20.5.7",
6167
"@types/nodemailer": "^6.4.9",
6268
"@types/react": "18.2.21",

0 commit comments

Comments
 (0)