Improve CSP inheritance semantics
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Sep 2019 21:56:43 +0000 (21:56 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Sep 2019 21:56:43 +0000 (21:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201884
<rdar://problem/50172407>

Reviewed by Brent Fulgham.

LayoutTests/imported/w3c:

Update expected results now that we pass more sub-tests.

* web-platform-tests/content-security-policy/inheritance/iframe-all-local-schemes-inherit-self.sub-expected.txt:
* web-platform-tests/content-security-policy/inheritance/window-expected.txt:

Source/WebCore:

Update the CSP inheritance semantics to more closely match the logic in section Initialize a Document's CSP list
of the CSP3 spec., <https://w3c.github.io/webappsec-csp/#initialize-document-csp>.

Towards this, move more of the inheritance logic out of Document::initContentSecurityPolicy() and into
DocumentWriter::begin() where details about the document being replaced live. This lets us remove the
need to track the previous content security policy to pass it to Document::initContentSecurityPolicy().
Moreover, DocumentWriter::begin() knows the owner document that will be replaced with the result of
executing a JavaScript URL. This is needed in order to fix up inheritance of CSP for such documents.

Tests: http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url.html
       http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url.html
       http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2.html

* dom/Document.cpp:
(WebCore::Document::initSecurityContext): If we are inheriting the security origin from the owner
document then inherit its CSP policy. We copy over both the CSP state from the owner as well as
update 'self' to match the owner's origin so that CSP source expressions that include 'self' work
correctly even from about:blank documents.
(WebCore::Document::initContentSecurityPolicy): Move most of the logic from here into DocumentWriter::begin()
to take advantage of the fact that DocumentWriter::begin() knows about the outgoing document (if there
is one) as well as whether the outgoing document is being replaced with a new document that is the result
of evaluating a JavaScript URL. We need do know both these things in order to inherit the correct CSP
policy. This function only exists to copy some upgrade-insecure-requests state and to fix up plugin documents
as we currently do.
(WebCore::Document::shouldInheritContentSecurityPolicy const): Deleted.
* dom/Document.h:
* dom/SecurityContext.cpp:
(WebCore::SecurityContext::setContentSecurityPolicy): Modified to take its param by rvalue-reference
to make it less error prone to use.
* dom/SecurityContext.h: Expose setContentSecurityPolicy() so that we can invoke it from DocumentWriter::begin().
* loader/DocumentWriter.cpp:
(WebCore::DocumentWriter::begin): For documents being replaced with the result of a JavaScript URL (i.e. ownerDocument
is non-null) inherit the CSP from the owner document. Similarly, if we have an existing document in the frame
and the protocol of the new document's URL is data: or blob: then inherit the CSP from the existing page. The latter
is what we currently do just moved from Document::initContentSecurityPolicy() and re-written in terms of the
existingDocument instead of previousContentSecurityPolicy. Also call setInsecureNavigationRequestsToUpgrade()
both when we have a non-null ownerDocument as well as when we have a non-null existingDocument. The former fixes
the block-all-mixed-content feature for documents loaded via JavaScript URLs and the latter is what we do now.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::didBeginDocument): Remove parameter previousContentSecurityPolicy as the logic that
made use of it moved to DocumentWriter::begin().
* loader/FrameLoader.h:
* page/csp/ContentSecurityPolicy.h:

LayoutTests:

Add some more tests and update expected results of existing tests now that we pass more sub-tests.

* http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url.html: Added.
* http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url.html: Added.
* http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2.html: Added.
* http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt: I don't understand
why there is another duplicte console log message emitted, but there are already two such messages, which is
already one too many. The duplicate messages are more cosmetic than functional though there may be implications
with respect to CSP reporting. Filed <https://bugs.webkit.org/show_bug.cgi?id=202004> to track this issue.
* platform/mac-wk1/http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt:
* platform/win/http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250255 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/content-security-policy/inheritance/iframe-all-local-schemes-inherit-self.sub-expected.txt
LayoutTests/imported/w3c/web-platform-tests/content-security-policy/inheritance/window-expected.txt
LayoutTests/platform/mac-wk1/http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt
LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/SecurityContext.cpp
Source/WebCore/dom/SecurityContext.h
Source/WebCore/loader/DocumentWriter.cpp
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/FrameLoader.h
Source/WebCore/page/csp/ContentSecurityPolicy.h

index b56853c..aada96c 100644 (file)
@@ -1,3 +1,26 @@
+2019-09-23  Daniel Bates  <dabates@apple.com>
+
+        Improve CSP inheritance semantics
+        https://bugs.webkit.org/show_bug.cgi?id=201884
+        <rdar://problem/50172407>
+
+        Reviewed by Brent Fulgham.
+
+        Add some more tests and update expected results of existing tests now that we pass more sub-tests.
+
+        * http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url.html: Added.
+        * http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url.html: Added.
+        * http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2.html: Added.
+        * http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt: I don't understand
+        why there is another duplicte console log message emitted, but there are already two such messages, which is
+        already one too many. The duplicate messages are more cosmetic than functional though there may be implications
+        with respect to CSP reporting. Filed <https://bugs.webkit.org/show_bug.cgi?id=202004> to track this issue.
+        * platform/mac-wk1/http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt:
+        * platform/win/http/tests/security/contentSecurityPolicy/user-style-sheet-font-crasher-expected.txt:
+
 2019-09-16  Jiewen Tan  <jiewen_tan@apple.com>
 
         [WebAuthn] LocalAuthenticator tests are failing on internal bots
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url-expected.txt
new file mode 100644 (file)
index 0000000..889df3b
--- /dev/null
@@ -0,0 +1,7 @@
+ALERT: PASS
+
+
+--------
+Frame: '<!--frame1-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url.html b/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url.html
new file mode 100644 (file)
index 0000000..9cf9ee8
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; child-src http://localhost:8000">
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body>
+<script>
+var test = "javascript:'<iframe src=http://localhost:8000/security/contentSecurityPolicy/resources/alert-pass.html></iframe><script>window.testRunner && window.setTimeout(() => testRunner.notifyDone(), 0);</" + "script>'";
+window.open(test, "_self");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url-expected.txt
new file mode 100644 (file)
index 0000000..65e9647
--- /dev/null
@@ -0,0 +1,7 @@
+CONSOLE MESSAGE: Refused to load http://localhost:8000/security/contentSecurityPolicy/resources/alert-fail.html because it appears in neither the child-src directive nor the default-src directive of the Content Security Policy.
+
+
+--------
+Frame: '<!--frame1-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url.html b/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url.html
new file mode 100644 (file)
index 0000000..3ae44e1
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'">
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body>
+<script>
+var test = "javascript:'<iframe src=http://localhost:8000/security/contentSecurityPolicy/resources/alert-fail.html></iframe><script>window.testRunner && window.setTimeout(() => testRunner.notifyDone(), 0);</" + "script>'";
+window.open(test, "_self");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2-expected.txt
new file mode 100644 (file)
index 0000000..c73abaf
--- /dev/null
@@ -0,0 +1,12 @@
+CONSOLE MESSAGE: Refused to load http://localhost:8000/security/contentSecurityPolicy/resources/alert-fail.html because it appears in neither the child-src directive nor the default-src directive of the Content Security Policy.
+
+
+--------
+Frame: '<!--frame1-->'
+--------
+
+
+--------
+Frame: '<!--frame2-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2.html b/LayoutTests/http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2.html
new file mode 100644 (file)
index 0000000..033dfbf
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'">
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body>
+<script>
+var test = "javascript:'<iframe src=\"javascript:`<iframe src=http://localhost:8000/security/contentSecurityPolicy/resources/alert-fail.html></iframe><script>window.testRunner && window.setTimeout(() => testRunner.notifyDone(), 0);</" + "script>`\"></iframe>'";
+window.open(test, "_self");
+</script>
+</body>
+</html>
index 5b71768..4790dda 100644 (file)
@@ -2,5 +2,7 @@ CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityP
 Blocked access to external URL http://webkit.org/report
 CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
 Blocked access to external URL http://webkit.org/report
+CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
+Blocked access to external URL http://webkit.org/report
 The iframe below triggers a violation report creating the initial empty document. It should not crash the web process.
 
index d454549..efcb180 100644 (file)
@@ -1,3 +1,16 @@
+2019-09-23  Daniel Bates  <dabates@apple.com>
+
+        Improve CSP inheritance semantics
+        https://bugs.webkit.org/show_bug.cgi?id=201884
+        <rdar://problem/50172407>
+
+        Reviewed by Brent Fulgham.
+
+        Update expected results now that we pass more sub-tests.
+
+        * web-platform-tests/content-security-policy/inheritance/iframe-all-local-schemes-inherit-self.sub-expected.txt:
+        * web-platform-tests/content-security-policy/inheritance/window-expected.txt:
+
 2019-09-23  Rob Buis  <rbuis@igalia.com>
 
         Sync operator dictionary
index c1172ad..10df77f 100644 (file)
@@ -1,9 +1,8 @@
 
-
 PASS <iframe>'s about:blank inherits policy. 
 PASS <iframe srcdoc>'s inherits policy. 
 PASS <iframe src='blob:...'>'s inherits policy. 
-FAIL <iframe src='data:...'>'s inherits policy. assert_equals: expected "load" but got "error"
+PASS <iframe src='data:...'>'s inherits policy. 
 PASS <iframe src='javascript:...'>'s inherits policy. 
-FAIL <iframe sandbox src='blob:...'>'s inherits policy. (opaque origin sandbox) assert_equals: expected "load" but got "error"
+PASS <iframe sandbox src='blob:...'>'s inherits policy. (opaque origin sandbox) 
 
index 98cf882..1bbfa07 100644 (file)
@@ -1,6 +1,6 @@
 
 PASS window.open() inherits policy. 
 PASS `document.write` into `window.open()` inherits policy. 
-FAIL window.open('blob:...') inherits policy. assert_equals: expected "error" but got "load"
+PASS window.open('blob:...') inherits policy. 
 PASS window.open('javascript:...') inherits policy. 
 
index 2b96adf..861557f 100644 (file)
@@ -1,4 +1,5 @@
 CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
 CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
+CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
 The iframe below triggers a violation report creating the initial empty document. It should not crash the web process.
 
index 2b96adf..861557f 100644 (file)
@@ -1,4 +1,5 @@
 CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
 CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
+CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/security/contentSecurityPolicy/example_font.woff because it does not appear in the font-src directive of the Content Security Policy.
 The iframe below triggers a violation report creating the initial empty document. It should not crash the web process.
 
index d341940..e70fc1a 100644 (file)
@@ -1,3 +1,55 @@
+2019-09-23  Daniel Bates  <dabates@apple.com>
+
+        Improve CSP inheritance semantics
+        https://bugs.webkit.org/show_bug.cgi?id=201884
+        <rdar://problem/50172407>
+
+        Reviewed by Brent Fulgham.
+
+        Update the CSP inheritance semantics to more closely match the logic in section Initialize a Document's CSP list
+        of the CSP3 spec., <https://w3c.github.io/webappsec-csp/#initialize-document-csp>.
+
+        Towards this, move more of the inheritance logic out of Document::initContentSecurityPolicy() and into
+        DocumentWriter::begin() where details about the document being replaced live. This lets us remove the
+        need to track the previous content security policy to pass it to Document::initContentSecurityPolicy().
+        Moreover, DocumentWriter::begin() knows the owner document that will be replaced with the result of
+        executing a JavaScript URL. This is needed in order to fix up inheritance of CSP for such documents.
+
+        Tests: http/tests/security/contentSecurityPolicy/iframe-allowed-when-loaded-via-javascript-url.html
+               http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url.html
+               http/tests/security/contentSecurityPolicy/iframe-blocked-when-loaded-via-javascript-url2.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::initSecurityContext): If we are inheriting the security origin from the owner
+        document then inherit its CSP policy. We copy over both the CSP state from the owner as well as
+        update 'self' to match the owner's origin so that CSP source expressions that include 'self' work
+        correctly even from about:blank documents.
+        (WebCore::Document::initContentSecurityPolicy): Move most of the logic from here into DocumentWriter::begin()
+        to take advantage of the fact that DocumentWriter::begin() knows about the outgoing document (if there
+        is one) as well as whether the outgoing document is being replaced with a new document that is the result
+        of evaluating a JavaScript URL. We need do know both these things in order to inherit the correct CSP
+        policy. This function only exists to copy some upgrade-insecure-requests state and to fix up plugin documents
+        as we currently do.
+        (WebCore::Document::shouldInheritContentSecurityPolicy const): Deleted.
+        * dom/Document.h:
+        * dom/SecurityContext.cpp:
+        (WebCore::SecurityContext::setContentSecurityPolicy): Modified to take its param by rvalue-reference
+        to make it less error prone to use.
+        * dom/SecurityContext.h: Expose setContentSecurityPolicy() so that we can invoke it from DocumentWriter::begin().
+        * loader/DocumentWriter.cpp:
+        (WebCore::DocumentWriter::begin): For documents being replaced with the result of a JavaScript URL (i.e. ownerDocument
+        is non-null) inherit the CSP from the owner document. Similarly, if we have an existing document in the frame
+        and the protocol of the new document's URL is data: or blob: then inherit the CSP from the existing page. The latter
+        is what we currently do just moved from Document::initContentSecurityPolicy() and re-written in terms of the
+        existingDocument instead of previousContentSecurityPolicy. Also call setInsecureNavigationRequestsToUpgrade()
+        both when we have a non-null ownerDocument as well as when we have a non-null existingDocument. The former fixes
+        the block-all-mixed-content feature for documents loaded via JavaScript URLs and the latter is what we do now.
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::didBeginDocument): Remove parameter previousContentSecurityPolicy as the logic that
+        made use of it moved to DocumentWriter::begin().
+        * loader/FrameLoader.h:
+        * page/csp/ContentSecurityPolicy.h:
+
 2019-09-23  Keith Rollin  <krollin@apple.com>
 
         Unreviewed build fix after r250173: tvOS build broken due to unused function.
