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

  1. 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.
  2. 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

  1. 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?

CommentFileSizeAuthor
#4 3619601-csp-manual-collector-4.diff11.53 KBjedihe

Issue fork csp-3619601

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

jedihe created an issue. See original summary.

jedihe’s picture

Issue summary: View changes
jedihe’s picture

Status: Active » Needs review
StatusFileSize
new11.53 KB

Attaching patch from current MR.

Implementation heavily assisted by Codex, with fine-grained steering by me.

jedihe’s picture

Issue summary: View changes
jedihe’s picture

Status: Needs review » Needs work

Important finding: this approach seems to only work for non-cacheable lazy builders (so the callback executes for every request).

jedihe’s picture