/** * Input Validation and Injection Tests * Tests for SQL injection, XSS, and other injection attack prevention */ import { describe, it, expect } from "bun:test"; import { isValidEmail, validatePassword, isValidDisplayName } from "~/lib/validation"; import { SQL_INJECTION_PAYLOADS, XSS_PAYLOADS } from "./test-utils"; import { ConnectionFactory } from "~/server/database"; describe("Input Validation and Injection Prevention", () => { describe("Email Validation", () => { it("should accept valid email addresses", () => { const validEmails = [ "user@example.com", "test.user@example.com", "user+tag@example.co.uk", "user123@test-domain.com", "first.last@subdomain.example.com" ]; for (const email of validEmails) { expect(isValidEmail(email)).toBe(true); } }); it("should reject invalid email addresses", () => { const invalidEmails = [ "not-an-email", "@example.com", "user@", "user @example.com", "user@example", "user..name@example.com", "user@.com", "", " ", "user@domain@domain.com" ]; for (const email of invalidEmails) { expect(isValidEmail(email)).toBe(false); } }); it("should reject SQL injection attempts in emails", () => { const sqlEmails = [ "admin'--@example.com", "user@example.com'; DROP TABLE User--", "' OR '1'='1@example.com", "user@example.com' UNION SELECT", "admin@example.com'--" ]; for (const email of sqlEmails) { // Either reject as invalid, or it's properly escaped in queries const isValid = isValidEmail(email); // Test documents the behavior expect(typeof isValid).toBe("boolean"); } }); it("should handle very long email addresses", () => { const longEmail = "a".repeat(1000) + "@example.com"; const result = isValidEmail(longEmail); // Should handle gracefully expect(typeof result).toBe("boolean"); }); it("should handle email with unicode characters", () => { const unicodeEmail = "üser@exämple.com"; const result = isValidEmail(unicodeEmail); expect(typeof result).toBe("boolean"); }); }); describe("Display Name Validation", () => { it("should accept valid display names", () => { const validNames = [ "John Doe", "Alice", "Bob Smith Jr.", "李明", "José García", "123User" ]; for (const name of validNames) { expect(isValidDisplayName(name)).toBe(true); } }); it("should reject empty display names", () => { const invalidNames = ["", " ", "\t", "\n"]; for (const name of invalidNames) { expect(isValidDisplayName(name)).toBe(false); } }); it("should reject excessively long display names", () => { const longName = "a".repeat(51); expect(isValidDisplayName(longName)).toBe(false); }); it("should handle display names with special characters", () => { const specialNames = [ "User"; // Validation should not crash const nameValid = isValidDisplayName(scriptInput); expect(typeof nameValid).toBe("boolean"); // Email validation const emailValid = isValidEmail(scriptInput); expect(typeof emailValid).toBe("boolean"); }); it("should handle event handler attributes", () => { const eventHandlers = [ "onclick=alert('XSS')", "onerror=alert('XSS')", "onload=alert('XSS')", "onfocus=alert('XSS')" ]; for (const handler of eventHandlers) { const result = isValidDisplayName(handler); expect(typeof result).toBe("boolean"); } }); it("should handle javascript: protocol", () => { const jsProtocol = "javascript:alert('XSS')"; const displayNameValid = isValidDisplayName(jsProtocol); const emailValid = isValidEmail(jsProtocol); expect(typeof displayNameValid).toBe("boolean"); expect(typeof emailValid).toBe("boolean"); }); }); describe("Command Injection Prevention", () => { it("should not execute shell commands from user input", () => { const commandPayloads = [ "; ls -la", "| cat /etc/passwd", "&& rm -rf /", "`whoami`", "$(whoami)", "; DROP TABLE User;--" ]; for (const payload of commandPayloads) { // These should be treated as strings, not executed const emailValid = isValidEmail(payload); const nameValid = isValidDisplayName(payload); expect(typeof emailValid).toBe("boolean"); expect(typeof nameValid).toBe("boolean"); } }); }); describe("Path Traversal Prevention", () => { it("should not allow directory traversal in inputs", () => { const traversalPayloads = [ "../../../etc/passwd", "..\\..\\..\\windows\\system32", "....//....//....//etc/passwd", "%2e%2e%2f", "..;/..;/" ]; for (const payload of traversalPayloads) { const emailValid = isValidEmail(payload); const nameValid = isValidDisplayName(payload); expect(typeof emailValid).toBe("boolean"); expect(typeof nameValid).toBe("boolean"); } }); }); describe("LDAP Injection Prevention", () => { it("should handle LDAP injection patterns", () => { const ldapPayloads = [ "*)(uid=*))(|(uid=*", "admin*", "*)(&(password=*))", "*))%00" ]; for (const payload of ldapPayloads) { const emailValid = isValidEmail(payload); const nameValid = isValidDisplayName(payload); expect(typeof emailValid).toBe("boolean"); expect(typeof nameValid).toBe("boolean"); } }); }); describe("XML Injection Prevention", () => { it("should handle XML special characters", () => { const xmlPayloads = [ "", '', ']>', "<script>alert('XSS')</script>" ]; for (const payload of xmlPayloads) { const emailValid = isValidEmail(payload); const nameValid = isValidDisplayName(payload); expect(typeof emailValid).toBe("boolean"); expect(typeof nameValid).toBe("boolean"); } }); }); describe("NoSQL Injection Prevention", () => { it("should handle MongoDB-style injection attempts", () => { const nosqlPayloads = [ '{"$gt": ""}', '{"$ne": null}', '{"$regex": ".*"}', '{"$where": "sleep(1000)"}', '{"username": {"$gt": ""}}' ]; for (const payload of nosqlPayloads) { const emailValid = isValidEmail(payload); const nameValid = isValidDisplayName(payload); expect(typeof emailValid).toBe("boolean"); expect(typeof nameValid).toBe("boolean"); } }); }); describe("Header Injection Prevention", () => { it("should reject inputs with newline characters", () => { const headerInjection = [ "user@example.com\r\nBcc: attacker@evil.com", "test\nSet-Cookie: admin=true", "user\r\nLocation: http://evil.com" ]; for (const payload of headerInjection) { const emailValid = isValidEmail(payload); expect(emailValid).toBe(false); } }); }); describe("Null Byte Injection Prevention", () => { it("should handle null bytes in input", () => { const nullBytePayloads = [ "admin\x00.jpg", "user@example.com\x00admin", "test\0injection" ]; for (const payload of nullBytePayloads) { const emailValid = isValidEmail(payload); const nameValid = isValidDisplayName(payload); expect(typeof emailValid).toBe("boolean"); expect(typeof nameValid).toBe("boolean"); } }); }); describe("Edge Cases", () => { it("should handle extremely long inputs", () => { const longInput = "a".repeat(100000); const start = performance.now(); const emailValid = isValidEmail(longInput); const nameValid = isValidDisplayName(longInput); const duration = performance.now() - start; expect(typeof emailValid).toBe("boolean"); expect(typeof nameValid).toBe("boolean"); // Should complete quickly (no ReDoS) expect(duration).toBeLessThan(100); }); it("should handle repeated characters", () => { const repeated = "a".repeat(10000) + "@example.com"; const emailValid = isValidEmail(repeated); expect(typeof emailValid).toBe("boolean"); }); it("should handle mixed encoding", () => { const mixedEncoding = "test%40example.com"; const emailValid = isValidEmail(mixedEncoding); expect(typeof emailValid).toBe("boolean"); }); it("should handle unicode normalization issues", () => { const unicodePayloads = [ "admin\u0041", // 'A' in unicode "test\u200B@example.com", // zero-width space "user\uFEFF@example.com" // zero-width no-break space ]; for (const payload of unicodePayloads) { const emailValid = isValidEmail(payload); expect(typeof emailValid).toBe("boolean"); } }); }); describe("Performance", () => { it("should validate emails efficiently", () => { const email = "test@example.com"; const start = performance.now(); for (let i = 0; i < 10000; i++) { isValidEmail(email); } const duration = performance.now() - start; expect(duration).toBeLessThan(100); }); it("should validate display names efficiently", () => { const name = "Test User"; const start = performance.now(); for (let i = 0; i < 10000; i++) { isValidDisplayName(name); } const duration = performance.now() - start; expect(duration).toBeLessThan(100); }); it("should not be vulnerable to ReDoS attacks", () => { // ReDoS payload with many repetitions const redosPayload = "a".repeat(1000) + "!"; const start = performance.now(); validatePassword(redosPayload); const duration = performance.now() - start; // Should complete quickly expect(duration).toBeLessThan(100); }); }); });