161 lines
5.5 KiB
PHP
161 lines
5.5 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Tests\Framework\Integration\Security;
|
||
|
|
||
|
use PHPUnit\Framework\TestCase;
|
||
|
|
||
|
require_once dirname(__DIR__, 3) . '/app/includes/security_headers_middleware.php';
|
||
|
|
||
|
class SecurityHeadersTest extends TestCase
|
||
|
{
|
||
|
protected function setUp(): void
|
||
|
{
|
||
|
parent::setUp();
|
||
|
unset($_GET['page']);
|
||
|
unset($_SERVER['HTTPS']);
|
||
|
unset($_SERVER['REQUEST_URI']);
|
||
|
}
|
||
|
|
||
|
public function testBasicSecurityHeaders()
|
||
|
{
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Check security headers
|
||
|
$this->assertContains('X-Frame-Options: DENY', $headers);
|
||
|
$this->assertContains('X-XSS-Protection: 1; mode=block', $headers);
|
||
|
$this->assertContains('X-Content-Type-Options: nosniff', $headers);
|
||
|
$this->assertContains('Referrer-Policy: strict-origin-when-cross-origin', $headers);
|
||
|
}
|
||
|
|
||
|
public function testContentSecurityPolicy()
|
||
|
{
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Get CSP header
|
||
|
$cspHeader = '';
|
||
|
foreach ($headers as $header) {
|
||
|
if (strpos($header, 'Content-Security-Policy:') === 0) {
|
||
|
$cspHeader = $header;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check CSP directives
|
||
|
$this->assertStringContainsString("default-src 'self'", $cspHeader);
|
||
|
$this->assertStringContainsString("script-src 'self' 'unsafe-inline' 'unsafe-eval'", $cspHeader);
|
||
|
$this->assertStringContainsString("style-src 'self' 'unsafe-inline'", $cspHeader);
|
||
|
$this->assertStringContainsString("frame-ancestors 'none'", $cspHeader);
|
||
|
$this->assertStringContainsString("form-action 'self'", $cspHeader);
|
||
|
$this->assertStringContainsString("base-uri 'self'", $cspHeader);
|
||
|
}
|
||
|
|
||
|
public function testHstsHeader()
|
||
|
{
|
||
|
// Simulate HTTPS
|
||
|
$_SERVER['HTTPS'] = 'on';
|
||
|
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Check HSTS header
|
||
|
$this->assertContains(
|
||
|
'Strict-Transport-Security: max-age=31536000; includeSubDomains; preload',
|
||
|
$headers
|
||
|
);
|
||
|
}
|
||
|
|
||
|
public function testNoHstsHeaderOnHttp()
|
||
|
{
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Check HSTS header is not present
|
||
|
$hasHsts = false;
|
||
|
foreach ($headers as $header) {
|
||
|
if (strpos($header, 'Strict-Transport-Security:') === 0) {
|
||
|
$hasHsts = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
$this->assertFalse($hasHsts, 'HSTS header should not be present on HTTP');
|
||
|
}
|
||
|
|
||
|
public function testCacheControlForSensitivePages()
|
||
|
{
|
||
|
$sensitivePages = ['login', 'register', 'profile', 'security'];
|
||
|
|
||
|
foreach ($sensitivePages as $page) {
|
||
|
// Set current page
|
||
|
$_GET['page'] = $page;
|
||
|
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Check cache control headers
|
||
|
$this->assertContains('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', $headers);
|
||
|
$this->assertContains('Pragma: no-cache', $headers);
|
||
|
$this->assertStringContainsString('Expires:', implode(' ', $headers));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function testNoCacheControlForNonSensitivePages()
|
||
|
{
|
||
|
$_GET['page'] = 'dashboard';
|
||
|
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Check cache control headers are not present
|
||
|
$this->assertNotContains('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', $headers);
|
||
|
$this->assertNotContains('Pragma: no-cache', $headers);
|
||
|
}
|
||
|
|
||
|
public function testPermissionsPolicy()
|
||
|
{
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Get Permissions-Policy header
|
||
|
$permissionsHeader = '';
|
||
|
foreach ($headers as $header) {
|
||
|
if (strpos($header, 'Permissions-Policy:') === 0) {
|
||
|
$permissionsHeader = $header;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check basic permissions
|
||
|
$this->assertStringContainsString('geolocation=()', $permissionsHeader);
|
||
|
$this->assertStringContainsString('payment=()', $permissionsHeader);
|
||
|
$this->assertStringContainsString('camera=()', $permissionsHeader);
|
||
|
$this->assertStringContainsString('microphone=()', $permissionsHeader);
|
||
|
$this->assertStringContainsString('fullscreen=(self)', $permissionsHeader);
|
||
|
$this->assertStringContainsString('sync-xhr=(self)', $permissionsHeader);
|
||
|
}
|
||
|
|
||
|
public function testPermissionsPolicyForMediaEnabledPages()
|
||
|
{
|
||
|
$_SERVER['REQUEST_URI'] = '/media/upload';
|
||
|
|
||
|
// Apply security headers in test mode
|
||
|
$headers = \applySecurityHeaders(true);
|
||
|
|
||
|
// Get Permissions-Policy header
|
||
|
$permissionsHeader = '';
|
||
|
foreach ($headers as $header) {
|
||
|
if (strpos($header, 'Permissions-Policy:') === 0) {
|
||
|
$permissionsHeader = $header;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check permissions policy header
|
||
|
$this->assertStringContainsString('camera=()', $permissionsHeader);
|
||
|
$this->assertStringContainsString('microphone=()', $permissionsHeader);
|
||
|
$this->assertStringContainsString('geolocation=()', $permissionsHeader);
|
||
|
$this->assertStringContainsString('payment=()', $permissionsHeader);
|
||
|
}
|
||
|
}
|