index 232afe3..8b0d3d4 100644 (file)
@@ -5851,14 +5851,15 @@ void Document::initSecurityContext()
         return;
     }
 
-    Document* openerDocument = openerFrame ? openerFrame->document() : nullptr;
+    contentSecurityPolicy()->copyStateFrom(ownerFrame->document()->contentSecurityPolicy());
+    contentSecurityPolicy()->updateSourceSelf(ownerFrame->document()->securityOrigin());
 
     // Per <http://www.w3.org/TR/upgrade-insecure-requests/>, new browsing contexts must inherit from an
     // ongoing set of upgraded requests. When opening a new browsing context, we need to capture its
     // existing upgrade request. Nested browsing contexts are handled during DocumentWriter::begin.
-    if (openerDocument)
+    if (auto* openerDocument = openerFrame ? openerFrame->document() : nullptr)
         contentSecurityPolicy()->inheritInsecureNavigationRequestsToUpgradeFromOpener(*openerDocument->contentSecurityPolicy());
-    
+
     if (isSandboxed(SandboxOrigin)) {
         // If we're supposed to inherit our security origin from our owner,
         // but we're also sandboxed, the only thing we inherit is the ability
@@ -5875,53 +5876,26 @@ void Document::initSecurityContext()
     setSecurityOriginPolicy(ownerFrame->document()->securityOriginPolicy());
 }
 
