Implement Subresource Integrity (SRI)
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 7 May 2017 10:24:48 +0000 (10:24 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 7 May 2017 10:24:48 +0000 (10:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148363
LayoutTests/imported/w3c:

Patch by Sam Weinig <sam@webkit.org> on 2017-05-07
Reviewed by Daniel Bates.

* web-platform-tests/html/dom/reflection-metadata-expected.txt:
* web-platform-tests/html/dom/reflection-misc-expected.txt:
Update results now that we support the reflected 'integrity' property.

Source/WebCore:

<rdar://problem/18945879>

Patch by Sam Weinig <sam@webkit.org> on 2017-05-07
Reviewed by Daniel Bates.

Tests: http/tests/subresource-integrity/sri-disabled-with-setting.html
       http/tests/subresource-integrity/sri-enabled-with-setting.html
       http/tests/subresource-integrity/sri-script-cors.html
       http/tests/subresource-integrity/sri-style-cors.html

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
Add new files.

* dom/LoadableClassicScript.cpp:
(WebCore::LoadableClassicScript::create):
(WebCore::LoadableClassicScript::notifyFinished):
* dom/LoadableClassicScript.h:
* dom/LoadableScript.h:
* dom/ScriptElement.cpp:
(WebCore::ScriptElement::requestClassicScript):
Store integrity metadata in the script fetcher so it can be passed to
the checked when script load finishes.

* html/HTMLAttributeNames.in:
Add 'integrity'.

* html/HTMLLinkElement.cpp:
(WebCore::HTMLLinkElement::process):
When requesting a stylesheet, cache the integrity metadata so it can
be used when the load completes (accessing the attribute at load completion
time is incorrect, as a script might have changed the attributes value since
the request was made).

(WebCore::HTMLLinkElement::setCSSStyleSheet):
Add an integrity check using the cached integrity metadata when a load
finishes.

* html/HTMLLinkElement.h:
Add cached integrity metadata member.

* html/HTMLLinkElement.idl:
* html/HTMLScriptElement.idl:
Add integrity property.

* html/parser/HTMLParserIdioms.h:
(WebCore::isNotHTMLSpace):
Templatize isNotHTMLSpace so it can work for both UChar and LChar.

* loader/ResourceCryptographicDigest.cpp:
(WebCore::parseCryptographicDigestImpl):
(WebCore::parseEncodedCryptographicDigestImpl):
(WebCore::parseEncodedCryptographicDigest):
(WebCore::decodeEncodedResourceCryptographicDigest):
* loader/ResourceCryptographicDigest.h:
Add concept of an encoded digest to more closely model the spec so that hashes
that match the grammar but are invalid (say, mixing base64 and base64URL) make
it through the algorithm longer, and don't cause us to load something that should
be blocked.

* loader/SubresourceIntegrity.cpp: Added.
* loader/SubresourceIntegrity.h: Added.
Add implementation of Subresource Integrity metadata validation allowing
for a CachedResource and integrity metadata to be passed for validation.

* page/Settings.in:
Add setting for Subresource Integrity, defaulted to enabled.

LayoutTests:

<rdar://problem/18945879>

Patch by Sam Weinig <sam@webkit.org> on 2017-05-07
Reviewed by Daniel Bates.

Add tests for Subresource Integrity based off the ones from Web
Platform Tests. They have been changed to:
- Split <link> and <script> testing.
- Add additional tests:
    - Integrity hashes using base64URL encoding.
    - Integrity hashes using mixed base64 and base64URL encoding.
    - Integrity metadata that does not conform to the grammar at all.
    - Multiple valid, but only one matching, integrity hashes.
    - Non-matching integrity hash with options.
- Run one at a time, so console output is consistent.

We can/should upstream these changes, but this avoids the possibility that
an update of the imported web-platform-tests could cause these tests to fail.

Also adds tests that show the Subresource Integrity setting works correctly.

* http/tests/subresource-integrity: Added.
* http/tests/subresource-integrity/.htaccess: Added.
* http/tests/subresource-integrity/resources: Added.
* http/tests/subresource-integrity/resources/alternate.css: Added.
* http/tests/subresource-integrity/resources/crossorigin-anon-script.js: Added.
* http/tests/subresource-integrity/resources/crossorigin-anon-style.css: Added.
* http/tests/subresource-integrity/resources/crossorigin-creds-script.js: Added.
* http/tests/subresource-integrity/resources/crossorigin-creds-style.css: Added.
* http/tests/subresource-integrity/resources/crossorigin-ineligible-script.js: Added.
* http/tests/subresource-integrity/resources/crossorigin-ineligible-style.css: Added.
* http/tests/subresource-integrity/resources/matching-digest.js: Added.
* http/tests/subresource-integrity/resources/non-matching-digest.js: Added.
* http/tests/subresource-integrity/resources/sri-utilities.js: Added.
* http/tests/subresource-integrity/resources/style.css: Added.
* http/tests/subresource-integrity/sri-disabled-with-setting-expected.txt: Added.
* http/tests/subresource-integrity/sri-disabled-with-setting.html: Added.
* http/tests/subresource-integrity/sri-enabled-with-setting-expected.txt: Added.
* http/tests/subresource-integrity/sri-enabled-with-setting.html: Added.
* http/tests/subresource-integrity/sri-script-expected.txt: Added.
* http/tests/subresource-integrity/sri-script.html: Added.
* http/tests/subresource-integrity/sri-style-expected.txt: Added.
* http/tests/subresource-integrity/sri-style.html: Added.

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

42 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/subresource-integrity/.htaccess [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/alternate.css [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/crossorigin-anon-script.js [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/crossorigin-anon-style.css [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/crossorigin-creds-script.js [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/crossorigin-creds-style.css [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/crossorigin-ineligible-script.js [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/crossorigin-ineligible-style.css [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/matching-digest.js [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/non-matching-digest.js [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/sri-utilities.js [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/resources/style.css [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-disabled-with-setting-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-disabled-with-setting.html [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-enabled-with-setting-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-enabled-with-setting.html [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-script-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-script.html [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-style-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/subresource-integrity/sri-style.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/html/dom/reflection-metadata-expected.txt
LayoutTests/imported/w3c/web-platform-tests/html/dom/reflection-misc-expected.txt
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/LoadableClassicScript.cpp
Source/WebCore/dom/LoadableClassicScript.h
Source/WebCore/dom/LoadableScript.h
Source/WebCore/dom/ScriptElement.cpp
Source/WebCore/html/HTMLAttributeNames.in
Source/WebCore/html/HTMLLinkElement.cpp
Source/WebCore/html/HTMLLinkElement.h
Source/WebCore/html/HTMLLinkElement.idl
Source/WebCore/html/HTMLScriptElement.idl
Source/WebCore/html/parser/HTMLParserIdioms.h
Source/WebCore/loader/ResourceCryptographicDigest.cpp
Source/WebCore/loader/ResourceCryptographicDigest.h
Source/WebCore/loader/SubresourceIntegrity.cpp [new file with mode: 0644]
Source/WebCore/loader/SubresourceIntegrity.h [new file with mode: 0644]
Source/WebCore/page/Settings.in

index 4b2cfd9..9c1b961 100644 (file)
@@ -1,3 +1,50 @@
+2017-05-07  Sam Weinig  <sam@webkit.org>
+
+        Implement Subresource Integrity (SRI)
+        https://bugs.webkit.org/show_bug.cgi?id=148363
+        <rdar://problem/18945879>
+
+        Reviewed by Daniel Bates.
+
+        Add tests for Subresource Integrity based off the ones from Web 
+        Platform Tests. They have been changed to:
+        - Split <link> and <script> testing.
+        - Add additional tests:
+            - Integrity hashes using base64URL encoding.
+            - Integrity hashes using mixed base64 and base64URL encoding.
+            - Integrity metadata that does not conform to the grammar at all.
+            - Multiple valid, but only one matching, integrity hashes.
+            - Non-matching integrity hash with options.
+        - Run one at a time, so console output is consistent.
+        
+        We can/should upstream these changes, but this avoids the possibility that
+        an update of the imported web-platform-tests could cause these tests to fail.
+        
+        Also adds tests that show the Subresource Integrity setting works correctly.
+
+        * http/tests/subresource-integrity: Added.
+        * http/tests/subresource-integrity/.htaccess: Added.
+        * http/tests/subresource-integrity/resources: Added.
+        * http/tests/subresource-integrity/resources/alternate.css: Added.
+        * http/tests/subresource-integrity/resources/crossorigin-anon-script.js: Added.
+        * http/tests/subresource-integrity/resources/crossorigin-anon-style.css: Added.
+        * http/tests/subresource-integrity/resources/crossorigin-creds-script.js: Added.
+        * http/tests/subresource-integrity/resources/crossorigin-creds-style.css: Added.
+        * http/tests/subresource-integrity/resources/crossorigin-ineligible-script.js: Added.
+        * http/tests/subresource-integrity/resources/crossorigin-ineligible-style.css: Added.
+        * http/tests/subresource-integrity/resources/matching-digest.js: Added.
+        * http/tests/subresource-integrity/resources/non-matching-digest.js: Added.
+        * http/tests/subresource-integrity/resources/sri-utilities.js: Added.
+        * http/tests/subresource-integrity/resources/style.css: Added.
+        * http/tests/subresource-integrity/sri-disabled-with-setting-expected.txt: Added.
+        * http/tests/subresource-integrity/sri-disabled-with-setting.html: Added.
+        * http/tests/subresource-integrity/sri-enabled-with-setting-expected.txt: Added.
+        * http/tests/subresource-integrity/sri-enabled-with-setting.html: Added.
+        * http/tests/subresource-integrity/sri-script-expected.txt: Added.
+        * http/tests/subresource-integrity/sri-script.html: Added.
+        * http/tests/subresource-integrity/sri-style-expected.txt: Added.
+        * http/tests/subresource-integrity/sri-style.html: Added.
+
 2017-05-06  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [Cocoa] CTFontDescriptorCreateMatchingFontDescriptor() is not case insensitive
diff --git a/LayoutTests/http/tests/subresource-integrity/.htaccess b/LayoutTests/http/tests/subresource-integrity/.htaccess
new file mode 100644 (file)
index 0000000..7437649
--- /dev/null
@@ -0,0 +1,8 @@
+<Files "*crossorigin-anon*">
+    Header set "Access-Control-Allow-Origin" "*"
+</Files>
+
+<Files "*crossorigin-creds*">
+    Header set "Access-Control-Allow-Origin" "http://127.0.0.1:8000"
+    Header set "Access-Control-Allow-Credentials" "true"
+</Files>
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/alternate.css b/LayoutTests/http/tests/subresource-integrity/resources/alternate.css
new file mode 100644 (file)
index 0000000..0ea6d22
--- /dev/null
@@ -0,0 +1 @@
+.testdiv{ background-color: red }
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-anon-script.js b/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-anon-script.js
new file mode 100644 (file)
index 0000000..8493585
--- /dev/null
@@ -0,0 +1 @@
+crossorigin_anon_script=true;
\ No newline at end of file
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-anon-style.css b/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-anon-style.css
new file mode 100644 (file)
index 0000000..3cde4df
--- /dev/null
@@ -0,0 +1 @@
+.testdiv{ background-color: yellow }
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-creds-script.js b/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-creds-script.js
new file mode 100644 (file)
index 0000000..6f39e25
--- /dev/null
@@ -0,0 +1 @@
+crossorigin_creds_script=true;
\ No newline at end of file
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-creds-style.css b/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-creds-style.css
new file mode 100644 (file)
index 0000000..3cde4df
--- /dev/null
@@ -0,0 +1 @@
+.testdiv{ background-color: yellow }
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-ineligible-script.js b/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-ineligible-script.js
new file mode 100644 (file)
index 0000000..dd2f968
--- /dev/null
@@ -0,0 +1 @@
+crossorigin_ineligible_script=true;
\ No newline at end of file
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-ineligible-style.css b/LayoutTests/http/tests/subresource-integrity/resources/crossorigin-ineligible-style.css
new file mode 100644 (file)
index 0000000..3cde4df
--- /dev/null
@@ -0,0 +1 @@
+.testdiv{ background-color: yellow }
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/matching-digest.js b/LayoutTests/http/tests/subresource-integrity/resources/matching-digest.js
new file mode 100644 (file)
index 0000000..ec41325
--- /dev/null
@@ -0,0 +1 @@
+matching_digest=true;
\ No newline at end of file
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/non-matching-digest.js b/LayoutTests/http/tests/subresource-integrity/resources/non-matching-digest.js
new file mode 100644 (file)
index 0000000..1b4943e
--- /dev/null
@@ -0,0 +1 @@
+non_matching_digest=true;
\ No newline at end of file
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/sri-utilities.js b/LayoutTests/http/tests/subresource-integrity/resources/sri-utilities.js
new file mode 100644 (file)
index 0000000..88b488a
--- /dev/null
@@ -0,0 +1,109 @@
+var SRITests = [];
+SRITests.execute = function() {
+    if (this.length > 0) {
+        this.shift().execute();
+    }
+}
+
+add_result_callback(function(res) {
+    if (res.name.startsWith("Script: ") || res.name.startsWith("Style: ")) {
+      SRITests.execute();
+    }
+});
+
+
+var SRIScriptTest = function(pass, name, src, integrityValue, crossoriginValue) {
+    this.pass = pass;
+    this.name = "Script: " + name;
+    this.src = src;
+    this.integrityValue = integrityValue;
+    this.crossoriginValue = crossoriginValue;
+
+    this.test = async_test(this.name);
+
+    this.queue = SRITests;
+    this.queue.push(this);
+}
+
+SRIScriptTest.prototype.execute = function() {
+    var test = this.test;
+    var e = document.createElement("script");
+    e.src = this.src;
+    e.setAttribute("integrity", this.integrityValue);
+    if(this.crossoriginValue) {
+        e.setAttribute("crossorigin", this.crossoriginValue);
+    }
+    if(this.pass) {
+        e.addEventListener("load", function() {test.done()});
+        e.addEventListener("error", function() {
+            test.step(function(){ assert_unreached("Good load fired error handler.") })
+        });
+    } else {
+       e.addEventListener("load", function() {
+            test.step(function() { assert_unreached("Bad load succeeded.") })
+        });
+       e.addEventListener("error", function() {test.done()});
+    }
+    document.body.appendChild(e);
+};
+
+
+var SRIStyleTest = function(pass, name, attrs, customCallback, altPassValue) {
+    this.pass = pass;
+    this.name = "Style: " + name;
+    this.attrs = attrs || {};
+    this.customCallback = customCallback || function () {};
+    this.passValue = altPassValue || "rgb(255, 255, 0)";
+
+    this.test = async_test(this.name);
+
+    this.queue = SRITests;
+    this.queue.push(this);
+}
+
+SRIStyleTest.prototype.execute = function() {
+    var that = this;
+    var container = document.getElementById("container");
+    while (container.hasChildNodes()) {
+      container.removeChild(container.firstChild);
+    }
+
+    var test = this.test;
+
+    var div = document.createElement("div");
+    div.className = "testdiv";
+    var e = document.createElement("link");
+    this.attrs.rel = this.attrs.rel || "stylesheet";
+    for (var key in this.attrs) {
+        if (this.attrs.hasOwnProperty(key)) {
+            e.setAttribute(key, this.attrs[key]);
+        }
+    }
+
+    if(this.pass) {
+        e.addEventListener("load", function() {
+            test.step(function() {
+                var background = window.getComputedStyle(div, null).getPropertyValue("background-color");
+                assert_equals(background, that.passValue);
+                test.done();
+            });
+        });
+        e.addEventListener("error", function() {
+            test.step(function(){ assert_unreached("Good load fired error handler.") })
+        });
+    } else {
+        e.addEventListener("load", function() {
+             test.step(function() { assert_unreached("Bad load succeeded.") })
+         });
+        e.addEventListener("error", function() {
+            test.step(function() {
+                var background = window.getComputedStyle(div, null).getPropertyValue("background-color");
+                assert_not_equals(background, that.passValue);
+                test.done();
+            });
+        });
+    }
+    container.appendChild(div);
+    container.appendChild(e);
+    this.customCallback(e, container);
+};
diff --git a/LayoutTests/http/tests/subresource-integrity/resources/style.css b/LayoutTests/http/tests/subresource-integrity/resources/style.css
new file mode 100644 (file)
index 0000000..3cde4df
--- /dev/null
@@ -0,0 +1 @@
+.testdiv{ background-color: yellow }
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-disabled-with-setting-expected.txt b/LayoutTests/http/tests/subresource-integrity/sri-disabled-with-setting-expected.txt
new file mode 100644 (file)
index 0000000..f60c548
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS Test that Subresource Integrity's 'integrity' property is not exposed on the <link> element. 
+PASS Test that Subresource Integrity's 'integrity' property is not exposed on the <script> element. 
+PASS Script: Same-origin with incorrect hash, but should evaluate successfully due to Subresource Integrity being disabled. 
+PASS Style: Same-origin with incorrect hash, but should evaluate successfully due to Subresource Integrity being disabled. 
+
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-disabled-with-setting.html b/LayoutTests/http/tests/subresource-integrity/sri-disabled-with-setting.html
new file mode 100644 (file)
index 0000000..6a967e3
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script>
+        if (window.internals)
+            window.internals.settings.setSubresourceIntegrityEnabled(false);
+    </script>
+    <script src="/js-test-resources/testharness.js"></script>
+    <script src="/js-test-resources/testharnessreport.js"></script>
+    <script src="resources/sri-utilities.js"></script>
+</head>
+<body>
+    <div id="log"></div>
+    <div id="container"></div>
+<script>
+
+test(function() {
+    var linkElement = document.createElement("link");
+    assert_false("integrity" in linkElement, "integrity", "The 'integrity' property is not exposed on the link element.");
+}, "Test that Subresource Integrity's 'integrity' property is not exposed on the <link> element.");
+
+test(function() {
+    var scriptElement = document.createElement("script");
+    assert_false("integrity" in scriptElement, "The 'integrity' property is not exposed on the script element.");
+    
+}, "Test that Subresource Integrity's 'integrity' property is not exposed on the <script> element.");
+
+new SRIScriptTest(
+    true,
+    "Same-origin with incorrect hash, but should evaluate successfully due to Subresource Integrity being disabled.",
+    "resources/non-matching-digest.js",
+    "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with incorrect hash, but should evaluate successfully due to Subresource Integrity being disabled.",
+    {
+        href: "resources/style.css",
+        integrity: "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+    }
+);
+
+SRITests.execute();
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-enabled-with-setting-expected.txt b/LayoutTests/http/tests/subresource-integrity/sri-enabled-with-setting-expected.txt
new file mode 100644 (file)
index 0000000..d327476
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Cannot load script http://127.0.0.1:8000/subresource-integrity/resources/non-matching-digest.js. Failed integrity metadata check.
+CONSOLE MESSAGE: line 107: Cannot load stylesheet http://127.0.0.1:8000/subresource-integrity/resources/style.css. Failed integrity metadata check.
+
+PASS Test that Subresource Integrity's 'integrity' property is exposed on the <link> element. 
+PASS Test that Subresource Integrity's 'integrity' property is exposed on the <script> element. 
+PASS Script: Same-origin with incorrect hash, should block evaluation. 
+PASS Style: Same-origin with incorrect hash, should block evaluation. 
+
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-enabled-with-setting.html b/LayoutTests/http/tests/subresource-integrity/sri-enabled-with-setting.html
new file mode 100644 (file)
index 0000000..19da264
--- /dev/null
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="/js-test-resources/testharness.js"></script>
+    <script src="/js-test-resources/testharnessreport.js"></script>
+    <script src="resources/sri-utilities.js"></script>
+</head>
+<body>
+    <div id="log"></div>
+    <div id="container"></div>
+<script>
+
+// SRI is enabled by default, no need to set the setting explicitly.
+
+test(function() {
+    var linkElement = document.createElement("link");
+    assert_true("integrity" in linkElement, "integrity", "The 'integrity' property is exposed on the link element.");
+}, "Test that Subresource Integrity's 'integrity' property is exposed on the <link> element.");
+
+test(function() {
+    var scriptElement = document.createElement("script");
+    assert_true("integrity" in scriptElement, "The 'integrity' property is exposed on the script element.");
+    
+}, "Test that Subresource Integrity's 'integrity' property is exposed on the <script> element.");
+
+new SRIScriptTest(
+    false,
+    "Same-origin with incorrect hash, should block evaluation.",
+    "resources/non-matching-digest.js",
+    "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+);
+
+new SRIStyleTest(
+    false,
+    "Same-origin with incorrect hash, should block evaluation.",
+    {
+        href: "resources/style.css",
+        integrity: "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+    }
+);
+
+SRITests.execute();
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-script-expected.txt b/LayoutTests/http/tests/subresource-integrity/sri-script-expected.txt
new file mode 100644 (file)
index 0000000..e8f078a
--- /dev/null
@@ -0,0 +1,36 @@
+CONSOLE MESSAGE: Cannot load script http://127.0.0.1:8000/subresource-integrity/resources/non-matching-digest.js. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load script http://127.0.0.1:8000/subresource-integrity/resources/matching-digest.js. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load script http://localhost:8000/subresource-integrity/resources/crossorigin-anon-script.js. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load script http://localhost:8000/subresource-integrity/resources/crossorigin-creds-script.js. Failed integrity metadata check.
+CONSOLE MESSAGE: Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: Cross-origin script load denied by Cross-Origin Resource Sharing policy.
+CONSOLE MESSAGE: Cannot load script http://localhost:8000/subresource-integrity/resources/crossorigin-anon-script.js. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load script http://localhost:8000/subresource-integrity/resources/crossorigin-anon-script.js. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load script http://127.0.0.1:8000/subresource-integrity/resources/matching-digest.js. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load script http://127.0.0.1:8000/subresource-integrity/resources/matching-digest.js. Failed integrity metadata check.
+
+PASS Script: Same-origin with correct sha256 hash. 
+PASS Script: Same-origin with correct sha384 hash. 
+PASS Script: Same-origin with correct sha512 hash. 
+PASS Script: Same-origin with empty integrity. 
+PASS Script: Same-origin with incorrect hash. 
+PASS Script: Same-origin with multiple sha256 hashes, including correct. 
+PASS Script: Same-origin with multiple sha256 hashes, including unknown algorithm. 
+PASS Script: Same-origin with sha256 mismatch, sha512 match 
+PASS Script: Same-origin with sha256 match, sha512 mismatch 
+PASS Script: <crossorigin='anonymous'> with correct hash, ACAO: * 
+PASS Script: <crossorigin='anonymous'> with incorrect hash, ACAO: * 
+PASS Script: <crossorigin='use-credentials'> with correct hash, CORS-eligible 
+PASS Script: <crossorigin='use-credentials'> with incorrect hash CORS-eligible 
+PASS Script: <crossorigin='anonymous'> with CORS-ineligible resource 
+PASS Script: Cross-origin, not CORS request, with correct hash 
+PASS Script: Cross-origin, not CORS request, with hash mismatch 
+PASS Script: Cross-origin, empty integrity 
+PASS Script: Same-origin with correct hash, options. 
+PASS Script: Same-origin with unknown algorithm only. 
+PASS Script: Same-origin with correct sha256 hash, using base64URL encoding. 
+PASS Script: Same-origin with correct sha256 hash, using intermixed base64 and base64URL encoding, should fail. 
+PASS Script: Same-origin with invalid syntax only. 
+PASS Script: Same-origin with multiple valid sha256 hashes, including correct. 
+PASS Script: Same-origin with incorrect hash, options. 
+
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-script.html b/LayoutTests/http/tests/subresource-integrity/sri-script.html
new file mode 100644 (file)
index 0000000..2eaa149
--- /dev/null
@@ -0,0 +1,205 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="/js-test-resources/testharness.js"></script>
+    <script src="/js-test-resources/testharnessreport.js"></script>
+    <script src="resources/sri-utilities.js"></script>
+</head>
+<body>
+    <div id="log"></div>
+    <div id="container"></div>
+<script>
+
+var main_host = '127.0.0.1';
+var remote_host = 'localhost';
+var port_string = "8000";
+var main_host_and_port = main_host + ':' + port_string;
+var remote_host_and_port = remote_host + ':' + port_string;
+
+var crossorigin_anon_script = location.protocol + '//' + remote_host_and_port + '/subresource-integrity/resources/crossorigin-anon-script.js';
+var crossorigin_creds_script = location.protocol + '//' + remote_host_and_port + '/subresource-integrity/resources/crossorigin-creds-script.js';
+var crossorigin_ineligible_script = location.protocol + '//' + remote_host_and_port + '/subresource-integrity/resources/crossorigin-ineligible-script.js';
+
+// Script tests from web-platform-tests/subresource-integrity
+
+new SRIScriptTest(
+    true,
+    "Same-origin with correct sha256 hash.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E="
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with correct sha384 hash.",
+    "resources/matching-digest.js",
+    "sha384-BDRTPSywZFyxfLEAzaLcL4FfERBgJgXfEkuT0r04LG93Yqn1PWNYPZMomaqEfE3H"
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with correct sha512 hash.",
+    "resources/matching-digest.js",
+    "sha512-geByvIIRspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg=="
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with empty integrity.",
+    "resources/matching-digest.js",
+    ""
+);
+
+new SRIScriptTest(
+    false,
+    "Same-origin with incorrect hash.",
+    "resources/non-matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9e="
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with multiple sha256 hashes, including correct.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E= sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with multiple sha256 hashes, including unknown algorithm.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E= foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with sha256 mismatch, sha512 match",
+    "resources/matching-digest.js",
+    "sha512-geByvIIRspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg== sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+);
+
+new SRIScriptTest(
+    false,
+    "Same-origin with sha256 match, sha512 mismatch",
+    "resources/matching-digest.js",
+    "sha512-deadbeefspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg== sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E="
+);
+
+new SRIScriptTest(
+    true,
+    "<crossorigin='anonymous'> with correct hash, ACAO: *",
+    crossorigin_anon_script,
+    "sha256-51AjITq701Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0=",
+    "anonymous"
+);
+
+new SRIScriptTest(
+    false,
+    "<crossorigin='anonymous'> with incorrect hash, ACAO: *",
+    crossorigin_anon_script,
+    "sha256-deadbeefcSLlbFZCj1OACLxTxVck2TOrBTEdUbwz1yU=",
+    "anonymous"
+);
+
+new SRIScriptTest(
+    true,
+    "<crossorigin='use-credentials'> with correct hash, CORS-eligible",
+    crossorigin_creds_script,
+    "sha256-IaGApVboXPQxVSm2wVFmhMq1Yu37gWklajgMdxKLIvc=",
+    "use-credentials"
+);
+
+new SRIScriptTest(
+    false,
+    "<crossorigin='use-credentials'> with incorrect hash CORS-eligible",
+    crossorigin_creds_script,
+    "sha256-deadbeef2S+pTRZgiw3DWrhC6JLDlt2zRyGpwH7unU8=",
+    "use-credentials"
+);
+
+new SRIScriptTest(
+    false,
+    "<crossorigin='anonymous'> with CORS-ineligible resource",
+    crossorigin_ineligible_script,
+    "sha256-F5fXKTX7SiWjtgybxiBZIo2qhh2WiQnNx372E60XrOo=",
+    "anonymous"
+);
+
+new SRIScriptTest(
+    false,
+    "Cross-origin, not CORS request, with correct hash",
+    crossorigin_anon_script,
+    "sha256-51AjITq701Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0="
+);
+
+new SRIScriptTest(
+    false,
+    "Cross-origin, not CORS request, with hash mismatch",
+    crossorigin_anon_script,
+    "sha256-deadbeef01Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0="
+);
+
+new SRIScriptTest(
+    true,
+    "Cross-origin, empty integrity",
+    crossorigin_anon_script,
+    ""
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with correct hash, options.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E=?foo=bar?spam=eggs"
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with unknown algorithm only.",
+    "resources/matching-digest.js",
+    "foo666-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E="
+);
+
+// WebKit additions to the web-platform-tests test cases.
+
+new SRIScriptTest(
+    true,
+    "Same-origin with correct sha256 hash, using base64URL encoding.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13-9UKk_3Q5eoqDc4YGxYb07EPWzb9E="
+);
+
+new SRIScriptTest(
+    false,
+    "Same-origin with correct sha256 hash, using intermixed base64 and base64URL encoding, should fail.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk_3Q5eoqDc4YGxYb07EPWzb9E="
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with invalid syntax only.",
+    "resources/matching-digest.js",
+    "?foo=bar?spam=eggs"
+);
+
+new SRIScriptTest(
+    true,
+    "Same-origin with multiple valid sha256 hashes, including correct.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9e= sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E="
+);
+
+new SRIScriptTest(
+    false,
+    "Same-origin with incorrect hash, options.",
+    "resources/matching-digest.js",
+    "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9e=?foo=bar?spam=eggs"
+);
+
+SRITests.execute();
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-style-expected.txt b/LayoutTests/http/tests/subresource-integrity/sri-style-expected.txt
new file mode 100644 (file)
index 0000000..456df6e
--- /dev/null
@@ -0,0 +1,33 @@
+CONSOLE MESSAGE: Cannot load stylesheet http://127.0.0.1:8000/subresource-integrity/resources/style.css?5. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load stylesheet http://127.0.0.1:8000/subresource-integrity/resources/style.css?9. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load stylesheet http://localhost:8000/subresource-integrity/resources/crossorigin-anon-style.css?&2. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load stylesheet http://localhost:8000/subresource-integrity/resources/crossorigin-creds-style.css?&2. Failed integrity metadata check.
+CONSOLE MESSAGE: Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: Cannot load stylesheet http://localhost:8000/subresource-integrity/resources/crossorigin-anon-style.css?&3. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load stylesheet http://localhost:8000/subresource-integrity/resources/crossorigin-anon-style.css?&4. Failed integrity metadata check.
+CONSOLE MESSAGE: Cannot load stylesheet http://127.0.0.1:8000/subresource-integrity/resources/alternate.css?2. Failed integrity metadata check.
+
+PASS Style: Same-origin with correct sha256 hash 
+PASS Style: Same-origin with correct sha384 hash 
+PASS Style: Same-origin with correct sha512 hash 
+PASS Style: Same-origin with empty integrity 
+PASS Style: Same-origin with incorrect hash. 
+PASS Style: Same-origin with multiple sha256 hashes, including correct. 
+PASS Style: Same-origin with multiple sha256 hashes, including unknown algorithm. 
+PASS Style: Same-origin with sha256 mismatch, sha512 match 
+PASS Style: Same-origin with sha256 match, sha512 mismatch 
+PASS Style: <crossorigin='anonymous'> with correct hash, ACAO: * 
+PASS Style: <crossorigin='anonymous'> with incorrect hash, ACAO: * 
+PASS Style: <crossorigin='use-credentials'> with correct hash, CORS-eligible 
+PASS Style: <crossorigin='use-credentials'> with incorrect hash CORS-eligible 
+PASS Style: <crossorigin='anonymous'> with CORS-ineligible resource 
+PASS Style: Cross-origin, not CORS request, with correct hash 
+PASS Style: Cross-origin, not CORS request, with hash mismatch 
+PASS Style: Cross-origin, empty integrity 
+PASS Style: Same-origin with correct hash, options. 
+PASS Style: Same-origin with unknown algorithm only. 
+PASS Style: Same-origin with correct sha256 hash, rel='stylesheet license' 
+PASS Style: Same-origin with correct sha256 hash, rel='license stylesheet' 
+PASS Style: Same-origin with correct sha256 and sha512 hash, rel='alternate stylesheet' enabled 
+PASS Style: Same-origin with incorrect sha256 and sha512 hash, rel='alternate stylesheet' enabled 
+
diff --git a/LayoutTests/http/tests/subresource-integrity/sri-style.html b/LayoutTests/http/tests/subresource-integrity/sri-style.html
new file mode 100644 (file)
index 0000000..5f3fb98
--- /dev/null
@@ -0,0 +1,263 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="/js-test-resources/testharness.js"></script>
+    <script src="/js-test-resources/testharnessreport.js"></script>
+    <script src="resources/sri-utilities.js"></script>
+</head>
+<body>
+    <div id="log"></div>
+    <div id="container"></div>
+<script>
+
+var main_host = '127.0.0.1';
+var remote_host = 'localhost';
+var port_string = "8000";
+var main_host_and_port = main_host + ':' + port_string;
+var remote_host_and_port = remote_host + ':' + port_string;
+
+var crossorigin_anon_style = location.protocol + '//' + remote_host_and_port + '/subresource-integrity/resources/crossorigin-anon-style.css?';
+var crossorigin_creds_style = location.protocol + '//' + remote_host_and_port + '/subresource-integrity/resources/crossorigin-creds-style.css?';
+var crossorigin_ineligible_style = location.protocol + '//' + remote_host_and_port + '/subresource-integrity/resources/crossorigin-ineligible-style.css?';
+
+// Style tests from web-platform-tests/subresource-integrity
+
+new SRIStyleTest(
+    true,
+    "Same-origin with correct sha256 hash",
+    {
+        href: "resources/style.css?1",
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4="
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with correct sha384 hash",
+    {
+        href: "resources/style.css?2",
+        integrity: "sha384-wDAWxH4tOWBwAwHfBn9B7XuNmFxHTMeigAMwn0iVQ0zq3FtmYMLxihcGnU64CwcX"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with correct sha512 hash",
+    {
+        href: "resources/style.css?3",
+        integrity: "sha512-9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2HSZX4l6w=="
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with empty integrity",
+    {
+        href: "resources/style.css?4",
+        integrity: ""
+    }
+);
+
+new SRIStyleTest(
+    false,
+    "Same-origin with incorrect hash.",
+    {
+        href: "resources/style.css?5",
+        integrity: "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with multiple sha256 hashes, including correct.",
+    {
+        href: "resources/style.css?6",
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4= sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with multiple sha256 hashes, including unknown algorithm.",
+    {
+        href: "resources/style.css?7",
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4= foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with sha256 mismatch, sha512 match",
+    {
+        href: "resources/style.css?8",
+        integrity: "sha512-9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2HSZX4l6w== sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+    }
+);
+
+new SRIStyleTest(
+    false,
+    "Same-origin with sha256 match, sha512 mismatch",
+    {
+        href: "resources/style.css?9",
+        integrity: "sha512-deadbeef9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2== sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4="
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "<crossorigin='anonymous'> with correct hash, ACAO: *",
+    {
+        href: crossorigin_anon_style + '&1',
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=",
+        crossorigin: "anonymous"
+    }
+);
+
+new SRIStyleTest(
+    false,
+    "<crossorigin='anonymous'> with incorrect hash, ACAO: *",
+    {
+        href: crossorigin_anon_style + '&2',
+        integrity: "sha256-deadbeefCzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk=",
+        crossorigin: "anonymous"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "<crossorigin='use-credentials'> with correct hash, CORS-eligible",
+    {
+        href: crossorigin_creds_style + '&1',
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=",
+        crossorigin: "use-credentials"
+    }
+);
+
+new SRIStyleTest(
+    false,
+    "<crossorigin='use-credentials'> with incorrect hash CORS-eligible",
+    {
+        href: crossorigin_creds_style + '&2',
+        integrity: "sha256-deadbeefCzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk=",
+        crossorigin: "use-credentials"
+    }
+);
+
+new SRIStyleTest(
+    false,
+    "<crossorigin='anonymous'> with CORS-ineligible resource",
+    {
+        href: crossorigin_ineligible_style + '&1',
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=",
+        crossorigin: "anonymous"
+    }
+);
+
+new SRIStyleTest(
+    false,
+    "Cross-origin, not CORS request, with correct hash",
+    {
+        href: crossorigin_anon_style + '&3',
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4="
+    }
+);
+
+new SRIStyleTest(
+    false,
+    "Cross-origin, not CORS request, with hash mismatch",
+    {
+        href: crossorigin_anon_style + '&4',
+        integrity: "sha256-deadbeefCzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk="
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Cross-origin, empty integrity",
+    {
+        href: crossorigin_anon_style + '&5',
+        integrity: ""
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with correct hash, options.",
+    {
+        href: "resources/style.css?10",
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=?foo=bar?spam=eggs"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with unknown algorithm only.",
+    {
+        href: "resources/style.css?11",
+        integrity: "foo666-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=?foo=bar?spam=eggs"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with correct sha256 hash, rel='stylesheet license'",
+    {
+        href: "resources/style.css?12",
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=",
+        rel: "stylesheet license"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with correct sha256 hash, rel='license stylesheet'",
+    {
+        href: "resources/style.css?13",
+        integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=",
+        rel: "license stylesheet"
+    }
+);
+
+new SRIStyleTest(
+    true,
+    "Same-origin with correct sha256 and sha512 hash, rel='alternate stylesheet' enabled",
+    {
+        href: "resources/alternate.css?1",
+        title: "alt",
+        type: "text/css",
+        class: "alternate",
+        disabled: "disabled",
+        rel: "alternate stylesheet",
+        integrity: "sha256-phbz83bWhnLig+d2VPKrRrTRyhqoDRo1ruGqZLZ0= sha512-8OYEB7ktnzcb6h+kB9CUIuc8qvKIyLpygRJdQSEEycRy74dUsB+Yu9rSjpOPjRUblle8WWX9Gn7v39LK2Oceig==",
+    },
+    function (link, container) {
+        var alternate = document.querySelector('link.alternate');
+        alternate.disabled = false;
+    },
+    "rgb(255, 0, 0)"
+);
+
+new SRIStyleTest(
+    false,
+    "Same-origin with incorrect sha256 and sha512 hash, rel='alternate stylesheet' enabled",
+    {
+        href: "resources/alternate.css?2",
+        title: "alt",
+        type: "text/css",
+        class: "alternate",
+        disabled: "disabled",
+        rel: "alternate stylesheet",
+        integrity: "sha256-fail83bWhnLig+d2VPKrRrTRyhqoDRo1ruGqZLZ0= sha512-failB7ktnzcb6h+kB9CUIuc8qvKIyLpygRJdQSEEycRy74dUsB+Yu9rSjpOPjRUblle8WWX9Gn7v39LK2Oceig==",
+    },
+    function (link, container) {
+        var alternate = document.querySelector('link.alternate');
+        alternate.disabled = false;
+    }
+);
+
+SRITests.execute();
+
+</script>
+</body>
+</html>
index 8101063..caac66e 100644 (file)
@@ -1,3 +1,14 @@
+2017-05-07  Sam Weinig  <sam@webkit.org>
+
+        Implement Subresource Integrity (SRI)
+        https://bugs.webkit.org/show_bug.cgi?id=148363
+
+        Reviewed by Daniel Bates.
+
+        * web-platform-tests/html/dom/reflection-metadata-expected.txt:
+        * web-platform-tests/html/dom/reflection-misc-expected.txt:
+        Update results now that we support the reflected 'integrity' property.
+
 2017-05-04  Daniel Bates  <dabates@apple.com>
 
         importScripts() should respect X-Content-Type-Options: nosniff
index 00bc225..b0197c3 100644 (file)
@@ -1207,38 +1207,38 @@ PASS link.nonce: IDL set to "\0"
 PASS link.nonce: IDL set to null 
 PASS link.nonce: IDL set to object "test-toString" 
 PASS link.nonce: IDL set to object "test-valueOf" 
-FAIL link.integrity: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL link.integrity: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL link.integrity: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL link.integrity: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL link.integrity: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL link.integrity: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL link.integrity: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL link.integrity: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL link.integrity: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL link.integrity: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL link.integrity: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL link.integrity: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL link.integrity: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL link.integrity: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL link.integrity: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL link.integrity: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL link.integrity: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL link.integrity: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL link.integrity: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
+PASS link.integrity: typeof IDL attribute 
+PASS link.integrity: IDL get with DOM attribute unset 
+PASS link.integrity: setAttribute() to "" 
+PASS link.integrity: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " 
+PASS link.integrity: setAttribute() to undefined 
+PASS link.integrity: setAttribute() to 7 
+PASS link.integrity: setAttribute() to 1.5 
+PASS link.integrity: setAttribute() to true 
+PASS link.integrity: setAttribute() to false 
+PASS link.integrity: setAttribute() to object "[object Object]" 
+PASS link.integrity: setAttribute() to NaN 
+PASS link.integrity: setAttribute() to Infinity 
+PASS link.integrity: setAttribute() to -Infinity 
+PASS link.integrity: setAttribute() to "\0" 
+PASS link.integrity: setAttribute() to null 
+PASS link.integrity: setAttribute() to object "test-toString" 
+PASS link.integrity: setAttribute() to object "test-valueOf" 
+PASS link.integrity: IDL set to "" 
+PASS link.integrity: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " 
+PASS link.integrity: IDL set to undefined 
+PASS link.integrity: IDL set to 7 
+PASS link.integrity: IDL set to 1.5 
+PASS link.integrity: IDL set to true 
+PASS link.integrity: IDL set to false 
+PASS link.integrity: IDL set to object "[object Object]" 
+PASS link.integrity: IDL set to NaN 
+PASS link.integrity: IDL set to Infinity 
+PASS link.integrity: IDL set to -Infinity 
+PASS link.integrity: IDL set to "\0" 
+PASS link.integrity: IDL set to null 
+PASS link.integrity: IDL set to object "test-toString" 
+PASS link.integrity: IDL set to object "test-valueOf" 
 PASS link.hreflang: typeof IDL attribute 
 PASS link.hreflang: IDL get with DOM attribute unset 
 PASS link.hreflang: setAttribute() to "" 
index 70a3cf8..31720f3 100644 (file)
@@ -779,38 +779,38 @@ PASS script.nonce: IDL set to "\0"
 PASS script.nonce: IDL set to null 
 PASS script.nonce: IDL set to object "test-toString" 
 PASS script.nonce: IDL set to object "test-valueOf" 
-FAIL script.integrity: typeof IDL attribute assert_equals: expected "string" but got "undefined"
-FAIL script.integrity: IDL get with DOM attribute unset assert_equals: expected (string) "" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to "" assert_equals: IDL get expected (string) "" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: IDL get expected (string) " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got (undefined) undefined
-FAIL script.integrity: setAttribute() to undefined assert_equals: IDL get expected (string) "undefined" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to 7 assert_equals: IDL get expected (string) "7" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to 1.5 assert_equals: IDL get expected (string) "1.5" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to true assert_equals: IDL get expected (string) "true" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to false assert_equals: IDL get expected (string) "false" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to object "[object Object]" assert_equals: IDL get expected (string) "[object Object]" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to NaN assert_equals: IDL get expected (string) "NaN" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to Infinity assert_equals: IDL get expected (string) "Infinity" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to -Infinity assert_equals: IDL get expected (string) "-Infinity" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to "\0" assert_equals: IDL get expected (string) "\0" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to null assert_equals: IDL get expected (string) "null" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to object "test-toString" assert_equals: IDL get expected (string) "test-toString" but got (undefined) undefined
-FAIL script.integrity: setAttribute() to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (undefined) undefined
-FAIL script.integrity: IDL set to "" assert_equals: getAttribute() expected "" but got "test-valueOf"
-FAIL script.integrity: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " assert_equals: getAttribute() expected " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " but got "test-valueOf"
-FAIL script.integrity: IDL set to undefined assert_equals: getAttribute() expected "undefined" but got "test-valueOf"
-FAIL script.integrity: IDL set to 7 assert_equals: getAttribute() expected "7" but got "test-valueOf"
-FAIL script.integrity: IDL set to 1.5 assert_equals: getAttribute() expected "1.5" but got "test-valueOf"
-FAIL script.integrity: IDL set to true assert_equals: getAttribute() expected "true" but got "test-valueOf"
-FAIL script.integrity: IDL set to false assert_equals: getAttribute() expected "false" but got "test-valueOf"
-FAIL script.integrity: IDL set to object "[object Object]" assert_equals: getAttribute() expected "[object Object]" but got "test-valueOf"
-FAIL script.integrity: IDL set to NaN assert_equals: getAttribute() expected "NaN" but got "test-valueOf"
-FAIL script.integrity: IDL set to Infinity assert_equals: getAttribute() expected "Infinity" but got "test-valueOf"
-FAIL script.integrity: IDL set to -Infinity assert_equals: getAttribute() expected "-Infinity" but got "test-valueOf"
-FAIL script.integrity: IDL set to "\0" assert_equals: getAttribute() expected "\0" but got "test-valueOf"
-FAIL script.integrity: IDL set to null assert_equals: getAttribute() expected "null" but got "test-valueOf"
-FAIL script.integrity: IDL set to object "test-toString" assert_equals: getAttribute() expected "test-toString" but got "test-valueOf"
-FAIL script.integrity: IDL set to object "test-valueOf" assert_equals: IDL get expected (string) "test-valueOf" but got (object) object "test-valueOf"
+PASS script.integrity: typeof IDL attribute 
+PASS script.integrity: IDL get with DOM attribute unset 
+PASS script.integrity: setAttribute() to "" 
+PASS script.integrity: setAttribute() to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " 
+PASS script.integrity: setAttribute() to undefined 
+PASS script.integrity: setAttribute() to 7 
+PASS script.integrity: setAttribute() to 1.5 
+PASS script.integrity: setAttribute() to true 
+PASS script.integrity: setAttribute() to false 
+PASS script.integrity: setAttribute() to object "[object Object]" 
+PASS script.integrity: setAttribute() to NaN 
+PASS script.integrity: setAttribute() to Infinity 
+PASS script.integrity: setAttribute() to -Infinity 
+PASS script.integrity: setAttribute() to "\0" 
+PASS script.integrity: setAttribute() to null 
+PASS script.integrity: setAttribute() to object "test-toString" 
+PASS script.integrity: setAttribute() to object "test-valueOf" 
+PASS script.integrity: IDL set to "" 
+PASS script.integrity: IDL set to " \0\x01\x02\x03\x04\x05\x06\x07 \b\t\n\v\f\r\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17 \x18\x19\x1a\x1b\x1c\x1d\x1e\x1f  foo " 
+PASS script.integrity: IDL set to undefined 
+PASS script.integrity: IDL set to 7 
+PASS script.integrity: IDL set to 1.5 
+PASS script.integrity: IDL set to true 
+PASS script.integrity: IDL set to false 
+PASS script.integrity: IDL set to object "[object Object]" 
+PASS script.integrity: IDL set to NaN 
+PASS script.integrity: IDL set to Infinity 
+PASS script.integrity: IDL set to -Infinity 
+PASS script.integrity: IDL set to "\0" 
+PASS script.integrity: IDL set to null 
+PASS script.integrity: IDL set to object "test-toString" 
+PASS script.integrity: IDL set to object "test-valueOf" 
 PASS script.event: typeof IDL attribute 
 PASS script.event: IDL get with DOM attribute unset 
 PASS script.event: setAttribute() to "" 
index 2825be4..020ee42 100644 (file)
@@ -1973,6 +1973,7 @@ set(WebCore_SOURCES
     loader/ResourceTimingInformation.cpp
     loader/SinkDocument.cpp
     loader/SubframeLoader.cpp
+    loader/SubresourceIntegrity.cpp
     loader/SubresourceLoader.cpp
     loader/TextResourceDecoder.cpp
     loader/TextTrackLoader.cpp
index a89e99d..4fb4e7f 100644 (file)
@@ -1,3 +1,74 @@
+2017-05-07  Sam Weinig  <sam@webkit.org>
+
+        Implement Subresource Integrity (SRI)
+        https://bugs.webkit.org/show_bug.cgi?id=148363
+        <rdar://problem/18945879>
+
+        Reviewed by Daniel Bates.
+
+        Tests: http/tests/subresource-integrity/sri-disabled-with-setting.html
+               http/tests/subresource-integrity/sri-enabled-with-setting.html
+               http/tests/subresource-integrity/sri-script-cors.html
+               http/tests/subresource-integrity/sri-style-cors.html
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * dom/LoadableClassicScript.cpp:
+        (WebCore::LoadableClassicScript::create):
+        (WebCore::LoadableClassicScript::notifyFinished):
+        * dom/LoadableClassicScript.h:
+        * dom/LoadableScript.h:
+        * dom/ScriptElement.cpp:
+        (WebCore::ScriptElement::requestClassicScript):
+        Store integrity metadata in the script fetcher so it can be passed to
+        the checked when script load finishes.
+
+        * html/HTMLAttributeNames.in:
+        Add 'integrity'.
+
+        * html/HTMLLinkElement.cpp:
+        (WebCore::HTMLLinkElement::process):
+        When requesting a stylesheet, cache the integrity metadata so it can
+        be used when the load completes (accessing the attribute at load completion
+        time is incorrect, as a script might have changed the attributes value since
+        the request was made).
+
+        (WebCore::HTMLLinkElement::setCSSStyleSheet):
+        Add an integrity check using the cached integrity metadata when a load
+        finishes.
+
+        * html/HTMLLinkElement.h:
+        Add cached integrity metadata member.
+
+        * html/HTMLLinkElement.idl:
+        * html/HTMLScriptElement.idl:
+        Add integrity property.
+
+        * html/parser/HTMLParserIdioms.h:
+        (WebCore::isNotHTMLSpace):
+        Templatize isNotHTMLSpace so it can work for both UChar and LChar.
+
+        * loader/ResourceCryptographicDigest.cpp:
+        (WebCore::parseCryptographicDigestImpl):
+        (WebCore::parseEncodedCryptographicDigestImpl):
+        (WebCore::parseEncodedCryptographicDigest):
+        (WebCore::decodeEncodedResourceCryptographicDigest):
+        * loader/ResourceCryptographicDigest.h:
+        Add concept of an encoded digest to more closely model the spec so that hashes
+        that match the grammar but are invalid (say, mixing base64 and base64URL) make
+        it through the algorithm longer, and don't cause us to load something that should
+        be blocked.
+
+        * loader/SubresourceIntegrity.cpp: Added.
+        * loader/SubresourceIntegrity.h: Added.
+        Add implementation of Subresource Integrity metadata validation allowing
+        for a CachedResource and integrity metadata to be passed for validation.
+
+        * page/Settings.in:
+        Add setting for Subresource Integrity, defaulted to enabled.
+
 2017-05-07  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [GTK] Cannot sign in with new Google sign-in page
index 502c9fa..1b513dc 100644 (file)
                7CE6CBFD187F394900D46BF5 /* FormatConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CE6CBFC187F394900D46BF5 /* FormatConverter.cpp */; };
                7CE9A12B1EA2F1DB00651AD1 /* IDLAttributes.json in Headers */ = {isa = PBXBuildFile; fileRef = A83B533814F399BB00720D9D /* IDLAttributes.json */; settings = {ATTRIBUTES = (Private, ); }; };
                7CEF26191D6A931700BE905D /* JSCryptoCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEF26181D6A92E300BE905D /* JSCryptoCustom.cpp */; };
+               7CF1589B1EBC4CFD00D4BFB7 /* SubresourceIntegrity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CF158991EBBCDC700D4BFB7 /* SubresourceIntegrity.cpp */; };
                7CF930E71E01F9B400BAFFBE /* PaymentHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CF930E61E01F9AD00BAFFBE /* PaymentHeaders.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7CFDC57C1AC1D80500E24A57 /* ContentExtensionError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CFDC57A1AC1D80500E24A57 /* ContentExtensionError.cpp */; };
                7CFDC57D1AC1D80500E24A57 /* ContentExtensionError.h in Headers */ = {isa = PBXBuildFile; fileRef = 7CFDC57B1AC1D80500E24A57 /* ContentExtensionError.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7CE6CBFA187F370700D46BF5 /* FormatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormatConverter.h; sourceTree = "<group>"; };
                7CE6CBFC187F394900D46BF5 /* FormatConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FormatConverter.cpp; sourceTree = "<group>"; };
                7CEF26181D6A92E300BE905D /* JSCryptoCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCryptoCustom.cpp; sourceTree = "<group>"; };
+               7CF158991EBBCDC700D4BFB7 /* SubresourceIntegrity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SubresourceIntegrity.cpp; sourceTree = "<group>"; };
+               7CF1589A1EBBCDC700D4BFB7 /* SubresourceIntegrity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SubresourceIntegrity.h; sourceTree = "<group>"; };
                7CF930E61E01F9AD00BAFFBE /* PaymentHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PaymentHeaders.h; sourceTree = "<group>"; };
                7CFDC57A1AC1D80500E24A57 /* ContentExtensionError.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentExtensionError.cpp; sourceTree = "<group>"; };
                7CFDC57B1AC1D80500E24A57 /* ContentExtensionError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentExtensionError.h; sourceTree = "<group>"; };
                                51327D5E11A33A2B004F9D65 /* SinkDocument.h */,
                                D000ED2511C1B9CD00C47726 /* SubframeLoader.cpp */,
                                D000ED2611C1B9CD00C47726 /* SubframeLoader.h */,
+                               7CF158991EBBCDC700D4BFB7 /* SubresourceIntegrity.cpp */,
+                               7CF1589A1EBBCDC700D4BFB7 /* SubresourceIntegrity.h */,
                                93E227DF0AF589AD00D48324 /* SubresourceLoader.cpp */,
                                656D37300ADBA5DE00A4554D /* SubresourceLoader.h */,
                                659A7D120B6DB4D9001155B3 /* SubstituteData.h */,
                                511EF2C717F0FD3500E4FA16 /* JSIDBObjectStore.cpp in Sources */,
                                511EF2D117F0FDF100E4FA16 /* JSIDBObjectStoreCustom.cpp in Sources */,
                                511EF2C817F0FD3500E4FA16 /* JSIDBOpenDBRequest.cpp in Sources */,
+                               7CF1589B1EBC4CFD00D4BFB7 /* SubresourceIntegrity.cpp in Sources */,
                                511EF2C917F0FD3500E4FA16 /* JSIDBRequest.cpp in Sources */,
                                511EF2CA17F0FD3500E4FA16 /* JSIDBTransaction.cpp in Sources */,
                                51E269331DD3BC4E006B6A58 /* JSIDBTransactionCustom.cpp in Sources */,
index fdcd223..fb668ea 100644 (file)
 
 #include "ScriptElement.h"
 #include "ScriptSourceCode.h"
+#include "SubresourceIntegrity.h"
 #include <wtf/NeverDestroyed.h>
 #include <wtf/text/StringImpl.h>
 
 namespace WebCore {
 
-Ref<LoadableClassicScript> LoadableClassicScript::create(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree)
+Ref<LoadableClassicScript> LoadableClassicScript::create(const String& nonce, const String& integrityMetadata, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree)
 {
-    return adoptRef(*new LoadableClassicScript(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree));
+    return adoptRef(*new LoadableClassicScript(nonce, integrityMetadata, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree));
 }
 
 LoadableClassicScript::~LoadableClassicScript()
@@ -96,6 +97,13 @@ void LoadableClassicScript::notifyFinished(CachedResource& resource)
     }
 #endif
 
+    if (!m_error && !resource.errorOccurred() && !matchIntegrityMetadata(resource, m_integrity)) {
+        m_error = Error {
+            ErrorType::FailedIntegrityCheck,
+            ConsoleMessage { MessageSource::Security, MessageLevel::Error, makeString("Cannot load script ", m_cachedScript->url().stringCenterEllipsizedToLength(), ". Failed integrity metadata check.") }
+        };
+    }
+
     notifyClientFinished();
 }
 
index dfac664..5c9b014 100644 (file)
@@ -41,7 +41,7 @@ class LoadableClassicScript final : public LoadableScript, private CachedResourc
 public:
     virtual ~LoadableClassicScript();
 
-    static Ref<LoadableClassicScript> create(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree);
+    static Ref<LoadableClassicScript> create(const String& nonce, const String& integrity, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree);
     bool isLoaded() const final;
     std::optional<Error> error() const final;
     bool wasCanceled() const final;
@@ -56,8 +56,9 @@ public:
     bool load(Document&, const URL&);
 
 private:
-    LoadableClassicScript(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree)
+    LoadableClassicScript(const String& nonce, const String& integrity, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree)
         : LoadableScript(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree)
+        , m_integrity(integrity)
     {
     }
 
@@ -65,6 +66,7 @@ private:
 
     CachedResourceHandle<CachedScript> m_cachedScript { };
     std::optional<Error> m_error { std::nullopt };
+    String m_integrity;
 };
 
 }
index 5b53c4f..4316d53 100644 (file)
@@ -42,6 +42,7 @@ public:
         CachedScript,
         CrossOriginLoad,
         Nosniff,
+        FailedIntegrityCheck,
     };
 
     struct ConsoleMessage {
index cf51d40..aef8ef6 100644 (file)
@@ -48,6 +48,7 @@
 #include "ScriptRunner.h"
 #include "ScriptSourceCode.h"
 #include "ScriptableDocumentParser.h"
+#include "Settings.h"
 #include "TextNodeTraversal.h"
 #include <wtf/StdLibExtras.h>
 #include <wtf/text/StringBuilder.h>
@@ -294,6 +295,7 @@ bool ScriptElement::requestClassicScript(const String& sourceURL)
     if (!stripLeadingAndTrailingHTMLSpaces(sourceURL).isEmpty()) {
         auto script = LoadableClassicScript::create(
             m_element.attributeWithoutSynchronization(HTMLNames::nonceAttr),
+            m_element.document().settings().subresourceIntegrityEnabled() ? m_element.attributeWithoutSynchronization(HTMLNames::integrityAttr).string() : emptyString(),
             m_element.attributeWithoutSynchronization(HTMLNames::crossoriginAttr),
             scriptCharset(),
             m_element.localName(),
index 2bfaed3..59d8466 100644 (file)
@@ -138,6 +138,7 @@ http_equiv
 id
 incremental
 indeterminate
+integrity
 is
 ismap
 itemid
index 0bd234f..c2c4bb4 100644 (file)
@@ -54,6 +54,7 @@
 #include "StyleResolveForDocument.h"
 #include "StyleScope.h"
 #include "StyleSheetContents.h"
+#include "SubresourceIntegrity.h"
 #include <wtf/Ref.h>
 #include <wtf/SetForScope.h>
 #include <wtf/StdLibExtras.h>
@@ -239,7 +240,7 @@ void HTMLLinkElement::process()
 
     if (m_disabledState != Disabled && treatAsStyleSheet && document().frame() && url.isValid()) {
         String charset = attributeWithoutSynchronization(charsetAttr);
-        if (charset.isEmpty() && document().frame())
+        if (charset.isEmpty())
             charset = document().charset();
 
         if (m_cachedSheet) {
@@ -275,6 +276,9 @@ void HTMLLinkElement::process()
         if (!isActive)
             priority = ResourceLoadPriority::VeryLow;
 
+        if (document().settings().subresourceIntegrityEnabled())
+            m_integrityMetadataForPendingSheetRequest = attributeWithoutSynchronization(HTMLNames::integrityAttr);
+
         ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
         options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set;
         if (document().contentSecurityPolicy()->allowStyleWithNonce(attributeWithoutSynchronization(HTMLNames::nonceAttr)))
@@ -282,7 +286,6 @@ void HTMLLinkElement::process()
 
         CachedResourceRequest request(url, options, priority, WTFMove(charset));
         request.setInitiator(*this);
-
         request.setAsPotentiallyCrossOrigin(crossOrigin(), document());
 
         ASSERT_WITH_SECURITY_IMPLICATION(!m_cachedSheet);
@@ -293,7 +296,8 @@ void HTMLLinkElement::process()
         else {
             // The request may have been denied if (for example) the stylesheet is local and the document is remote.
             m_loading = false;
-            removePendingSheet();
+            sheetLoaded();
+            notifyLoadedSheetAndAllCriticalSubresources(false);
         }
     } else if (m_sheet) {
         // we no longer contain a stylesheet, e.g. perhaps rel or type was changed
@@ -342,7 +346,6 @@ void HTMLLinkElement::removedFrom(ContainerNode& insertionPoint)
         m_styleScope->removeStyleSheetCandidateNode(*this);
         m_styleScope = nullptr;
     }
-
 }
 
 void HTMLLinkElement::finishParsingChildren()
@@ -376,6 +379,15 @@ void HTMLLinkElement::setCSSStyleSheet(const String& href, const URL& baseURL, c
     // Completing the sheet load may cause scripts to execute.
     Ref<HTMLLinkElement> protectedThis(*this);
 
+    if (!cachedStyleSheet->errorOccurred() && !matchIntegrityMetadata(*cachedStyleSheet, m_integrityMetadataForPendingSheetRequest)) {
+        document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, makeString("Cannot load stylesheet ", cachedStyleSheet->url().stringCenterEllipsizedToLength(), ". Failed integrity metadata check."));
+
+        m_loading = false;
+        sheetLoaded();
+        notifyLoadedSheetAndAllCriticalSubresources(true);
+        return;
+    }
+
     CSSParserContext parserContext(document(), baseURL, charset);
     auto cachePolicy = frame->loader().subresourceCachePolicy(baseURL);
 
index 317aaa1..bd5b5ee 100644 (file)
@@ -135,8 +135,9 @@ private:
     bool m_isHandlingBeforeLoad { false };
 
     PendingSheetType m_pendingSheetType;
+    String m_integrityMetadataForPendingSheetRequest;
 
     std::unique_ptr<DOMTokenList> m_relList;
 };
 
-} //namespace
+}
index 92fbf98..733a0b4 100644 (file)
@@ -42,5 +42,6 @@
     [PutForwards=value] readonly attribute DOMTokenList relList;
 
     [Reflect] attribute DOMString nonce;
+    [Reflect, EnabledBySetting=SubresourceIntegrity] attribute DOMString integrity;
 };
 
index 019f4d5..261c4eb 100644 (file)
@@ -30,4 +30,5 @@ interface HTMLScriptElement : HTMLElement {
     attribute DOMString? crossOrigin;
     [Reflect] attribute DOMString nonce;
     [Reflect] attribute boolean noModule;
+    [Reflect, EnabledBySetting=SubresourceIntegrity] attribute DOMString integrity;
 };
index e92cc42..269cc62 100644 (file)
@@ -38,10 +38,10 @@ class QualifiedName;
 
 // Space characters as defined by the HTML specification.
 template<typename CharacterType> bool isHTMLSpace(CharacterType);
+template<typename CharacterType> bool isNotHTMLSpace(CharacterType);
 template<typename CharacterType> bool isComma(CharacterType);
 template<typename CharacterType> bool isHTMLSpaceOrComma(CharacterType);
 bool isHTMLLineBreak(UChar);
-bool isNotHTMLSpace(UChar);
 bool isHTMLSpaceButNotLineBreak(UChar);
 
 // 2147483647 is 2^31 - 1.
@@ -106,6 +106,11 @@ template<typename CharacterType> inline bool isHTMLSpace(CharacterType character
     return character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r' || character == '\f');
 }
 
+template<typename CharacterType> inline bool isNotHTMLSpace(CharacterType character)
+{
+    return !isHTMLSpace(character);
+}
+
 inline bool isHTMLLineBreak(UChar character)
 {
     return character <= '\r' && (character == '\n' || character == '\r');
@@ -121,11 +126,6 @@ template<typename CharacterType> inline bool isHTMLSpaceOrComma(CharacterType ch
     return isComma(character) || isHTMLSpace(character);
 }
 
-inline bool isNotHTMLSpace(UChar character)
-{
-    return !isHTMLSpace(character);
-}
-
 inline bool isHTMLSpaceButNotLineBreak(UChar character)
 {
     return isHTMLSpace(character) && !isHTMLLineBreak(character);
index e0d73b8..bae912a 100644 (file)
 #include "config.h"
 #include "ResourceCryptographicDigest.h"
 
-#include "CachedResource.h"
-#include "HTMLParserIdioms.h"
 #include "ParsingUtilities.h"
-#include "SharedBuffer.h"
 #include <pal/crypto/CryptoDigest.h>
 #include <wtf/text/Base64.h>
 
@@ -58,7 +55,7 @@ static bool parseHashAlgorithmAdvancingPosition(const CharacterType*& position,
 }
 
 template<typename CharacterType>
-std::optional<ResourceCryptographicDigest> parseCryptographicDigestImpl(const CharacterType*& position, const CharacterType* end)
+static std::optional<ResourceCryptographicDigest> parseCryptographicDigestImpl(const CharacterType*& position, const CharacterType* end)
 {
     if (position == end)
         return std::nullopt;
@@ -98,6 +95,51 @@ std::optional<ResourceCryptographicDigest> parseCryptographicDigest(const LChar*
     return parseCryptographicDigestImpl(begin, end);
 }
 
+template<typename CharacterType>
+static std::optional<EncodedResourceCryptographicDigest> parseEncodedCryptographicDigestImpl(const CharacterType*& position, const CharacterType* end)
+{
+    if (position == end)
+        return std::nullopt;
+
+    EncodedResourceCryptographicDigest::Algorithm algorithm;
+    if (!parseHashAlgorithmAdvancingPosition(position, end, algorithm))
+        return std::nullopt;
+
+    if (!skipExactly<CharacterType>(position, end, '-'))
+        return std::nullopt;
+
+    const CharacterType* beginHashValue = position;
+    skipWhile<CharacterType, isBase64OrBase64URLCharacter>(position, end);
+    skipExactly<CharacterType>(position, end, '=');
+    skipExactly<CharacterType>(position, end, '=');
+
+    if (position == beginHashValue)
+        return std::nullopt;
+
+    return EncodedResourceCryptographicDigest { algorithm, String(beginHashValue, position - beginHashValue) };
+}
+
+std::optional<EncodedResourceCryptographicDigest> parseEncodedCryptographicDigest(const UChar*& begin, const UChar* end)
+{
+    return parseEncodedCryptographicDigestImpl(begin, end);
+}
+
+std::optional<EncodedResourceCryptographicDigest> parseEncodedCryptographicDigest(const LChar*& begin, const LChar* end)
+{
+    return parseEncodedCryptographicDigestImpl(begin, end);
+}
+
+std::optional<ResourceCryptographicDigest> decodeEncodedResourceCryptographicDigest(const EncodedResourceCryptographicDigest& encodedDigest)
+{
+    Vector<uint8_t> digest;
+    if (!base64Decode(encodedDigest.digest, digest, Base64ValidatePadding)) {
+        if (!base64URLDecode(encodedDigest.digest, digest))
+            return std::nullopt;
+    }
+
+    return ResourceCryptographicDigest { encodedDigest.algorithm, WTFMove(digest) };
+}
+
 static PAL::CryptoDigest::Algorithm toCryptoDigestAlgorithm(ResourceCryptographicDigest::Algorithm algorithm)
 {
     switch (algorithm) {
index 59f5566..638a4a3 100644 (file)
@@ -61,9 +61,21 @@ struct ResourceCryptographicDigest {
     }
 };
 
+struct EncodedResourceCryptographicDigest {
+    using Algorithm = ResourceCryptographicDigest::Algorithm;
+    
+    Algorithm algorithm;
+    String digest;
+};
+
 std::optional<ResourceCryptographicDigest> parseCryptographicDigest(const UChar*& begin, const UChar* end);
 std::optional<ResourceCryptographicDigest> parseCryptographicDigest(const LChar*& begin, const LChar* end);
 
+std::optional<EncodedResourceCryptographicDigest> parseEncodedCryptographicDigest(const UChar*& begin, const UChar* end);
+std::optional<EncodedResourceCryptographicDigest> parseEncodedCryptographicDigest(const LChar*& begin, const LChar* end);
+
+std::optional<ResourceCryptographicDigest> decodeEncodedResourceCryptographicDigest(const EncodedResourceCryptographicDigest&);
+
 ResourceCryptographicDigest cryptographicDigestForBytes(ResourceCryptographicDigest::Algorithm, const char* bytes, size_t length);
 
 }
diff --git a/Source/WebCore/loader/SubresourceIntegrity.cpp b/Source/WebCore/loader/SubresourceIntegrity.cpp
new file mode 100644 (file)
index 0000000..0816051
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SubresourceIntegrity.h"
+
+#include "CachedResource.h"
+#include "HTMLParserIdioms.h"
+#include "ParsingUtilities.h"
+#include "ResourceCryptographicDigest.h"
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+namespace {
+
+template<typename CharacterType>
+static bool isVCHAR(CharacterType c)
+{
+    return c >= 0x21 && c <= 0x7e;
+}
+
+template<typename CharacterType>
+struct IntegrityMetadataParser {
+public:
+    IntegrityMetadataParser(std::optional<Vector<EncodedResourceCryptographicDigest>>& digests)
+        : m_digests(digests)
+    {
+    }
+
+    bool operator()(const CharacterType*& position, const CharacterType* end)
+    {
+        // Initialize hashes to be something other std::nullopt, to indicate
+        // that at least one token was seen, and thus setting the empty flag
+        // from section 3.3.3 Parse metadata, to false.
+        if (!m_digests)
+            m_digests = Vector<EncodedResourceCryptographicDigest> { };
+
+        auto digest = parseEncodedCryptographicDigest(position, end);
+        if (!digest)
+            return false;
+
+        // The spec allows for options following the digest, but so far, no
+        // specific options have been specified. Thus, we just parse and ignore
+        // them. Their syntax is a '?' follow by any number of VCHARs.
+        if (skipExactly<CharacterType>(position, end, '?'))
+            skipWhile<CharacterType, isVCHAR>(position, end);
+
+        // After the base64 value and options, the current character pointed to by position
+        // should either be the end or a space.
+        if (position != end && !isHTMLSpace(*position))
+            return false;
+
+        m_digests->append(WTFMove(*digest));
+        return true;
+    }
+
+private:
+    std::optional<Vector<EncodedResourceCryptographicDigest>>& m_digests;
+};
+
+}
+
+template <typename CharacterType, typename Functor>
+static inline void splitOnSpaces(const CharacterType* begin, const CharacterType* end, Functor&& functor)
+{
+    const CharacterType* position = begin;
+
+    skipWhile<CharacterType, isHTMLSpace>(position, end);
+
+    while (position < end) {
+        if (!functor(position, end))
+            skipWhile<CharacterType, isNotHTMLSpace>(position, end);
+
+        skipWhile<CharacterType, isHTMLSpace>(position, end);
+    }
+}
+
+static std::optional<Vector<EncodedResourceCryptographicDigest>> parseIntegrityMetadata(const String& integrityMetadata)
+{
+    if (integrityMetadata.isEmpty())
+        return std::nullopt;
+
+    std::optional<Vector<EncodedResourceCryptographicDigest>> result;
+    
+    const StringImpl& stringImpl = *integrityMetadata.impl();
+    if (stringImpl.is8Bit())
+        splitOnSpaces(stringImpl.characters8(), stringImpl.characters8() + stringImpl.length(), IntegrityMetadataParser<LChar> { result });
+    else
+        splitOnSpaces(stringImpl.characters16(), stringImpl.characters16() + stringImpl.length(), IntegrityMetadataParser<UChar> { result });
+
+    return result;
+}
+
+static bool isResponseEligible(const CachedResource& resource)
+{
+    // FIXME: The spec says this should check XXX.
+    return resource.isCORSSameOrigin();
+}
+
+static std::optional<EncodedResourceCryptographicDigest::Algorithm> prioritizedHashFunction(EncodedResourceCryptographicDigest::Algorithm a, EncodedResourceCryptographicDigest::Algorithm b)
+{
+    if (a == b)
+        return std::nullopt;
+    return (a > b) ? a : b;
+}
+
+static Vector<EncodedResourceCryptographicDigest> strongestMetadataFromSet(Vector<EncodedResourceCryptographicDigest>&& set)
+{
+    // 1. Let result be the empty set and strongest be the empty string.
+    Vector<EncodedResourceCryptographicDigest> result;
+    auto strongest = EncodedResourceCryptographicDigest::Algorithm::SHA256;
+
+    // 2. For each item in set:
+    for (auto& item : set) {
+        // 1. If result is the empty set, add item to result and set strongest to item, skip to the next item.
+        if (result.isEmpty()) {
+            strongest = item.algorithm;
+            result.append(WTFMove(item));
+            continue;
+        }
+        
+        // 2. Let currentAlgorithm be the alg component of strongest.
+        auto currentAlgorithm = strongest;
+
+        // 3. Let newAlgorithm be the alg component of item.
+        auto newAlgorithm = item.algorithm;
+        
+        // 4. If the result of getPrioritizedHashFunction(currentAlgorithm, newAlgorithm) is
+        //    the empty string, add item to result. If the result is newAlgorithm, set strongest
+        //    to item, set result to the empty set, and add item to result.
+        auto priority = prioritizedHashFunction(currentAlgorithm, newAlgorithm);
+        if (!priority)
+            result.append(WTFMove(item));
+        else if (priority.value() == newAlgorithm) {
+            strongest = item.algorithm;
+
+            result.clear();
+            result.append(WTFMove(item));
+        }
+    }
+
+    return result;
+}
+
+bool matchIntegrityMetadata(const CachedResource& resource, const String& integrityMetadataList)
+{
+    // FIXME: Consider caching digests on the CachedResource rather than always recomputing it.
+
+    // 1. Let parsedMetadata be the result of parsing metadataList.
+    auto parsedMetadata = parseIntegrityMetadata(integrityMetadataList);
+    
+    // 2. If parsedMetadata is no metadata, return true.
+    if (!parsedMetadata)
+        return true;
+
+    // 3. If response is not eligible for integrity validation, return false.
+    if (!isResponseEligible(resource))
+        return false;
+
+    // 4. If parsedMetadata is the empty set, return true.
+    if (parsedMetadata->isEmpty())
+        return true;
+
+    // 5. Let metadata be the result of getting the strongest metadata from parsedMetadata.
+    auto metadata = strongestMetadataFromSet(WTFMove(*parsedMetadata));
+
+    const auto& sharedBuffer = *resource.resourceBuffer();
+    
+    // 6. For each item in metadata:
+    for (auto& item : metadata) {
+        // 1. Let algorithm be the alg component of item.
+        auto algorithm = item.algorithm;
+        
+        // 2. Let expectedValue be the val component of item.
+        auto expectedValue = decodeEncodedResourceCryptographicDigest(item);
+
+        // 3. Let actualValue be the result of applying algorithm to response.
+        auto actualValue = cryptographicDigestForBytes(algorithm, sharedBuffer.data(), sharedBuffer.size());
+
+        // 4. If actualValue is a case-sensitive match for expectedValue, return true.
+        if (expectedValue && actualValue.value == expectedValue->value)
+            return true;
+    }
+    
+    return false;
+}
+
+}
diff --git a/Source/WebCore/loader/SubresourceIntegrity.h b/Source/WebCore/loader/SubresourceIntegrity.h
new file mode 100644 (file)
index 0000000..841cb40
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class CachedResource;
+
+bool matchIntegrityMetadata(const CachedResource&, const String& integrityMetadata);
+
+}
index db9c51d..029f188 100644 (file)
@@ -290,3 +290,5 @@ webkitImageReadyEventEnabled initial=false
 shouldSuppressKeyboardInputDuringProvisionalNavigation initial=false
 
 langAttributeAwareFormControlUIEnabled initial=false
+
+subresourceIntegrityEnabled initial=true