Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ContentSecurityPolicy/PolicyManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ public function getAvailableDirective(Request $request): array
return $this->getFirefoxDirectives();
case UserAgentParserInterface::BROWSER_SAFARI:
return $this->getLevel1();
default:
return [];
}

return [];
}

/**
Expand Down
28 changes: 28 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public function getConfigTreeBuilder(): TreeBuilder
->append($this->addCspNode())

->append($this->addReferrerPolicyNode())

->append($this->addCrossOriginPolicyNodes())
->end()
->end();

Expand Down Expand Up @@ -392,4 +394,30 @@ private function getFlexibleSslNode(): ArrayNodeDefinition

return $node;
}

private function addCrossOriginPolicyNodes(): ArrayNodeDefinition
{
$node = new ArrayNodeDefinition('cross_origin_policy');
$node
->canBeEnabled()
->children()
->enumNode('coep')
->defaultValue('unsafe-none')
->values(['unsafe-none', 'require-corp', 'credentialless'])
->info('Cross-Origin-Embedder-Policy (COEP) header value')
->end()
->enumNode('coop')
->defaultValue('unsafe-none')
->values(['unsafe-none', 'same-origin-allow-popups', 'same-origin', 'noopener-allow-popups'])
->info('Cross-Origin-Opener-Policy (COOP) header value')
->end()
->enumNode('corp')
->defaultValue('same-site')
->values(['same-site', 'same-origin', 'cross-origin'])
->info('Cross-Origin Resource Policy (CORP) header value')
->end()
->end();

return $node;
}
}
7 changes: 7 additions & 0 deletions src/DependencyInjection/NelmioSecurityExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ public function load(array $configs, ContainerBuilder $container): void
$loader->load('referrer_policy.php');
$container->setParameter('nelmio_security.referrer_policy.policies', $config['referrer_policy']['policies']);
}

if ($this->isConfigEnabled($container, $config['cross_origin_policy'])) {
$loader->load('cross_origin_policy.php');
$container->setParameter('nelmio_security.cross_origin_policy.coep', $config['cross_origin_policy']['coep']);
$container->setParameter('nelmio_security.cross_origin_policy.coop', $config['cross_origin_policy']['coop']);
$container->setParameter('nelmio_security.cross_origin_policy.corp', $config['cross_origin_policy']['corp']);
}
}

/**
Expand Down
46 changes: 46 additions & 0 deletions src/EventListener/CrossOriginPolicyListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Nelmio SecurityBundle.
*
* (c) Nelmio <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Nelmio\SecurityBundle\EventListener;

use Symfony\Component\HttpKernel\Event\ResponseEvent;

/**
* @author Florent Morselli <[email protected]>
*/
final class CrossOriginPolicyListener
{
private string $coep;
private string $coop;
private string $corp;

public function __construct(string $coep, string $coop, string $corp)
{
$this->coep = $coep;
$this->coop = $coop;
$this->corp = $corp;
}

public function onKernelResponse(ResponseEvent $e): void
{
if (!$e->isMainRequest()) {
return;
}

$response = $e->getResponse();

$response->headers->set('Cross-Origin-Embedder-Policy', $this->coep);
$response->headers->set('Cross-Origin-Opener-Policy', $this->coop);
$response->headers->set('Cross-Origin-Resource-Policy', $this->corp);
}
}
30 changes: 30 additions & 0 deletions src/Resources/config/cross_origin_policy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Nelmio SecurityBundle.
*
* (c) Nelmio <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use Nelmio\SecurityBundle\EventListener\CrossOriginPolicyListener;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->services()

->set('nelmio_security.cross_origin_policy_listener', CrossOriginPolicyListener::class)
->args([
'%nelmio_security.cross_origin_policy.coep%',
'%nelmio_security.cross_origin_policy.coop%',
'%nelmio_security.cross_origin_policy.corp%',
])
->tag('kernel.event_listener', [
'event' => 'kernel.response',
'method' => 'onKernelResponse',
]);
};
6 changes: 6 additions & 0 deletions tests/App/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ nelmio_security:
- 'no-referrer'
- 'strict-origin-when-cross-origin'

cross_origin_policy:
enabled: true
coep: credentialless
coop: same-origin
corp: cross-origin

csp:
enabled: true
hosts: [ ]
Expand Down
4 changes: 2 additions & 2 deletions tests/ContentSecurityPolicy/DirectiveSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,12 @@ public function provideVariousConfig(): array
/**
* @dataProvider provideConfigAndSignatures
*
* @param array<string, list<string>> $signatures
*
* @phpstan-param array<string, array{
* enforce?: array<string, mixed>,
* report?: array<string, mixed>,
* }> $config
*
* @param array<string, list<string>> $signatures
*/
public function testBuildHeaderValueWithInlineSignatures(string $expected, array $config, array $signatures): void
{
Expand Down
19 changes: 19 additions & 0 deletions tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,25 @@ public function testReferrerPolicy(): void
], $config['referrer_policy']['policies']);
}

public function testCrossOriginPolicy(): void
{
$config = $this->processYamlConfiguration(
"cross_origin_policy:\n".
" enabled: true\n".
" coep: credentialless\n".
" coop: same-origin-allow-popups\n".
" corp: same-site\n"
);

$this->assertIsArray($config['cross_origin_policy']);
$this->assertIsString($config['cross_origin_policy']['coep']);
$this->assertSame('credentialless', $config['cross_origin_policy']['coep']);
$this->assertIsString($config['cross_origin_policy']['coop']);
$this->assertSame('same-origin-allow-popups', $config['cross_origin_policy']['coop']);
$this->assertIsString($config['cross_origin_policy']['corp']);
$this->assertSame('same-site', $config['cross_origin_policy']['corp']);
}

public function testReferrerPolicyInvalid(): void
{
$this->expectException(InvalidConfigurationException::class);
Expand Down
30 changes: 30 additions & 0 deletions tests/Functional/CrossOriginPolicyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Nelmio SecurityBundle.
*
* (c) Nelmio <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Nelmio\SecurityBundle\Tests\Functional;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

final class CrossOriginPolicyTest extends WebTestCase
{
public function testHasHeaders(): void
{
$client = static::createClient();

$client->request('GET', '/');

$this->assertResponseHeaderSame('cross-origin-embedder-policy', 'credentialless');
$this->assertResponseHeaderSame('cross-origin-opener-policy', 'same-origin');
$this->assertResponseHeaderSame('cross-origin-resource-policy', 'cross-origin');
}
}