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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |