alert("XSS")'; $escaped = htmlspecialchars($maliciousLog); expect($escaped)->toContain('<script>'); expect($escaped)->not->toContain('">'; $escaped = htmlspecialchars($maliciousLog); expect($escaped)->toContain('<iframe'); expect($escaped)->toContain('data:'); expect($escaped)->not->toContain('test'; $escaped = htmlspecialchars($maliciousLog); expect($escaped)->toContain('<div'); expect($escaped)->toContain('style'); expect($escaped)->not->toContain('
test
'; $escaped = htmlspecialchars($maliciousLog); expect($escaped)->toContain('<div'); expect($escaped)->toContain('x-html'); expect($escaped)->not->toContain('
toBe('<>&"''); }); it('preserves legitimate text content', function () { $legitimateLog = 'INFO: Application started successfully'; $escaped = htmlspecialchars($legitimateLog); expect($escaped)->toBe($legitimateLog); }); it('handles ANSI color codes after escaping', function () { $logWithAnsi = "\e[31mERROR:\e[0m Something went wrong"; $escaped = htmlspecialchars($logWithAnsi); // ANSI codes should be preserved in escaped form expect($escaped)->toContain('ERROR'); expect($escaped)->toContain('Something went wrong'); }); it('escapes complex nested HTML structures', function () { $maliciousLog = '
'; $escaped = htmlspecialchars($maliciousLog); expect($escaped)->toContain('<div'); expect($escaped)->toContain('<img'); expect($escaped)->toContain('<script>'); expect($escaped)->not->toContain('not->toContain('not->toContain(''; $escaped = htmlspecialchars($contentWithHtml); // When stored in data attribute and rendered with x-text: // 1. Server escapes to: <script>alert("XSS")</script> // 2. Browser decodes the attribute value to: // 3. x-text renders it as textContent (plain text), NOT innerHTML // 4. Result: User sees "" as text, script never executes expect($escaped)->toContain('<script>'); expect($escaped)->not->toContain(''; // Step 1: Server-side escaping (PHP) $escaped = htmlspecialchars($rawLog); expect($escaped)->toBe('<script>alert("XSS")</script>'); // Step 2: Stored in data-log-content attribute //
// Step 3: Client-side getDisplayText() decodes HTML entities // const decoded = doc.documentElement.textContent; // Result: '' (as text string) // Step 4: x-text renders as textContent (NOT innerHTML) // Alpine.js sets element.textContent = decoded // Result: Browser displays '' as visible text // The script tag is never parsed or executed - it's just text // Step 5: Highlighting via CSS class // If search query matches, 'log-highlight' class is added // Visual feedback is provided through CSS, not HTML injection }); it('documents search highlighting with CSS classes', function () { $legitimateLog = '2024-01-01T12:00:00.000Z ERROR: Database connection failed'; // Server-side: Escape and store $escaped = htmlspecialchars($legitimateLog); expect($escaped)->toBe($legitimateLog); // No special chars // Client-side: If user searches for "ERROR" // 1. splitTextForHighlight() divides the text into parts: // - Part 1: "2024-01-01T12:00:00.000Z " (highlight: false) // - Part 2: "ERROR" (highlight: true) <- This part gets highlighted // - Part 3: ": Database connection failed" (highlight: false) // 2. Each part is rendered as a with x-text (safe) // 3. Only Part 2 gets the 'log-highlight' class via :class binding // 4. CSS provides yellow/warning background color on "ERROR" only // 5. No HTML injection occurs - just multiple safe text spans expect($legitimateLog)->toContain('ERROR'); }); it('verifies no HTML injection occurs during search', function () { $logWithHtml = 'User input: '; $escaped = htmlspecialchars($logWithHtml); // Even if log contains malicious HTML: // 1. Server escapes it // 2. x-text renders as plain text // 3. Search highlighting uses CSS class, not HTML tags // 4. User sees the literal text with highlight background // 5. No script execution possible expect($escaped)->toContain('<img'); expect($escaped)->toContain('onerror'); expect($escaped)->not->toContain('toContain('