Source/WebCore/ChangeLog

 12018-01-21 Ryosuke Niwa <rniwa@webkit.org>
 2
 3 Blob conversion and sanitization doesn't work with Microsoft Word for Mac 2011
 4 https://bugs.webkit.org/show_bug.cgi?id=181616
 5 <rdar://problem/36484908>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 The bug was caused by WebContentReader::readHTML and WebContentMarkupReader::readHTML not sanitizing plain HTML string
 10 as done for web archives even when custom pasteboard data is enabled. Fixed the bug by doing the sanitization.
 11
 12 Unfortunately, we can't make file URLs available in this case because WebContent process doesn't have sandbox extensions
 13 to access local files referenced by the HTML source in the clipboard, and we can't make WebContent process request for
 14 a sandbox extension¸on an arbitrary local file, as it would defeat the whole point of sandboxing.
 15
 16 Instead, we strip away all HTML attributes referencing a file URL when sanitizing text/html from the clipboard to avoid
 17 exposing local file paths, which can reveal privacy & security sensitive information such as the user's full name, and
 18 the location of private containers of other applications in the system.
 19
 20 Tests: PasteHTML.DoesNotSanitizeHTMLWhenCustomPasteboardDataIsDisabled
 21 PasteHTML.DoesNotStripHTTPURLsWhenCustomPasteboardDataIsDisabled
 22 PasteHTML.ExposesHTMLTypeInDataTransfer
 23 PasteHTML.KeepsHTTPURLs
 24 PasteHTML.SanitizesHTML
 25
 26 * editing/cocoa/WebContentReaderCocoa.mm:
 27 (WebCore::WebContentReader::readHTML): Fixed the bug by sanitizing the markup, and stripping away file URLs.
 28 (WebCore::WebContentMarkupReader::readHTML): Ditto.
 29 * editing/markup.cpp:
 30 (WebCore::removeSubresourceURLAttributes): Added.
 31 (WebCore::sanitizeMarkup): Added.
 32 * editing/markup.h:
 33
1342018-01-21 Ryosuke Niwa <rniwa@webkit.org>
235
336 Turning off custom pasteboard data doesn't actually turn it off in WK2

Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm

@@bool WebContentReader::readHTML(const String& string)
527527 if (stringOmittingMicrosoftPrefix.isEmpty())
528528 return false;
529529
530  addFragment(createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent));
 530 String markup;
 531 if (RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled() && shouldSanitize()) {
 532 markup = sanitizeMarkup(stringOmittingMicrosoftPrefix, std::function<void (DocumentFragment&)> { [] (DocumentFragment& fragment) {
 533 removeSubresourceURLAttributes(fragment, [] (const URL& url ) {
 534 return shouldReplaceSubresourceURL(url);
 535 });
 536 } });
 537 } else
 538 markup = stringOmittingMicrosoftPrefix;
 539
 540 addFragment(createFragmentFromMarkup(document, markup, emptyString(), DisallowScriptingAndPluginContent));
531541 return true;
532542}
533543

@@bool WebContentMarkupReader::readHTML(const String& string)
537547 return false;
538548
539549 String rawHTML = stripMicrosoftPrefix(string);
540  if (shouldSanitize())
541  markup = sanitizeMarkup(rawHTML);
542  else
 550 if (shouldSanitize()) {
 551 markup = sanitizeMarkup(rawHTML, std::function<void (DocumentFragment&)> { [] (DocumentFragment& fragment) {
 552 removeSubresourceURLAttributes(fragment, [] (const URL& url ) {
 553 return shouldReplaceSubresourceURL(url);
 554 });
 555 } });
 556 } else
543557 markup = rawHTML;
544558
545559 return !markup.isEmpty();

Source/WebCore/editing/markup.cpp

7272#include "TextIterator.h"
7373#include "TypedElementDescendantIterator.h"
7474#include "URL.h"
 75#include "URLParser.h"
7576#include "VisibleSelection.h"
7677#include "VisibleUnits.h"
7778#include <wtf/StdLibExtras.h>

@@void replaceSubresourceURLs(Ref<DocumentFragment>&& fragment, HashMap<AtomicStri
144145 change.apply();
145146}
146147
 148struct ElementAttribute {
 149 Ref<Element> element;
 150 QualifiedName attributeName;
 151};
 152
 153void removeSubresourceURLAttributes(Ref<DocumentFragment>&& fragment, std::function<bool(const URL&)> shouldRemoveURL)
 154{
 155 Vector<ElementAttribute> attributesToRemove;
 156 for (auto& element : descendantsOfType<Element>(fragment)) {
 157 if (!element.hasAttributes())
 158 continue;
 159 for (const Attribute& attribute : element.attributesIterator()) {
 160 // FIXME: This won't work for srcset.
 161 if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty()) {
 162 URL url = URLParser { attribute.value() }.result();
 163 if (shouldRemoveURL(url))
 164 attributesToRemove.append({ element, attribute.name() });
 165 }
 166 }
 167 }
 168 for (auto& item : attributesToRemove)
 169 item.element->removeAttribute(item.attributeName);
 170}
 171
147172std::unique_ptr<Page> createPageForSanitizingWebContent()
148173{
149174 PageConfiguration pageConfiguration(createEmptyEditorClient(), SocketProvider::create(), LibWebRTCProvider::create(), CacheStorageProvider::create());

@@std::unique_ptr<Page> createPageForSanitizingWebContent()
172197}
173198
174199
175 String sanitizeMarkup(const String& rawHTML)
 200String sanitizeMarkup(const String& rawHTML, std::optional<std::function<void(DocumentFragment&)>> fragmentSanitizer)
176201{
177202 auto page = createPageForSanitizingWebContent();
178203 Document* stagingDocument = page->mainFrame().document();

@@String sanitizeMarkup(const String& rawHTML)
181206 ASSERT(bodyElement);
182207
183208 auto fragment = createFragmentFromMarkup(*stagingDocument, rawHTML, emptyString(), DisallowScriptingAndPluginContent);
 209
 210 if (fragmentSanitizer)
 211 (*fragmentSanitizer)(fragment);
 212
184213 bodyElement->appendChild(fragment.get());
185214
186215 auto range = Range::create(*stagingDocument);

Source/WebCore/editing/markup.h

2929#include "FragmentScriptingPermission.h"
3030#include "HTMLInterchange.h"
3131#include <wtf/Forward.h>
 32#include <wtf/Function.h>
3233#include <wtf/HashMap.h>
3334
3435namespace WebCore {

@@class QualifiedName;
4748class Range;
4849
4950void replaceSubresourceURLs(Ref<DocumentFragment>&&, HashMap<AtomicString, AtomicString>&&);
 51void removeSubresourceURLAttributes(Ref<DocumentFragment>&&, std::function<bool(const URL&)> shouldRemoveURL);
 52
5053std::unique_ptr<Page> createPageForSanitizingWebContent();
51 String sanitizeMarkup(const String&);
 54String sanitizeMarkup(const String&, std::optional<std::function<void(DocumentFragment&)>> fragmentSanitizer = std::nullopt);
5255
5356enum EChildrenOnly { IncludeNode, ChildrenOnly };
5457enum EAbsoluteURLs { DoNotResolveURLs, ResolveAllURLs, ResolveNonLocalURLs };

Tools/ChangeLog

 12018-01-21 Ryosuke Niwa <rniwa@webkit.org>
 2
 3 Blob conversion and sanitization doesn't work with Microsoft Word for Mac 2011
 4 https://bugs.webkit.org/show_bug.cgi?id=181616
 5 <rdar://problem/36484908>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 Added tests to make sure we sanitize plain HTML, not just web archives,
 10 when and only when custom pasteboard data is enabled.
 11
 12 * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
 13 * TestWebKitAPI/Tests/WebKitCocoa/PasteHTML.mm: Added.
 14 (writeHTMLToPasteboard): Added.
 15 (createWebViewWithCustomPasteboardDataSetting): Added.
 16
1172018-01-21 Wenson Hsieh <wenson_hsieh@apple.com>
218
319 Add a new feature flag for EXTRA_ZOOM_MODE and reintroduce AdditionalFeatureDefines.h

Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

577577 9B62630C1F8C25C8007EE29B /* copy-url.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B62630B1F8C2510007EE29B /* copy-url.html */; };
578578 9B7A37C41F8AEBA5004AA228 /* CopyURL.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */; };
579579 9B7D740F1F8378770006C432 /* paste-rtfd.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B7D740E1F8377E60006C432 /* paste-rtfd.html */; };
 580 9BCB7C2820130600003E7C0C /* PasteHTML.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BCB7C2620130600003E7C0C /* PasteHTML.mm */; };
580581 9BD4239A1E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BD423991E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm */; };
581582 9BD4239C1E04C01C00200395 /* chinese-character-with-image.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9BD4239B1E04BFD000200395 /* chinese-character-with-image.html */; };
582583 9BD5111C1FE8E11600D2B630 /* AccessingPastedImage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BD5111B1FE8E11600D2B630 /* AccessingPastedImage.mm */; };

15891590 9B79164F1BD89D0D00D50B8F /* FirstResponderScrollingPosition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FirstResponderScrollingPosition.mm; sourceTree = "<group>"; };
15901591 9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CopyURL.mm; sourceTree = "<group>"; };
15911592 9B7D740E1F8377E60006C432 /* paste-rtfd.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "paste-rtfd.html"; sourceTree = "<group>"; };
 1593 9BCB7C2620130600003E7C0C /* PasteHTML.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PasteHTML.mm; sourceTree = "<group>"; };
15921594 9BD423991E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AttributedSubstringForProposedRangeWithImage.mm; sourceTree = "<group>"; };
15931595 9BD4239B1E04BFD000200395 /* chinese-character-with-image.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "chinese-character-with-image.html"; sourceTree = "<group>"; };
15941596 9BD5111B1FE8E11600D2B630 /* AccessingPastedImage.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AccessingPastedImage.mm; sourceTree = "<group>"; };

21152117 CEBCA12E1E3A660100C73293 /* OverrideContentSecurityPolicy.mm */,
21162118 9BDCCD851F7D0B0700009A18 /* PasteImage.mm */,
21172119 9BDD95561F83683600D20C60 /* PasteRTFD.mm */,
 2120 9BCB7C2620130600003E7C0C /* PasteHTML.mm */,
21182121 9B2346411F943A2400DB1D23 /* PasteWebArchive.mm */,
21192122 3FCC4FE41EC4E8520076E37C /* PictureInPictureDelegate.mm */,
21202123 83BAEE8C1EF4625500DDE894 /* PluginLoadClientPolicies.mm */,

33963399 7CCE7EBB1A411A7E00447C4C /* DOMHTMLTableCellCellAbove.mm in Sources */,
33973400 2D51A0C71C8BF00C00765C45 /* DOMHTMLVideoElementWrapper.mm in Sources */,
33983401 46397B951DC2C850009A78AE /* DOMNode.mm in Sources */,
 3402 9BCB7C2820130600003E7C0C /* PasteHTML.mm in Sources */,
33993403 7CCE7EBC1A411A7E00447C4C /* DOMNodeFromJSObject.mm in Sources */,
34003404 7CCE7EBD1A411A7E00447C4C /* DOMRangeOfString.mm in Sources */,
34013405 7CCE7EEC1A411AE600447C4C /* DOMWindowExtensionBasic.cpp in Sources */,

Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteHTML.mm

 1/*
 2 * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26#include "config.h"
 27
 28#if WK_API_ENABLED && PLATFORM(COCOA)
 29
 30#import "PlatformUtilities.h"
 31#import "TestWKWebView.h"
 32#import <WebCore/LegacyNSPasteboardTypes.h>
 33#import <WebKit/WKPreferencesPrivate.h>
 34#import <WebKit/WKPreferencesRefPrivate.h>
 35#import <WebKit/WKWebViewConfigurationPrivate.h>
 36#import <wtf/RetainPtr.h>
 37#import <wtf/text/WTFString.h>
 38
 39#if PLATFORM(IOS)
 40#include <MobileCoreServices/MobileCoreServices.h>
 41#endif
 42
 43@interface WKWebView ()
 44- (void)paste:(id)sender;
 45@end
 46
 47#if PLATFORM(MAC)
 48void writeHTMLToPasteboard(NSString *html)
 49{
 50 [[NSPasteboard generalPasteboard] declareTypes:@[WebCore::legacyHTMLPasteboardType()] owner:nil];
 51 [[NSPasteboard generalPasteboard] setString:html forType:WebCore::legacyHTMLPasteboardType()];
 52}
 53#else
 54void writeHTMLToPasteboard(NSString *html)
 55{
 56 [[UIPasteboard generalPasteboard] setItems:@[@{ (NSString *)kUTTypeHTML : html}]];
 57}
 58#endif
 59
 60static RetainPtr<TestWKWebView> createWebViewWithCustomPasteboardDataSetting(bool enabled)
 61{
 62 auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
 63 auto preferences = (WKPreferencesRef)[[webView configuration] preferences];
 64 WKPreferencesSetDataTransferItemsEnabled(preferences, true);
 65 WKPreferencesSetCustomPasteboardDataEnabled(preferences, enabled);
 66 return webView;
 67}
 68
 69TEST(PasteHTML, ExposesHTMLTypeInDataTransfer)
 70{
 71 auto webView = createWebViewWithCustomPasteboardDataSetting(true);
 72 [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
 73
 74 writeHTMLToPasteboard(@"<!DOCTYPE html><html><body><p><u>hello</u>, world</p></body></html>");
 75 [webView paste:nil];
 76
 77 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"]);
 78 [webView stringByEvaluatingJavaScript:@"editor.innerHTML = clipboardData.values[0]; editor.focus()"];
 79 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue);
 80 [webView stringByEvaluatingJavaScript:@"getSelection().modify('move', 'forward', 'lineboundary')"];
 81 EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue);
 82 EXPECT_WK_STREQ("hello, world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
 83}
 84
 85TEST(PasteHTML, SanitizesHTML)
 86{
 87 auto webView = createWebViewWithCustomPasteboardDataSetting(true);
 88 [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
 89
 90 writeHTMLToPasteboard(@"<!DOCTYPE html><meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b>"
 91 "<!-- secret-->, world<script>dangerousCode()</script>';");
 92 [webView paste:nil];
 93
 94 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"].boolValue);
 95 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('hello')"].boolValue);
 96 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('world')"].boolValue);
 97 EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('secret')"].boolValue);
 98 EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('dangerousCode')"].boolValue);
 99}
 100
 101TEST(PasteHTML, DoesNotSanitizeHTMLWhenCustomPasteboardDataIsDisabled)
 102{
 103 auto webView = createWebViewWithCustomPasteboardDataSetting(false);
 104 [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
 105
 106 writeHTMLToPasteboard(@"<!DOCTYPE html><meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b>"
 107 "<!-- secret-->, world<script>dangerousCode()</script>';");
 108 [webView paste:nil];
 109
 110 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"].boolValue);
 111 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('hello')"].boolValue);
 112 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('world')"].boolValue);
 113 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('secret')"].boolValue);
 114 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('dangerousCode')"].boolValue);
 115}
 116
 117TEST(PasteHTML, StripsHTTPURLs)
 118{
 119 auto webView = createWebViewWithCustomPasteboardDataSetting(true);
 120 [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
 121
 122 writeHTMLToPasteboard(@"<!DOCTYPE html><html><body><a alt='hello' href='file:///private/var/folders/secret/files/'>world</a>");
 123 [webView paste:nil];
 124
 125 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"].boolValue);
 126 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('hello')"].boolValue);
 127 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('world')"].boolValue);
 128 EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('secret')"].boolValue);
 129}
 130
 131TEST(PasteHTML, DoesNotStripHTTPURLsWhenCustomPasteboardDataIsDisabled)
 132{
 133 auto webView = createWebViewWithCustomPasteboardDataSetting(false);
 134 [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
 135
 136 writeHTMLToPasteboard(@"<!DOCTYPE html><html><body><a alt='hello' href='file:///private/var/folders/secret/files/'>world</a>");
 137 [webView paste:nil];
 138
 139 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"].boolValue);
 140 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('hello')"].boolValue);
 141 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('world')"].boolValue);
 142 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('secret')"].boolValue);
 143}
 144
 145TEST(PasteHTML, KeepsHTTPURLs)
 146{
 147 auto webView = createWebViewWithCustomPasteboardDataSetting(true);
 148 [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
 149
 150 writeHTMLToPasteboard(@"<!DOCTYPE html><html><body><a title='hello' href='https://svn.webkit.org/repository/webkit/trunk/LayoutTests/editing/resources/abe.png'>world</a>");
 151 [webView paste:nil];
 152
 153 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"].boolValue);
 154 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('hello')"].boolValue);
 155 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('world')"].boolValue);
 156 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.values[0].includes('abe.png')"].boolValue);
 157}
 158
 159#endif // WK_API_ENABLED && PLATFORM(COCOA)