Implement the plugin-types Content Security Policy directive.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 Aug 2012 09:16:51 +0000 (09:16 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 Aug 2012 09:16:51 +0000 (09:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=91919

Patch by Mike West <mkwst@chromium.org> on 2012-08-14
Reviewed by Adam Barth.

Source/WebCore:

The CSP 1.1 editor's draft defines the 'plugin-types' directive as a
mechanism for whitelisting only specific types of plugin content on a
page. A protected resource might trust only Flash content, for instance,
and could enforce that preference via a Content Security Policy of
'plugin-types application/x-shockwave-flash'. Flash would load, no other
plugin type would.

Specification details available at: https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#plugin-types--experimental

This experimental directive is gated on the ENABLE_CSP_NEXT flag, which
is currently only enabled in Chromium.

Tests: http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01.html
       http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02.html

* loader/SubframeLoader.cpp:
(WebCore::SubframeLoader::pluginIsLoadable):
    Adding a check against 'allowPluginType', and passing in both the
    MIME type of the plugin, as well as the declared MIME type from the
    object/embed element (ensuring that we do this correctly, even if
    we're inside a PluginDocument).
(WebCore::SubframeLoader::createJavaAppletWidget):
    Same as 'pluginIsLoadable', but hard-coded to
    'application/x-java-applet'.
* page/ContentSecurityPolicy.cpp:
(CSPDirectiveList):
(WebCore::CSPDirectiveList::logInvalidPluginTypes):
    Plugin types that don't match the grammar ('not/a/mime/type') are
    logged to the console, and ignored for purposes of matching.
(WebCore):
(WebCore::CSPDirectiveList::checkPluginType):
    Given both the plugin type and the declared type attribute, returns
    true if both types match, and are contained in the list of accepted
    plugin types.
(WebCore::CSPDirectiveList::checkPluginTypeAndReportViolation):
    Calls out to checkPluginType, and reports a violation if that check
    fails.
(WebCore::CSPDirectiveList::allowPluginType):
    Analog to the other 'CSPDirectiveList::allowXXX' methods, this
    branches between simply checking the type against the policy, and
    checking against the policy and then reporting violations.
(WebCore::CSPDirectiveList::parsePluginTypes):
    Given a directive value, parse out the media types contained within
    by splitting on spaces, and validating each token. Valid tokens are
    added to 'm_pluginTypes' for use in 'checkPluginType'.
(WebCore::CSPDirectiveList::addDirective):
    Wire up 'plugin-types' as a valid directive (if the ENABLE_CSP_NEXT
    flag is set). This has been combined with the other implemented 1.1
    header, 'script-nonce'.
(WebCore::ContentSecurityPolicy::allowPluginType):
    The public interface to this set of functionality.
* page/ContentSecurityPolicy.h:

LayoutTests:

* http/tests/plugins/resources/mock-plugin-unknown-type.pl:
    Adding a mock plugin resource that is served with a type that WebKit
    doesn't understand. Using it to test a confusion attack in
    plugintypes-url-02.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02.html: Added.
* http/tests/security/contentSecurityPolicy/object-src-none-allowed.html:
* http/tests/security/contentSecurityPolicy/object-src-none-blocked.html:
    Renaming the `q` parameter to `plugin` in these two tests.
* http/tests/security/contentSecurityPolicy/resources/echo-object-data.pl:
    Add output of explicit MIME types to the object data renderer, and
    changed the `q` parameter to be slightly less confusingly named.
    It's now `plugin`.
* http/tests/security/contentSecurityPolicy/resources/multiple-iframe-plugin-test.js: Added.
    Copy `multiple-iframe-test.js`, and add in plugin-specific details,
    like `plugin`, `log`, and `type`.
(test):
(finishTesting):

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

28 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/plugins/resources/mock-plugin-unknown-type.pl [new file with mode: 0755]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/object-src-none-allowed.html
LayoutTests/http/tests/security/contentSecurityPolicy/object-src-none-blocked.html
LayoutTests/http/tests/security/contentSecurityPolicy/resources/echo-object-data.pl
LayoutTests/http/tests/security/contentSecurityPolicy/resources/multiple-iframe-plugin-test.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/loader/SubframeLoader.cpp
Source/WebCore/page/ContentSecurityPolicy.cpp
Source/WebCore/page/ContentSecurityPolicy.h

index 3daed28..c3edeee 100644 (file)
@@ -1,3 +1,45 @@
+2012-08-14  Mike West  <mkwst@chromium.org>
+
+        Implement the plugin-types Content Security Policy directive.
+        https://bugs.webkit.org/show_bug.cgi?id=91919
+
+        Reviewed by Adam Barth.
+
+        * http/tests/plugins/resources/mock-plugin-unknown-type.pl:
+            Adding a mock plugin resource that is served with a type that WebKit
+            doesn't understand. Using it to test a confusion attack in
+            plugintypes-url-02.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02.html: Added.
+        * http/tests/security/contentSecurityPolicy/object-src-none-allowed.html:
+        * http/tests/security/contentSecurityPolicy/object-src-none-blocked.html:
+            Renaming the `q` parameter to `plugin` in these two tests.
+        * http/tests/security/contentSecurityPolicy/resources/echo-object-data.pl:
+            Add output of explicit MIME types to the object data renderer, and
+            changed the `q` parameter to be slightly less confusingly named.
+            It's now `plugin`.
+        * http/tests/security/contentSecurityPolicy/resources/multiple-iframe-plugin-test.js: Added.
+            Copy `multiple-iframe-test.js`, and add in plugin-specific details,
+            like `plugin`, `log`, and `type`.
+        (test):
+        (finishTesting):
+
 2012-08-14  Hans Wennborg  <hans@chromium.org>
 
         Speech Input: wrong position was reported for scolled-down elements
diff --git a/LayoutTests/http/tests/plugins/resources/mock-plugin-unknown-type.pl b/LayoutTests/http/tests/plugins/resources/mock-plugin-unknown-type.pl
new file mode 100755 (executable)
index 0000000..cd90b85
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/perl -wT
+use strict;
+
+print "Content-Type: application/x-unknown-type\n\n";
+print "This is a mock plugin of a type that WebKit doesn't natively understand.";
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid-expected.txt
new file mode 100644 (file)
index 0000000..049ded9
--- /dev/null
@@ -0,0 +1,75 @@
+CONSOLE MESSAGE: 'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.
+
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types '.
+
+CONSOLE MESSAGE: 'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.
+
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types '.
+
+CONSOLE MESSAGE: Invalid plugin type in 'plugin-types' Content Security Policy directive: 'text'.
+
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types text'.
+
+CONSOLE MESSAGE: Invalid plugin type in 'plugin-types' Content Security Policy directive: 'text/'.
+
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types text/'.
+
+CONSOLE MESSAGE: Invalid plugin type in 'plugin-types' Content Security Policy directive: '/text'.
+
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types /text'.
+
+CONSOLE MESSAGE: Invalid plugin type in 'plugin-types' Content Security Policy directive: 'text//plain'.
+
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types text//plain'.
+
+CONSOLE MESSAGE: Invalid plugin type in 'plugin-types' Content Security Policy directive: 'text/plainapplication/nospace'.
+
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types text/plainapplication/nospace'.
+
+CONSOLE MESSAGE: Invalid plugin type in 'plugin-types' Content Security Policy directive: 'text'.
+
+This tests our handling of invalid `plugin-types` CSP directives. Consider this test passing if each of the following frames contains either "PASS" or no text at all.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame2-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame3-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame4-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame5-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame6-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame7-->-->'
+--------
+PASS.
+
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html
new file mode 100644 (file)
index 0000000..c9a397f
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../resources/multiple-iframe-plugin-test.js'></script>
+<script>
+var tests = [
+    [false, 'plugin-types;'],
+    [false, 'plugin-types       ;'],
+    [false, 'plugin-types text'],
+    [false, 'plugin-types text/'],
+    [false, 'plugin-types /text'],
+    [false, 'plugin-types text//plain'],
+    [false, 'plugin-types text/plainapplication/nospace'],
+    [true, 'plugin-types text application/x-webkit-test-netscape'],
+];
+</script>
+</head>
+<body onload="test()">
+    <p>
+        This tests our handling of invalid `plugin-types` CSP directives.
+        Consider this test passing if each of the following frames contains
+        either "PASS" or no text at all.
+    </p>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data-expected.txt
new file mode 100644 (file)
index 0000000..f3ce984
--- /dev/null
@@ -0,0 +1 @@
+This tests that plugin content that doesn't match the declared type doesn't load, even if the document's CSP would allow it. This test passes if "FAIL!" isn't logged.  
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html
new file mode 100644 (file)
index 0000000..4fea7d0
--- /dev/null
@@ -0,0 +1,20 @@
+<!DOCTYPE html>                                                                                                                                                                                
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+}
+</script>
+<meta http-equiv="X-WebKit-CSP" content="plugin-types application/x-invalid-type">
+</head>
+<body>
+    This tests that plugin content that doesn't match the declared type doesn't
+    load, even if the document's CSP would allow it. This test passes if "FAIL!"
+    isn't logged.
+    <object type="application/x-invalid-type"
+            data="data:application/x-webkit-test-netscape,logifloaded"
+            log="FAIL!"></object>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url-expected.txt
new file mode 100644 (file)
index 0000000..f7c1809
--- /dev/null
@@ -0,0 +1 @@
+This tests that plugin content that doesn't match the declared type doesn't load, even if the document's CSP would allow it. This test passes if no iframe is dumped (meaning that no PluginDocument was created). 
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url.html
new file mode 100644 (file)
index 0000000..4ea6f52
--- /dev/null
@@ -0,0 +1,21 @@
+<!DOCTYPE html>                                                                                                                                                                                
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+}
+</script>
+<script src="/plugins/resources/mock-plugin-logger.js"></script>
+<meta http-equiv="X-WebKit-CSP" content="plugin-types application/x-invalid-type">
+</head>
+<body>
+    This tests that plugin content that doesn't match the declared type doesn't
+    load, even if the document's CSP would allow it. This test passes if no
+    iframe is dumped (meaning that no PluginDocument was created).
+    <object type="application/x-invalid-type"
+            data="/plugins/resources/mock-plugin.pl"
+            log="FAIL!"></object>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data-expected.txt
new file mode 100644 (file)
index 0000000..d79bc42
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type '') because it violates the following Content Security Policy Directive: 'plugin-types application/x-invalid-type'.
+
+Given a `plugin-types` directive, plugins have to declare a type explicitly. No declared type, no load. This test passes if there's a console message above and "FAIL!" isn't logged.  
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data.html
new file mode 100644 (file)
index 0000000..1b41b01
--- /dev/null
@@ -0,0 +1,21 @@
+<!DOCTYPE html>                                                                                                                                                                                
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+}
+</script>
+<script src="/plugins/resources/mock-plugin-logger.js"></script>
+<meta http-equiv="X-WebKit-CSP" content="plugin-types application/x-invalid-type">
+</head>
+<body>
+    Given a `plugin-types` directive, plugins have to declare a type explicitly.
+    No declared type, no load. This test passes if there's a console message
+    above and "FAIL!" isn't logged.
+    <object data="data:application/x-webkit-test-netscape,logifloaded"
+            log="FAIL!"></object>
+</body>
+</html>
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url-expected.txt
new file mode 100644 (file)
index 0000000..93338a9
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Refused to load 'http://127.0.0.1:8000/plugins/resources/mock-plugin.pl' (MIME type '') because it violates the following Content Security Policy Directive: 'plugin-types application/x-invalid-type'.
+
+Given a `plugin-types` directive, plugins have to declare a type explicitly. No declared type, no load. This test passes if there's a console message above.  
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url.html
new file mode 100644 (file)
index 0000000..e36bda9
--- /dev/null
@@ -0,0 +1,20 @@
+<!DOCTYPE html>                                                                                                                                                                                
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+}
+</script>
+<meta http-equiv="X-WebKit-CSP" content="plugin-types application/x-invalid-type">
+</head>
+<body>
+    Given a `plugin-types` directive, plugins have to declare a type explicitly.
+    No declared type, no load. This test passes if there's a console message
+    above.
+    <object data="/plugins/resources/mock-plugin.pl"
+            log="FAIL!"></object>
+</body>
+</html>
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed-expected.txt
new file mode 100644 (file)
index 0000000..6d4b948
--- /dev/null
@@ -0,0 +1 @@
+This test passes if there isn't a console message saying the plugin was blocked. 
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed.html
new file mode 100644 (file)
index 0000000..17d6a0a
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+</script>
+<meta http-equiv="X-WebKit-CSP" content="plugin-types application/x-webkit-test-netscape">
+</head>
+<body>
+This test passes if there isn't a console message saying the plugin was blocked.
+<object type="application/x-webkit-test-netscape"></object>
+</body>
+</html>
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked-expected.txt
new file mode 100644 (file)
index 0000000..3d7b6e4
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: Refused to load '' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types text/plain'.
+
+This test passes if there is a console message saying the plugin was blocked. 
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked.html
new file mode 100644 (file)
index 0000000..18db2d7
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+</script>
+<meta http-equiv="X-WebKit-CSP" content="plugin-types text/plain">
+</head>
+<body>
+This test passes if there is a console message saying the plugin was blocked.
+<object type="application/x-webkit-test-netscape"></object>
+</body>
+</html>
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01-expected.txt
new file mode 100644 (file)
index 0000000..fb68d60
--- /dev/null
@@ -0,0 +1,29 @@
+This tests our handling of `data:` URLs, given a `plugin-types` CSP directive. Consider this test passing if each of the following frames contains "PASS" or no text at all, and no console warnings appear above.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+PASS.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+PASS.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame2-->-->'
+--------
+PASS.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame3-->-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01.html
new file mode 100644 (file)
index 0000000..7fc5aa7
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../resources/multiple-iframe-plugin-test.js'></script>
+<script>
+var tests = [
+    [true, 'plugin-types application/x-webkit-test-netscape'],
+    [true, 'plugin-types text/plain application/x-webkit-test-netscape'],
+    [true, 'plugin-types application/x-webkit-test-netscape text/plain'],
+    [true, 'plugin-types application/x-webkit-test-netscape', '/plugins/resources/mock-plugin.pl', 'application/x-webkit-test-netscape'],
+];
+</script>
+</head>
+<body onload="test()">
+    <p>
+        This tests our handling of `data:` URLs, given a `plugin-types` CSP
+        directive. Consider this test passing if each of the following frames
+        contains "PASS" or no text at all, and no console warnings appear above.
+    </p>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02-expected.txt
new file mode 100644 (file)
index 0000000..2184c18
--- /dev/null
@@ -0,0 +1,24 @@
+CONSOLE MESSAGE: Refused to load 'data:application/x-webkit-test-netscape,logifloaded' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types text/plain'.
+
+CONSOLE MESSAGE: Refused to load 'http://127.0.0.1:8000/plugins/resources/mock-plugin.pl?url-doesnt-match-csp' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types application/x-shockwave-flash'.
+
+CONSOLE MESSAGE: Refused to load 'http://127.0.0.1:8000/plugins/resources/mock-plugin-unknown-type.pl?type-attribute-doesnt-match-csp' (MIME type 'application/x-webkit-test-netscape') because it violates the following Content Security Policy Directive: 'plugin-types application/x-unknown-type'.
+
+This tests our handling of non-`data:` URLs, given a `plugin-types` CSP directive. Consider this test passing if none of the following frames contains "FAIL" and four sets of console logs appear above.
+
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame2-->-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02.html
new file mode 100644 (file)
index 0000000..4d80d9d
--- /dev/null
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../resources/multiple-iframe-plugin-test.js'></script>
+<script>
+var tests = [
+    [false, 'plugin-types text/plain'],
+    [false, 'plugin-types application/x-shockwave-flash', '/plugins/resources/mock-plugin.pl?url-doesnt-match-csp', 'application/x-webkit-test-netscape'],
+    [false, 'plugin-types application/x-unknown-type', '/plugins/resources/mock-plugin-unknown-type.pl?type-attribute-doesnt-match-csp', 'application/x-webkit-test-netscape'],
+];
+</script>
+</head>
+<body onload="test()">
+    <p>
+        This tests our handling of non-`data:` URLs, given a `plugin-types` CSP
+        directive. Consider this test passing if none of the following frames
+        contains "FAIL" and four sets of console logs appear above.
+    </p>
index c2cdae1..7afb449 100644 (file)
@@ -9,6 +9,6 @@ if (window.testRunner) {
 </script>
 </head>
 <body>
-  <iframe src="http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-object-data.pl?q=data:application/x-webkit-test-netscape,logifloaded&log=PASS!&csp=img-src%20'none'"></iframe>
+  <iframe src="http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-object-data.pl?plugin=data:application/x-webkit-test-netscape,logifloaded&log=PASS!&csp=img-src%20'none'"></iframe>
 </body>
 </html>
index 520245a..b49f3c2 100644 (file)
@@ -9,6 +9,6 @@ if (window.testRunner) {
 </script>
 </head>
 <body>
-  <iframe src="http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-object-data.pl?q=data:application/x-webkit-test-netscape,logifloaded&log=FAIL&csp=object-src%20'none'"></iframe>
+  <iframe src="http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-object-data.pl?plugin=data:application/x-webkit-test-netscape,logifloaded&log=FAIL&csp=object-src%20'none'"></iframe>
 </body>
 </html>
index 19d2332..ada7cc1 100755 (executable)
@@ -11,6 +11,9 @@ print "<!DOCTYPE html>\n";
 print "<html>\n";
 print "<body>\n";
 print "<script src=\"/plugins/resources/mock-plugin-logger.js\"></script>\n";
-print "<object data=\"".$cgi->param('q')."\" log=\"".$cgi->param('log')."\"></object>\n";
+print "<object data=\"".$cgi->param('plugin')."\"\n";
+print "        log=\"".$cgi->param('log')."\"\n" if $cgi->param('log');
+print "        type=\"".$cgi->param('type')."\"\n" if $cgi->param('type');
+print "></object>\n";
 print "</body>\n";
 print "</html>\n";
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/resources/multiple-iframe-plugin-test.js b/LayoutTests/http/tests/security/contentSecurityPolicy/resources/multiple-iframe-plugin-test.js
new file mode 100644 (file)
index 0000000..5fe6bb9
--- /dev/null
@@ -0,0 +1,41 @@
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+}
+
+function test() {
+    if (tests.length === 0)
+        return finishTesting();
+    var baseURL = "http://127.0.0.1:8000/security/contentSecurityPolicy/";
+    var current = tests.shift();
+    var iframe = document.createElement("iframe");
+    iframe.src = baseURL + "resources/echo-object-data.pl?" +
+                 "&csp=" + escape(current[1]);
+
+    if (current[0])
+        iframe.src += "&log=PASS.";
+    else
+        iframe.src += "&log=FAIL.";
+
+    if (current[2])
+        iframe.src += "&plugin=" + escape(current[2]);
+    else {
+        iframe.src += "&plugin=data:application/x-webkit-test-netscape,logifloaded";
+    }
+
+    if (current[3] !== undefined)
+        iframe.src += "&type=" + escape(current[3]);
+    else
+        iframe.src += "&type=application/x-webkit-test-netscape";
+
+    iframe.onload = test;
+    document.body.appendChild(iframe);
+}
+
+function finishTesting() {
+    if (window.testRunner) {
+        setTimeout("testRunner.notifyDone()", 0);
+    }
+    return true;
+}
index 01614ba..d37bdbc 100644 (file)
@@ -1,3 +1,70 @@
+2012-08-14  Mike West  <mkwst@chromium.org>
+
+        Implement the plugin-types Content Security Policy directive.
+        https://bugs.webkit.org/show_bug.cgi?id=91919
+
+        Reviewed by Adam Barth.
+
+        The CSP 1.1 editor's draft defines the 'plugin-types' directive as a
+        mechanism for whitelisting only specific types of plugin content on a
+        page. A protected resource might trust only Flash content, for instance,
+        and could enforce that preference via a Content Security Policy of
+        'plugin-types application/x-shockwave-flash'. Flash would load, no other
+        plugin type would.
+
+        Specification details available at: https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#plugin-types--experimental
+
+        This experimental directive is gated on the ENABLE_CSP_NEXT flag, which
+        is currently only enabled in Chromium.
+
+        Tests: http/tests/security/contentSecurityPolicy/1.1/plugintypes-invalid.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-data.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-mismatched-url.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-data.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-notype-url.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-allowed.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-nourl-blocked.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-01.html
+               http/tests/security/contentSecurityPolicy/1.1/plugintypes-url-02.html
+
+        * loader/SubframeLoader.cpp:
+        (WebCore::SubframeLoader::pluginIsLoadable):
+            Adding a check against 'allowPluginType', and passing in both the
+            MIME type of the plugin, as well as the declared MIME type from the
+            object/embed element (ensuring that we do this correctly, even if
+            we're inside a PluginDocument).
+        (WebCore::SubframeLoader::createJavaAppletWidget):
+            Same as 'pluginIsLoadable', but hard-coded to
+            'application/x-java-applet'.
+        * page/ContentSecurityPolicy.cpp:
+        (CSPDirectiveList):
+        (WebCore::CSPDirectiveList::logInvalidPluginTypes):
+            Plugin types that don't match the grammar ('not/a/mime/type') are
+            logged to the console, and ignored for purposes of matching.
+        (WebCore):
+        (WebCore::CSPDirectiveList::checkPluginType):
+            Given both the plugin type and the declared type attribute, returns
+            true if both types match, and are contained in the list of accepted
+            plugin types.
+        (WebCore::CSPDirectiveList::checkPluginTypeAndReportViolation):
+            Calls out to checkPluginType, and reports a violation if that check
+            fails.
+        (WebCore::CSPDirectiveList::allowPluginType):
+            Analog to the other 'CSPDirectiveList::allowXXX' methods, this
+            branches between simply checking the type against the policy, and
+            checking against the policy and then reporting violations.
+        (WebCore::CSPDirectiveList::parsePluginTypes):
+            Given a directive value, parse out the media types contained within
+            by splitting on spaces, and validating each token. Valid tokens are
+            added to 'm_pluginTypes' for use in 'checkPluginType'.
+        (WebCore::CSPDirectiveList::addDirective):
+            Wire up 'plugin-types' as a valid directive (if the ENABLE_CSP_NEXT
+            flag is set). This has been combined with the other implemented 1.1
+            header, 'script-nonce'.
+        (WebCore::ContentSecurityPolicy::allowPluginType):
+            The public interface to this set of functionality.
+        * page/ContentSecurityPolicy.h:
+
 2012-08-14  Charles Wei  <charles.wei@torchmobile.com.cn>
 
         [BlackBerry] Enable DNS prefetch
index 410bfe3..9fdcb76 100644 (file)
@@ -126,7 +126,11 @@ bool SubframeLoader::pluginIsLoadable(HTMLPlugInImageElement* pluginElement, con
             return false;
         }
 
-        if (!document()->contentSecurityPolicy()->allowObjectFromSource(url)) {
+        String declaredMimeType = document()->isPluginDocument() ?
+            document()->ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
+            pluginElement->fastGetAttribute(HTMLNames::typeAttr);
+        if (!document()->contentSecurityPolicy()->allowObjectFromSource(url)
+            || !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
             RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject();
             renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
             return false;
@@ -293,7 +297,9 @@ PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, H
             return 0;
         }
 
-        if (!element->document()->contentSecurityPolicy()->allowObjectFromSource(codeBaseURL))
+        const char javaAppletMimeType[] = "application/x-java-applet";
+        if (!element->document()->contentSecurityPolicy()->allowObjectFromSource(codeBaseURL)
+            || !element->document()->contentSecurityPolicy()->allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL))
             return 0;
     }
 
index 55faf87..335ca30 100644 (file)
@@ -40,6 +40,7 @@
 #include "ScriptCallStack.h"
 #include "SecurityOrigin.h"
 #include "TextEncoding.h"
+#include <wtf/HashSet.h>
 #include <wtf/text/TextPosition.h>
 #include <wtf/text/WTFString.h>
 
@@ -85,6 +86,11 @@ bool isNotColonOrSlash(UChar c)
     return c != ':' && c != '/';
 }
 
