-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Append to existing CSP list in Document::set_csp_list #36828
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
|
@TimvdLippe There are some trusted types failures with this change that might interest you! |
|
Are we sure this is the right behaviour? I thought that CSP lists would override and only inherit for local iframes. |
|
Hmm. The intent here is that CSP lists from meta elements are appended to any CSP list received in the headers for a document. We may need to be more selective about this behavior change. |
|
I was reading https://www.w3.org/TR/CSP/#multiple-policies as supporting this use case, and the note under https://www.w3.org/TR/CSP/#meta-element seems to reinforce this. |
|
I see no new WPT pass with this change. Does that mean the behaviour is untested (unlikely, but possible) or we already pass it or the patch is insufficient? I would expect at least one test to pass with this fix and then we can look into why the other ones started failing. |
|
Yeah, it's worth writing a new test that checks this case explicitly. |
|
i'm tryna add test but no assert is run |
|
🤖 Opened new upstream WPT pull request (web-platform-tests/wpt#52322) with upstreamable changes. |
The test throws an exception because the first policy is blocked by CSP—using the same directive name ( |
just pushed, I used script-src instead of media-src, I could not find video file to load |
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#52322). |
| if let Some(new_list) = csp_list { | ||
| let mut current = self.get_csp_list(); | ||
| match current { | ||
| Some(ref mut existing) => existing.append(new_list), | ||
| None => current = Some(new_list), | ||
| } | ||
| self.policy_container.borrow_mut().set_csp_list(current); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the test failures, I think this patch is incorrect. The test in question has the following:
<meta http-equiv="Content-Security-Policy" content="trusted-types 'none'">
<meta http-equiv="Content-Security-Policy" content="connect-src 'none'">It has no CSP headers, so the only thing here are the meta elements.
Based on this patch, I see that it does two things when the second meta element is parsed:
- It adds the new list to current (by mutating the object)
- It sets the new list.
Notably, it doesn't clone the list, like the other implementation does. I don't know enough Rust, but I have a feeling that means it gets duplicated somehow.
Lastly, shouldn't we update the policy_container.set_csp_list instead of only the set_csp_list on document? We also have it on Window, so why are we only updating a single version of it?
All in all, it is unlikely to me that the Trusted Types code is wrong. Instead, I think the logic here somehow ends up duplicating the list the contents of the list twice.
| if let Some(new_list) = csp_list { | |
| let mut current = self.get_csp_list(); | |
| match current { | |
| Some(ref mut existing) => existing.append(new_list), | |
| None => current = Some(new_list), | |
| } | |
| self.policy_container.borrow_mut().set_csp_list(current); | |
| } | |
| if let Some(new_list) = csp_list { | |
| let new_list = match self.get_csp_list() { | |
| Some(original_list) => { | |
| let mut combined_list = original_list.clone(); | |
| combined_list.append(new_list); | |
| combined_list | |
| }, | |
| None => new_list, | |
| } | |
| self.policy_container.borrow_mut().set_csp_list(Some(new_list)); | |
| } |
Can you try with this patch and see if the Trusted Types tests still fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lastly, shouldn't we update the policy_container.set_csp_list instead of only the set_csp_list on document? We also have it on Window, so why are we only updating a single version of it?
The only copy of the CSP list is in the policy container that lives in the document:
servo/components/script/dom/globalscope.rs
Lines 3041 to 3044 in ffccc5c
| pub fn get_csp_list(&self) -> Option<CspList> { | |
| if let Some(window) = self.downcast::<Window>() { | |
| return window.Document().get_csp_list().map(|c| c.clone()); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notably, it doesn't clone the list, like the other implementation does. I don't know enough Rust, but I have a feeling that means it gets duplicated somehow.
No cloning is required. Document::get_csp_list already returns a clone, so the clone is updated and the original value is overwritten by the updated value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i added another same test to confirm the multiple meta header and its passing which did not help,
add added logging to the failing test (tests/wpt/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html, https://github.com/elomscansio/servo/blob/9fe700f23d9d24e307d1d3925a20ddcbb1da25c8/tests/wpt/tests/trusted-types/support/csp-violations.js#L22-L23) js and it turned out that the trusted-type in being thrown twice which i don't know why
0:07.42 pid:17791 0 .........................
0:07.42 pid:17791 isTrusted true
0:07.42 pid:17791 documentURI http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.42 pid:17791 referrer http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.42 pid:17791 blockedURI trusted-types-policy
0:07.42 pid:17791 effectiveDirective trusted-types
0:07.42 pid:17791 violatedDirective trusted-types
0:07.42 pid:17791 originalPolicy trusted-types 'none'
0:07.42 pid:17791 sourceFile http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.42 pid:17791 sample SomeName
0:07.42 pid:17791 disposition enforce
0:07.42 pid:17791 statusCode 200
0:07.42 pid:17791 lineNumber 14
0:07.42 pid:17791 columnNumber 27
0:07.42 pid:17791 composedPath function composedPath() {
0:07.42 pid:17791 [native code]
0:07.42 pid:17791 }
0:07.42 pid:17791 stopPropagation function stopPropagation() {
0:07.42 pid:17791 [native code]
0:07.42 pid:17791 }
0:07.42 pid:17791 stopImmediatePropagation function stopImmediatePropagation() {
0:07.42 pid:17791 [native code]
0:07.42 pid:17791 }
0:07.42 pid:17791 preventDefault function preventDefault() {
0:07.42 pid:17791 [native code]
0:07.42 pid:17791 }
0:07.42 pid:17791 initEvent function initEvent() {
0:07.42 pid:17791 [native code]
0:07.42 pid:17791 }
0:07.42 pid:17791 type securitypolicyviolation
0:07.43 pid:17791 target …
0:07.43 pid:17791 srcElement …
0:07.44 pid:17791 currentTarget …
0:07.44 pid:17791 eventPhase 2
0:07.44 pid:17791 cancelBubble false
0:07.44 pid:17791 bubbles true
0:07.44 pid:17791 cancelable true
0:07.44 pid:17791 returnValue true
0:07.44 pid:17791 defaultPrevented false
0:07.44 pid:17791 composed false
0:07.44 pid:17791 timeStamp 2530.83
0:07.44 pid:17791 NONE 0
0:07.44 pid:17791 CAPTURING_PHASE 1
0:07.44 pid:17791 AT_TARGET 2
0:07.44 pid:17791 BUBBLING_PHASE 3
0:07.44 pid:17791
0:07.44 pid:17791 1 .........................
0:07.44 pid:17791 isTrusted true
0:07.44 pid:17791 documentURI http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.44 pid:17791 referrer http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.44 pid:17791 blockedURI trusted-types-policy
0:07.44 pid:17791 effectiveDirective trusted-types
0:07.44 pid:17791 violatedDirective trusted-types
0:07.44 pid:17791 originalPolicy trusted-types 'none'
0:07.44 pid:17791 sourceFile http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.44 pid:17791 sample SomeName
0:07.44 pid:17791 disposition enforce
0:07.44 pid:17791 statusCode 200
0:07.44 pid:17791 lineNumber 14
0:07.44 pid:17791 columnNumber 27
0:07.45 pid:17791 composedPath function composedPath() {
0:07.45 pid:17791 [native code]
0:07.45 pid:17791 }
0:07.45 pid:17791 stopPropagation function stopPropagation() {
0:07.45 pid:17791 [native code]
0:07.45 pid:17791 }
0:07.45 pid:17791 stopImmediatePropagation function stopImmediatePropagation() {
0:07.45 pid:17791 [native code]
0:07.45 pid:17791 }
0:07.45 pid:17791 preventDefault function preventDefault() {
0:07.45 pid:17791 [native code]
0:07.45 pid:17791 }
0:07.45 pid:17791 initEvent function initEvent() {
0:07.45 pid:17791 [native code]
0:07.45 pid:17791 }
0:07.45 pid:17791 type securitypolicyviolation
0:07.45 pid:17791 target …
0:07.45 pid:17791 srcElement …
0:07.45 pid:17791 currentTarget …
0:07.45 pid:17791 eventPhase 2
0:07.45 pid:17791 cancelBubble false
0:07.45 pid:17791 bubbles true
0:07.45 pid:17791 cancelable true
0:07.45 pid:17791 returnValue true
0:07.45 pid:17791 defaultPrevented false
0:07.45 pid:17791 composed false
0:07.45 pid:17791 timeStamp 2552.05
0:07.45 pid:17791 NONE 0
0:07.45 pid:17791 CAPTURING_PHASE 1
0:07.45 pid:17791 AT_TARGET 2
0:07.45 pid:17791 BUBBLING_PHASE 3
0:07.45 pid:17791
0:07.46 pid:17791 2 .........................
0:07.46 pid:17791 isTrusted true
0:07.46 pid:17791 documentURI http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.46 pid:17791 referrer http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:07.46 pid:17791 blockedURI ws://common/blank.html
0:07.46 pid:17791 effectiveDirective connect-src
0:07.46 pid:17791 violatedDirective connect-src
0:07.46 pid:17791 originalPolicy connect-src 'none'
0:07.46 pid:17791 sourceFile
0:07.46 pid:17791 sample
0:07.46 pid:17791 disposition enforce
0:07.46 pid:17791 statusCode 200
0:07.46 pid:17791 lineNumber 0
0:07.46 pid:17791 columnNumber 1
0:07.46 pid:17791 composedPath function composedPath() {
0:07.46 pid:17791 [native code]
0:07.46 pid:17791 }
0:07.46 pid:17791 stopPropagation function stopPropagation() {
0:07.46 pid:17791 [native code]
0:07.46 pid:17791 }
0:07.46 pid:17791 stopImmediatePropagation function stopImmediatePropagation() {
0:07.46 pid:17791 [native code]
0:07.46 pid:17791 }
0:07.46 pid:17791 preventDefault function preventDefault() {
0:07.46 pid:17791 [native code]
0:07.46 pid:17791 }
0:07.46 pid:17791 initEvent function initEvent() {
0:07.46 pid:17791 [native code]
0:07.46 pid:17791 }
0:07.46 pid:17791 type securitypolicyviolation
0:07.46 pid:17791 target …
0:07.46 pid:17791 srcElement …
0:07.47 pid:17791 currentTarget …
0:07.47 pid:17791 eventPhase 2
0:07.47 pid:17791 cancelBubble false
0:07.47 pid:17791 bubbles true
0:07.47 pid:17791 cancelable true
0:07.47 pid:17791 returnValue true
0:07.47 pid:17791 defaultPrevented false
0:07.47 pid:17791 composed false
0:07.47 pid:17791 timeStamp 2566.36
0:07.47 pid:17791 NONE 0
0:07.47 pid:17791 CAPTURING_PHASE 1
0:07.47 pid:17791 AT_TARGET 2
0:07.47 pid:17791 BUBBLING_PHASE 3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe there are some other places that policy_container are updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some printlns in your function to print csp_list.to_string() and see if the directive is twice in it? I used that before to debug the contents of the list. You can also add this to the getter, so that you can always see when it is retrieved what the value is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
our problem stems from
| "content-security-policy" => self.apply_csp_list(), |
| head.set_content_security_policy(); |
servo/components/script/dom/htmlheadelement.rs
Lines 67 to 96 in c6de8fc
| let candidates = node | |
| .traverse_preorder(ShadowIncluding::No) | |
| .filter_map(DomRoot::downcast::<Element>) | |
| .filter(|elem| elem.is::<HTMLMetaElement>()) | |
| .filter(|elem| { | |
| elem.get_string_attribute(&local_name!("http-equiv")) | |
| .to_ascii_lowercase() == | |
| *"content-security-policy" | |
| }) | |
| .filter(|elem| { | |
| elem.get_attribute(&ns!(), &local_name!("content")) | |
| .is_some() | |
| }); | |
| for meta in candidates { | |
| if let Some(ref content) = meta.get_attribute(&ns!(), &local_name!("content")) { | |
| let content = content.value(); | |
| let content_val = content.trim(); | |
| if !content_val.is_empty() { | |
| let policies = | |
| CspList::parse(content_val, PolicySource::Meta, PolicyDisposition::Enforce); | |
| match csp_list { | |
| Some(ref mut csp_list) => csp_list.append(policies), | |
| None => csp_list = Some(policies), | |
| } | |
| } | |
| } | |
| } | |
| doc.set_csp_list(csp_list); |
And i think we are doing unnecessary work, since meta is the only valid element to carry the csp props we can just push the csp directly to doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see the log
elomscansio@elomscansio:~/Desktop/projects/servo
$ ./mach test-wpt tests/wpt/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
No build type specified so assuming `--dev`.
Running WPT tests with /home/elomscansio/Desktop/projects/servo/target/debug/servo
0:04.79 wptserve INFO Starting http server on http://127.0.0.1:8001
0:04.84 wptserve INFO Starting http server on http://127.0.0.1:8002
0:04.91 wptserve INFO Create socket on: (<AddressFamily.AF_INET6: 10>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:04.93 wptserve INFO Create socket on: (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:04.94 wptserve INFO Bind on: (<AddressFamily.AF_INET6: 10>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:04.94 wptserve INFO Bind on: (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:04.94 wptserve INFO Listen on: (<AddressFamily.AF_INET6: 10>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:04.94 wptserve INFO Listen on: (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:04.94 wptserve INFO Skip by failure: OSError(98, 'Address already in use')
0:05.00 wptserve INFO Create socket on: (<AddressFamily.AF_INET6: 10>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:05.00 wptserve INFO Create socket on: (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:05.00 wptserve INFO Bind on: (<AddressFamily.AF_INET6: 10>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:05.00 wptserve INFO Bind on: (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:05.00 wptserve INFO Listen on: (<AddressFamily.AF_INET6: 10>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:05.00 wptserve INFO Listen on: (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, '', '', '')
0:05.00 wptserve INFO Skip by failure: OSError(98, 'Address already in use')
0:05.03 wptserve INFO Starting https server on https://127.0.0.1:8443
0:05.04 wptserve INFO Starting https server on https://127.0.0.1:8446
0:05.10 wptserve INFO Starting https server on https://127.0.0.1:8445
0:05.12 wptserve INFO Starting https server on https://127.0.0.1:8444
0:05.12 wptserve INFO Starting http2 server on https://127.0.0.1:9000
0:05.13 wptserve INFO Starting http server on http://127.0.0.1:8000
0:05.14 wptserve INFO Starting http server on http://127.0.0.1:8003
0:05.57 SUITE_START: web-platform-test - running 1 tests
0:05.58 INFO Using 1 child processes
0:05.58 INFO Starting runner
0:06.00 TEST_START: /trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html
0:06.00 WARNING Got command init_succeeded in state running
0:20.11 pid:7793 Full command: /home/elomscansio/Desktop/projects/servo/target/debug/servo --hard-fail -u Servo/wptrunner --ignore-certificate-errors --enable-experimental-web-platform-features http://web-platform.test:8000/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html --user-stylesheet /home/elomscansio/Desktop/projects/servo/resources/ahem.css --pref dom_testutils_enabled=true --pref dom_urlpattern_enabled=true --prefs-file /home/elomscansio/Desktop/projects/servo/resources/wpt-prefs.json --certificate-path /home/elomscansio/Desktop/projects/servo/tests/wpt/tests/tools/certs/cacert.pem
pid:7793 new csp list "trusted-types 'none'"
0:20.12 pid:7793 csp list after update "trusted-types 'none'"
0:20.12 pid:7793 new csp list "trusted-types 'none',connect-src 'none'"
0:20.12 pid:7793 csp list before update "trusted-types 'none'"
0:20.12 pid:7793 csp list after update "trusted-types 'none',trusted-types 'none',connect-src 'none'"
0:20.69 pid:7793
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0:20.12 pid:7793 new csp list "trusted-types 'none',connect-src 'none'" the new csp list still includes what we've added before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdm @TimvdLippe @PotatoCP I made few changes i'm not sure whether it complies with spec
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#52322). |
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#52322). |
| }); | ||
| }, "script without `abc` nonce is blocked by header CSP"); | ||
| </script> | ||
| </body> No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to add newline at the end of the file. Same for tests/wpt/tests/content-security-policy/meta/meta-multiple-csp.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#52322). |
44abbdf to
8e08c28
Compare
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#52322). |
8e08c28 to
24ab4ee
Compare
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#52322). |
|
ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have maintainer's rights on this repository, but from my perspective this PR looks good to me. Can you run a try run on your fork to see if this fixes any issues? You can do so with ./mach try wpt
wow, Thanks |
|
I see you ran it successfully in https://github.com/elomscansio/servo/actions/runs/15079633979 Note that the single test failure has already been resolved on |
Signed-off-by: Emmanuel Paul Elom <elomemmanuel007@gmail.com>
24ab4ee to
7e61cee
Compare
|
📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#52322). |
|
I found this WPT test that might be a duplicate https://github.com/web-platform-tests/wpt/blob/master/content-security-policy/meta/combine-header-and-meta-policies.sub.html I wonder why that doesn't pass with this PR? |
|
@TimvdLippe This PR has been here for a while. I'm curious what's your opinion: is it still relevant? |
|
We are currently still failing https://wpt.fyi/results/content-security-policy/meta/combine-header-and-meta-policies.sub.html?product=servo so I think this fix is still relevant. Therefore, we don't need to introduce a new WPT test for this. Can you remove the new test and make the existing one pass? |
The
Document::set_csp_listmethod now appends new policies to anyexisting
CspListrather than replacing it. This ensures that CSPsfrom multiple sources (e.g., HTTP headers and
<meta>tags) aremerged as expected, following the spec.
This change avoids silently discarding header-defined policies when
a
<meta http-equiv="Content-Security-Policy">is present in thedocument.
./mach build -ddoes not report any errors./mach test-tidydoes not report any errorsThese changes fix CSP from <meta> overrides any existing CSP #36822
There are tests for these changes (if applicable)