-// FIXME: The current criterion is stricter than <https://www.w3.org/TR/CSP3/#security-inherit-csp> (Editor's Draft, 28 February 2019).
-bool Document::shouldInheritContentSecurityPolicy() const
-{
-    ASSERT(m_frame);
-    if (SecurityPolicy::shouldInheritSecurityOriginFromOwner(m_url))
-        return true;
-    if (m_url.protocolIsData() || m_url.protocolIsBlob())
-        return true;
-    if (!isPluginDocument())
-        return false;
-    if (m_frame->tree().parent())
-        return true;
-    Frame* openerFrame = m_frame->loader().opener();
-    if (!openerFrame)
-        return false;
-    return openerFrame->document()->securityOrigin().canAccess(securityOrigin());
-}
-
-void Document::initContentSecurityPolicy(ContentSecurityPolicy* previousPolicy)
+void Document::initContentSecurityPolicy()
 {
-    // 1. Inherit Upgrade Insecure Requests
-    Frame* parentFrame = m_frame->tree().parent();
+    auto* parentFrame = m_frame->tree().parent();
     if (parentFrame)
         contentSecurityPolicy()->copyUpgradeInsecureRequestStateFrom(*parentFrame->document()->contentSecurityPolicy());
 
-    // 2. Inherit Content Security Policy (without copying Upgrade Insecure Requests state).
-    if (!shouldInheritContentSecurityPolicy())
-        return;
-    ContentSecurityPolicy* ownerPolicy = nullptr;
-    if (previousPolicy && (m_url.protocolIsData() || m_url.protocolIsBlob()))
-        ownerPolicy = previousPolicy;
-    if (!ownerPolicy) {
-        Frame* ownerFrame = parentFrame;
-        if (!ownerFrame)
-            ownerFrame = m_frame->loader().opener();
-        if (ownerFrame)
-            ownerPolicy = ownerFrame->document()->contentSecurityPolicy();
-    }
-    if (!ownerPolicy)
-        return;
-    // FIXME: We are stricter than the CSP 3 spec. with regards to plugins: we prefer to inherit the full policy unless the plugin
-    // document is opened in a new window. The CSP 3 spec. implies that only plugin documents delivered with a local scheme (e.g. blob,
-    // file, data) should inherit a policy.
-    if (isPluginDocument() && m_frame->loader().opener())
-        contentSecurityPolicy()->createPolicyForPluginDocumentFrom(*ownerPolicy);
+    // FIXME: Remove this special plugin document logic. We are stricter than the CSP 3 spec. with regards to plugins: we prefer to
+    // inherit the full policy unless the plugin document is opened in a new window. The CSP 3 spec. implies that only plugin documents
+    // delivered with a local scheme (e.g. blob, file, data) should inherit a policy.
+    if (!isPluginDocument())
+        return;
+    auto* openerFrame = m_frame->loader().opener();
+    bool shouldInhert = parentFrame || (openerFrame && openerFrame->document()->securityOrigin().canAccess(securityOrigin()));
+    if (!shouldInhert)
+        return;
+    setContentSecurityPolicy(makeUnique<ContentSecurityPolicy>(URL { m_url }, *this));
+    if (openerFrame)
+        contentSecurityPolicy()->createPolicyForPluginDocumentFrom(*openerFrame->document()->contentSecurityPolicy());
     else
-        contentSecurityPolicy()->copyStateFrom(ownerPolicy);
+        contentSecurityPolicy()->copyStateFrom(parentFrame->document()->contentSecurityPolicy());
 }
 
 bool Document::isContextThread() const