+bool isMediaTypeCharacter(UChar c)
+{
+    return !isASCIISpace(c) && c != '/';
+}
+
 } // namespace
 
 static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter)
@@ -561,6 +567,7 @@ public:
     bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
     bool allowEval(PassRefPtr<ScriptCallStack>, ContentSecurityPolicy::ReportingStatus) const;
     bool allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL&) const;
+    bool allowPluginType(const String& type, const String& typeAttribute, const KURL&, ContentSecurityPolicy::ReportingStatus) const;
 
     bool allowScriptFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
     bool allowObjectFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
@@ -581,6 +588,7 @@ private:
     bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value);
     void parseReportURI(const String& name, const String& value);
     void parseScriptNonce(const String& name, const String& value);
+    void parsePluginTypes(const String& name, const String& value);
     void addDirective(const String& name, const String& value);
     void applySandboxPolicy(const String& name, const String& sandboxPolicy);
 
@@ -593,11 +601,13 @@ private:
     bool checkInline(CSPDirective*) const;
     bool checkNonce(const String&) const;
     bool checkSource(CSPDirective*, const KURL&) const;
+    bool checkPluginType(const String& type, const String& typeAttribute) const;
 
     bool checkEvalAndReportViolation(CSPDirective*, const String& consoleMessage, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), PassRefPtr<ScriptCallStack> = 0) const;
     bool checkInlineAndReportViolation(CSPDirective*, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const;
     bool checkNonceAndReportViolation(const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const;
     bool checkSourceAndReportViolation(CSPDirective*, const KURL&, const String& type) const;
+    bool checkPluginTypeAndReportViolation(const String& type, const String& typeAttribute, const String& consoleMessage) const;
 
     bool denyIfEnforcingPolicy() const { return m_reportOnly; }
 
@@ -618,6 +628,8 @@ private:
     OwnPtr<CSPDirective> m_connectSrc;
 
     Vector<KURL> m_reportURIs;
+    HashSet<String> m_pluginTypes;
+    String m_pluginTypesDirective;
     String m_scriptNonce;
 };
 
@@ -674,6 +686,15 @@ bool CSPDirectiveList::checkSource(CSPDirective* directive, const KURL& url) con
     return !directive || directive->allows(url);
 }
 
