Sphinxxxx/vanilla-picker

Feature request: Make vanilla-picker work with strict CSP header

Closed this issue · 9 comments

Hi! We are trying to migrate to a stricter Content-Security-Policy header. However, this currently does not work with vanilla-picker. The header we want to enforce is as follows:

Content-Security-Policy: img-src 'self' data:; default-src 'self'

This header causes dynamically injected <style> elements to no longer work. They will be ignored and produce an error. Example in Chrome:

Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self'". [...]
./node_modules/vanilla-picker/dist/vanilla-picker.mjs @ vanilla-picker.mjs:504

The way to fix this could be to add a new option externalCss: true somewhere, which would prevent the dynamic <style> injection. Additionally the user would have to manually import the CSS file using a <link> element, or bundling it in webpack.

Note that directly applying styles to an element is not affected by this, e.g. uiSL.style.color = ... still works fine.

Right... To avoid a breaking change, how about we wrap the injection in a try/catch and output a warning to the console if it fails?

It does not throw an exception. In particular, appending the style-node to the DOM is still successful, it just won't have any effect.

Wrapping the injection inside if (!options.externalCss) { ... } wouldn't be a breaking change, as long as externalCss defaults to false.

Ok, so in your case this is more of an annoyance, as you can add an external CSS file and everything works?

The breaking change is: Right now, the <style> element is injected when the script is executed. It doesn't wait for a picker to be created (where we have the options object). Changing that may break how someone uses the generated style element.

Maybe a fair trade-off would be to create the element (thus keeping it accessible in Picker.StyleElement), but only inject it once the first picker is created...

Yes, the injection part is just an annoyance, since it doesn't prevent the plugin from working correctly. However, it's quite annoying to constantly have an error in the dev console. Further, the npm package currently doesn't provide a compiled version of the styles, e.g. a CSS file. This makes it a bit harder to pull in the required styles, if for example I do not already use SCSS in my project.

Delaying the injection until first use would be of marginal benefit. I would still get an error in the end. If compatibility is an issue, perhaps providing an alternate entry point is a solution, e.g. import Picker from 'vanilla-picker/no-styles';

I also found this discussion about detecting the CSP at runtime, which might be interesting.

Thanks for your input on this!

I think the solution will be to build an additional pair of files: vanilla-picker.csp.js and .csp.css. Then, if I understand this question correctly, an extra entry point can be added in package.json for easy importing.

Could you check if the new v2.12.0 works for you?

import Picker from 'vanilla-picker/csp';

The CSS is in dist/vanilla-picker.csp.css.

Hi, thanks for the update. I can't seem to import the CSS file (I'm using webpack 5.52.1).

import 'vanilla-picker/dist/vanilla-picker.csp.css';

Output:

ERROR in ./js/backend/admin/colorPicker.js 4:0-52
Module not found: Error: Package path ./dist/vanilla-picker.csp.css is not exported from package /path/to/project/node_modules/vanilla-picker (see exports field in /path/to/project/node_modules/vanilla-picker/package.json)

Apparently it's not possible to import files that weren't explicitly exported in package.json.

This seems to be the only issue though, if I manually copy vanilla-picker.csp.css into the project and include it, everything works fine, with no CSP errors.

Ok, we're getting closer :)
Try v2.12.1

Works perfectly now! Many thanks! ❤️