index 70538c5..9c1163c 100644 (file)
@@ -1144,7 +1144,7 @@ public:
     HashSet<SVGUseElement*> const svgUseElements() const { return m_svgUseElements; }
 
     void initSecurityContext();
-    void initContentSecurityPolicy(ContentSecurityPolicy* previousPolicy);
+    void initContentSecurityPolicy();
 
     void updateURLForPushOrReplaceState(const URL&);
     void statePopped(Ref<SerializedScriptValue>&&);
@@ -1550,8 +1550,6 @@ private:
     friend class IgnoreOpensDuringUnloadCountIncrementer;
     friend class IgnoreDestructiveWriteCountIncrementer;
 
-    bool shouldInheritContentSecurityPolicy() const;
-
     void updateTitleElement(Element& changingTitleElement);
     void willDetachPage() final;
     void frameDestroyed() final;
index 4f8f1a8..9824673 100644 (file)
@@ -53,7 +53,7 @@ SecurityOrigin* SecurityContext::securityOrigin() const
     return &m_securityOriginPolicy->origin();
 }
 
-void SecurityContext::setContentSecurityPolicy(std::unique_ptr<ContentSecurityPolicy> contentSecurityPolicy)
+void SecurityContext::setContentSecurityPolicy(std::unique_ptr<ContentSecurityPolicy>&& contentSecurityPolicy)
 {
     m_contentSecurityPolicy = WTFMove(contentSecurityPolicy);
 }
