middleware = new HSTSMiddleware($this->client->getKernel()); $this->middlewareReflection = new \ReflectionClass($this->middleware); $this->addHSTS = $this->middlewareReflection->getProperty('enableHSTS'); $this->addHSTS->setAccessible(true); $this->includeDubDomains = $this->middlewareReflection->getProperty('includeDubDomains'); $this->includeDubDomains->setAccessible(true); $this->preload = $this->middlewareReflection->getProperty('preload'); $this->preload->setAccessible(true); } protected function testResponseHeaders(): void { $response = $this->getMiddlewareResponse(); Assert::assertNotEmpty($response->headers); } public function testHSTSEnabled(): void { $this->setHSTS(true); $response = $this->getMiddlewareResponse(); Assert::assertTrue( $response->headers->has(self::HSTS_KEY), 'Strict-Transport-Security is enabled but is missing from the response headers' ); } public function testHSTSDisabled(): void { $this->setHSTS(false); $response = $this->getMiddlewareResponse(); Assert::assertFalse( $response->headers->has(self::HSTS_KEY), 'Strict-Transport-Security is disabled but is present in response headers' ); } public function testIncludeSubdomainsEnabled(): void { $needle = 'includeSubDomains'; $this->setHSTS(true); $this->setIncludeDubDomainsValue(true); $response = $this->getMiddlewareResponse(); Assert::assertStringContainsString( $needle, $response->headers->get(self::HSTS_KEY), 'Option include Subdomains is enabled but is missing from the HSTS value' ); } public function testIncludeSubdomainsDisabled(): void { $needle = 'includeSubDomains'; $this->setHSTS(true); $this->setIncludeDubDomainsValue(false); $response = $this->getMiddlewareResponse(); Assert::assertStringNotContainsStringIgnoringCase( $needle, $this->getHSTSValue($response), 'Option include Subdomains is disabled but is present in HSTS value' ); } public function testPreloadEnabled(): void { $needle = 'preload'; $this->setHSTS(true); $this->setPreloadValue(true); $response = $this->getMiddlewareResponse(); Assert::assertStringContainsString( $needle, $response->headers->get(self::HSTS_KEY), 'Option preload is enabled but is missing from the HSTS value' ); } public function testPreloadDisabled(): void { $needle = 'preload'; $this->setHSTS(true); $this->setPreloadValue(false); $response = $this->getMiddlewareResponse(); Assert::assertStringNotContainsStringIgnoringCase( $needle, $this->getHSTSValue($response), 'Option preload is disabled but is present in HSTS value' ); } /** * @throws \ReflectionException */ public function testExpireTime(): void { $this->setHSTS(true); $expireTimeValue = 12345; $expireTime = $this->middlewareReflection->getProperty('expireTime'); $expireTime->setAccessible(true); $expireTime->setValue($this->middleware, $expireTimeValue); $response = $this->getMiddlewareResponse(); Assert::assertMatchesRegularExpression( '/max-age='.$expireTimeValue.'(; includeSubDomains)?/', $this->getHSTSValue($response), 'Expire time does not match the configuration' ); } private function setHSTS(bool $value): void { $this->addHSTS->setValue($this->middleware, $value); } private function setIncludeDubDomainsValue(bool $value): void { $this->includeDubDomains->setValue($this->middleware, $value); } private function setPreloadValue(bool $value): void { $this->preload->setValue($this->middleware, $value); } private function getHSTSValue(Response $response): string { return $response->headers->get(self::HSTS_KEY) ?? ''; } private function getMiddlewareResponse(): Response { try { return $this->middleware->handle(Request::create('s/login', Request::METHOD_GET)); } catch (\Exception $e) { throw new PHPUnitException($e->getMessage()); } } }