Problem/Motivation
The Content Security Policy module needs to add 'unsafe-inline' exceptions for scripts and styles in order to support AJAX responses (#3100068: Script/style included in AJAX responses blocked without 'unsafe-inline').
This is because CSP blocks script and style elements dynamically added to the page by appending HTML, even if their sources comply with the page's policy.
Since AJAX is widely used, this opens a significant gap in the Cross-Site Scripting protections that Content Security Policy offers.
Proposed resolution
- Add a new Drupal\Core\Ajax\CommandInterface() implementation that serializes an array of assets the calling page needs to add.
- Update \Drupal\Core\Ajax\AjaxResponseAttachmentsProcessor::buildAttachmentsCommands() to use the new command instead of rendering the assets to HTML and using an append/prepend command.
- Update Drupal.AjaxCommands with a new method to execute the corresponding command and add assets in a CSP-safe manner (see https://github.com/jquery/jquery/issues/3969).
- Deprecate \Drupal\Core\Ajax\AddCssCommand, which expects a rendered HTML string.
Remaining tasks
- Determine whether retaining support for to-be-deprecated 'browsers' is necessary (@see #3095113: Deprecate IE conditional comments support)
User interface changes
None
API changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | drupal-3100151-9.patch | 8.9 KB | gapple |
Comments
Comment #2
gappleComment #3
gappleI'm uncertain if the distinction between header and footer JS assets is necessary in AJAX responses. My assumption is that all JS from the original page is already parsed before any AJAX requests are sent, so only the relative order of the new files matters, and appending them all to the end of the body tag would have the same affect as placing the header scripts in the page head.
No page scripts can be dependent on the new files required from an AJAX request (otherwise they would have already been include on the initial page), so it shouldn't be breaking to move them to the footer.
Comment #4
gappleHere's a quick patch to test the concept. One current limitation is that asset attributes (e.g.
mediafor stylesheets) are not included in the data sent in the response.I tested this by editing the "Featured Articles" view of the Umami profile. Before the patch, adding views.module.css (stable), node.css (classy), and picturefill.min.js are all blocked by Content Security Policy when loading the preview.
With the patch, the corresponding HTML elements are added to the page (verifiable in the inspector), the network requests are not blocked, and the picturefill library is available in the browser console.
Comment #6
gappleThis patch adds deprecation notices, any additional attributes set for an asset file (e.g. css media, js async), and the cache-buster query parameter when needed.
I set the deprecation notice to 9.0.0, as my understanding is that no new deprecations can be added in 8.9, and I'm not sure if this can be added in a 8.8 patch release?
Comment #8
gappleSaw some "CKEDITOR is not defined" errors while testing this:
https://www.html5rocks.com/en/tutorials/speed/script-loading/
Comment #9
gappleComment #10
gappleThere's a TODO in the patch regarding support for the
browserattribute of library assets. The conditional comments that the feature makes use of are unsupported in IE10+, and Drupal now only supports IE11.#3095113: Deprecate IE conditional comments support
If an asset has any browser conditions set, it could just be omitted from the AJAX response's assets.
Comment #12
gappleI ran one of the failing tests locally and it passed, so I'm not sure if this change introduces a timing issue since it's not blocking rendering in the same way as before.
Comment #13
gappleRemoval of
'browsers'was postponed to 10.0 (with deprecation in 9.1).Comment #14
gapple\Drupal\Core\Ajax\AddCssCommandwas introduced for IE <= 9, because they didn't support@importstatements if they were in aPrependCommand.Drupal 8 no longer uses
@importstatements (#2897408: Remove IE9 support from CssCollectionRenderer and provide it in contrib instead), and has now also removed the IE9 support code fromAddCssCommand(#3100147: Remove @import parsing from Drupal.AjaxCommands.prototype.add_css), soAddCssCommandcould be deprecated (in 9.1), and removed (in 10.0) separately from this issue.Comment #15
gapple#3110517: Improve Drupal\Core\Ajax\AddCssCommand to accept an array of CSS assets
Comment #16
gappleComment #17
gappleComment #19
gappleComment #20
gappleContent Security Policy added a submodule to override core to use this approach #3166840: Override core's handling of library assets in AJAX responses
Some refactoring was done that should be ported to this patch.
Comment #24
gappleThis will be resolved by these two issues:
#1988968: Drupal.ajax does not guarantee that "add new JS file to page" commands have finished before calling said JS
#3110517: Improve Drupal\Core\Ajax\AddCssCommand to accept an array of CSS assets