Skip to content

Commit a5c2626

Browse files
authored
Merge pull request #392 from ash1shkumar/strengthen-content-processing-security
feat: strengthen content sanitization and validation safeguards
2 parents af3e516 + 0bb8cf7 commit a5c2626

1 file changed

Lines changed: 108 additions & 14 deletions

File tree

frontend/lib/sanitizeInput.ts

Lines changed: 108 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import sanitizeHtml from "sanitize-html";
22

33
const MAX_INPUT_LENGTH = 10000;
4+
const MAX_LINE_COUNT = 500;
5+
6+
type ValidationResult = {
7+
isValid: boolean;
8+
reason?: string;
9+
};
410

511
function normalizeInput(input: string): string {
612
return input
@@ -21,32 +27,120 @@ function enforceLengthLimit(input: string): string {
2127
: input;
2228
}
2329

30+
function removeControlCharacters(
31+
input: string
32+
): string {
33+
return input.replace(
34+
/[\u0001-\u001F\u007F]/g,
35+
""
36+
);
37+
}
38+
39+
function removeSuspiciousUnicode(
40+
input: string
41+
): string {
42+
return input.replace(
43+
/[\u202A-\u202E\u2066-\u2069]/g,
44+
""
45+
);
46+
}
47+
48+
function limitLineCount(
49+
input: string
50+
): string {
51+
const lines =
52+
input.split("\n");
53+
54+
return lines
55+
.slice(0, MAX_LINE_COUNT)
56+
.join("\n");
57+
}
58+
59+
export function validateInput(
60+
input: string
61+
): ValidationResult {
62+
if (
63+
input.length >
64+
MAX_INPUT_LENGTH * 2
65+
) {
66+
return {
67+
isValid: false,
68+
reason:
69+
"Input exceeds validation threshold",
70+
};
71+
}
72+
73+
return {
74+
isValid: true,
75+
};
76+
}
77+
78+
export function securityAuditInput(
79+
input: string
80+
) {
81+
return {
82+
containsScript:
83+
/<script/i.test(input),
84+
containsEventHandlers:
85+
/on\w+=/i.test(input),
86+
containsJavascriptUrl:
87+
/javascript:/i.test(input),
88+
inputLength:
89+
input.length,
90+
};
91+
}
92+
2493
export function sanitizeInput(
2594
input: string | null | undefined
2695
): string {
2796
if (!input) {
2897
return "";
2998
}
3099

100+
const validation =
101+
validateInput(input);
102+
103+
if (!validation.isValid) {
104+
return "";
105+
}
106+
31107
const normalizedInput =
32108
normalizeInput(input);
33109

34-
const sanitized = sanitizeHtml(
35-
normalizedInput,
36-
{
37-
allowedTags: [],
38-
allowedAttributes: {},
39-
allowedSchemes: [],
40-
parser: {
41-
lowerCaseTags: true,
42-
},
43-
}
44-
);
110+
const controlSafe =
111+
removeControlCharacters(
112+
normalizedInput
113+
);
114+
115+
const unicodeSafe =
116+
removeSuspiciousUnicode(
117+
controlSafe
118+
);
119+
120+
const sanitized =
121+
sanitizeHtml(
122+
unicodeSafe,
123+
{
124+
allowedTags: [],
125+
allowedAttributes: {},
126+
allowedSchemes: [],
127+
parser: {
128+
lowerCaseTags: true,
129+
},
130+
}
131+
);
132+
133+
const normalizedWhitespace =
134+
normalizeWhitespace(
135+
sanitized
136+
);
45137

46-
const cleanedInput =
47-
normalizeWhitespace(sanitized);
138+
const lineLimited =
139+
limitLineCount(
140+
normalizedWhitespace
141+
);
48142

49143
return enforceLengthLimit(
50-
cleanedInput
144+
lineLimited
51145
);
52146
}

0 commit comments

Comments
 (0)