Adding a nonce
for higher security
#2625
NicholasBoll
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
A nonce is a Content Security Policy feature and is supported by Emotion. This is fine for
@emotion/react
or@emotion/styled
where theCacheProvider
can be used.Our new style utilities (
createStyles
andcreateStencil
) use@emotion/css
which doesn't have a runtime dependency injection framework. If you importcache
from@emotion/css
, you'll get a reference that is stable, but does not include anonce
or any other way of changing the behavior ofstylis
or Emotion.In v10, we introduced static styling an simply used
@emotion/css
for easier backwards compatibility with SSR and client styling merging. We simply re-use everything given by@emotion/css
which already creates the Emotion cache. The Emotion cache is what holds references tostylis
plugins, including thenonce
. Supporting anonce
means we'll have to keep access to an Emotion cache within@workday/canvas-kit-styling
and export a function to get the cache rather than exporting a reference to a cache.In most cases, this change won't be noticeable, but if you import
cache
from@emotion/css
in any way, AND you make a call to create a nonced cache, this will effect you. This should be fine as this is an opt-in feature, but it is possible for style merging to not work across versions of this change.Breaking changes
Emotion uses a cache to track all styles injected into the browser. Our
CanvasProvider
injects the@emotion/css
cache and our style utilities use the same cache. This means if you use theCanvasProvider
and don't use Emotion's ownCacheProvider
and inject your own cache, Canvas Kit will always have access to the same cache. The cache is only important when style merging.Style Merging (and "compat" mode)
Our static style utility functions don't have a runtime and will inject styles when defined rather than during React's render cycle. This has performance benefits. Style mering is done by the browser's style merging rules via CSS specificity. Emotion doesn't use the browser's style merging rules and instead creates it's own. Emotion tries to merge styles predictably and in a way a developer expects it to. Emotion needs to be as efficient as possible. Injecting a new style rule into the browser's StyleSheetList causes the browser to flush it's Style Recalculation cache which is inefficient. Emotion keeps track of styles by creating a hash for each style and using this hash as the cache key. If a cache key is a hit, the style is not injected. Emotion also tries to have a flat specificity of
(0,1,0)
. But since styles are injected in the order they are rendered, style injection order can be non-deterministic. If the specificity of styles are the same, injection order is used - last defined wins. Emotion "fixes" this by keeping track of all the cache keys encountered. When you override the styles by usingstyled(SomeStyledComponent)
or<SomeStyledComponent css=
, Emotion will run an algorithm against theclassName
prop. If theclassName
looks something likeclassName="css-abc123 css-xyz890"
, it will collect all the CSS class names and extract the cache key and see if there's a cache hit. All cache hit styles are then combined into a new style. The resultingclassName
is something likeclassName="css-axy170"
and that new style is injected into the page. JavaScript is used to merge styles and specificity is again thwarted to do what a developer expects to do. Thecs
prop works the same way when a dynamic style is encountered. We call this compat mode. Our components will not use Emotion's style merging by default, but will use it if you add some dynamic style to a component with static styling. For example,<PrimaryButton css=
,<PrimaryButton cs={{padding:10}}>
,const StyledPrimaryButton = styled(PrimaryButton)
. This compat mode ensures that our styles stay as fast as possible, but styling still looks correct.This compat mode can only work for styles of the same cache. If your components uses a different cache than our components, there won't be a cache hit and styles may not be merged properly and you'll notice strange visual artifacts.
This will create a style merge conflict across Canvas Kit v10 versions though. Versions before this change will reference the cache from
@emotion/css
while versions after will reference the cache created bycreateCache
if called. IfcreateCache
is never called, there's no incompatibility. IfcreateCache
is called, there may be a version split for style merging.Mitigation
The plan is to have
getCache
andcreateCache
functions exported from@workday/canvas-kit-styling
. ThegetCache
will always need to be used to make sure you're getting the correct cache reference. ThecreateCache
is the opt-in. If called, the internal cache reference will point to this cache. If it is not used,getCache
will point to@emotion/css
's cache. ThecreateStyles
andcreateStencil
functions will add an extra bit of functionality to belay the creating of a cache to allow a different cache to be created. IfcreateCache
is not called by the timecreateStyles
orcreateStencil
is called, thecache
exported from@emotion/css
will be used. This should mean introducing this change should have no backwards breaking issues. It will be up to whoever callscreateCache
to make sure there's no direct reference to@emotion/css
used anywhere. If you usewithEmotionCache
, you should be fine. If you use Emotion'sCacheProvider
, there might be issues, but that's already an incompatibility with Canvas Kit v10.There may be a period of time that
createCache
should not be called by any application code if you expect multiple versions of Canvas Kit to exist on the page at the same time. This will be to the discretion of application developers.Beta Was this translation helpful? Give feedback.
All reactions