Problem/Motivation
With CSP 2.2.4, #attached['csp_hash'] added by a render array returned from a lazy builder is not reflected in the CSP response header.
The lazy builder itself is nested inside an html_head attachment added from hook_page_attachments().
Steps to reproduce
Minimal repro case:
mymodule.module
/**
* Implements hook_page_attachments().
*/
function mymodule_page_attachments(array &$attachments): void {
$attachments['#attached']['html_head'][] = [
[
'#type' => 'html_tag',
'#tag' => 'script',
'nested' => [
'#lazy_builder' => ['Drupal\mymodule\LazyBuilder::build', []],
'#create_placeholder' => TRUE,
],
],
'mymodule-test',
];
}
src/LazyBuilder.php
namespace Drupal\mymodule;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\csp\Csp;
final class LazyBuilder implements TrustedCallbackInterface {
public static function trustedCallbacks() {
return ['build'];
}
public static function build(): array {
$script = 'console.log("CSP lazy builder test");';
$hash = Csp::calculateHash($script);
return [
'#markup' => $script,
'#attached' => [
'csp_hash' => [
'script-src-elem' => [
$hash => Csp::POLICY_UNSAFE_INLINE,
],
],
],
];
}
}
I also tested attaching the hash to an additional wrapper render array:
return [
[
'#markup' => $script,
'#attached' => [
'csp_hash' => [
'script-src-elem' => [
$hash => Csp::POLICY_UNSAFE_INLINE,
],
],
],
],
// ... or even starting #attached at this level.
];
Proposed resolution
- Implement a collector service with a simple API to let e.g. lazy builders like this to get hashes added to the CSP header, in the appropriate directive.
- Add CollectorCspSubscriber, subscribed to CspEvents::POLICY_ALTER, with method ::applyFromCollector() that works very similar to RenderElementAttachedCspSubscriber::applyNoncesAndHashes().
Remaining tasks
Review MR, discuss?
User interface changes
None.
API changes
- New csp.collector service, with public method ::addHash($directive, $type, $hash, $fallback); useful for cases where #attached[csp...] doesn't get properly collected (e.g. renderables returned from lazy builder callbacks)
Data model changes
None?
Comments
Comment #3
jedihe commentedComment #4
jedihe commentedAttaching patch from current MR.
Implementation heavily assisted by Codex, with fine-grained steering by me.
Comment #5
jedihe commentedComment #6
jedihe commentedImportant finding: this approach seems to only work for non-cacheable lazy builders (so the callback executes for every request).
Comment #7
jedihe commentedJust found that this issue is, very likely, a duplicate of #3470867: RenderElementAttachedCspSubscriber does not detect elements with nonces added in hook_page_attachments.