assertFileExists($path); $this->conf = (string) file_get_contents($path); } public function testServerTokensDisabled(): void { $this->assertMatchesRegularExpression( '/^\s*server_tokens\s+off;/m', $this->conf, 'server_tokens must be active (not commented) to hide the nginx version' ); } public function testHstsIncludesSubdomainsAndPreload(): void { $this->assertMatchesRegularExpression( '/add_header\s+Strict-Transport-Security\s+"max-age=\d+;\s*includeSubDomains;\s*preload;"/', $this->conf, 'HSTS must include includeSubDomains; preload' ); } /** * Every php-fpm location must forward HTTPS + the original scheme, or * RequestUtil falls back to insecure and cookies lose the Secure flag. */ public function testEveryPhpLocationForwardsHttpsSignal(): void { $phpValueCount = preg_match_all('/fastcgi_param\s+PHP_VALUE\s+"upload_max_filesize/m', $this->conf); $httpsCount = preg_match_all('/fastcgi_param\s+HTTPS\s+\$https;/m', $this->conf); $protoCount = preg_match_all('/fastcgi_param\s+HTTP_X_FORWARDED_PROTO\s+\$scheme;/m', $this->conf); $this->assertGreaterThan(0, $phpValueCount, 'expected php-fpm locations'); $this->assertSame( $phpValueCount, $httpsCount, "every php-fpm block must set fastcgi_param HTTPS (\$phpValue=$phpValueCount, https=$httpsCount)" ); $this->assertSame( $phpValueCount, $protoCount, "every php-fpm block must set X-Forwarded-Proto (\$phpValue=$phpValueCount, proto=$protoCount)" ); } /** * The admin location must keep its CSP and noindex header, and must not * inherit a broken frame-ancestors from the parent. */ public function testAdminLocationKeepsSecurityHeaders(): void { $this->assertMatchesRegularExpression( '#location\s+\^~\s+/admin/\s*\{.*?add_header\s+Content-Security-Policy#s', $this->conf, 'admin location must define its own CSP' ); $this->assertStringContainsString('X-Robots-Tag "noindex, nofollow"', $this->conf); } }