+bool CSPDirectiveList::checkPluginType(const String& type, const String& typeAttribute) const
+{
+    if (m_pluginTypesDirective.isNull())
+        return true;
+    if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type)
+        return false;
+    return m_pluginTypes.contains(type);
+}
+
 CSPDirective* CSPDirectiveList::operativeDirective(CSPDirective* directive) const
 {
     return directive ? directive : m_defaultSrc.get();
@@ -695,6 +716,15 @@ bool CSPDirectiveList::checkNonceAndReportViolation(const String& nonce, const S
     return denyIfEnforcingPolicy();
 }
 
+bool CSPDirectiveList::checkPluginTypeAndReportViolation(const String& type, const String& typeAttribute, const String& consoleMessage) const
+{
+    if (checkPluginType(type, typeAttribute))
+        return true;
+
+    reportViolation(m_pluginTypesDirective, consoleMessage + "'plugin-types " + m_pluginTypesDirective + "'.\n", KURL());
+    return denyIfEnforcingPolicy();
+}
+
 bool CSPDirectiveList::checkInlineAndReportViolation(CSPDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const
 {
     if (checkInline(directive))
@@ -768,6 +798,13 @@ bool CSPDirectiveList::allowScriptNonce(const String& nonce, const String& conte
     return checkNonceAndReportViolation(nonce, "Refused to load '" + url.string() + "' because it violates the following Content Security Policy directive: ", contextURL, contextLine);
 }
 
+bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
+{
+    return reportingStatus == ContentSecurityPolicy::SendReport ?
+        checkPluginTypeAndReportViolation(type, typeAttribute, "Refused to load '" + url.string() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") :
+        checkPluginType(type, typeAttribute);
+}
+
 bool CSPDirectiveList::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
 {
     DEFINE_STATIC_LOCAL(String, type, ("script"));
@@ -975,6 +1012,72 @@ void CSPDirectiveList::parseScriptNonce(const String& name, const String& value)
         m_scriptNonce = nonce;
 }
 
+void CSPDirectiveList::parsePluginTypes(const String& name, const String& value)
+{
+    if (!m_pluginTypesDirective.isNull()) {
+        m_policy->reportDuplicateDirective(name);
+        return;
+    }
+
+    const UChar* begin = value.characters();
+    const UChar* position = begin;
+    const UChar* end = begin + value.length();
+    m_pluginTypesDirective = value;
+
+    // 'plugin-types ____;' OR 'plugin-types;'
+    if (value.isEmpty()) {
+        m_policy->reportInvalidPluginTypes(value);
+        m_pluginTypesDirective = "";
+        return;
+    }
+
+    while (position < end) {
+        // _____ OR _____mime1/mime1
+        // ^        ^
+        skipWhile<isASCIISpace>(position, end);
+        if (position == end)
+            return;
+
+        // mime1/mime1 mime2/mime2
+        // ^
+        begin = position;
+        if (!skipExactly<isMediaTypeCharacter>(position, end)) {
+            skipWhile<isNotASCIISpace>(position, end);
+            m_policy->reportInvalidPluginTypes(String(begin, position - begin));
+            continue;
+        }
+        skipWhile<isMediaTypeCharacter>(position, end);
+
+        // mime1/mime1 mime2/mime2
+        //      ^
+        if (!skipExactly(position, end, '/')) {
+            skipWhile<isNotASCIISpace>(position, end);
+            m_policy->reportInvalidPluginTypes(String(begin, position - begin));
+            continue;
+        }
+
+        // mime1/mime1 mime2/mime2
+        //       ^
+        if (!skipExactly<isMediaTypeCharacter>(position, end)) {
+            skipWhile<isNotASCIISpace>(position, end);
+            m_policy->reportInvalidPluginTypes(String(begin, position - begin));
+            continue;
+        }
+        skipWhile<isMediaTypeCharacter>(position, end);
+
+        // mime1/mime1 mime2/mime2 OR mime1/mime1  OR mime1/mime1/error
+        //            ^                          ^               ^
+        if (position < end && isNotASCIISpace(*position)) {
+            skipWhile<isNotASCIISpace>(position, end);
+            m_policy->reportInvalidPluginTypes(String(begin, position - begin));
+            continue;
+        }
+        m_pluginTypes.add(String(begin, position - begin));
+
+        ASSERT(position == end || isASCIISpace(*position));
+    }
+}
+
 void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirective>& directive)
 {
     if (directive) {
@@ -998,9 +1101,6 @@ void CSPDirectiveList::addDirective(const String& name, const String& value)
 {
     DEFINE_STATIC_LOCAL(String, defaultSrc, ("default-src"));
     DEFINE_STATIC_LOCAL(String, scriptSrc, ("script-src"));
-#if ENABLE(CSP_NEXT)
-    DEFINE_STATIC_LOCAL(String, scriptNonce, ("script-nonce"));
-#endif
     DEFINE_STATIC_LOCAL(String, objectSrc, ("object-src"));
     DEFINE_STATIC_LOCAL(String, frameSrc, ("frame-src"));
     DEFINE_STATIC_LOCAL(String, imgSrc, ("img-src"));
@@ -1010,6 +1110,10 @@ void CSPDirectiveList::addDirective(const String& name, const String& value)
     DEFINE_STATIC_LOCAL(String, connectSrc, ("connect-src"));
     DEFINE_STATIC_LOCAL(String, sandbox, ("sandbox"));
     DEFINE_STATIC_LOCAL(String, reportURI, ("report-uri"));
+#if ENABLE(CSP_NEXT)
+    DEFINE_STATIC_LOCAL(String, scriptNonce, ("script-nonce"));
+    DEFINE_STATIC_LOCAL(String, pluginTypes, ("plugin-types"));
+#endif
 
     ASSERT(!name.isEmpty());
 
@@ -1038,6 +1142,8 @@ void CSPDirectiveList::addDirective(const String& name, const String& value)
 #if ENABLE(CSP_NEXT)
     else if (equalIgnoringCase(name, scriptNonce))
         parseScriptNonce(name, value);
+    else if (equalIgnoringCase(name, pluginTypes))
+        parsePluginTypes(name, value);
 #endif
     else
         m_policy->reportUnrecognizedDirective(name);
@@ -1175,6 +1281,15 @@ bool ContentSecurityPolicy::allowScriptNonce(const String& nonce, const String&
     return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce, contextURL, contextLine, url);
 }
 
+bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
+{
+    for (size_t i = 0; i < m_policies.size(); ++i) {
+        if (!m_policies[i].get()->allowPluginType(type, typeAttribute, url, reportingStatus))
+            return false;
+    }
+    return true;
+}
+
 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
 {
     return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
@@ -1304,6 +1419,16 @@ void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
     logToConsole(message);
 }
 
+void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
+{
+    String message;
+    if (pluginType.isNull())
+        message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
+    else
+        message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
+    logToConsole(message);
+}
+
 void ContentSecurityPolicy::reportInvalidNonce(const String& nonce) const
 {
     String message = makeString("Ignoring invalid Content Security Policy script nonce: '", nonce, "'.\n");
index 51f3869..72a106e 100644 (file)
@@ -81,6 +81,7 @@ public:
     bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ReportingStatus = SendReport) const;
     bool allowEval(PassRefPtr<ScriptCallStack>, ReportingStatus = SendReport) const;
     bool allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const KURL& = KURL()) const;
+    bool allowPluginType(const String& type, const String& typeAttribute, const KURL&, ReportingStatus = SendReport) const;
 
     bool allowScriptFromSource(const KURL&, ReportingStatus = SendReport) const;
     bool allowObjectFromSource(const KURL&, ReportingStatus = SendReport) const;
@@ -99,6 +100,7 @@ public:
     void reportDuplicateDirective(const String&) const;
     void reportIgnoredPathComponent(const String& directiveName, const String& completeSource, const String& path) const;
     void reportInvalidNonce(const String&) const;
+    void reportInvalidPluginTypes(const String&) const;
     void reportInvalidSourceExpression(const String& directiveName, const String& source) const;
     void reportUnrecognizedDirective(const String&) const;
     void reportViolation(const String& directiveText, const String& consoleMessage, const KURL& blockedURL, const Vector<KURL>& reportURIs, const String& header, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), PassRefPtr<ScriptCallStack> = 0) const;