Diff
Modified: trunk/LayoutTests/ChangeLog (223677 => 223678)
--- trunk/LayoutTests/ChangeLog 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/ChangeLog 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1,3 +1,32 @@
+2017-10-18 Ryosuke Niwa <[email protected]>
+
+ Don't expose raw HTML in pasteboard to the web content
+ https://bugs.webkit.org/show_bug.cgi?id=178422
+
+ Reviewed by Wenson Hsieh.
+
+ Added tests to copy & paste web contents within the same origin as well as cross origin.
+
+ * TestExpectations:
+ * editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt: Now contains DOCTYPE.
+ * editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Ditto.
+ * editing/pasteboard/onpaste-text-html-expected.txt: Rebaselined as now inline styles are stripped.
+ * editing/pasteboard/onpaste-text-html.html: Strip away the inline style data since they differ on each platform.
+ * http/tests/misc/copy-resolves-urls-expected.txt:
+ * http/tests/misc/copy-resolves-urls.html: Now uses blob URL for the pasted image as expected.
+ * http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin-expected.txt: Added.
+ * http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin.html: Added.
+ * http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin-expected.txt: Added.
+ * http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin.html: Added.
+ * http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin-expected.txt: Added.
+ * http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin.html: Added.
+ * http/tests/security/clipboard/resources/content-to-copy.html: Added.
+ * http/tests/security/clipboard/resources/subdirectory/paste-html.html: Added.
+ * platform/ios/TestExpectations: Unskip tests that have started passing.
+ * platform/mac-wk1/TestExpectations: Unskip the drag & drop test which only works in Mac WK1.
+ * platform/win/TestExpectations: Skip the newly added tests since we don't support custom pasteboard
+ data on Windows port.
+
2017-10-18 Chris Dumez <[email protected]>
Implement ServiceWorkerRegistration.scope / updateViaCache
Modified: trunk/LayoutTests/TestExpectations (223677 => 223678)
--- trunk/LayoutTests/TestExpectations 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/TestExpectations 2017-10-19 05:44:33 UTC (rev 223678)
@@ -76,6 +76,7 @@
editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Skip ]
editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html [ Skip ]
editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html [ Skip ]
+http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin.html [ Skip ]
editing/pasteboard/drag-end-crash-accessing-item-list.html [ Skip ]
editing/pasteboard/data-transfer-item-list-add-file-on-drag.html [ Skip ]
Modified: trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt (223677 => 223678)
--- trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -5,7 +5,7 @@
"text/plain": ""
},
"drop": {
- "text/html": "<strong style=\"font-style: normal; font-variant-caps: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-family: -apple-system; font-size: 150px; white-space: nowrap; color: purple;\">Rich text</strong>",
+ "text/html": "<!DOCTYPE html><strong style=\"font-family: -apple-system; font-size: 150px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: nowrap; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; color: purple;\">Rich text</strong>",
"text/plain": "Rich text"
}
}
Modified: trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt (223677 => 223678)
--- trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1,7 +1,7 @@
Rich text
{
"paste": {
- "text/html": "<span style=\"...\"\">Rich text</span>",
+ "text/html": "<!DOCTYPE html><span style=\"...\"\">Rich text</span>",
"text/plain": "Rich text"
}
}
Modified: trunk/LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt (223677 => 223678)
--- trunk/LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1,5 +1,5 @@
CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the clipboard during an onpaste event.
-CONSOLE MESSAGE: line 23: text/html: <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; float: none; display: inline !important;">This test verifies that we can get text/html from the clipboard during an onpaste event.<span class="Apple-converted-space"> </span></span>
+CONSOLE MESSAGE: line 23: text/html: <span style="...">This test verifies that we can get text/html from the clipboard during an onpaste event.<span class="Apple-converted-space"> </span></span>
This test verifies that we can get text/html from the clipboard during an onpaste event. This test requires DRT.
Paste content in this div.This test verifies that we can get text/html from the clipboard during an onpaste event.
PASS
Modified: trunk/LayoutTests/editing/pasteboard/onpaste-text-html.html (223677 => 223678)
--- trunk/LayoutTests/editing/pasteboard/onpaste-text-html.html 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/editing/pasteboard/onpaste-text-html.html 2017-10-19 05:44:33 UTC (rev 223678)
@@ -20,7 +20,7 @@
{
console.log("text/plain: " + ev.clipboardData.getData("text/plain"));
// Remove the font name because it varies depending on the platform.
- console.log("text/html: " + removeFontName(ev.clipboardData.getData("text/html")));
+ console.log("text/html: " + ev.clipboardData.getData("text/html").replace(/style="[^"]+"/, 'style="..."'));
if (ev.clipboardData.getData("text/html") != undefined)
document.getElementById("results").innerHTML = "PASS";
}
Modified: trunk/LayoutTests/fast/events/ondrop-text-html-expected.txt (223677 => 223678)
--- trunk/LayoutTests/fast/events/ondrop-text-html-expected.txt 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/fast/events/ondrop-text-html-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1,4 +1,4 @@
CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the drag object during an ondrop event.
-CONSOLE MESSAGE: line 23: text/html: <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; float: none; display: inline !important;">This test verifies that we can get text/html from the drag object during an ondrop event.<span class="Apple-converted-space"> </span></span>
+CONSOLE MESSAGE: line 23: text/html: <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-size: medium; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;">This test verifies that we can get text/html from the drag object during an ondrop event.<span class="Apple-converted-space"> </span></span>
This test verifies that we can get text/html from the drag object during an ondrop event. This test requires DRT.
PASS
Modified: trunk/LayoutTests/http/tests/misc/copy-resolves-urls-expected.txt (223677 => 223678)
--- trunk/LayoutTests/http/tests/misc/copy-resolves-urls-expected.txt 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/http/tests/misc/copy-resolves-urls-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -2,4 +2,4 @@
link
link
-<a href="" src=""
+<a href="" src=""
Modified: trunk/LayoutTests/http/tests/misc/copy-resolves-urls.html (223677 => 223678)
--- trunk/LayoutTests/http/tests/misc/copy-resolves-urls.html 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/http/tests/misc/copy-resolves-urls.html 2017-10-19 05:44:33 UTC (rev 223678)
@@ -42,13 +42,15 @@
{
var pasteHere = document.getElementById("pastehere");
var results = document.getElementById("results");
- results.appendChild(document.createTextNode(pasteHere.innerHTML));
+ results.appendChild(document.createTextNode(pasteHere.innerHTML.replace(/blob\:http\:\/\/localhost\:8080\/[a-z0-9\-]+/, 'blob://localhost:8080/...')));
if (window.testRunner)
testRunner.notifyDone();
}
-if (document.location.search == "?paste")
- doPaste();
-else
- test();
+window._onload_ = () => {
+ if (document.location.search == "?paste")
+ doPaste();
+ else
+ test();
+}
</script>
Added: trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin-expected.txt (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,24 @@
+This tests copying and pasting HTML by the default action. WebKit should sanitize the HTML across origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+html in DataTransfer
+PASS html.includes("hello") is true
+PASS fragment = (new DOMParser).parseFromString(html, "text/html"); img = fragment.querySelector("img"); !!img is true
+PASS new URL(img.src).protocol is "blob:"
+PASS new URL(fragment.querySelector(".same-origin-frame").src).protocol is "blob:"
+PASS new URL(fragment.querySelector(".cross-origin-frame").src).protocol is "blob:"
+PASS frames.length is 2
+PASS new URL(frames[0].src).protocol is "blob:"
+PASS frames[0].canAccessContentDocument is true
+PASS frames[0].hasImage is true
+PASS frames[0].imageWidth is 80
+PASS new URL(frames[1].src).protocol is "blob:"
+PASS frames[1].canAccessContentDocument is true
+PASS frames[1].hasImage is true
+PASS frames[1].imageWidth is 80
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin.html (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin.html 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<p id="description"></p>
+<div id="console"></div>
+<div id="container">
+<button _onclick_="runTest()">Copy</button>
+<div><br></div>
+<div id="source" contenteditable>
+ hello, <meta content="some secret"><!-- secret -->
+ <img _onclick_="dangerousCode()" src=""
+ <iframe class="same-origin-frame" src="" width=80 height=80></iframe>
+ <iframe class="cross-origin-frame" src="" width="100" height="100"></iframe>
+</div>
+<iframe id="destinationFrame" src=""
+</div>
+<script>
+
+description('This tests copying and pasting HTML by the default action. WebKit should sanitize the HTML across origin.');
+jsTestIsAsync = true;
+
+if (window.internals)
+ internals.settings.setCustomPasteboardDataEnabled(true);
+
+function runTest() {
+ document.getElementById('source').focus();
+ document.execCommand('selectAll');
+ document.execCommand('copy');
+ getSelection().removeAllRanges();
+ setTimeout(() => {
+ destinationFrame.postMessage({type: 'paste'}, '*');
+ }, 0);
+}
+
+window._onmessage_ = function (event) {
+ if (event.data.type == 'pasted') {
+ html = event.data.html;
+ debug('html in DataTransfer');
+ shouldBeTrue('html.includes("hello")');
+ shouldBeTrue('fragment = (new DOMParser).parseFromString(html, "text/html"); img = fragment.querySelector("img"); !!img');
+ shouldBeEqualToString('new URL(img.src).protocol', 'blob:');
+ shouldBeEqualToString('new URL(fragment.querySelector(".same-origin-frame").src).protocol', 'blob:');
+ shouldBeEqualToString('new URL(fragment.querySelector(".cross-origin-frame").src).protocol', 'blob:');
+ } else if (event.data.type == 'checkedState') {
+ frames = event.data.frames;
+ shouldBe('frames.length', '2');
+ shouldBeEqualToString('new URL(frames[0].src).protocol', 'blob:');
+ shouldBeTrue('frames[0].canAccessContentDocument');
+ shouldBeTrue('frames[0].hasImage');
+ shouldBe('frames[0].imageWidth', '80');
+ shouldBeEqualToString('new URL(frames[1].src).protocol', 'blob:');
+ shouldBeTrue('frames[1].canAccessContentDocument');
+ shouldBeTrue('frames[1].hasImage');
+ shouldBe('frames[1].imageWidth', '80');
+ if (window.testRunner)
+ container.remove();
+ finishJSTest();
+ }
+}
+
+if (window.testRunner)
+ window._onload_ = runTest;
+
+setTimeout(finishJSTest, 3000);
+
+</script>
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin-expected.txt (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,24 @@
+CONSOLE MESSAGE: line 233: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+CONSOLE MESSAGE: line 233: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+This tests copying and pasting HTML by the default action. WebKit should not sanitize the HTML in the same document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS html = event.clipboardData.getData("text/html"); html.includes("hello") is true
+PASS destination.innerHTML = html; img = destination.querySelector("img"); !!img is true
+PASS new URL(img.src).protocol is "http:"
+PASS html.includes("http://localhost:8000/security/clipboard/resources/content-to-copy.html") is true
+PASS html.includes("secret") is false
+destination.innerHTML = ""
+PASS destination.textContent.includes("hello") is true
+PASS destination.innerHTML.includes("secret") is false
+PASS destination.innerHTML.includes("dangerousCode") is false
+PASS destination.querySelector("img"); !!img is true
+PASS new URL(img.src).protocol is "http:"
+PASS source.querySelector("iframe").contentDocument is null
+PASS destination.querySelector("iframe").contentDocument is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin.html (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin.html 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<p id="description"></p>
+<div id="console"></div>
+<div id="container">
+<button _onclick_="runTest()">Copy</button>
+<div><br></div>
+<div id="source" contenteditable>
+ hello, <meta content="some secret"><!-- secret -->
+ <img _onclick_="dangerousCode()" src=""
+ <iframe src=""
+</div>
+<div id="destination" _onpaste_="doPaste(event)" contenteditable>Paste here</div>
+</div>
+<script>
+
+description('This tests copying and pasting HTML by the default action. WebKit should not sanitize the HTML in the same document.');
+jsTestIsAsync = true;
+
+if (window.internals)
+ internals.settings.setCustomPasteboardDataEnabled(true);
+
+function runTest() {
+ document.getElementById('source').focus();
+ document.execCommand('selectAll');
+ document.execCommand('copy');
+ setTimeout(() => {
+ document.getElementById('destination').focus();
+ document.execCommand('selectAll');
+ if (window.testRunner)
+ document.execCommand('paste');
+ }, 0);
+}
+
+function doPaste(event) {
+ shouldBeTrue('html = event.clipboardData.getData("text/html"); html.includes("hello")');
+ shouldBeTrue('destination.innerHTML = html; img = destination.querySelector("img"); !!img');
+ shouldBeEqualToString('new URL(img.src).protocol', 'http:');
+ shouldBeTrue('html.includes("http://localhost:8000/security/clipboard/resources/content-to-copy.html")');
+ shouldBeFalse('html.includes("secret")');
+ evalAndLog('destination.innerHTML = ""');
+
+ const observer = new MutationObserver((recordList) => {
+ for (const record of recordList) {
+ for (const node of record.addedNodes) {
+ if (node.nodeValue === null)
+ continue;
+ if (node.nodeValue.includes('secret'))
+ testFailed(`Saw secret in a node ${node}`);
+ if (node.nodeValue.includes('dangerousCode'))
+ testFailed(`Saw dangerous code in a node ${node}`);
+ }
+ }
+ });
+ observer.observe(destination, {childList: true, subtree: true});
+
+ window._onmessage_ = checkFrameAccess;
+}
+
+function checkFrameAccess() {
+ shouldBeTrue('destination.textContent.includes("hello")');
+ shouldBeFalse('destination.innerHTML.includes("secret")');
+ shouldBeFalse('destination.innerHTML.includes("dangerousCode")');
+ shouldBeTrue('destination.querySelector("img"); !!img');
+ shouldBeEqualToString('new URL(img.src).protocol', 'http:');
+ shouldBeNull('source.querySelector("iframe").contentDocument');
+ shouldBeNull('destination.querySelector("iframe").contentDocument');
+ container.remove();
+ finishJSTest();
+}
+
+if (window.testRunner)
+ window._onload_ = runTest;
+
+var successfullyParsed = true;
+
+</script>
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin-expected.txt (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin-expected.txt 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,24 @@
+CONSOLE MESSAGE: line 233: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+This tests draggin and dropping HTML by the default action. WebKit should not sanitize the HTML in the same document.
+To manually test, drag & drop the content in the block above to the blue box on the right.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS html = event.dataTransfer.getData("text/html"); html.includes("Drag this,") is true
+PASS destination.innerHTML = html; img = destination.querySelector("img"); !!img is true
+PASS new URL(img.src).protocol is "http:"
+PASS html.includes("http://localhost:8000/security/clipboard/resources/content-to-copy.html") is true
+PASS html.includes("secret") is false
+destination.innerHTML = ""
+PASS source.innerHTML is ""
+PASS destination.textContent.includes("Drag this,") is true
+PASS destination.innerHTML.includes("secret") is false
+PASS destination.innerHTML.includes("dangerousCode") is false
+PASS destination.querySelector("img"); !!img is true
+PASS new URL(img.src).protocol is "http:"
+PASS destination.querySelector("iframe").contentDocument is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin.html (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin.html 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<div id="container">
+<style>
+ #container { position: relative; }
+ #container > div { position: relative; border: solid 1px black; width: 200px; height: 200px; }
+</style>
+<div id="source" contenteditable>
+ Drag this, <meta content="some secret"><!-- secret -->
+ <img _onclick_="dangerousCode()" src=""
+ <iframe src="" width=100 height=100></iframe>
+</div>
+<div id="destination" style="position: absolute; top: 0px; left: 220px; border-color: blue;" _ondrop_="doDrop(event)" contenteditable></div>
+</div>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description('This tests draggin and dropping HTML by the default action. WebKit should not sanitize the HTML in the same document.<br>'
+ + 'To manually test, drag & drop the content in the block above to the blue box on the right.');
+jsTestIsAsync = true;
+
+window._onload_ = () => {
+ document.getElementById('source').focus();
+ document.execCommand('selectAll');
+ if (window.testRunner) {
+ internals.settings.setCustomPasteboardDataEnabled(true);
+ const sourceRect = source.getBoundingClientRect();
+ const destinationRect = destination.getBoundingClientRect();
+ eventSender.mouseMoveTo(sourceRect.x + 10, sourceRect.y + 10);
+ eventSender.mouseDown();
+ eventSender.leapForward(500);
+ eventSender.mouseMoveTo(destinationRect.x + 10, destinationRect.y + 10);
+ eventSender.mouseUp();
+ }
+}
+
+function doDrop(event) {
+ shouldBeTrue('html = event.dataTransfer.getData("text/html"); html.includes("Drag this,")');
+ shouldBeTrue('destination.innerHTML = html; img = destination.querySelector("img"); !!img');
+ shouldBeEqualToString('new URL(img.src).protocol', 'http:');
+ shouldBeTrue('html.includes("http://localhost:8000/security/clipboard/resources/content-to-copy.html")');
+ shouldBeFalse('html.includes("secret")');
+ evalAndLog('destination.innerHTML = ""');
+
+ const observer = new MutationObserver((recordList) => {
+ for (const record of recordList) {
+ for (const node of record.addedNodes) {
+ if (node.nodeValue === null)
+ continue;
+ if (node.nodeValue.includes('secret'))
+ testFailed(`Saw secret in a node ${node}`);
+ if (node.nodeValue.includes('dangerousCode'))
+ testFailed(`Saw dangerous code in a node ${node}`);
+ }
+ }
+ });
+ observer.observe(destination, {childList: true, subtree: true});
+
+ window._onmessage_ = checkFrameAccess;
+}
+
+function checkFrameAccess() {
+ shouldBeEqualToString('source.innerHTML', '');
+ shouldBeTrue('destination.textContent.includes("Drag this,")');
+ shouldBeFalse('destination.innerHTML.includes("secret")');
+ shouldBeFalse('destination.innerHTML.includes("dangerousCode")');
+ shouldBeTrue('destination.querySelector("img"); !!img');
+ shouldBeEqualToString('new URL(img.src).protocol', 'http:');
+ shouldBeNull('destination.querySelector("iframe").contentDocument');
+
+ if (window.testRunner)
+ container.remove();
+ finishJSTest();
+}
+
+var successfullyParsed = true;
+
+</script>
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/http/tests/security/clipboard/resources/content-to-copy.html (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/resources/content-to-copy.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/resources/content-to-copy.html 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body>
+<img src=""
+<script>
+top.postMessage({type: 'contentLoaded'}, '*');
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/http/tests/security/clipboard/resources/subdirectory/paste-html.html (0 => 223678)
--- trunk/LayoutTests/http/tests/security/clipboard/resources/subdirectory/paste-html.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/clipboard/resources/subdirectory/paste-html.html 2017-10-19 05:44:33 UTC (rev 223678)
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+_onmessage_ = function (event) {
+ if (event.data.type == 'paste') {
+ document.body.focus();
+ document.execCommand('selectAll');
+ if (window.testRunner)
+ document.execCommand('paste');
+ }
+}
+
+let frames = [];
+function doPaste(event) {
+ top.postMessage({type: 'pasted', html: event.clipboardData.getData('text/html')}, '*');
+ setTimeout(() => {
+ frames = Array.from(document.body.querySelectorAll('iframe'));
+
+ Promise.all(frames.map((frame) => {
+ return new Promise((resolve) => {
+ const waitForImage = () => {
+ const img = frame.contentDocument.querySelector('img');
+ if (img && !img.complete)
+ img._onload_ = resolve;
+ else
+ resolve();
+ }
+
+ if (frame.contentDocument && frame.contentDocument.body.innerHTML == '')
+ frame._onload_ = waitForImage;
+ else
+ waitForImage();
+
+ });
+ })).then(checkState);
+ }, 0);
+}
+
+function checkState() {
+ top.postMessage({
+ type: 'checkedState',
+ html: document.body.innerHTML,
+ frames: frames.map((frame) => {
+ const img = frame.contentDocument ? frame.contentDocument.querySelector('img') : null;
+ return {
+ src: frame.src,
+ class: frame.className,
+ canAccessContentDocument: !!frame.contentDocument,
+ hasImage: !!img,
+ imageWidth: img ? img.width : null,
+ }
+ })}, '*');
+}
+
+</script>
+<body _onpaste_="doPaste(event)" contenteditable>
+Paste here.
+</body>
+</html>
Modified: trunk/LayoutTests/platform/ios/TestExpectations (223677 => 223678)
--- trunk/LayoutTests/platform/ios/TestExpectations 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/platform/ios/TestExpectations 2017-10-19 05:44:33 UTC (rev 223678)
@@ -2087,8 +2087,6 @@
editing/pasteboard/merge-end-5.html [ Failure ]
editing/pasteboard/merge-end-list-2.html [ Failure ]
editing/pasteboard/merge-end-table-2.html [ Failure ]
-editing/pasteboard/onpaste-text-html-types.html [ Failure ]
-editing/pasteboard/onpaste-text-html.html [ Failure ]
editing/pasteboard/page-zoom.html [ Failure ]
editing/pasteboard/paste-before-tab-span.html [ Failure ]
editing/pasteboard/paste-blockquote-3.html [ Failure ]
@@ -2099,7 +2097,6 @@
editing/pasteboard/paste-sanitize-crash-1.html [ Failure ]
editing/pasteboard/paste-sanitize-crash-2.html [ Failure ]
editing/pasteboard/paste-text-events.html [ Failure ]
-editing/pasteboard/pasting-empty-html-falls-back-to-text.html [ Failure ]
editing/pasteboard/smart-paste-001.html [ Failure ]
editing/pasteboard/smart-paste-002.html [ Failure ]
editing/pasteboard/smart-paste-003.html [ Failure ]
Modified: trunk/LayoutTests/platform/mac-wk1/TestExpectations (223677 => 223678)
--- trunk/LayoutTests/platform/mac-wk1/TestExpectations 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/platform/mac-wk1/TestExpectations 2017-10-19 05:44:33 UTC (rev 223678)
@@ -17,6 +17,7 @@
editing/pasteboard/drag-end-crash-accessing-item-list.html [ Pass ]
editing/pasteboard/data-transfer-item-list-add-file-on-drag.html [ Pass ]
editing/pasteboard/data-transfer-items-drop-file.html [ Pass ]
+http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin.html [ Pass ]
#//////////////////////////////////////////////////////////////////////////////////////////
# End platform-specific directories.
Modified: trunk/LayoutTests/platform/win/TestExpectations (223677 => 223678)
--- trunk/LayoutTests/platform/win/TestExpectations 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/LayoutTests/platform/win/TestExpectations 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1135,8 +1135,11 @@
[ Debug ] editing/selection/4975120.html [ Skip ] # Debug Assertion
# Custom pasteboard data is not supported on Windows.
+editing/pasteboard/clipboard-customData.html [ Skip ]
http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html [ Skip ]
http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html [ Skip ]
+http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin.html [ Skip ]
+http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin.html [ Skip ]
webkit.org/b/140783 [ Release ] editing/pasteboard/copy-standalone-image.html [ Failure ImageOnlyFailure ]
webkit.org/b/140783 [ Debug ] editing/pasteboard/copy-standalone-image.html [ Skip ] # Debug Assertion
@@ -1154,8 +1157,6 @@
editing/pasteboard/data-transfer-items-drag-drop-file.html [ Skip ]
editing/pasteboard/data-transfer-items-drag-drop-entry.html [ Skip ]
editing/pasteboard/data-transfer-items-drag-drop-string.html [ Skip ]
-# TODO Custom MIME type support in DataTransfer is not yet implemented.
-editing/pasteboard/clipboard-customData.html [ Skip ]
# TODO Windows does not have global selection.
editing/pasteboard/paste-global-selection.html [ Skip ]
webkit.org/b/116564 editing/pasteboard/copy-without-selection.html [ Skip ]
Modified: trunk/Source/WebCore/ChangeLog (223677 => 223678)
--- trunk/Source/WebCore/ChangeLog 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/ChangeLog 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1,3 +1,61 @@
+2017-10-18 Ryosuke Niwa <[email protected]>
+
+ Don't expose raw HTML in pasteboard to the web content
+ https://bugs.webkit.org/show_bug.cgi?id=178422
+ <rdar://problem/34567052>
+
+ Reviewed by Wenson Hsieh.
+
+ This patch enables HTML sanitization added in r223440 when WebKit pastes & concludes edit drag as opposed to
+ just when dataTransfer.get is used. This is important to avoid leaking privacy sensitive information such as
+ local file paths and pasting potentially harmful content such as scripts in event handler serialized by
+ WebKit prior to r223462. In addition, we start using blob URLs in the pasted content instead of retaining
+ the original URL and overriding the document loader like r222839 for RTFD and r222119 for image files.
+
+ To do this, a new superclass FrameWebContentReader of PasteboardWebContentReader and WebContentMarkupReader
+ is introduced, and helper functions are extracted out of WebContentMarkupReader in WebContentReaderCocoa.mm
+ to be also used in WebContentReader.
+
+ Tests: http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-across-origin.html
+ http/tests/security/clipboard/copy-paste-html-cross-origin-iframe-in-same-origin.html
+ http/tests/security/clipboard/drag-drop-html-cross-origin-iframe-in-same-origin.html
+ PasteWebArchive.SanitizesHTML
+
+ * editing/WebContentReader.cpp:
+ (WebCore::FrameWebContentReader::shouldSanitize const): Moved from WebContentMarkupReader.
+ * editing/WebContentReader.h:
+ (WebCore::FrameWebContentReader): Added to share code between WebContentReader and WebContentMarkupReader.
+ (WebCore::FrameWebContentReader::FrameWebContentReader): Added.
+ * editing/cocoa/EditorCocoa.mm:
+ (WebCore::Editor::writeSelectionToPasteboard): Store the content's origin in the pasteboard so that we can
+ avoid sanitizing the content when pasting into the same document. This is important since converting all URLs
+ into blob URLs would break editors on the Web which tracks images, etc... in the content using URLs.
+ (WebCore::Editor::writeSelection): Ditto.
+ * editing/cocoa/WebContentReaderCocoa.mm:
+ (WebCore::MarkupAndArchive): Replaced FragmentAndArchive. Now returns the markup string in the archive
+ instead of the parsed fragment.
+ (WebCore::extractMarkupAndArchive): Renamed from createFragmentFromWebArchive. Now returns the markup string.
+ (WebCore::sanitizeMarkupWithArchive): Extracted out of WebContentMarkupReader::readWebArchive to share code
+ between WebContentReader and WebContentMarkupReader, and added the code to handle subframes recursively.
+ As inefficient as this code is, we can't delay the conversion of subframes' marksup until later time since
+ the main frame's markup would contain blob URLs to refer to those subframes.
+ (WebCore::WebContentReader::readWebArchive): Use sanitizeMarkupWithArchive when shouldSanitize() is true.
+ Don't add the subresources to the document loader when the content will be loaded into the same origin since
+ subresouces are mostly likely available in the document anyway.
+ (WebCore::WebContentMarkupReader::readWebArchive):
+ * platform/Pasteboard.h:
+ (WebCore::PasteboardWebContent): Added contentOrigin.
+ * platform/PasteboardWriterData.h:
+ (WebCore::PasteboardWriterData): Ditto.
+ * platform/ios/PasteboardIOS.mm:
+ (WebCore::Pasteboard::read): Read the origin before branching out to readRespectingUTIFidelities.
+ * platform/ios/PlatformPasteboardIOS.mm:
+ (WebCore::PlatformPasteboard::write): Record the content origin into the pasteboard.
+ * platform/mac/PasteboardMac.mm:
+ (WebCore::Pasteboard::write): Ditto.
+ * platform/mac/PasteboardWriter.mm:
+ (WebCore::createPasteboardWriter): Ditto.
+
2017-10-18 Sam Weinig <[email protected]>
Another attempt to fix the windows build.
Modified: trunk/Source/WebCore/editing/WebContentReader.cpp (223677 => 223678)
--- trunk/Source/WebCore/editing/WebContentReader.cpp 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/editing/WebContentReader.cpp 2017-10-19 05:44:33 UTC (rev 223678)
@@ -39,9 +39,10 @@
fragment->appendChild(newFragment.get());
}
-bool WebContentMarkupReader::shouldSanitize() const
+bool FrameWebContentReader::shouldSanitize() const
{
- return frame.document() && frame.document()->originIdentifierForPasteboard() != contentOrigin;
+ ASSERT(frame.document());
+ return frame.document()->originIdentifierForPasteboard() != contentOrigin;
}
}
Modified: trunk/Source/WebCore/editing/WebContentReader.h (223677 => 223678)
--- trunk/Source/WebCore/editing/WebContentReader.h 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/editing/WebContentReader.h 2017-10-19 05:44:33 UTC (rev 223678)
@@ -33,9 +33,21 @@
class ArchiveResource;
-class WebContentReader final : public PasteboardWebContentReader {
+class FrameWebContentReader : public PasteboardWebContentReader {
public:
Frame& frame;
+
+ FrameWebContentReader(Frame& frame)
+ : frame(frame)
+ {
+ }
+
+protected:
+ bool shouldSanitize() const;
+};
+
+class WebContentReader final : public FrameWebContentReader {
+public:
Range& context;
const bool allowPlainText;
@@ -43,7 +55,7 @@
bool madeFragmentFromPlainText;
WebContentReader(Frame& frame, Range& context, bool allowPlainText)
- : frame(frame)
+ : FrameWebContentReader(frame)
, context(context)
, allowPlainText(allowPlainText)
, madeFragmentFromPlainText(false)
@@ -65,19 +77,16 @@
bool readPlainText(const String&) override;
};
-class WebContentMarkupReader final : public PasteboardWebContentReader {
+class WebContentMarkupReader final : public FrameWebContentReader {
public:
- Frame& frame;
String markup;
explicit WebContentMarkupReader(Frame& frame)
- : frame(frame)
+ : FrameWebContentReader(frame)
{
}
private:
- bool shouldSanitize() const;
-
#if PLATFORM(COCOA)
bool readWebArchive(SharedBuffer&) override;
bool readFilenames(const Vector<String>&) override { return false; }
Modified: trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm (223677 => 223678)
--- trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm 2017-10-19 05:44:33 UTC (rev 223678)
@@ -156,6 +156,7 @@
NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
PasteboardWebContent content;
+ content.contentOrigin = m_frame.document()->originIdentifierForPasteboard();
content.canSmartCopyOrDelete = canSmartCopyOrDelete();
content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
content.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
@@ -173,6 +174,7 @@
NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
PasteboardWriterData::WebContent webContent;
+ webContent.contentOrigin = m_frame.document()->originIdentifierForPasteboard();
webContent.canSmartCopyOrDelete = canSmartCopyOrDelete();
webContent.dataInWebArchiveFormat = selectionInWebArchiveFormat();
webContent.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
Modified: trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm (223677 => 223678)
--- trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm 2017-10-19 05:44:33 UTC (rev 223678)
@@ -28,6 +28,7 @@
#import "ArchiveResource.h"
#import "Blob.h"
+#import "BlobURL.h"
#import "CachedResourceLoader.h"
#import "DOMURL.h"
#import "Document.h"
@@ -37,10 +38,12 @@
#import "FrameLoader.h"
#import "FrameLoaderClient.h"
#import "HTMLBodyElement.h"
+#import "HTMLIframeElement.h"
#import "HTMLImageElement.h"
#import "LegacyWebArchive.h"
#import "MainFrame.h"
#import "Page.h"
+#import "PublicURLManager.h"
#import "Settings.h"
#import "SocketProvider.h"
#import "WebArchiveResourceFromNSAttributedString.h"
@@ -181,12 +184,13 @@
return WTFMove(fragmentAndResources.fragment);
}
-struct FragmentAndArchive {
- Ref<DocumentFragment> fragment;
+struct MarkupAndArchive {
+ String markup;
+ Ref<ArchiveResource> mainResource;
Ref<Archive> archive;
};
-static std::optional<FragmentAndArchive> createFragmentFromWebArchive(Document& document, SharedBuffer& buffer, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
+static std::optional<MarkupAndArchive> extractMarkupAndArchive(SharedBuffer& buffer, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
{
auto archive = LegacyWebArchive::create(URL(), buffer);
if (!archive)
@@ -200,10 +204,57 @@
if (!canShowMIMETypeAsHTML(type))
return std::nullopt;
- auto markupString = String::fromUTF8(mainResource->data().data(), mainResource->data().size());
- auto fragment = createFragmentFromMarkup(document, markupString, mainResource->url(), DisallowScriptingAndPluginContent);
+ return MarkupAndArchive { String::fromUTF8(mainResource->data().data(), mainResource->data().size()), mainResource.releaseNonNull(), archive.releaseNonNull() };
+}
- return FragmentAndArchive { WTFMove(fragment), archive.releaseNonNull() };
+static String sanitizeMarkupWithArchive(Document& destinationDocument, MarkupAndArchive& markupAndArchive, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
+{
+ auto page = createPageForSanitizingWebContent();
+ Document* stagingDocument = page->mainFrame().document();
+ ASSERT(stagingDocument);
+ auto fragment = createFragmentFromMarkup(*stagingDocument, markupAndArchive.markup, markupAndArchive.mainResource->url(), DisallowScriptingAndPluginContent);
+
+ HashMap<AtomicString, AtomicString> blobURLMap;
+ for (const Ref<ArchiveResource>& subresource : markupAndArchive.archive->subresources()) {
+ auto blob = Blob::create(subresource->data(), subresource->mimeType());
+ String blobURL = DOMURL::createObjectURL(destinationDocument, blob);
+ blobURLMap.set(subresource->url().string(), blobURL);
+ }
+
+ auto contentOrigin = SecurityOrigin::create(markupAndArchive.mainResource->url());
+ for (const Ref<Archive>& subframeArchive : markupAndArchive.archive->subframeArchives()) {
+ RefPtr<ArchiveResource> subframeMainResource = subframeArchive->mainResource();
+ if (!subframeMainResource)
+ continue;
+
+ auto type = subframeMainResource->mimeType();
+ if (!canShowMIMETypeAsHTML(type))
+ continue;
+
+ auto subframeURL = subframeMainResource->url();
+ MarkupAndArchive subframeContent = { String::fromUTF8(subframeMainResource->data().data(), subframeMainResource->data().size()),
+ subframeMainResource.releaseNonNull(), subframeArchive.copyRef() };
+ auto subframeMarkup = sanitizeMarkupWithArchive(destinationDocument, subframeContent, canShowMIMETypeAsHTML);
+
+ CString utf8 = subframeMarkup.utf8();
+ Vector<uint8_t> blobBuffer;
+ blobBuffer.reserveCapacity(utf8.length());
+ blobBuffer.append(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
+ auto blob = Blob::create(WTFMove(blobBuffer), type);
+
+ String subframeBlobURL = DOMURL::createObjectURL(destinationDocument, blob);
+ blobURLMap.set(subframeURL.string(), subframeBlobURL);
+ }
+
+ replaceSubresourceURLs(fragment.get(), WTFMove(blobURLMap));
+
+ auto* bodyElement = stagingDocument->body();
+ ASSERT(bodyElement);
+ bodyElement->appendChild(fragment);
+
+ auto range = Range::create(*stagingDocument);
+ range->selectNodeContents(*bodyElement);
+ return createMarkup(range.get(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
}
bool WebContentReader::readWebArchive(SharedBuffer& buffer)
@@ -212,48 +263,45 @@
return false;
DeferredLoadingScope scope(frame);
- auto result = createFragmentFromWebArchive(*frame.document(), buffer, [&] (const String& type) {
+ auto result = extractMarkupAndArchive(buffer, [&] (const String& type) {
return frame.loader().client().canShowMIMETypeAsHTML(type);
});
if (!result)
return false;
- fragment = WTFMove(result->fragment);
- if (DocumentLoader* loader = frame.loader().documentLoader())
- loader->addAllArchiveResources(result->archive.get());
+ if (!shouldSanitize()) {
+ fragment = createFragmentFromMarkup(*frame.document(), result->markup, result->mainResource->url(), DisallowScriptingAndPluginContent);
+ return true;
+ }
+ String sanitizedMarkup = sanitizeMarkupWithArchive(*frame.document(), *result, [&] (const String& type) {
+ return frame.loader().client().canShowMIMETypeAsHTML(type);
+ });
+ fragment = createFragmentFromMarkup(*frame.document(), sanitizedMarkup, blankURL(), DisallowScriptingAndPluginContent);
+
return true;
}
bool WebContentMarkupReader::readWebArchive(SharedBuffer& buffer)
{
- auto page = createPageForSanitizingWebContent();
- Document* stagingDocument = page->mainFrame().document();
- ASSERT(stagingDocument);
+ if (!frame.document())
+ return false;
- DeferredLoadingScope scope(frame);
- auto result = createFragmentFromWebArchive(*stagingDocument, buffer, [&] (const String& type) {
+ auto result = extractMarkupAndArchive(buffer, [&] (const String& type) {
return frame.loader().client().canShowMIMETypeAsHTML(type);
});
if (!result)
return false;
- HashMap<AtomicString, AtomicString> blobURLMap;
- for (const Ref<ArchiveResource>& subresource : result->archive->subresources()) {
- auto blob = Blob::create(subresource->data(), subresource->mimeType());
- String blobURL = DOMURL::createObjectURL(*frame.document(), blob);
- blobURLMap.set(subresource->url().string(), blobURL);
+ if (!shouldSanitize()) {
+ markup = result->markup;
+ return true;
}
- replaceSubresourceURLs(result->fragment.get(), WTFMove(blobURLMap));
- auto* bodyElement = stagingDocument->body();
- ASSERT(bodyElement);
- bodyElement->appendChild(result->fragment);
+ markup = sanitizeMarkupWithArchive(*frame.document(), *result, [&] (const String& type) {
+ return frame.loader().client().canShowMIMETypeAsHTML(type);
+ });
- auto range = Range::create(*stagingDocument);
- range->selectNodeContents(*bodyElement);
- markup = createMarkup(range.get(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
-
return true;
}
Modified: trunk/Source/WebCore/platform/Pasteboard.h (223677 => 223678)
--- trunk/Source/WebCore/platform/Pasteboard.h 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/platform/Pasteboard.h 2017-10-19 05:44:33 UTC (rev 223678)
@@ -71,6 +71,7 @@
#if PLATFORM(COCOA)
WEBCORE_EXPORT PasteboardWebContent();
WEBCORE_EXPORT ~PasteboardWebContent();
+ String contentOrigin;
bool canSmartCopyOrDelete;
RefPtr<SharedBuffer> dataInWebArchiveFormat;
RefPtr<SharedBuffer> dataInRTFDFormat;
Modified: trunk/Source/WebCore/platform/PasteboardWriterData.h (223677 => 223678)
--- trunk/Source/WebCore/platform/PasteboardWriterData.h 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/platform/PasteboardWriterData.h 2017-10-19 05:44:33 UTC (rev 223678)
@@ -50,6 +50,7 @@
~WebContent();
#if PLATFORM(COCOA)
+ String contentOrigin;
bool canSmartCopyOrDelete;
RefPtr<SharedBuffer> dataInWebArchiveFormat;
RefPtr<SharedBuffer> dataInRTFDFormat;
Modified: trunk/Source/WebCore/platform/ios/PasteboardIOS.mm (223677 => 223678)
--- trunk/Source/WebCore/platform/ios/PasteboardIOS.mm 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/platform/ios/PasteboardIOS.mm 2017-10-19 05:44:33 UTC (rev 223678)
@@ -226,6 +226,7 @@
void Pasteboard::read(PasteboardWebContentReader& reader)
{
+ reader.contentOrigin = readOrigin();
if (respectsUTIFidelities()) {
readRespectingUTIFidelities(reader);
return;
@@ -238,8 +239,6 @@
if (!numberOfItems)
return;
- reader.contentOrigin = readOrigin();
-
NSArray *types = supportedWebContentPasteboardTypes();
int numberOfTypes = [types count];
Modified: trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm (223677 => 223678)
--- trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm 2017-10-19 05:44:33 UTC (rev 223678)
@@ -283,6 +283,10 @@
if (!content.dataInStringFormat.isEmpty())
addRepresentationsForPlainText(representationsToRegister.get(), content.dataInStringFormat);
+ PasteboardCustomData customData;
+ customData.origin = content.contentOrigin;
+ [representationsToRegister addData:customData.createSharedBuffer()->createNSData().get() forType:@(PasteboardCustomData::cocoaType())];
+
registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
}
Modified: trunk/Source/WebCore/platform/mac/PasteboardMac.mm (223677 => 223678)
--- trunk/Source/WebCore/platform/mac/PasteboardMac.mm 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/platform/mac/PasteboardMac.mm 2017-10-19 05:44:33 UTC (rev 223678)
@@ -138,6 +138,7 @@
if (!content.dataInStringFormat.isNull())
types.append(String(NSStringPboardType));
types.appendVector(content.clientTypes);
+ types.append(PasteboardCustomData::cocoaType());
m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
@@ -156,6 +157,11 @@
m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInHTMLFormat, NSHTMLPboardType, m_pasteboardName);
if (!content.dataInStringFormat.isNull())
m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInStringFormat, NSStringPboardType, m_pasteboardName);
+
+ PasteboardCustomData data;
+ data.origin = content.contentOrigin;
+ m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(data.createSharedBuffer().ptr(), PasteboardCustomData::cocoaType(), m_pasteboardName);
+
}
void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
Modified: trunk/Source/WebCore/platform/mac/PasteboardWriter.mm (223677 => 223678)
--- trunk/Source/WebCore/platform/mac/PasteboardWriter.mm 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebCore/platform/mac/PasteboardWriter.mm 2017-10-19 05:44:33 UTC (rev 223678)
@@ -28,6 +28,7 @@
#if PLATFORM(MAC)
+#import "Pasteboard.h"
#import "PasteboardWriterData.h"
#import "SharedBuffer.h"
#import <pal/spi/mac/NSPasteboardSPI.h>
@@ -114,6 +115,10 @@
for (unsigned i = 0; i < webContent->clientTypes.size(); ++i)
[pasteboardItem setData:webContent->clientData[i]->createNSData().get() forType:toUTIUnlessAlreadyUTI(webContent->clientTypes[i]).get()];
+
+ PasteboardCustomData customData;
+ customData.origin = webContent->contentOrigin;
+ [pasteboardItem setData:customData.createSharedBuffer()->createNSData().get() forType:toUTIUnlessAlreadyUTI(String(PasteboardCustomData::cocoaType())).get()];
}
return pasteboardItem;
Modified: trunk/Source/WebKit/ChangeLog (223677 => 223678)
--- trunk/Source/WebKit/ChangeLog 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebKit/ChangeLog 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1,3 +1,16 @@
+2017-10-18 Ryosuke Niwa <[email protected]>
+
+ Don't expose raw HTML in pasteboard to the web content
+ https://bugs.webkit.org/show_bug.cgi?id=178422
+
+ Reviewed by Wenson Hsieh.
+
+ Encode & decode the origin string of the copied content written into the system pasteboard.
+
+ * Shared/WebCoreArgumentCoders.cpp:
+ (IPC::ArgumentCoder<PasteboardWebContent>::encode):
+ (IPC::ArgumentCoder<PasteboardWebContent>::decode):
+
2017-10-18 Chris Dumez <[email protected]>
Implement ServiceWorkerRegistration.scope / updateViaCache
Modified: trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp (223677 => 223678)
--- trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1689,6 +1689,7 @@
void ArgumentCoder<PasteboardWebContent>::encode(Encoder& encoder, const PasteboardWebContent& content)
{
+ encoder << content.contentOrigin;
encoder << content.canSmartCopyOrDelete;
encoder << content.dataInStringFormat;
encoder << content.dataInHTMLFormat;
@@ -1703,6 +1704,8 @@
bool ArgumentCoder<PasteboardWebContent>::decode(Decoder& decoder, PasteboardWebContent& content)
{
+ if (!decoder.decode(content.contentOrigin))
+ return false;
if (!decoder.decode(content.canSmartCopyOrDelete))
return false;
if (!decoder.decode(content.dataInStringFormat))
Modified: trunk/Tools/ChangeLog (223677 => 223678)
--- trunk/Tools/ChangeLog 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Tools/ChangeLog 2017-10-19 05:44:33 UTC (rev 223678)
@@ -1,3 +1,16 @@
+2017-10-18 Ryosuke Niwa <[email protected]>
+
+ Don't expose raw HTML in pasteboard to the web content
+ https://bugs.webkit.org/show_bug.cgi?id=178422
+
+ Reviewed by Wenson Hsieh.
+
+ Added a test case for sanitizing web archive in the system pasteboard to strip privacy sensitive information
+ such as local file paths and potentially harmful scripts like event handlers serialized by WebKit prior to r223462.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm:
+ (PasteWebArchive.SanitizesHTML):
+
2017-10-18 Youenn Fablet <[email protected]>
TestController should clear all fetch caches when resetting its state
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm (223677 => 223678)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm 2017-10-19 05:15:05 UTC (rev 223677)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm 2017-10-19 05:44:33 UTC (rev 223678)
@@ -77,6 +77,30 @@
EXPECT_WK_STREQ("hello, world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
}
+TEST(PasteWebArchive, SanitizesHTML)
+{
+ auto* url = "" URLWithString:@"file:///some-file.html"];
+ auto* markup = [@"<!DOCTYPE html><html><body><meta content=\"secret\"><b _onmouseover_=\"dangerousCode()\">hello</b>"
+ "<!-- secret-->, world<script>dangerousCode()</script></html>" dataUsingEncoding:NSUTF8StringEncoding];
+ auto mainResource = adoptNS([[WebResource alloc] initWithData:markup URL:url MIMEType:@"text/html" textEncodingName:@"utf-8" frameName:nil]);
+ auto archive = adoptNS([[WebArchive alloc] initWithMainResource:mainResource.get() subresources:nil subframeArchives:nil]);
+
+ [[NSPasteboard generalPasteboard] declareTypes:@[WebArchivePboardType] owner:nil];
+ [[NSPasteboard generalPasteboard] setData:[archive data] forType:WebArchivePboardType];
+
+ auto webView = createWebViewWithCustomPasteboardDataEnabled();
+ [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
+ [webView paste:nil];
+
+ EXPECT_WK_STREQ("hello, world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
+ [webView stringByEvaluatingJavaScript:@"editor.focus(); getSelection().setPosition(editor, 0)"];
+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('bold')"].boolValue);
+ [webView stringByEvaluatingJavaScript:@"getSelection().modify('move', 'forward', 'lineboundary')"];
+ EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('bold')"].boolValue);
+ EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"editor.innerHTML.includes('secret')"].boolValue);
+ EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"editor.innerHTML.includes('dangerousCode')"].boolValue);
+}
+
#endif // WK_API_ENABLED && PLATFORM(MAC)