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