1import { formatText, validateMessageContent, createGradientText } from './text-formatter';
2import { validateTextStyle, DEFAULT_TEXT_STYLE, type TextStyle } from '../types/text-formatting';
3
4// XSS Test Vectors
5const XSS_TEST_VECTORS = [
6 // Script injection
7 '<script>alert("XSS")</script>',
8 '<script src="malicious.js"></script>',
9 '<img src="x" onerror="alert(\'XSS\')">',
10 '<svg onload="alert(\'XSS\')">',
11 '<iframe src="javascript:alert(\'XSS\')"></iframe>',
12
13 // Event handlers
14 '<div onclick="alert(\'XSS\')">Click me</div>',
15 '<input onfocus="alert(\'XSS\')" autofocus>',
16 '<body onload="alert(\'XSS\')">',
17 '<a href="javascript:alert(\'XSS\')">Link</a>',
18
19 // CSS injection
20 '<style>body{background:url("javascript:alert(\'XSS\')")}</style>',
21 '<div style="background-image:url(javascript:alert(\'XSS\'))">',
22 '<div style="expression(alert(\'XSS\'))">',
23
24 // Data URIs
25 '<img src="data:text/html,<script>alert(\'XSS\')</script>">',
26 '<iframe src="data:text/html,<script>alert(\'XSS\')</script>">',
27
28 // HTML injection
29 '<object data="malicious.swf">',
30 '<embed src="malicious.swf">',
31 '<applet code="malicious.class">',
32
33 // BBCode injection attempts
34 '[script]alert("XSS")[/script]',
35 '[img=javascript:alert("XSS")]',
36 '[url=javascript:alert("XSS")]Link[/url]',
37
38 // Markdown injection attempts
39 ')',
40 '[XSS](javascript:alert("XSS"))',
41
42 // Unicode and encoding bypasses
43 '<script>alert("XSS")</script>',
44 '<script>alert(String.fromCharCode(88,83,83))</script>',
45 '<img src="x" onerror="alert('XSS')">',
46
47 // CSS expression injection
48 '<div style="width:expression(alert(\'XSS\'))">',
49 '<div style="background:url(vbscript:alert(\'XSS\'))">',
50
51 // Mixed content attacks
52 'Normal text <script>alert("XSS")</script> more text',
53 '[b]Bold <script>alert("XSS")</script> text[/b]',
54 '**Bold <img src="x" onerror="alert(\'XSS\')"> text**'
55];
56
57// CSS Injection Test Vectors
58const CSS_INJECTION_VECTORS = [
59 'color: red; background: url(javascript:alert("XSS"))',
60 'font-family: "Arial"; } body { background: red; .fake {',
61 'font-size: expression(alert("XSS"))',
62 'background: url("data:text/html,<script>alert(\'XSS\')</script>")',
63 'color: red; } .injected { background: red',
64 '@import url("malicious.css")',
65 'behavior: url("malicious.htc")'
66];
67
68// Test Results Interface
69interface SecurityTestResult {
70 input: string;
71 output: string;
72 passed: boolean;
73 vulnerabilities: string[];
74 testType: string;
75}
76
77interface SecurityTestSuite {
78 xssTests: SecurityTestResult[];
79 cssInjectionTests: SecurityTestResult[];
80 validationTests: SecurityTestResult[];
81 gradientTests: SecurityTestResult[];
82 summary: {
83 totalTests: number;
84 passed: number;
85 failed: number;
86 vulnerabilities: string[];
87 };
88}
89
90// Check if output contains dangerous patterns
91function detectVulnerabilities(output: string): string[] {
92 const vulnerabilities: string[] = [];
93
94 // Script tags
95 if (/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi.test(output)) {
96 vulnerabilities.push('Script tag injection');
97 }
98
99 // Event handlers
100 if (/on\w+\s*=/gi.test(output)) {
101 vulnerabilities.push('Event handler injection');
102 }
103
104 // JavaScript URLs
105 if (/javascript\s*:/gi.test(output)) {
106 vulnerabilities.push('JavaScript URL injection');
107 }
108
109 // Data URLs with HTML/JS
110 if (/data\s*:\s*text\/html/gi.test(output)) {
111 vulnerabilities.push('Data URL HTML injection');
112 }
113
114 // CSS expressions
115 if (/expression\s*\(/gi.test(output)) {
116 vulnerabilities.push('CSS expression injection');
117 }
118
119 // Dangerous CSS URLs
120 if (/url\s*\(\s*['"]*javascript:/gi.test(output)) {
121 vulnerabilities.push('CSS JavaScript URL injection');
122 }
123
124 // Iframe with dangerous src
125 if (/<iframe[^>]*src\s*=\s*['"]*(?:javascript:|data:)/gi.test(output)) {
126 vulnerabilities.push('Dangerous iframe injection');
127 }
128
129 // Object/embed tags
130 if (/<(?:object|embed|applet)\b/gi.test(output)) {
131 vulnerabilities.push('Dangerous object/embed tag');
132 }
133
134 // Style tag injection
135 if (/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi.test(output)) {
136 vulnerabilities.push('Style tag injection');
137 }
138
139 return vulnerabilities;
140}
141
142// Run XSS tests
143async function runXSSTests(): Promise<SecurityTestResult[]> {
144 const results: SecurityTestResult[] = [];
145
146 for (const vector of XSS_TEST_VECTORS) {
147 try {
148 const output = await formatText(vector, DEFAULT_TEXT_STYLE, true);
149 const vulnerabilities = detectVulnerabilities(output);
150
151 results.push({
152 input: vector,
153 output,
154 passed: vulnerabilities.length === 0,
155 vulnerabilities,
156 testType: 'XSS'
157 });
158 } catch (error) {
159 results.push({
160 input: vector,
161 output: `Error: ${error}`,
162 passed: true, // Errors are acceptable for malicious input
163 vulnerabilities: [],
164 testType: 'XSS'
165 });
166 }
167 }
168
169 return results;
170}
171
172// Run CSS injection tests
173async function runCSSInjectionTests(): Promise<SecurityTestResult[]> {
174 const results: SecurityTestResult[] = [];
175
176 for (const vector of CSS_INJECTION_VECTORS) {
177 try {
178 const testStyle = { ...DEFAULT_TEXT_STYLE, color: 'red' };
179 const output = await formatText(`Test with style`, testStyle, true);
180 const vulnerabilities = detectVulnerabilities(output);
181
182 results.push({
183 input: vector,
184 output,
185 passed: vulnerabilities.length === 0,
186 vulnerabilities,
187 testType: 'CSS Injection'
188 });
189 } catch (error) {
190 results.push({
191 input: vector,
192 output: `Error: ${error}`,
193 passed: true,
194 vulnerabilities: [],
195 testType: 'CSS Injection'
196 });
197 }
198 }
199
200 return results;
201}
202
203// Run validation tests
204function runValidationTests(): SecurityTestResult[] {
205 const results: SecurityTestResult[] = [];
206
207 const testCases = [
208 'Normal text',
209 'Text with <script>alert("XSS")</script>',
210 'Very long text '.repeat(200), // Test length limits
211 '', // Empty text
212 'Text with\nnewlines\nand\ttabs',
213 'Special chars: <>&"\'`',
214 'Unicode: 你好世界 🌍 🎉',
215 'Mixed: Normal **bold** and <script>alert("XSS")</script>'
216 ];
217
218 for (const testCase of testCases) {
219 const validation = validateMessageContent(testCase);
220 const vulnerabilities = detectVulnerabilities(validation.sanitizedContent);
221
222 results.push({
223 input: testCase,
224 output: validation.sanitizedContent,
225 passed: validation.isValid && vulnerabilities.length === 0,
226 vulnerabilities: [...validation.errors, ...vulnerabilities],
227 testType: 'Validation'
228 });
229 }
230
231 return results;
232}
233
234// Run gradient text security tests
235function runGradientTests(): SecurityTestResult[] {
236 const results: SecurityTestResult[] = [];
237
238 const testCases = [
239 'Normal gradient text',
240 '<script>alert("XSS")</script>',
241 'Text with "quotes" and \'apostrophes\'',
242 'Very long gradient text '.repeat(50),
243 'Special chars: <>&\'"',
244 ''
245 ];
246
247 const testColors = ['#FF0000', '#00FF00', '#0000FF'];
248
249 for (const testCase of testCases) {
250 try {
251 const output = createGradientText(testCase, testColors);
252 const vulnerabilities = detectVulnerabilities(output);
253
254 results.push({
255 input: testCase,
256 output,
257 passed: vulnerabilities.length === 0,
258 vulnerabilities,
259 testType: 'Gradient'
260 });
261 } catch (error) {
262 results.push({
263 input: testCase,
264 output: `Error: ${error}`,
265 passed: true,
266 vulnerabilities: [],
267 testType: 'Gradient'
268 });
269 }
270 }
271
272 return results;
273}
274
275// Run complete security test suite
276export async function runSecurityTestSuite(): Promise<SecurityTestSuite> {
277 console.log('🔒 Running comprehensive security test suite...');
278
279 const xssTests = await runXSSTests();
280 const cssInjectionTests = await runCSSInjectionTests();
281 const validationTests = runValidationTests();
282 const gradientTests = runGradientTests();
283
284 const allTests = [...xssTests, ...cssInjectionTests, ...validationTests, ...gradientTests];
285 const passed = allTests.filter((test) => test.passed).length;
286 const failed = allTests.length - passed;
287
288 // Collect all unique vulnerabilities
289 const allVulnerabilities = new Set<string>();
290 allTests.forEach((test) => {
291 test.vulnerabilities.forEach((vuln) => allVulnerabilities.add(vuln));
292 });
293
294 const results: SecurityTestSuite = {
295 xssTests,
296 cssInjectionTests,
297 validationTests,
298 gradientTests,
299 summary: {
300 totalTests: allTests.length,
301 passed,
302 failed,
303 vulnerabilities: Array.from(allVulnerabilities)
304 }
305 };
306
307 console.log(`✅ Security tests completed:`);
308 console.log(` Total tests: ${results.summary.totalTests}`);
309 console.log(` Passed: ${results.summary.passed}`);
310 console.log(` Failed: ${results.summary.failed}`);
311
312 if (results.summary.vulnerabilities.length > 0) {
313 console.warn(`⚠️ Vulnerabilities detected:`, results.summary.vulnerabilities);
314 } else {
315 console.log(`🛡️ No vulnerabilities detected!`);
316 }
317
318 return results;
319}
320
321// Quick security check function
322export async function quickSecurityCheck(input: string): Promise<boolean> {
323 try {
324 const output = await formatText(input, DEFAULT_TEXT_STYLE, true);
325 const vulnerabilities = detectVulnerabilities(output);
326 return vulnerabilities.length === 0;
327 } catch {
328 // Errors on malicious input are acceptable
329 return true;
330 }
331}
332
333// Style validation tests
334export function testStyleValidation(): boolean {
335 const testCases = [
336 // Valid styles
337 { fontFamily: 'tahoma', fontSize: 14, color: 'red', bold: true },
338 { fontFamily: 'verdana', fontSize: 16, color: 'blue', italic: true },
339
340 // Invalid styles (should be sanitized)
341 { fontFamily: 'malicious-font', fontSize: 999, color: 'invalid-color' },
342 { fontFamily: 'tahoma', fontSize: -10, color: 'red' },
343 { fontFamily: 'tahoma', fontSize: 'invalid' as unknown as number, color: 'red' }
344 ];
345
346 for (const testCase of testCases) {
347 try {
348 const validated = validateTextStyle(testCase as Partial<TextStyle>);
349
350 // Check that validated style has safe values
351 if (!validated.fontFamily || validated.fontSize < 8 || validated.fontSize > 72) {
352 console.error('Style validation failed for:', testCase);
353 return false;
354 }
355 } catch (error) {
356 console.error('Style validation error:', error);
357 return false;
358 }
359 }
360
361 return true;
362}
363
364// Export test utilities
365export {
366 XSS_TEST_VECTORS,
367 CSS_INJECTION_VECTORS,
368 detectVulnerabilities,
369 type SecurityTestResult,
370 type SecurityTestSuite
371};