Spaces:
No application file
No application file
declare(strict_types=1); | |
namespace Mautic\Middleware\Tests; | |
use Mautic\CoreBundle\Test\AbstractMauticTestCase; | |
use Mautic\Middleware\HSTSMiddleware; | |
use PHPUnit\Framework\Assert; | |
use PHPUnit\Framework\ExpectationFailedException as PHPUnitException; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
class HSTSMiddlewareTest extends AbstractMauticTestCase | |
{ | |
public const HSTS_KEY = 'strict-transport-security'; | |
protected \ReflectionProperty $addHSTS; | |
protected \ReflectionProperty $includeDubDomains; | |
protected \ReflectionProperty $preload; | |
protected HSTSMiddleware $middleware; | |
protected \ReflectionClass $middlewareReflection; | |
/** | |
* @throws \ReflectionException | |
*/ | |
protected function setUp(): void | |
{ | |
parent::setUp(); | |
$this->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()); | |
} | |
} | |
} | |