Skip to content

Commit

Permalink
introduce cspInlineStyle method
Browse files Browse the repository at this point in the history
  • Loading branch information
fritzmg committed Jan 18, 2024
1 parent 4e273f0 commit 546a992
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 11 deletions.
20 changes: 20 additions & 0 deletions core-bundle/contao/library/Contao/TemplateTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,26 @@ public function addCspHash(string $directive, string $script, string $algorithm
$csp->addHash($directive, $script, $algorithm);
}

/**
* Adds a CSP hash for a given inline style and also adds the 'unsafe-hashes' source to the directive automatically.
*/
public function cspInlineStyle(string $style, string $algorithm = 'sha384'): string
{
$responseContext = System::getContainer()->get('contao.routing.response_context_accessor')->getResponseContext();

if ($responseContext?->has(CspHandler::class))
{
/** @var CspHandler $csp */
$csp = $responseContext->get(CspHandler::class);
$csp
->addHash('style-src', $style, $algorithm)
->addSource('style-src', "'unsafe-hashes'")
;
}

return $style;
}

/**
* Render a figure
*
Expand Down
4 changes: 1 addition & 3 deletions core-bundle/contao/templates/forms/form_captcha.html5
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
<input type="hidden" name="<?= $this->name ?>_hash" value="<?= $this->hasErrors() ? $this->getHash() : '' ?>">

<?php if (!$this->hasErrors()): ?>
<?php $this->addCspHash('style-src', 'display:none'); ?>
<?php $this->addCspSource('style-src', 'unsafe-hashes'); ?>
<div style="display:none">
<div style="<?= $this->cspInlineStyle('display:none') ?>">
<label for="ctrl_<?= $this->id ?>_hp">Do not fill in this field</label>
<input type="text" name="<?= $this->name ?>_name" id="ctrl_<?= $this->id ?>_hp" value="">
</div>
Expand Down
4 changes: 1 addition & 3 deletions core-bundle/contao/templates/modules/mod_two_factor.html5
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
</div>
<div class="widget">
<p><?= $this->trans('MSC.twoFactorTextCode') ?></p>
<?php $this->addCspHash('style-src', 'word-break:break-all'); ?>
<?php $this->addCspSource('style-src', 'unsafe-hashes'); ?>
<code style="word-break:break-all"><?= $this->secret ?></code>
<code style="<?= $this->cspInlineStyle('word-break:break-all') ?>"><?= $this->secret ?></code>
</div>
<div class="widget widget-text">
<label for="verify"><?= $this->trans('MSC.twoFactorVerification') ?></label>
Expand Down
5 changes: 2 additions & 3 deletions core-bundle/src/Routing/ResponseContext/Csp/CspHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,8 @@ public function addHash(string $directive, string $script, string $algorithm = '
return $this;
}

$hash = base64_encode(hash($algorithm, $script, true));

$this->signatures[$directive][] = $algorithm.'-'.$hash;
$this->signatures[$directive][] = $algorithm.'-'.base64_encode(hash($algorithm, $script, true));
$this->signatures[$directive] = array_unique($this->signatures[$directive]);

return $this;
}
Expand Down
35 changes: 34 additions & 1 deletion core-bundle/tests/Contao/TemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ public function testAddsCspHash(): void
$script = 'this.form.requestSubmit()';
$algorithm = 'sha384';

(new FrontendTemplate())->addCspHash('script-src', 'this.form.requestSubmit()', $algorithm);
(new FrontendTemplate())->addCspHash('script-src', $script, $algorithm);

$response = new Response();
$cspHandler->applyHeaders($response);
Expand All @@ -500,4 +500,37 @@ public function testAddsCspHash(): void

$this->assertSame(sprintf("script-src 'self' '%s-%s'", $algorithm, $expectedHash), $response->headers->get('Content-Security-Policy'));
}

public function testAddsCspInlineStyleHash(): void
{
$directives = new DirectiveSet(new PolicyManager());
$directives->setLevel1Fallback(false);
$directives->setDirective('style-src', "'self'");

$cspHandler = new CspHandler($directives);
$responseContext = (new ResponseContext())->add($cspHandler);

$responseContextAccessor = $this->createMock(ResponseContextAccessor::class);
$responseContextAccessor
->expects($this->once())
->method('getResponseContext')
->willReturn($responseContext)
;

System::getContainer()->set('contao.routing.response_context_accessor', $responseContextAccessor);
System::getContainer()->set('request_stack', new RequestStack());

$style = 'display:none';
$algorithm = 'sha384';

$result = (new FrontendTemplate())->cspInlineStyle($style, $algorithm);

$response = new Response();
$cspHandler->applyHeaders($response);

$expectedHash = base64_encode(hash($algorithm, $style, true));

$this->assertSame($style, $result);
$this->assertSame(sprintf("style-src 'self' 'unsafe-hashes' '%s-%s'", $algorithm, $expectedHash), $response->headers->get('Content-Security-Policy'));
}
}
2 changes: 1 addition & 1 deletion core-bundle/tests/Twig/FragmentTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function testDisallowsAccessOfParentMethods(string $method, array $args):

public function provideIllegalParentMethods(): \Generator
{
$excluded = ['__construct', '__set', '__get', '__isset', 'setData', 'getData', 'setName', 'getName', 'getResponse', 'addCspSource', 'addCspHash', 'nonce', 'attr'];
$excluded = ['__construct', '__set', '__get', '__isset', 'setData', 'getData', 'setName', 'getName', 'getResponse', 'addCspSource', 'addCspHash', 'cspInlineStyle', 'nonce', 'attr'];
$parent = (new \ReflectionClass(FragmentTemplate::class))->getParentClass();

foreach ($parent->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
Expand Down

0 comments on commit 546a992

Please sign in to comment.