Files
timebank-cc-public/tests/Feature/PostContentXssProtectionTest.php
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

298 lines
10 KiB
PHP

<?php
namespace Tests\Feature;
use App\Helpers\StringHelper;
use App\Models\Post;
use App\Models\PostTranslation;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PostContentXssProtectionTest extends TestCase
{
use RefreshDatabase;
/**
* Test that the sanitizeHtml method properly escapes script tags.
*
* @return void
*/
public function test_post_content_escapes_script_tags()
{
$maliciousContent = '<p>Hello</p><script>alert("XSS")</script><p>World</p>';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// Script tags should be completely removed
$this->assertStringNotContainsString('<script>', $sanitized);
$this->assertStringNotContainsString('alert("XSS")', $sanitized);
// Safe HTML should be preserved
$this->assertStringContainsString('<p>Hello</p>', $sanitized);
$this->assertStringContainsString('<p>World</p>', $sanitized);
}
/**
* Test that img tag with onerror handler is sanitized.
*
* @return void
*/
public function test_post_content_sanitizes_img_onerror()
{
$maliciousContent = '<p>Check this image</p><img src="x" onerror="alert(document.cookie)" />';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// Image should be allowed but without dangerous attributes
$this->assertStringContainsString('<img', $sanitized);
$this->assertStringNotContainsString('onerror', $sanitized);
$this->assertStringNotContainsString('alert(document.cookie)', $sanitized);
}
/**
* Test that iframe injection is completely removed.
*
* @return void
*/
public function test_post_content_removes_iframe()
{
$maliciousContent = '<p>Content</p><iframe src="https://evil.com"></iframe><p>More content</p>';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// Iframe should be completely removed
$this->assertStringNotContainsString('<iframe', $sanitized);
$this->assertStringNotContainsString('evil.com', $sanitized);
// Safe content preserved
$this->assertStringContainsString('<p>Content</p>', $sanitized);
$this->assertStringContainsString('<p>More content</p>', $sanitized);
}
/**
* Test that safe rich text formatting is preserved.
*
* @return void
*/
public function test_post_content_preserves_safe_formatting()
{
$safeContent = '<h1>Title</h1><p>Paragraph with <strong>bold</strong> and <em>italic</em> text.</p><ul><li>Item 1</li><li>Item 2</li></ul><a href="https://example.com">Link</a>';
$sanitized = StringHelper::sanitizeHtml($safeContent);
// All safe formatting should be preserved
$this->assertStringContainsString('<h1>Title</h1>', $sanitized);
$this->assertStringContainsString('<strong>bold</strong>', $sanitized);
$this->assertStringContainsString('<em>italic</em>', $sanitized);
$this->assertStringContainsString('<ul>', $sanitized);
$this->assertStringContainsString('<li>Item 1</li>', $sanitized);
$this->assertStringContainsString('<a href="https://example.com">Link</a>', $sanitized);
}
/**
* Test that event handler attributes are removed.
*
* @return void
*/
public function test_post_content_removes_event_handlers()
{
$maliciousContent = '<a href="#" onclick="alert(1)">Click me</a><div onmouseover="steal()">Hover</div>';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// Event handlers should be removed
$this->assertStringNotContainsString('onclick', $sanitized);
$this->assertStringNotContainsString('onmouseover', $sanitized);
$this->assertStringNotContainsString('alert(1)', $sanitized);
$this->assertStringNotContainsString('steal()', $sanitized);
// Safe parts should remain (div is allowed with class attribute)
$this->assertStringContainsString('Click me', $sanitized);
$this->assertStringContainsString('Hover', $sanitized);
}
/**
* Test that data URIs with JavaScript are removed.
*
* @return void
*/
public function test_post_content_removes_javascript_data_uris()
{
$maliciousContent = '<a href="data:text/html,<script>alert(1)</script>">Click</a>';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// JavaScript data URIs should be removed
$this->assertStringNotContainsString('data:text/html', $sanitized);
$this->assertStringNotContainsString('<script>', $sanitized);
}
/**
* Test that style tags with CSS injection are removed.
*
* @return void
*/
public function test_post_content_removes_style_tags()
{
$maliciousContent = '<p>Text</p><style>body { background: url("javascript:alert(1)") }</style>';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// Style tags should be removed
$this->assertStringNotContainsString('<style>', $sanitized);
$this->assertStringNotContainsString('javascript:alert', $sanitized);
}
/**
* Test that object and embed tags are removed.
*
* @return void
*/
public function test_post_content_removes_object_embed_tags()
{
$maliciousContent = '<object data="malicious.swf"></object><embed src="evil.swf">';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// Object and embed tags should be removed
$this->assertStringNotContainsString('<object', $sanitized);
$this->assertStringNotContainsString('<embed', $sanitized);
$this->assertStringNotContainsString('malicious.swf', $sanitized);
}
/**
* Test that null input returns empty string.
*
* @return void
*/
public function test_post_content_handles_null_input()
{
$sanitized = StringHelper::sanitizeHtml(null);
$this->assertEquals('', $sanitized);
}
/**
* Test that empty string returns empty string.
*
* @return void
*/
public function test_post_content_handles_empty_string()
{
$sanitized = StringHelper::sanitizeHtml('');
$this->assertEquals('', $sanitized);
}
/**
* Test that table HTML is preserved for structured content.
*
* @return void
*/
public function test_post_content_preserves_tables()
{
$tableContent = '<table><thead><tr><th>Header</th></tr></thead><tbody><tr><td>Data</td></tr></tbody></table>';
$sanitized = StringHelper::sanitizeHtml($tableContent);
// Table structure should be preserved
$this->assertStringContainsString('<table>', $sanitized);
$this->assertStringContainsString('<thead>', $sanitized);
$this->assertStringContainsString('<th>Header</th>', $sanitized);
$this->assertStringContainsString('<tbody>', $sanitized);
$this->assertStringContainsString('<td>Data</td>', $sanitized);
}
/**
* Test that images with valid attributes are preserved.
*
* @return void
*/
public function test_post_content_preserves_safe_images()
{
$imageContent = '<img src="https://example.com/image.jpg" alt="Description" width="500" height="300" title="My Image">';
$sanitized = StringHelper::sanitizeHtml($imageContent);
// Valid image attributes should be preserved
$this->assertStringContainsString('src="https://example.com/image.jpg"', $sanitized);
$this->assertStringContainsString('alt="Description"', $sanitized);
$this->assertStringContainsString('width="500"', $sanitized);
$this->assertStringContainsString('height="300"', $sanitized);
$this->assertStringContainsString('title="My Image"', $sanitized);
}
/**
* Test that links with target="_blank" are preserved.
*
* @return void
*/
public function test_post_content_preserves_target_blank_links()
{
$linkContent = '<a href="https://example.com" target="_blank" title="External Link">Visit</a>';
$sanitized = StringHelper::sanitizeHtml($linkContent);
// Link with target="_blank" should be preserved
$this->assertStringContainsString('href="https://example.com"', $sanitized);
$this->assertStringContainsString('target="_blank"', $sanitized);
$this->assertStringContainsString('title="External Link"', $sanitized);
}
/**
* Test mixed safe and unsafe content.
*
* @return void
*/
public function test_post_content_handles_mixed_content()
{
$mixedContent = '<h2>Article Title</h2><p>This is <strong>safe</strong> content.</p><script>alert("unsafe")</script><p>More safe content with <a href="https://example.com">a link</a>.</p>';
$sanitized = StringHelper::sanitizeHtml($mixedContent);
// Safe content preserved
$this->assertStringContainsString('<h2>Article Title</h2>', $sanitized);
$this->assertStringContainsString('<strong>safe</strong>', $sanitized);
$this->assertStringContainsString('<a href="https://example.com">a link</a>', $sanitized);
// Unsafe content removed
$this->assertStringNotContainsString('<script>', $sanitized);
$this->assertStringNotContainsString('alert("unsafe")', $sanitized);
}
/**
* Test that code and pre tags are preserved for technical content.
*
* @return void
*/
public function test_post_content_preserves_code_blocks()
{
$codeContent = '<pre><code>function hello() { return "world"; }</code></pre>';
$sanitized = StringHelper::sanitizeHtml($codeContent);
// Code blocks should be preserved
$this->assertStringContainsString('<pre>', $sanitized);
$this->assertStringContainsString('<code>', $sanitized);
$this->assertStringContainsString('function hello()', $sanitized);
}
/**
* Test SVG injection attempts are blocked.
*
* @return void
*/
public function test_post_content_blocks_svg_injection()
{
$maliciousContent = '<svg onload="alert(1)"><circle cx="50" cy="50" r="40"/></svg>';
$sanitized = StringHelper::sanitizeHtml($maliciousContent);
// SVG should be removed (not in allowed tags list)
$this->assertStringNotContainsString('<svg', $sanitized);
$this->assertStringNotContainsString('onload', $sanitized);
$this->assertStringNotContainsString('alert(1)', $sanitized);
}
}