-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from GoogleChromeLabs/trusted-types
Added initial TT implementation
- Loading branch information
Showing
9 changed files
with
321 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
namespace Ise\WebSecurityBundle\EventSubscriber; | ||
|
||
use Ise\WebSecurityBundle\Options\ConfigProviderInterface; | ||
use Ise\WebSecurityBundle\Options\ContextChecker; | ||
use Psr\Log\LoggerInterface; | ||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
use Symfony\Component\HttpKernel\Event\ResponseEvent; | ||
use Symfony\Component\HttpKernel\KernelEvents; | ||
|
||
/** | ||
* TrustedTypesSubscriber | ||
* Request subscriber for implementing the Trusted Types CSP policy. This subscriber will work with already defined CSP policies. | ||
*/ | ||
class TrustedTypesSubscriber implements EventSubscriberInterface | ||
{ | ||
private $configProvider; | ||
private $context; | ||
private $logger; | ||
private $policyIssueMessage = "Trusted types policy already defined in CSP header in request from %s. This may cause unexpected behaviour."; | ||
|
||
public function __construct(ConfigProviderInterface $configProvider, ContextChecker $context, LoggerInterface $logger) | ||
{ | ||
$this->configProvider = $configProvider; | ||
$this->context = $context; | ||
$this->logger = $logger; | ||
} | ||
|
||
public static function getSubscribedEvents() | ||
{ | ||
return [ | ||
KernelEvents::RESPONSE => [ | ||
['responseEvent', -512], | ||
] | ||
]; | ||
} | ||
|
||
public function responseEvent(ResponseEvent $event) | ||
{ | ||
$response = $event->getResponse(); | ||
$request = $event->getRequest(); | ||
|
||
$options = $this->configProvider->getPathConfig($request); | ||
//Check is trusted types is active, if not then leave handler | ||
if (!$options['trusted_types']['active']) { | ||
return; | ||
} | ||
//Check if CSP header is set | ||
$this->context->checkSecure($request, 'Trusted types'); | ||
$headerSet = $response->headers->has("Content-Security-Policy"); | ||
//If CSP header is set, pull it and append a ';' separator, else set an empty prefix. | ||
$headerPrefix = $headerSet ? $response->headers->get("Content-Security-Policy").';' : ''; | ||
//Check if trusted types policy is set. If so, print unexpected behaviour error | ||
if (strpos($headerPrefix, 'trusted-types')) { | ||
$policyIssue = sprintf($this->policyIssueMessage, $request->getUri()); | ||
$this->logger->log(0, $policyIssue, ['CSP header' => $headerPrefix]); | ||
} | ||
|
||
//Set trusted types CSP policy, and append it to the current policy if one exists | ||
$response->headers->set("Content-Security-Policy", $this->constructTrustedTypesHeader($options['trusted_types'], $headerPrefix)); | ||
} | ||
|
||
/** | ||
* constructTrustedTypesHeader method constructs the CSP policy for trusted types. If a CSP policy already exists, the trusted types policy is appended to it. | ||
* | ||
* @param Array $options | ||
* @param String $headerSet | ||
* @return String | ||
*/ | ||
private function constructTrustedTypesHeader($options, $headerPrefix) | ||
{ | ||
$policies = "trusted-types ".implode(" ", $options['policies']); | ||
$requireFor = "require-trusted-types-for ".implode(" ", array_map(function ($value) { | ||
return sprintf('\'%s\'', $value); | ||
}, $options['require_for'])); | ||
return sprintf("%s %s; %s;", $headerPrefix, $policies, $requireFor); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
namespace Ise\WebSecurityBundle\Tests; | ||
|
||
use Ise\WebSecurityBundle\EventSubscriber\ResponseSubscriber; | ||
use Ise\WebSecurityBundle\Options\ConfigProvider; | ||
use Ise\WebSecurityBundle\Options\ContextChecker; | ||
use PHPUnit\Framework\TestCase; | ||
use Psr\Log\LoggerInterface; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\Event\ResponseEvent; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
|
||
class COOPCOEPTest extends TestCase | ||
{ | ||
private $default = [ | ||
"coop" => [ | ||
"active" => true, | ||
"policy" => 'same-origin' | ||
], | ||
"coep" => [ | ||
"active" => true, | ||
"policy" => 'require-corp' | ||
] | ||
]; | ||
|
||
private $coop = "same-origin"; | ||
private $coep = "require-corp"; | ||
|
||
public function testCOOP() | ||
{ | ||
$logger = $this->getMockBuilder(LoggerInterface::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$context = new ContextChecker($logger); | ||
$requestSub = new ResponseSubscriber( | ||
new ConfigProvider($this->default, []), | ||
$context | ||
); | ||
|
||
$kernel = $this->getMockBuilder(HttpKernelInterface::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$request = Request::create('/test'); | ||
$res = new ResponseEvent( | ||
$kernel, | ||
$request, | ||
HttpKernelInterface::MASTER_REQUEST, | ||
new Response() | ||
); | ||
|
||
$result = $requestSub->responseEvent($res); | ||
$this->assertNull($result); | ||
$this->assertEquals($res->getResponse()->headers->get('Cross-Origin-Opener-Policy'), $this->coop); | ||
} | ||
|
||
public function testCOEP() | ||
{ | ||
$logger = $this->getMockBuilder(LoggerInterface::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$context = new ContextChecker($logger); | ||
$requestSub = new ResponseSubscriber( | ||
new ConfigProvider($this->default, []), | ||
$context | ||
); | ||
|
||
$kernel = $this->getMockBuilder(HttpKernelInterface::class) | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$request = Request::create('/test'); | ||
$res = new ResponseEvent( | ||
$kernel, | ||
$request, | ||
HttpKernelInterface::MASTER_REQUEST, | ||
new Response() | ||
); | ||
|
||
$result = $requestSub->responseEvent($res); | ||
$this->assertNull($result); | ||
$this->assertEquals($res->getResponse()->headers->get('Cross-Origin-Embedder-Policy'), $this->coep); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.