index c0cc8e2..f1ef7c6 100644 (file)
@@ -77,6 +77,11 @@ public:
     //       that already contains content.
     void setSecurityOriginPolicy(RefPtr<SecurityOriginPolicy>&&);
 
+    // Explicitly override the content security policy for this security context.
+    // Note: It is dangerous to change the content security policy of a script
+    //       context that already contains content.
+    void setContentSecurityPolicy(std::unique_ptr<ContentSecurityPolicy>&&);
+
     WEBCORE_EXPORT SecurityOrigin* securityOrigin() const;
 
     static SandboxFlags parseSandboxPolicy(const String& policy, String& invalidTokensErrorMessage);
@@ -105,8 +110,6 @@ protected:
     SecurityContext();
     virtual ~SecurityContext();
 
-    void setContentSecurityPolicy(std::unique_ptr<ContentSecurityPolicy>);
-
     // It's only appropriate to call this during security context initialization; it's needed for
     // flags that can't be disabled with allow-* attributes, such as SandboxNavigation.
     void disableSandboxFlags(SandboxFlags mask) { m_sandboxFlags &= ~mask; }
index ef02de1..f2bd600 100644 (file)
@@ -140,9 +140,8 @@ bool DocumentWriter::begin(const URL& urlReference, bool dispatch, Document* own
 
     // Temporarily extend the lifetime of the existing document so that FrameLoader::clear() doesn't destroy it as
     // we need to retain its ongoing set of upgraded requests in new navigation contexts per <http://www.w3.org/TR/upgrade-insecure-requests/>
-    // and we may also need to inherit its Content Security Policy in FrameLoader::didBeginDocument().
+    // and we may also need to inherit its Content Security Policy below.
     RefPtr<Document> existingDocument = m_frame->document();
-    auto* previousContentSecurityPolicy = existingDocument ? existingDocument->contentSecurityPolicy() : nullptr;
 
     WTF::Function<void()> handleDOMWindowCreation = [this, document = document.copyRef(), url] {
         if (m_frame->loader().stateMachine().isDisplayingInitialEmptyDocument() && m_frame->document()->isSecureTransitionTo(url))
@@ -165,18 +164,31 @@ bool DocumentWriter::begin(const URL& urlReference, bool dispatch, Document* own
     m_frame->loader().setOutgoingReferrer(url);
     m_frame->setDocument(document.copyRef());
 
-    if (previousContentSecurityPolicy)
-        document->contentSecurityPolicy()->setInsecureNavigationRequestsToUpgrade(previousContentSecurityPolicy->takeNavigationRequestsToUpgrade());
-
     if (m_decoder)
         document->setDecoder(m_decoder.get());
     if (ownerDocument) {
+        // |document| is the result of evaluating a JavaScript URL.
         document->setCookieURL(ownerDocument->cookieURL());
         document->setSecurityOriginPolicy(ownerDocument->securityOriginPolicy());
         document->setStrictMixedContentMode(ownerDocument->isStrictMixedContentMode());
+
+        document->setContentSecurityPolicy(makeUnique<ContentSecurityPolicy>(URL { url }, document));
+        document->contentSecurityPolicy()->copyStateFrom(ownerDocument->contentSecurityPolicy());
+        document->contentSecurityPolicy()->setInsecureNavigationRequestsToUpgrade(ownerDocument->contentSecurityPolicy()->takeNavigationRequestsToUpgrade());
+    } else if (existingDocument) {
+        if (url.protocolIsData() || url.protocolIsBlob()) {
+            document->setContentSecurityPolicy(makeUnique<ContentSecurityPolicy>(URL { url }, document));
+            document->contentSecurityPolicy()->copyStateFrom(existingDocument->contentSecurityPolicy());
+
+            // Fix up 'self' for blob: and data:, which is inherited from its embedding document or opener.
+            auto* parentFrame = m_frame->tree().parent();
+            if (auto* ownerFrame = parentFrame ? parentFrame : m_frame->loader().opener())
+                document->contentSecurityPolicy()->updateSourceSelf(ownerFrame->document()->securityOrigin());
+        }
+        document->contentSecurityPolicy()->setInsecureNavigationRequestsToUpgrade(existingDocument->contentSecurityPolicy()->takeNavigationRequestsToUpgrade());
     }
 
-    m_frame->loader().didBeginDocument(dispatch, previousContentSecurityPolicy);
+    m_frame->loader().didBeginDocument(dispatch);
 
     document->implicitOpen();
 
index 0402962..57e6a32 100644 (file)
@@ -719,7 +719,7 @@ void FrameLoader::setOutgoingReferrer(const URL& url)
     m_outgoingReferrer = url.strippedForUseAsReferrer();
 }
 
-void FrameLoader::didBeginDocument(bool dispatch, ContentSecurityPolicy* previousPolicy)
+void FrameLoader::didBeginDocument(bool dispatch)
 {
     m_needsClear = true;
     m_isComplete = false;
@@ -735,7 +735,7 @@ void FrameLoader::didBeginDocument(bool dispatch, ContentSecurityPolicy* previou
         dispatchDidClearWindowObjectsInAllWorlds();
 
     updateFirstPartyForCookies();
-    m_frame.document()->initContentSecurityPolicy(previousPolicy);
+    m_frame.document()->initContentSecurityPolicy();
 
     const Settings& settings = m_frame.settings();
     m_frame.document()->cachedResourceLoader().setImagesEnabled(settings.areImagesEnabled());
index 682d36f..2602545 100644 (file)
@@ -231,7 +231,7 @@ public:
     void didExplicitOpen();
 
     // Callbacks from DocumentWriter
-    void didBeginDocument(bool dispatchWindowObjectAvailable, ContentSecurityPolicy* previousPolicy);
+    void didBeginDocument(bool dispatchWindowObjectAvailable);
 
     void receivedFirstData();
 
index 3370a41..6b642f1 100644 (file)
@@ -172,10 +172,10 @@ public:
     void setInsecureNavigationRequestsToUpgrade(HashSet<SecurityOriginData>&&);
 
     void setClient(ContentSecurityPolicyClient* client) { m_client = client; }
+    void updateSourceSelf(const SecurityOrigin&);
 
 private:
     void logToConsole(const String& message, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), const WTF::OrdinalNumber& contextColumn = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const;
-    void updateSourceSelf(const SecurityOrigin&);
     void applyPolicyToScriptExecutionContext();
 
     // Implements the deprecated CSP2 "strip uri for reporting" algorithm from <https://www.w3.org/TR/CSP2/#violation-reports>.