silverstripe / silverstripe-framework

Silverstripe Framework, the MVC framework that powers Silverstripe CMS
https://www.silverstripe.org
BSD 3-Clause "New" or "Revised" License
721 stars 821 forks source link

Allow the 'nonce' (and/or arbitrary) attributes in the Requirements backend #9910

Open JamesDPC opened 3 years ago

JamesDPC commented 3 years ago

Affected Version

^4

Description

The requirements backend doesn't allow certain attributes that are documented in https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script - a good example is "nonce" but others are listed at that URL

While there might be reasons for restricting attributes in the Requirements_Backend it would be helpful to allow a developer to specify arbitrary per-request attributes via an injected requirements backend, such as "nonce".

In the context of a Content Security Policy, allowing the "nonce" attribute on an inline script/style would alleviate the need to create an SRI hash for each inline element and store that hash in a CSP.

A good example is @ https://content-security-policy.com/examples/allow-inline-style/ (Allow Inline Styles using a Nonce)

Currently, the only sensible way to do this is by replacing the entire includeInHTML() method in an injected Requirements backend.

References:

Steps to Reproduce

For Requirements::javascript/css

  1. Add a nonce attribute to a script/style/link via an injected Requirements backend (e.g by overriding the getCSS() method)
  2. Expect to see nonce="$nonce" for the element in the returned HTML document
  3. There is no nonce attribute

Help us with step-by-step instructions.

  1. Create a custom Requirements backend that extends SilverStripe\View\Requirements_Backend and add the following methods:
    
    namespace MyNamespace;

use SilverStripe\View\Requirements_Backend;

class NonceRequirements_Backend extends Requirements_Backend {

/**
 * @inheritdoc
 */
public function getJavascript()
{
    $items = parent::getJavascript();
    foreach($items as $file => $attributes) {
        $items[$file]['nonce'] = 'the_request_nonce';
    }
    return $items;
}

/**
 * @inheritdoc
 */
public function getCSS()
{
    $items = parent::getCSS();
    foreach($items as $file => $attributes) {
        $items[$file]['nonce'] = 'the_request_nonce';
    }
    return $items;
}

}

2. Add the requirements backend configuration via Injector:
```yaml
SilverStripe\Core\Injector\Injector:
  SilverStripe\View\Requirements_Backend:
    class: MyNamespace\NonceRequirements_Backend
  1. nonce="the_request_nonce" should be added to the attributes for the elements.

Inline scripts and styles would benefit from nonce inclusion via getCustomCSS / getCustomScripts but these requirements do not support attributes beyond "type".

lekoala commented 3 years ago

Just adding my two cents here since i've been working quite a lot with the requirements backend on my module https://github.com/lekoala/silverstripe-defer-backend

it is indeed annoying that customScripts have not the same structure as regular stricts. For instance, in my module, i had to resort to conventions in the ID attribute to pass custom values for the cookie-consent attribute. Not ideal at all. I feel that the customScript property would benefit from being a flat array that looks like ["id"=> null, "content"=> "console.log('my script');"], ["id" => "unique script", "content" => "console.log('my unique script')", "some-attribute" => "some-value"]. It wouldn't affect the external api which would be extended to accept an array after $uniquenessID. The current api is just too restrictive.

i recently added js modules support to my module. And I discovered that by default you cannot set the type of custom scripts as well. Again, this is not ideal and could be easily dealt with an array structure as explained above where you could just set the type attribute. Something like Requirements::customScript('mymodule.js',null,['type' => 'module']);

lerni commented 3 years ago

I like what @lekoala suggests. Issues with type to pass W3C validator without warning for JS & CSS or for json+ld schema data etc. could be addressed with this as well?

https://github.com/silverstripe/silverstripe-framework/issues/8710