Skip to content

Conversation

@swac
Copy link
Contributor

@swac swac commented Aug 24, 2018

Summary

Modern browsers ignore the 'unsafe-inline' CSP setting if any hash or nonce exists in that directive. This means that this plugin's automatic sha256 hashes that it generates for inline scripts and styles implicitly disable 'unsafe-inline' for modern browsers even if the developer explicitly wants 'unsafe-inline' enabled.

This PR addresses the issue by adding an option to the plugin config to disable the implicit hashing. Setting hashIfUnsafeInlinePresent to false when initializing the plugin will make it skip hashing if a script-src or style-src directive contains an 'unsafe-inline' value.

Requirements (place an x in each [ ])

@swac swac force-pushed the allow-unsafe-inline branch 2 times, most recently from 7318cda to d341222 Compare August 24, 2018 23:20
Copy link
Contributor

@AnujRNair AnujRNair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @swac! Thanks for the contribution, and sorry for the delay in getting back to you!

I've added some comments which I would love your thoughts on - thanks!

plugin.js Outdated
enabled: true,
hashingMethod: 'sha256'
hashingMethod: 'sha256',
hashIfUnsafeInlinePresent: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be best to infer this setting from the policy which is explicitly passed in by the dev - if they set unsafe-inline, then we can skip creating hashes instead of requiring them to set a setting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing 😄! I considered inferring the hashing behavior, but I realized that might complicate things in a different way. The default policy already contains unsafe-inline, so I'd need to save a copy of the passed-in policy in addition to the merged policy. Then the code that does the hashing would get a bit more complex as well, since it'd need to compare against the user-specified policy, but operate on the merged policy. If that's fine, I can go ahead and make the change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think thats ok to do - really, the default policy exists simply to provide something incase the user hasn't specified the policy themselves - we can always assume the default policy will have unsafe-inlines, so we just have to do a check against the user defined one like you've mentioned.

Thanks!

plugin.js Outdated
const inlineSrc = $('script:not([src])')
.map((i, element) => this.hash($(element).html()))
.get();
policyObj['script-src'] = flattenedPolicyObjScriptSrc.concat(inlineSrc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move creating this into it's own function - it can have the interface:

createObjPolicy($, selector) { return $(selector) .map((i, element) => this.hash($(element).html())) .get(); } 

Then we can add (pseudo):

policyObj['style-src'] = hasUnsafeInline ? don't do anything: this.createPolicyObj($, 'style:not([href])'); etc. 
@CLAassistant
Copy link

CLAassistant commented Sep 28, 2018

CLA assistant check
All committers have signed the CLA.

@swac
Copy link
Contributor Author

swac commented Sep 28, 2018

@AnujRNair apologies for the long turnaround here. I just updated the code based on your feedback. Thanks!

@codecov
Copy link

codecov bot commented Sep 28, 2018

Codecov Report

Merging #15 into master will increase coverage by 0.17%.
The diff coverage is 100%.

Impacted file tree graph

@@ Coverage Diff @@ ## master #15 +/- ## ========================================== + Coverage 98.54% 98.71% +0.17%  ========================================== Files 2 2 Lines 137 156 +19 Branches 7 8 +1 ========================================== + Hits 135 154 +19  Misses 2 2
Impacted Files Coverage Δ
spec/plugin.spec.js 100% <100%> (ø) ⬆️
plugin.js 96.55% <100%> (+0.32%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6e0724d...cc7116d. Read the comment docs.

Copy link
Contributor

@AnujRNair AnujRNair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, this is looking great! 2 small comments added

plugin.js Outdated
// Wrapped in flatten([]) to handle both when policy is a string and an array
const flattenedUserPolicy = flatten(userPolicyObj[policyName]);
if (flattenedUserPolicy.includes("'unsafe-inline'")) {
return policyObj[policyName];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to return the userPolicyObj here, no? If the user has defined unsafe inline then we want to return their defined policy - I guess it's almost the same since the policies are merged in the constructor, but for readability to other devs, we should use the correct variable reference :)

We should also extend this behaviour for unsafe-eval as well!

return compileCb(null, htmlPluginData);
}

createPolicyObj($, policyName, selector, policyObj, userPolicyObj) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a doc block for this function please?

@swac swac force-pushed the allow-unsafe-inline branch from f166ca9 to 4e3fc00 Compare October 16, 2018 16:18
@swac swac force-pushed the allow-unsafe-inline branch from 4e3fc00 to f858181 Compare October 19, 2018 16:07
@swac
Copy link
Contributor Author

swac commented Oct 19, 2018

@AnujRNair I made the changes you requested. As for unsafe-eval, I'm not sure if it's quite the same, since these policies only deal with included scripts and styles… I suppose I could change line 159 to something like intersection(flattenedUserPolicy, ["'unsafe-inline'", "'unsafe-eval'"]).length > 0) to support that though.

@AnujRNair
Copy link
Contributor

Thanks for the change! Let's start with this, and if we find we need to expand this more in the future, we can do

@AnujRNair AnujRNair merged commit 5d642ce into slackhq:master Oct 26, 2018
@swac swac deleted the allow-unsafe-inline branch October 26, 2018 20:31
draconisNoctis added a commit to draconisNoctis/csp-html-webpack-plugin that referenced this pull request Nov 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants