-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change transitions to not require style-src 'unsafe-inline'
CSP.
#6662
Comments
I had the same experience a while back, luckily creating a custom (fly in/out) transition for my purpose was trivial, thanks to the brilliantly simple but flexible API. e.g. a simple fly in animation (similar to slide which caused CSP errors): |
I think I actually have a lead on this! I've traced the error, and it turns out to be a single instance of appending an empty stylesheet to the
This act of inserting an inline CSS style sheet (empty or otherwise) is disallowed under CSP. As far as I can see, all subsequent operations are performed via the CSSOM which are allowed. Experimentally replacing Ideally we would modify Svelte to in some way use an existing stylesheet at document load. It is then the responsibility of the application framework to ensure that said stylesheet is loaded in a CSP-friendly way (via a |
Something like this: const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = (document.styleSheets.filter(s => s.title === 'svelte-stylesheet')[0] || append_empty_stylesheet(node)) as CSSStyleSheet); is a little better. In theory we could use some method (like looking for a styleSheet with a specific title or from an element with a specific id) to use a application/framework-provided (hopefully) CSP-approved stylesheet, and fallback to doing the same as today in order to avoid breaking. The title/id would have to be passed to the runtime somehow? Or use a pre-defined value (like 'svelte-stylesheet')? |
A workaround for this issue: adding This is not in my opinion a very good solution (it's a very weird and specific thing to require), but it does work. |
This is a cool trick, unfortunately it doesn't work in Safari (at least up to version 14.1) and breaks any code using transitions. This may be due to a charset issue in Safari: Content Security Policy hash not recognized by Safari 11.0.3: See this answer in particular: Some were able to solve this by changing the output of the minimizer (Uglify JS) to ascii_only. I don't know how to do this in Rollup/Terser (any suggestions are welcome!). In any case, I'll have to go with unsafe-inline for style-src for now. If the built-in Svelte transitions can be modified to allow strict CSP in the near future that would be awesome (sorry I can't help with that!). |
A question: since by default the first stylesheet loaded by Svelte should be global.css, (opening a Svelte project/website in a web browser and typing 'document.styleSheets[0]' in the console shows the loaded global.css for me), might using the global.css here (instead of appending a blank stylesheet as is currently done which as you suggest is the problem) actually be the simplest solution? This may likely be what happened in your case and it did work (i.e. the transitions under a strict CSP). Perhaps there should be some way to verify that it is the correct file. Checking in the console, the title field for the global.css styleSheet object is null (the href field does contain the file url), perhaps at the very least it should be given one when it is loaded? In any case, since global.css both needs to be loaded by Svelte in order to work at all, and therefore must be allowed under a strict CSP, this should fix the issue with the transitions without having to add any additional CSP directives (assuming the transitions don't actually require any inline styles as you suggest). Unless there is a good reason a blank or additional external stylesheet needs to be loaded? I'm new to all this so please forgive me if I'm missing something :) |
Ok, so my initial idea above definitely did not work; appending global.css messes up the styles, seems obvious in hindsight. However I did find another workaround that suits my needs. I added a 3rd blank style-sheet called 'svelte-style.css' to index.html of an a test Svelte project as follows (after global.css and bundle.css):
Then in line 34 of stylemanager.ts I simply replaced I might attempt a better implementation of this tomorrow, but at least this hack works for me. Props to Karlinator for finding the cause of this issue! |
So I made a working version (see link below, which includes all the changes) that looks for a stylesheet which has the text 'svelte-style.css' (a blank file in the same directory/location as 'global.css') in the href field. If it finds this, then it uses this for the stylesheet object. If not, it uses I might modify this to check that the script is from a tag (i.e. external) as opposed to a <style> tag, and/or to confirm that the 'svelte-styles.css' file is empty as extra precautions. If anyone has any suggestions for a better/more appropriate name for the added stylesheet feel free to let me know. Cheers! |
This is great work! In general the solution being "the framework/app needs to provide an empty stylesheet" can be prone to errors (and is a bit of a hidden dependency), but I don't really think that can be avoided. CSP is a very app-level thing, and there's just no good way to get around it at this level. Grabbing a stylesheet deliberately prepared for the occasion and falling back to the old behaviour is probably the best we can do. I would caution against restricting the source of the sheet too much I think. I would filter them based on the title (which is passed in by the element's |
Thanks Karl! I changed it to filter by title (you have to specify the title attribute as 'svelte-stylesheet' in the link/style tag in index.html) and also check whether the css file is empty (using |
I would be really curious if there are actually any cases where that cast to CSSStyleSheet would be problematic. I mean, I guess there is, like, DSSSL and XSL? The MDN docs doesn't seem to mention any case where a Other than that I think this should be good. |
Following your suggestion, I removed the cast to Later this week I'll do more thorough testing/write some tests and see how it goes. Cheers! |
Hmm, the above commit with |
The casting happens purely at compile time, I think the guard rails should be sufficient (in fact right now you're double checking the existence of There is also the |
Interesting, I guess this makes sense since the
I think it is worth keeping the check for https://stackoverflow.com/questions/46356349/document-stylesheetsx-cssrules-are-null
Good call, I did add a check for this in the latest version. I honestly couldn't find a way to add a style sheet without it being converted to type="text/css" in the browser, but if there is a way for a web browser do this, the code won't blow up. This passes everything in I'm off to write some tests, cheers! |
Just adding a note here:
|
Came here via sveltejs/kit#5215 — thanks for all the research everyone. Long term the solution is definitely to switch to the Web Animations API, but I wonder if we can't get this to behave in the meantime. As noted above, apps can specify function append_empty_stylesheet(node) {
const style_element = element('style');
console.log('appending empty stylesheet');
+ style_element.textContent = '/* empty */';
append_stylesheet(get_root_for_style(node), style_element);
return style_element.sheet;
} ...and then use this hash instead...
...things work in Chrome, Firefox and Safari. I don't know if this is a good solution. It definitely feels hacky, and app developers would have to add that hash to their CSP headers (though frameworks like SvelteKit could add it automatically), but maybe it's better than the alternatives? |
Hi Rich, I tried this out and it works in Safari for Mac/iOS and the other browsers like you said. Adding a hash to a CSP header isn't any trouble, so I think this 1 LOC solution is great until the Web Animations API is implemented. Thanks for this and everything you do. Cheers! |
@Rich-Harris are you interested in opening a PR for the proposed fix? The instructions on using the hash could be added to the API documentation (maybe under runtime at the end of the transitions section?). All the best! |
It appears the proposed fix using the hash no longer works because #7662 removed the |
…Safari (#7800) This changes the inserted style element for transitions to initially include the string '/* empty */'. This allows you to work around requiring unsafe-inline CSP discussed in #6662 by adding a hash to your CSP: 'sha256-9OlNO0DNEeaVzHL4RZwCLsBHA8WBQ8toBp/4F5XV2nc=' --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Is this something that will be addressed in Svelte 5? |
Fixed in Svelte 5 via using the web animations API |
Describe the problem
The way CSS transitions are currently handled they inject inline attribute styles into the element. This requires the site use
style-src 'unsafe-inline'
.Describe the proposed solution
I'm not entirely sure how this works, but I've seen some inline attribute styles be accepted under
style-src 'self'
while others are not. Specifically, these spinners seem to work just fine even under a strict CSP.It appears as though defining CSS variables inline is actually allowed? I haven't seen any documentation about that in particular, though. If true, Svelte transitions could in principle be changed to work like the above, where the transitions are defined in CSS documents, configured by inline variables, and applied by a class change.
This is essentially a cnange only in the internal handling of transitions (and possible animations?), and wouldn't have any effect on how they are used in Svelte code.
Alternatives considered
Don't use transitions if you want a strict CSP.
Make your own transitions by hand.
Importance
would make my life easier
The text was updated successfully, but these errors were encountered: