Commit c02e4409 authored by Mike Dougherty's avatar Mike Dougherty Committed by Commit Bot

Restrict web share feature to URLs with http/https protocols

Bug: 1122059
Change-Id: Id9644b9dff7e9854f20218f09750fbf191d5a318
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378274
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803970}
parent 60b9ce1a
......@@ -438,6 +438,7 @@ source_set("eg2_tests") {
"stop_loading_egtest.mm",
"tab_order_egtest.mm",
"visible_url_egtest.mm",
"web_share_egtest.mm",
"window_open_by_dom_egtest.mm",
]
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/strings/stringprintf.h"
#import "ios/chrome/test/earl_grey/chrome_actions.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
const char kWebShareButtonId[] = "shareButton";
const char kWebShareStatusSuccess[] = "success";
const char kWebShareStatusFailure[] = "failure";
const char kWebShareValidLinkUrl[] = "/share_link.html";
const char kWebShareFileUrl[] = "/share_file.html";
const char kWebShareRelativeLinkUrl[] = "/share_relative_link.html";
const char kWebShareRelativeFilenameFileUrl[] = "/share_filename_file.html";
const char kWebSharePageContents[] =
"<html>"
"<head>"
"<script>"
"async function tryUrl() {"
" document.getElementById(\"result\").innerHTML = '';"
" try {"
" var opts = {url: \"%s\"};"
" navigator.share(opts);"
" document.getElementById(\"result\").innerHTML = 'success';"
" } catch {"
" document.getElementById(\"result\").innerHTML = 'failure';"
" }"
"}"
"</script>"
"</head><body>"
"<button id=\"shareButton\" onclick=\"tryUrl()\">Share</button>"
"<div id=\"result\"></div>"
"</body></html>";
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->set_content_type("text/html");
if (request.relative_url == kWebShareValidLinkUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "https://example.com");
http_response->set_content(content);
} else if (request.relative_url == kWebShareFileUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "file:///Users/u/data");
http_response->set_content(content);
} else if (request.relative_url == kWebShareRelativeLinkUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "/something.png");
http_response->set_content(content);
} else if (request.relative_url == kWebShareRelativeFilenameFileUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "filename.zip");
http_response->set_content(content);
} else {
return nullptr;
}
return std::move(http_response);
}
} // namespace
@interface WebShareTestCase : ChromeTestCase
@end
@implementation WebShareTestCase
- (void)setUp {
[super setUp];
self.testServer->RegisterRequestHandler(base::BindRepeating(&HandleRequest));
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
}
// Tests that a fully specified url can be shared.
- (void)testShareUrl {
const GURL pageURL = self.testServer->GetURL(kWebShareValidLinkUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
performAction:grey_tap()];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusSuccess];
}
// Tests that a relative url can be shared.
- (void)testShareRelativeUrl {
const GURL pageURL = self.testServer->GetURL(kWebShareRelativeLinkUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
performAction:grey_tap()];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusSuccess];
}
// Tests that a relative url can be shared when the filename starts with "file".
- (void)testShareRelativeFilenameUrl {
const GURL pageURL =
self.testServer->GetURL(kWebShareRelativeFilenameFileUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
performAction:grey_tap()];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusSuccess];
}
// Tests that a "file://" url can not be shared.
- (void)testShareFileUrl {
const GURL pageURL = self.testServer->GetURL(kWebShareFileUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusFailure];
// Share sheet should not display.
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
assertWithMatcher:grey_nil()];
}
@end
......@@ -606,6 +606,7 @@ js_compile_bundle("all_frames_web_bundle") {
"web_state/js/resources/common.js",
"web_state/js/resources/cookie.js",
"web_state/js/resources/find_in_page.js",
"web_state/js/resources/share_workaround.js",
]
}
......
......@@ -11,3 +11,4 @@ goog.require('__crWeb.common');
goog.require('__crWeb.cookie');
goog.require('__crWeb.findInPage');
goog.require('__crWeb.message');
goog.require('__crWeb.shareWorkaround');
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Workaround for preventing the leaking of local file contents.
* See crbug.com/1122059.
*/
goog.provide('__crWeb.shareWorkaround');
/** Beginning of anonymous object */
(function() {
/** @private */
// Store originals to prevent calling a modified version later.
const originalNavigator_ = navigator;
const originalNavigatorShare_ = Navigator.prototype.share;
const originalReflectApply_ = Reflect.apply;
const originalObjectDefineProperty_ = Object.defineProperty;
/**
* Wraps navigator.share() to prevent sharing URLs with "file:" scheme.
* NOTE: This code is sensitive and easy to break. See comments in
* crbug.com/1122059 and review comments in crrev.com/c/2378274.
* TODO:(crbug.com/1123689): Remove this workaround once WebKit fix is released.
*/
Navigator.prototype.share = function(data) {
// Copy values to a new Object to prevent functions returning different
// data from data.url. crbug.com/1122059#c23
const validatedData = {};
if (data.hasOwnProperty('files')) {
originalObjectDefineProperty_(validatedData, 'files',
{ value: data.files, configurable: false, writable: false })
}
if (data.hasOwnProperty('text')) {
originalObjectDefineProperty_(validatedData, 'text',
{ value: data.text, configurable: false, writable: false })
}
if (data.hasOwnProperty('title')) {
originalObjectDefineProperty_(validatedData, 'title',
{ value: data.title, configurable: false, writable: false })
}
let url = undefined;
if (data.hasOwnProperty('url')) {
url = data['url'];
let proceed = false;
if (url === undefined) {
// Allow url key to be set without value.
proceed = true;
} else if (typeof url === "string") {
// file: URLs are not allowed.
if (url.length >= 5 &&
(url[0] == 'f' || url[0] == 'F') &&
(url[1] == 'i' || url[1] == 'I') &&
(url[2] == 'l' || url[2] == 'L') &&
(url[3] == 'e' || url[3] == 'E') &&
url[4] == ':') {
proceed = false;
} else {
proceed = true;
}
}
if (!proceed) {
throw new Error("Sharing is not supported for this type of url.");
}
}
originalObjectDefineProperty_(validatedData, 'url',
{ value: url, configurable: false, writable: false })
return originalReflectApply_(originalNavigatorShare_,
originalNavigator_,
[validatedData]);
};
}()); // End of anonymous object
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment