diff -Nru chromium-browser-63.0.3239.84/android_webview/browser/aw_cookie_access_policy.cc chromium-browser-63.0.3239.132/android_webview/browser/aw_cookie_access_policy.cc --- chromium-browser-63.0.3239.84/android_webview/browser/aw_cookie_access_policy.cc 2017-12-06 20:05:21.000000000 +0000 +++ chromium-browser-63.0.3239.132/android_webview/browser/aw_cookie_access_policy.cc 2018-01-04 20:05:36.000000000 +0000 @@ -9,6 +9,7 @@ #include "android_webview/browser/aw_contents_io_thread_client.h" #include "base/logging.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/browser/resource_request_info.h" #include "content/public/browser/websocket_handshake_request_info.h" #include "net/base/net_errors.h" @@ -48,9 +49,14 @@ bool AwCookieAccessPolicy::GetShouldAcceptThirdPartyCookies( int render_process_id, - int render_frame_id) { + int render_frame_id, + int frame_tree_node_id) { std::unique_ptr io_thread_client = - AwContentsIoThreadClient::FromID(render_process_id, render_frame_id); + (frame_tree_node_id != content::RenderFrameHost::kNoFrameTreeNodeId) + ? AwContentsIoThreadClient::FromID(frame_tree_node_id) + : AwContentsIoThreadClient::FromID(render_process_id, + render_frame_id); + if (!io_thread_client) { return false; } @@ -60,20 +66,23 @@ bool AwCookieAccessPolicy::GetShouldAcceptThirdPartyCookies( const net::URLRequest& request) { int child_id = 0; - int frame_id = 0; + int render_frame_id = 0; + int frame_tree_node_id = content::RenderFrameHost::kNoFrameTreeNodeId; const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(&request); if (info) { child_id = info->GetChildID(); - frame_id = info->GetRenderFrameID(); + render_frame_id = info->GetRenderFrameID(); + frame_tree_node_id = info->GetFrameTreeNodeId(); } else { const WebSocketHandshakeRequestInfo* websocket_info = WebSocketHandshakeRequestInfo::ForRequest(&request); if (!websocket_info) return false; child_id = websocket_info->GetChildId(); - frame_id = websocket_info->GetRenderFrameId(); + render_frame_id = websocket_info->GetRenderFrameId(); } - return GetShouldAcceptThirdPartyCookies(child_id, frame_id); + return GetShouldAcceptThirdPartyCookies(child_id, render_frame_id, + frame_tree_node_id); } bool AwCookieAccessPolicy::OnCanGetCookies(const net::URLRequest& request, @@ -100,8 +109,9 @@ int render_process_id, int render_frame_id) { bool global = GetShouldAcceptCookies(); - bool thirdParty = - GetShouldAcceptThirdPartyCookies(render_process_id, render_frame_id); + bool thirdParty = GetShouldAcceptThirdPartyCookies( + render_process_id, render_frame_id, + content::RenderFrameHost::kNoFrameTreeNodeId); return AwStaticCookiePolicy(global, thirdParty).AllowGet(url, first_party); } @@ -113,8 +123,9 @@ int render_frame_id, const net::CookieOptions& options) { bool global = GetShouldAcceptCookies(); - bool thirdParty = - GetShouldAcceptThirdPartyCookies(render_process_id, render_frame_id); + bool thirdParty = GetShouldAcceptThirdPartyCookies( + render_process_id, render_frame_id, + content::RenderFrameHost::kNoFrameTreeNodeId); return AwStaticCookiePolicy(global, thirdParty).AllowSet(url, first_party); } diff -Nru chromium-browser-63.0.3239.84/android_webview/browser/aw_cookie_access_policy.h chromium-browser-63.0.3239.132/android_webview/browser/aw_cookie_access_policy.h --- chromium-browser-63.0.3239.84/android_webview/browser/aw_cookie_access_policy.h 2017-12-06 20:05:21.000000000 +0000 +++ chromium-browser-63.0.3239.132/android_webview/browser/aw_cookie_access_policy.h 2018-01-04 20:05:36.000000000 +0000 @@ -36,8 +36,12 @@ void SetShouldAcceptCookies(bool allow); // Can we read/write third party cookies? + // |render_process_id| and |render_frame_id| must be valid. + // Navigation requests are not associated with a renderer process. In this + // case, |frame_tree_node_id| must be valid instead. bool GetShouldAcceptThirdPartyCookies(int render_process_id, - int render_frame_id); + int render_frame_id, + int frame_tree_node_id); bool GetShouldAcceptThirdPartyCookies(const net::URLRequest& request); // These are the functions called when operating over cookies from the diff -Nru chromium-browser-63.0.3239.84/base/android/java/src/org/chromium/base/ApplicationStatus.java chromium-browser-63.0.3239.132/base/android/java/src/org/chromium/base/ApplicationStatus.java --- chromium-browser-63.0.3239.84/base/android/java/src/org/chromium/base/ApplicationStatus.java 2017-12-06 20:05:21.000000000 +0000 +++ chromium-browser-63.0.3239.132/base/android/java/src/org/chromium/base/ApplicationStatus.java 2018-01-04 20:05:37.000000000 +0000 @@ -9,6 +9,7 @@ import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.os.Bundle; +import android.support.annotation.Nullable; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -302,7 +303,8 @@ * @return The state of the specified activity (see {@link ActivityState}). */ @ActivityState - public static int getStateForActivity(Activity activity) { + public static int getStateForActivity(@Nullable Activity activity) { + if (activity == null) return ActivityState.DESTROYED; ActivityInfo info = sActivityInfo.get(activity); return info != null ? info.getStatus() : ActivityState.DESTROYED; } diff -Nru chromium-browser-63.0.3239.84/build/util/LASTCHANGE chromium-browser-63.0.3239.132/build/util/LASTCHANGE --- chromium-browser-63.0.3239.84/build/util/LASTCHANGE 2017-12-06 20:06:59.000000000 +0000 +++ chromium-browser-63.0.3239.132/build/util/LASTCHANGE 2018-01-04 20:07:14.000000000 +0000 @@ -1 +1 @@ -LASTCHANGE=0f53a5abc7a405fbe9f1cfd3db38f2092c8a7816- +LASTCHANGE=76cbfebbcc5236da3346c8389b94b648d36f8be6- diff -Nru chromium-browser-63.0.3239.84/build/util/LASTCHANGE.blink chromium-browser-63.0.3239.132/build/util/LASTCHANGE.blink --- chromium-browser-63.0.3239.84/build/util/LASTCHANGE.blink 2017-12-06 20:06:59.000000000 +0000 +++ chromium-browser-63.0.3239.132/build/util/LASTCHANGE.blink 2018-01-04 20:07:14.000000000 +0000 @@ -1 +1 @@ -LASTCHANGE=0f53a5abc7a405fbe9f1cfd3db38f2092c8a7816- +LASTCHANGE=76cbfebbcc5236da3346c8389b94b648d36f8be6- diff -Nru chromium-browser-63.0.3239.84/chrome/browser/chrome_content_browser_client.cc chromium-browser-63.0.3239.132/chrome/browser/chrome_content_browser_client.cc --- chromium-browser-63.0.3239.84/chrome/browser/chrome_content_browser_client.cc 2017-12-06 20:05:22.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/chrome_content_browser_client.cc 2018-01-04 20:05:40.000000000 +0000 @@ -1197,6 +1197,28 @@ return true; } +bool ChromeContentBrowserClient::ShouldBypassDocumentBlocking( + const url::Origin& initiator, + const GURL& url, + ResourceType resource_type) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::ShouldBypassDocumentBlocking( + initiator)) { + return true; + } +#endif + + // LinkDoctor requests are made with no initiator. Allow them through for + // SUB_RESOURCE requests when the initiator is unique (which matches the use + // of LinkDoctor in error pages), since the site is not critical to protect. + if (resource_type == content::RESOURCE_TYPE_SUB_RESOURCE && + initiator.unique() && url == google_util::LinkDoctorBaseURL()) { + return true; + } + + return false; +} + // These are treated as WebUI schemes but do not get WebUI bindings. Also, // view-source is allowed for these schemes. void ChromeContentBrowserClient::GetAdditionalWebUISchemes( diff -Nru chromium-browser-63.0.3239.84/chrome/browser/chrome_content_browser_client.h chromium-browser-63.0.3239.132/chrome/browser/chrome_content_browser_client.h --- chromium-browser-63.0.3239.84/chrome/browser/chrome_content_browser_client.h 2017-12-06 20:05:22.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/chrome_content_browser_client.h 2018-01-04 20:05:40.000000000 +0000 @@ -20,6 +20,7 @@ #include "build/build_config.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/network_service.mojom.h" +#include "content/public/common/resource_type.h" #include "extensions/features/features.h" #include "media/media_features.h" #include "ppapi/features/features.h" @@ -107,6 +108,10 @@ const GURL& effective_site_url) override; bool ShouldLockToOrigin(content::BrowserContext* browser_context, const GURL& effective_site_url) override; + bool ShouldBypassDocumentBlocking( + const url::Origin& initiator, + const GURL& url, + content::ResourceType resource_type) override; void GetAdditionalWebUISchemes( std::vector* additional_schemes) override; void GetAdditionalViewSourceSchemes( diff -Nru chromium-browser-63.0.3239.84/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc chromium-browser-63.0.3239.132/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc --- chromium-browser-63.0.3239.84/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc 2017-12-06 20:05:22.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc 2018-01-04 20:05:41.000000000 +0000 @@ -598,8 +598,13 @@ if (cert_slot != state->slot_) continue; + // Allow UTF-8 inside PrintableStrings in client certificates. See + // crbug.com/770323 and crbug.com/788655. + net::X509Certificate::UnsafeCreateOptions options; + options.printable_string_is_utf8 = true; scoped_refptr cert = - net::x509_util::CreateX509CertificateFromCERTCertificate(cert_handle); + net::x509_util::CreateX509CertificateFromCERTCertificate(cert_handle, + {}, options); if (!cert) continue; diff -Nru chromium-browser-63.0.3239.84/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc chromium-browser-63.0.3239.132/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc --- chromium-browser-63.0.3239.84/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc 2017-12-06 20:05:23.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc 2018-01-04 20:05:41.000000000 +0000 @@ -64,6 +64,7 @@ #include "extensions/common/manifest_handlers/web_accessible_resources_info.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/switches.h" +#include "url/origin.h" #if defined(OS_CHROMEOS) #include "extensions/browser/api/vpn_provider/vpn_service.h" @@ -377,6 +378,14 @@ return true; } +bool ChromeContentBrowserClientExtensionsPart::ShouldBypassDocumentBlocking( + const url::Origin& initiator) { + // Don't block responses for extension processes or for content scripts. + // TODO(creis): This check can be made stricter by checking what the extension + // has access to. + return initiator.scheme() == extensions::kExtensionScheme; +} + // static bool ChromeContentBrowserClientExtensionsPart::CanCommitURL( content::RenderProcessHost* process_host, const GURL& url) { diff -Nru chromium-browser-63.0.3239.84/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h chromium-browser-63.0.3239.132/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h --- chromium-browser-63.0.3239.84/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h 2017-12-06 20:05:23.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h 2018-01-04 20:05:41.000000000 +0000 @@ -16,6 +16,10 @@ class VpnServiceProxy; } +namespace url { +class Origin; +} + namespace extensions { // Implements the extensions portion of ChromeContentBrowserClient. @@ -36,6 +40,7 @@ const GURL& effective_site_url); static bool ShouldLockToOrigin(content::BrowserContext* browser_context, const GURL& effective_site_url); + static bool ShouldBypassDocumentBlocking(const url::Origin& initiator); static bool CanCommitURL(content::RenderProcessHost* process_host, const GURL& url); static bool IsSuitableHost(Profile* profile, diff -Nru chromium-browser-63.0.3239.84/chrome/browser/net/safe_search_util_unittest.cc chromium-browser-63.0.3239.132/chrome/browser/net/safe_search_util_unittest.cc --- chromium-browser-63.0.3239.84/chrome/browser/net/safe_search_util_unittest.cc 2017-12-06 20:05:23.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/net/safe_search_util_unittest.cc 2018-01-04 20:05:42.000000000 +0000 @@ -117,6 +117,9 @@ CheckAddedParameters("http://google.de/?q=goog&safe=off&ssui=off", "q=goog&" + kBothParameters); + CheckAddedParameters("http://google.de/?q=&tbs=rimg:", + "q=&tbs=rimg:&" + kBothParameters); + // Test various combinations where we should not add anything. CheckAddedParameters("http://google.com/?q=goog&" + kSsuiParameter + "&" + kSafeParameter, diff -Nru chromium-browser-63.0.3239.84/chrome/browser/resources/chromeos/login/screen_gaia_signin.js chromium-browser-63.0.3239.132/chrome/browser/resources/chromeos/login/screen_gaia_signin.js --- chromium-browser-63.0.3239.84/chrome/browser/resources/chromeos/login/screen_gaia_signin.js 2017-12-06 20:05:23.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/resources/chromeos/login/screen_gaia_signin.js 2018-01-04 20:05:42.000000000 +0000 @@ -681,6 +681,8 @@ params.menuKeyboardOptions = false; params.menuEnterpriseEnrollment = !(data.enterpriseManagedDevice || data.hasDeviceOwner); + params.isFirstUser = + !(data.enterpriseManagedDevice || data.hasDeviceOwner); this.gaiaAuthParams_ = params; diff -Nru chromium-browser-63.0.3239.84/chrome/browser/resources/gaia_auth_host/authenticator.js chromium-browser-63.0.3239.132/chrome/browser/resources/gaia_auth_host/authenticator.js --- chromium-browser-63.0.3239.84/chrome/browser/resources/gaia_auth_host/authenticator.js 2017-12-06 20:05:23.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/resources/gaia_auth_host/authenticator.js 2018-01-04 20:05:42.000000000 +0000 @@ -91,6 +91,9 @@ 'menuGuestMode', // Enables "Guest mode" menu item 'menuKeyboardOptions', // Enables "Keyboard options" menu item 'menuEnterpriseEnrollment', // Enables "Enterprise enrollment" menu item. + 'lsbReleaseBoard', // Chrome OS Release board name + 'isFirstUser', // True if this is non-enterprise device, + // and there are no users yet. // The email fields allow for the following possibilities: // @@ -328,6 +331,11 @@ mi += 'ee,'; if (mi.length) url = appendParam(url, 'mi', mi); + + if (data.lsbReleaseBoard) + url = appendParam(url, 'chromeos_board', data.lsbReleaseBoard); + if (data.isFirstUser) + url = appendParam(url, 'is_first_user', true); } } else { url = appendParam(url, 'continue', this.continueUrl_); diff -Nru chromium-browser-63.0.3239.84/chrome/browser/resources/settings/device_page/display.html chromium-browser-63.0.3239.132/chrome/browser/resources/settings/device_page/display.html --- chromium-browser-63.0.3239.84/chrome/browser/resources/settings/device_page/display.html 2017-12-06 20:05:23.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/resources/settings/device_page/display.html 2018-01-04 20:05:43.000000000 +0000 @@ -145,7 +145,7 @@ [[getResolutionText_(selectedDisplay, selectedModePref_.value)]] - diff -Nru chromium-browser-63.0.3239.84/chrome/browser/search_engines/ui_thread_search_terms_data.cc chromium-browser-63.0.3239.132/chrome/browser/search_engines/ui_thread_search_terms_data.cc --- chromium-browser-63.0.3239.84/chrome/browser/search_engines/ui_thread_search_terms_data.cc 2017-12-06 20:05:23.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/search_engines/ui_thread_search_terms_data.cc 2018-01-04 20:05:43.000000000 +0000 @@ -76,15 +76,11 @@ BrowserThread::CurrentlyOn(BrowserThread::UI)); base::string16 rlz_string; #if BUILDFLAG(ENABLE_RLZ) - static std::string* brand = []() { - auto* extracted = new std::string(); - if (!google_brand::GetBrand(extracted)) - extracted->clear(); - return extracted; - }(); // For organic brandcodes do not use rlz at all. Empty brandcode usually // means a chromium install. This is ok. - if (!brand->empty() && !google_brand::IsOrganic(*brand)) { + std::string brand; + if (google_brand::GetBrand(&brand) && !brand.empty() && + !google_brand::IsOrganic(brand)) { // This call will return false the first time(s) it is called until the // value has been cached. This normally would mean that at most one omnibox // search might not send the RLZ data but this is not really a problem. diff -Nru chromium-browser-63.0.3239.84/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc chromium-browser-63.0.3239.132/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc --- chromium-browser-63.0.3239.84/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc 2017-12-06 20:05:24.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc 2018-01-04 20:05:44.000000000 +0000 @@ -12,6 +12,7 @@ #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/sys_info.h" #include "base/task_scheduler/post_task.h" #include "base/values.h" #include "chrome/browser/browser_process.h" @@ -35,6 +36,7 @@ #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" +#include "chrome/installer/util/google_update_settings.h" #include "chromeos/chromeos_switches.h" #include "chromeos/login/auth/authpolicy_login_helper.h" #include "chromeos/login/auth/user_context.h" @@ -212,6 +214,11 @@ return status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; } +void GetVersionAndConsent(std::string* out_version, bool* out_consent) { + *out_version = version_loader::GetVersion(version_loader::VERSION_SHORT); + *out_consent = GoogleUpdateSettings::GetCollectStatsConsent(); +} + } // namespace // A class that's used to specify the way how Gaia should be loaded. @@ -289,16 +296,24 @@ } void GaiaScreenHandler::LoadGaia(const GaiaContext& context) { - base::PostTaskWithTraitsAndReplyWithResult( - FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, - base::Bind(&version_loader::GetVersion, version_loader::VERSION_SHORT), - base::Bind(&GaiaScreenHandler::LoadGaiaWithVersion, - weak_factory_.GetWeakPtr(), context)); + std::unique_ptr version = std::make_unique(); + std::unique_ptr consent = std::make_unique(); + base::OnceClosure get_version_and_consent = + base::BindOnce(&GetVersionAndConsent, base::Unretained(version.get()), + base::Unretained(consent.get())); + base::OnceClosure load_gaia = base::BindOnce( + &GaiaScreenHandler::LoadGaiaWithVersionAndConsent, + weak_factory_.GetWeakPtr(), context, base::Owned(version.release()), + base::Owned(consent.release())); + base::PostTaskWithTraitsAndReply( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, + std::move(get_version_and_consent), std::move(load_gaia)); } -void GaiaScreenHandler::LoadGaiaWithVersion( +void GaiaScreenHandler::LoadGaiaWithVersionAndConsent( const GaiaContext& context, - const std::string& platform_version) { + const std::string* platform_version, + const bool* collect_stats_consent) { base::DictionaryValue params; params.SetBoolean("forceReload", context.force_reload); @@ -348,8 +363,8 @@ params.SetString("clientId", GaiaUrls::GetInstance()->oauth2_chrome_client_id()); params.SetString("clientVersion", version_info::GetVersionNumber()); - if (!platform_version.empty()) - params.SetString("platformVersion", platform_version); + if (!platform_version->empty()) + params.SetString("platformVersion", *platform_version); params.SetString("releaseChannel", chrome::GetChannelString()); params.SetString("endpointGen", kEndpointGen); @@ -370,6 +385,10 @@ // (see https://crbug.com/709244 ). params.SetString("chromeOSApiVersion", "2"); } + // We only send |chromeos_board| Gaia URL parameter if user has opted into + // sending device statistics. + if (*collect_stats_consent) + params.SetString("lsbReleaseBoard", base::SysInfo::GetLsbReleaseBoard()); frame_state_ = FRAME_STATE_LOADING; CallJS("loadAuthExtension", params); diff -Nru chromium-browser-63.0.3239.84/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h chromium-browser-63.0.3239.132/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h --- chromium-browser-63.0.3239.84/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h 2017-12-06 20:05:24.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h 2018-01-04 20:05:44.000000000 +0000 @@ -58,9 +58,11 @@ void LoadGaia(const GaiaContext& context); - // Callback that loads GAIA after version information has been retrieved. - void LoadGaiaWithVersion(const GaiaContext& context, - const std::string& platform_version); + // Callback that loads GAIA after version and stat consent information has + // been retrieved. + void LoadGaiaWithVersionAndConsent(const GaiaContext& context, + const std::string* platform_version, + const bool* collect_stats_consent); // Sends request to reload Gaia. If |force_reload| is true, request // will be sent in any case, otherwise it will be sent only when Gaia is diff -Nru chromium-browser-63.0.3239.84/chrome/common/extensions/api/_permission_features.json chromium-browser-63.0.3239.132/chrome/common/extensions/api/_permission_features.json --- chromium-browser-63.0.3239.84/chrome/common/extensions/api/_permission_features.json 2017-12-06 20:05:24.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/common/extensions/api/_permission_features.json 2018-01-04 20:05:44.000000000 +0000 @@ -138,7 +138,9 @@ "2B6C6A4A5940017146F3E58B7F90116206E84685", // http://crbug.com/642141 "B6C2EFAB3EC3BF6EF03701408B6B09A67B2D0069", // http://crbug.com/642141 "96FF2FFA5C9173C76D47184B3E86D267B37781DE", // http://crbug.com/642141 - "0136FCB13DB29FD5CD442F56E59E53B61F1DF96F" // http://crbug.com/642141 + "0136FCB13DB29FD5CD442F56E59E53B61F1DF96F", // http://crbug.com/642141 + "9834387FDA1F66A1B5CA06CB442137B556F12F2A", // http://crbug.com/772346 + "930F7D9989A5FBCDCCD7D85BB5C3B7006C24D91D" // http://crbug.com/782139 ] }, "clipboardRead": { diff -Nru chromium-browser-63.0.3239.84/chrome/VERSION chromium-browser-63.0.3239.132/chrome/VERSION --- chromium-browser-63.0.3239.84/chrome/VERSION 2017-12-06 20:05:22.000000000 +0000 +++ chromium-browser-63.0.3239.132/chrome/VERSION 2018-01-04 20:05:38.000000000 +0000 @@ -1,4 +1,4 @@ MAJOR=63 MINOR=0 BUILD=3239 -PATCH=84 +PATCH=132 diff -Nru chromium-browser-63.0.3239.84/chromeos/network/client_cert_resolver.cc chromium-browser-63.0.3239.132/chromeos/network/client_cert_resolver.cc --- chromium-browser-63.0.3239.84/chromeos/network/client_cert_resolver.cc 2017-12-06 20:05:25.000000000 +0000 +++ chromium-browser-63.0.3239.132/chromeos/network/client_cert_resolver.cc 2018-01-04 20:05:47.000000000 +0000 @@ -121,9 +121,13 @@ bool operator()(const CertAndIssuer& cert_and_issuer) { if (!pattern.issuer().Empty() || !pattern.subject().Empty()) { + // Allow UTF-8 inside PrintableStrings in client certificates. See + // crbug.com/770323 and crbug.com/788655. + net::X509Certificate::UnsafeCreateOptions options; + options.printable_string_is_utf8 = true; scoped_refptr x509_cert = net::x509_util::CreateX509CertificateFromCERTCertificate( - cert_and_issuer.cert.get()); + cert_and_issuer.cert.get(), {}, options); if (!x509_cert) return false; if (!pattern.issuer().Empty() && diff -Nru chromium-browser-63.0.3239.84/chromeos/network/client_cert_resolver_unittest.cc chromium-browser-63.0.3239.132/chromeos/network/client_cert_resolver_unittest.cc --- chromium-browser-63.0.3239.84/chromeos/network/client_cert_resolver_unittest.cc 2017-12-06 20:05:25.000000000 +0000 +++ chromium-browser-63.0.3239.132/chromeos/network/client_cert_resolver_unittest.cc 2018-01-04 20:05:47.000000000 +0000 @@ -32,6 +32,7 @@ #include "crypto/scoped_test_nss_db.h" #include "net/base/net_errors.h" #include "net/cert/nss_cert_database_chromeos.h" +#include "net/cert/pem_tokenizer.h" #include "net/cert/x509_certificate.h" #include "net/cert/x509_util_nss.h" #include "net/test/cert_test_util.h" @@ -43,10 +44,10 @@ namespace { -const char* kWifiStub = "wifi_stub"; -const char* kWifiSSID = "wifi_ssid"; -const char* kUserProfilePath = "user_profile"; -const char* kUserHash = "user_hash"; +constexpr char kWifiStub[] = "wifi_stub"; +constexpr char kWifiSSID[] = "wifi_ssid"; +constexpr char kUserProfilePath[] = "user_profile"; +constexpr char kUserHash[] = "user_hash"; } // namespace @@ -106,9 +107,10 @@ } } - // Imports a client certificate. Its PKCS#11 ID is stored in |test_cert_id_|. - // If |import_issuer| is true, also imports the CA cert (stored as PEM in - // test_ca_cert_pem_) that issued the client certificate. + // Imports a client certificate. After a subsequent StartCertLoader() + // invocation, the PKCS#11 ID of the imported certificate will be stored in + // |test_cert_id_|. If |import_issuer| is true, also imports the CA cert + // (stored as PEM in test_ca_cert_pem_) that issued the client certificate. void SetupTestCerts(const std::string& prefix, bool import_issuer) { // Load a CA cert. net::ScopedCERTCertificateList ca_cert_list = @@ -134,6 +136,35 @@ ASSERT_TRUE(test_client_cert_.get()); } + // Imports a client certificate with a subject CommonName encoded as + // PrintableString, but containing invalid characters. It is imported into the + // user slot. After a subsequent StartCertLoader() invocation, the PKCS#11 ID + // of the imported certificate will be stored in |test_cert_id_|. + void SetupTestCertWithBadPrintableString() { + base::FilePath certs_dir = net::GetTestNetDataDirectory().AppendASCII( + "parse_certificate_unittest"); + ASSERT_TRUE(net::ImportSensitiveKeyFromFile( + certs_dir, "v3_certificate_template.pk8", test_nssdb_.slot())); + + std::string file_data; + ASSERT_TRUE(base::ReadFileToString( + certs_dir.AppendASCII( + "subject_printable_string_containing_utf8_client_cert.pem"), + &file_data)); + + net::PEMTokenizer pem_tokenizer(file_data, {"CERTIFICATE"}); + ASSERT_TRUE(pem_tokenizer.GetNext()); + std::string cert_der(pem_tokenizer.data()); + ASSERT_FALSE(pem_tokenizer.GetNext()); + + test_client_cert_ = net::x509_util::CreateCERTCertificateFromBytes( + reinterpret_cast(cert_der.data()), cert_der.size()); + ASSERT_TRUE(test_client_cert_); + + ASSERT_TRUE(net::ImportClientCertToSlot(test_client_cert_.get(), + test_nssdb_.slot())); + } + void SetupTestCertInSystemToken(const std::string& prefix) { test_nsscertdb_->SetSystemSlot( crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_system_nssdb_.slot()))); @@ -190,9 +221,10 @@ } // Sets up a policy with a certificate pattern that matches any client cert - // with a certain Issuer CN. It will match the test client cert. + // with a certain Issuer CN. It will match the test client cert imported by + // SetupTestCerts. void SetupPolicyMatchingIssuerCN(onc::ONCSource onc_source) { - const char* kTestPolicy = + const char* test_policy = "[ { \"GUID\": \"wifi_stub\"," " \"Name\": \"wifi_stub\"," " \"Type\": \"WiFi\"," @@ -214,7 +246,47 @@ std::string error; std::unique_ptr policy_value = base::JSONReader::ReadAndReturnError( - kTestPolicy, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error); + test_policy, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error); + ASSERT_TRUE(policy_value) << error; + + base::ListValue* policy = nullptr; + ASSERT_TRUE(policy_value->GetAsList(&policy)); + + std::string user_hash = + onc_source == onc::ONC_SOURCE_USER_POLICY ? kUserHash : ""; + managed_config_handler_->SetPolicy( + onc_source, user_hash, *policy, + base::DictionaryValue() /* no global network config */); + } + + // Sets up a policy with a certificate pattern that matches any client cert + // with a Subject Organization set to "Blar". It will match the test client + // cert imported by SetupTestCertWithBadPrintableString. + void SetupPolicyMatchingSubjectOrgForBadPrintableStringCert( + onc::ONCSource onc_source) { + const char* test_policy = + "[ { \"GUID\": \"wifi_stub\"," + " \"Name\": \"wifi_stub\"," + " \"Type\": \"WiFi\"," + " \"WiFi\": {" + " \"Security\": \"WPA-EAP\"," + " \"SSID\": \"wifi_ssid\"," + " \"EAP\": {" + " \"Outer\": \"EAP-TLS\"," + " \"ClientCertType\": \"Pattern\"," + " \"ClientCertPattern\": {" + " \"Subject\": {" + " \"Organization\": \"Blar\"" + " }" + " }" + " }" + " }" + "} ]"; + + std::string error; + std::unique_ptr policy_value = + base::JSONReader::ReadAndReturnError( + test_policy, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error); ASSERT_TRUE(policy_value) << error; base::ListValue* policy = nullptr; @@ -230,7 +302,7 @@ void SetupCertificateConfigMatchingIssuerCN( onc::ONCSource onc_source, client_cert::ClientCertConfig* client_cert_config) { - const char* kTestOncPattern = + const char* test_onc_pattern = "{" " \"Issuer\": {" " \"CommonName\": \"B CA\"" @@ -238,8 +310,9 @@ "}"; std::string error; std::unique_ptr onc_pattern_value = - base::JSONReader::ReadAndReturnError( - kTestOncPattern, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error); + base::JSONReader::ReadAndReturnError(test_onc_pattern, + base::JSON_ALLOW_TRAILING_COMMAS, + nullptr, &error); ASSERT_TRUE(onc_pattern_value) << error; base::DictionaryValue* onc_pattern_dict; @@ -254,7 +327,7 @@ // particular it will match the test client cert. void SetupPolicyMatchingIssuerPEM(onc::ONCSource onc_source, const std::string& identity) { - const char* kTestPolicyTemplate = + const char* test_policy_template = "[ { \"GUID\": \"wifi_stub\"," " \"Name\": \"wifi_stub\"," " \"Type\": \"WiFi\"," @@ -272,7 +345,7 @@ " }" "} ]"; std::string policy_json = base::StringPrintf( - kTestPolicyTemplate, identity.c_str(), test_ca_cert_pem_.c_str()); + test_policy_template, identity.c_str(), test_ca_cert_pem_.c_str()); std::string error; std::unique_ptr policy_value = @@ -363,6 +436,32 @@ scoped_task_environment_.RunUntilIdle(); network_properties_changed_count_ = 0; + StartCertLoader(); + scoped_task_environment_.RunUntilIdle(); + + // Verify that the resolver positively matched the pattern in the policy with + // the test client cert and configured the network. + std::string pkcs11_id; + GetServiceProperty(shill::kEapCertIdProperty, &pkcs11_id); + EXPECT_EQ(test_cert_id_, pkcs11_id); + EXPECT_EQ(1, network_properties_changed_count_); +} + +// Test that matching works on a certificate with invalid characters in a +// PrintableString field. See crbug.com/788655. +TEST_F(ClientCertResolverTest, MatchSubjectOrgOnBadPrintableStringCert) { + ASSERT_NO_FATAL_FAILURE(SetupTestCertWithBadPrintableString()); + + SetupWifi(); + scoped_task_environment_.RunUntilIdle(); + + SetupNetworkHandlers(); + ASSERT_NO_FATAL_FAILURE( + SetupPolicyMatchingSubjectOrgForBadPrintableStringCert( + onc::ONC_SOURCE_USER_POLICY)); + scoped_task_environment_.RunUntilIdle(); + + network_properties_changed_count_ = 0; StartCertLoader(); scoped_task_environment_.RunUntilIdle(); diff -Nru chromium-browser-63.0.3239.84/components/autofill/core/browser/autofill_manager_unittest.cc chromium-browser-63.0.3239.132/components/autofill/core/browser/autofill_manager_unittest.cc --- chromium-browser-63.0.3239.84/components/autofill/core/browser/autofill_manager_unittest.cc 2017-12-06 20:05:25.000000000 +0000 +++ chromium-browser-63.0.3239.132/components/autofill/core/browser/autofill_manager_unittest.cc 2018-01-04 20:05:47.000000000 +0000 @@ -95,6 +95,12 @@ const base::Time kArbitraryTime = base::Time::FromDoubleT(25); const base::Time kMuchLaterTime = base::Time::FromDoubleT(5000); +const std::string NextYear() { + base::Time::Exploded now; + base::Time::Now().LocalExplode(&now); + return std::to_string(now.year + 1); +} + class MockAutofillClient : public TestAutofillClient { public: MockAutofillClient() {} @@ -1044,7 +1050,7 @@ // Edit the data, and submit. form.fields[1].value = ASCIIToUTF16("4111111111111111"); form.fields[2].value = ASCIIToUTF16("11"); - form.fields[3].value = ASCIIToUTF16("2017"); + form.fields[3].value = ASCIIToUTF16(NextYear()); EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)); FormSubmitted(form); } @@ -1059,7 +1065,7 @@ FormsSeen(std::vector(1, *form)); *card = CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"); test::SetCreditCardInfo(card, "John Dillinger", "1881" /* Visa */, "01", - "2017", "1"); + NextYear().c_str(), "1"); card->SetNetworkForMaskedCard(kVisaCard); EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _, _)) @@ -1750,7 +1756,7 @@ CreditCard credit_card; test::SetCreditCardInfo(&credit_card, "John Smith", "5255667890123123", // Mastercard - "08", "2017", "1"); + "08", NextYear().c_str(), "1"); credit_card.set_guid("00000000-0000-0000-0000-000000000007"); autofill_manager_->AddCreditCard(credit_card); @@ -1765,11 +1771,13 @@ field.value = ASCIIToUTF16("5255-66__-____-____"); GetAutofillSuggestions(form, field); + std::string expected_date = "08/" + NextYear().substr(2, 2); + // Test that we sent the right value to the external delegate. external_delegate_->CheckSuggestions( kDefaultPageID, Suggestion(std::string("Mastercard") + kUTF8MidlineEllipsis + "3123", - "08/17", kMasterCard, + expected_date, kMasterCard, autofill_manager_->GetPackedCreditCardID(7))); } @@ -4039,7 +4047,7 @@ // Edit the data, and submit. form.fields[1].value = ASCIIToUTF16("4111111111111111"); form.fields[2].value = ASCIIToUTF16("11"); - form.fields[3].value = ASCIIToUTF16("2017"); + form.fields[3].value = ASCIIToUTF16(NextYear()); EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)); FormSubmitted(form); } @@ -4058,7 +4066,7 @@ ASSERT_FALSE(autofill::IsValidCreditCardNumber(ASCIIToUTF16(card))); form.fields[1].value = ASCIIToUTF16(card); form.fields[2].value = ASCIIToUTF16("11"); - form.fields[3].value = ASCIIToUTF16("2017"); + form.fields[3].value = ASCIIToUTF16(NextYear()); EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); FormSubmitted(form); } @@ -4787,7 +4795,7 @@ else if (form.fields[i].name == ASCIIToUTF16("ccmonth")) form.fields[i].value = ASCIIToUTF16("01"); else if (form.fields[i].name == ASCIIToUTF16("ccyear")) - form.fields[i].value = ASCIIToUTF16("2017"); + form.fields[i].value = ASCIIToUTF16(NextYear()); } CardUnmaskDelegate::UnmaskResponse response; @@ -4835,7 +4843,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4911,7 +4919,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4962,7 +4970,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); @@ -4999,7 +5007,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16(card_number); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -5044,7 +5052,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5082,7 +5090,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING base::HistogramTester histogram_tester; @@ -5120,7 +5128,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("1234"); base::HistogramTester histogram_tester; @@ -5181,7 +5189,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING credit_card_form.fields[5].value = ASCIIToUTF16("123"); @@ -5239,7 +5247,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); base::HistogramTester histogram_tester; @@ -5298,7 +5306,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("1234"); base::HistogramTester histogram_tester; @@ -5358,7 +5366,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5420,7 +5428,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5487,7 +5495,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); base::HistogramTester histogram_tester; @@ -5550,7 +5558,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); base::HistogramTester histogram_tester; @@ -5592,7 +5600,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Confirm upload happened and the new UI flag was sent in the request. @@ -5625,7 +5633,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Confirm upload happened and the new UI flag was not sent in the request. @@ -5658,7 +5666,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Confirm upload happened and the show Google logo flag was sent in the @@ -5692,7 +5700,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Confirm upload happened and the show Google logo flag was not sent in the @@ -5719,7 +5727,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5765,7 +5773,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5803,7 +5811,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING base::HistogramTester histogram_tester; @@ -5849,7 +5857,7 @@ // Edit the data, but don't include a name, and submit. credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5897,7 +5905,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5941,7 +5949,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5985,7 +5993,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6032,7 +6040,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6077,7 +6085,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6122,7 +6130,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo W. Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6170,7 +6178,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo W. Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6215,7 +6223,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6259,7 +6267,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6296,7 +6304,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("John Quincy Adams"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6335,7 +6343,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("John Quincy Adams"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6375,7 +6383,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("John Adams"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6416,7 +6424,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("John Adams"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6465,7 +6473,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6517,7 +6525,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Master Blaster"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6564,7 +6572,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6605,7 +6613,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -6642,8 +6650,8 @@ // Add a masked credit card whose |TypeAndLastFourDigits| matches what we will // enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", "2017", - "1"); + test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data_.AddServerCreditCard(credit_card); @@ -6656,7 +6664,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // The local save prompt should be shown. @@ -6681,8 +6689,8 @@ // Add a masked credit card whose |TypeAndLastFourDigits| matches what we will // enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", "2017", - "1"); + test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data_.AddServerCreditCard(credit_card); @@ -6695,7 +6703,7 @@ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16("11"); - credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // The local save prompt should not be shown because the experiment is off. diff -Nru chromium-browser-63.0.3239.84/components/autofill/core/browser/credit_card_unittest.cc chromium-browser-63.0.3239.132/components/autofill/core/browser/credit_card_unittest.cc --- chromium-browser-63.0.3239.84/components/autofill/core/browser/credit_card_unittest.cc 2017-12-06 20:05:25.000000000 +0000 +++ chromium-browser-63.0.3239.132/components/autofill/core/browser/credit_card_unittest.cc 2018-01-04 20:05:47.000000000 +0000 @@ -15,6 +15,7 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/test_autofill_clock.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/form_field_data.h" @@ -1236,6 +1237,9 @@ EXPECT_TRUE( base::Time::FromLocalExploded(kTestDateTimeExploded, &kArbitraryTime)); + TestAutofillClock test_clock; + test_clock.SetNow(kArbitraryTime); + // Test for added to chrome/chromium. CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com"); credit_card0.set_use_count(1); diff -Nru chromium-browser-63.0.3239.84/components/google/core/browser/google_util.cc chromium-browser-63.0.3239.132/components/google/core/browser/google_util.cc --- chromium-browser-63.0.3239.84/components/google/core/browser/google_util.cc 2017-12-06 20:05:26.000000000 +0000 +++ chromium-browser-63.0.3239.132/components/google/core/browser/google_util.cc 2018-01-04 20:05:47.000000000 +0000 @@ -142,11 +142,9 @@ bool HasGoogleSearchQueryParam(base::StringPiece str) { url::Component query(0, static_cast(str.length())), key, value; while (url::ExtractQueryKeyValue(str.data(), &query, &key, &value)) { - if (value.is_nonempty()) { - base::StringPiece key_str = str.substr(key.begin, key.len); - if (key_str == "q" || key_str == "as_q") - return true; - } + base::StringPiece key_str = str.substr(key.begin, key.len); + if (key_str == "q" || key_str == "as_q") + return true; } return false; } diff -Nru chromium-browser-63.0.3239.84/components/google/core/browser/google_util_unittest.cc chromium-browser-63.0.3239.132/components/google/core/browser/google_util_unittest.cc --- chromium-browser-63.0.3239.84/components/google/core/browser/google_util_unittest.cc 2017-12-06 20:05:26.000000000 +0000 +++ chromium-browser-63.0.3239.132/components/google/core/browser/google_util_unittest.cc 2018-01-04 20:05:47.000000000 +0000 @@ -147,6 +147,8 @@ "%s://www.google.co.uk/search?%s=something", // It's actually valid for both to have the query parameter. "%s://www.google.com/search?%s=something#q=other", + // Also valid to have an empty query parameter + "%s://www.google.com/search?%s=", // Queries with path "/webhp", "/" or "" need to have the query parameter // in the hash fragment. @@ -216,12 +218,6 @@ } const std::string patterns_q[] = { - // Can't have an empty query parameter. - "%s://www.google.com/search?%s=", - "%s://www.google.com/search?name=bob&%s=", - "%s://www.google.com/webhp#%s=", - "%s://www.google.com/webhp#name=bob&%s=", - // Home page searches without a hash fragment query parameter are invalid. "%s://www.google.com/webhp?%s=something", "%s://www.google.com/webhp?%s=something#no=good", diff -Nru chromium-browser-63.0.3239.84/content/browser/browser_main_loop.cc chromium-browser-63.0.3239.132/content/browser/browser_main_loop.cc --- chromium-browser-63.0.3239.84/content/browser/browser_main_loop.cc 2017-12-06 20:05:27.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/browser_main_loop.cc 2018-01-04 20:05:49.000000000 +0000 @@ -874,6 +874,9 @@ EVP_set_buggy_rsa_parser( base::FeatureList::IsEnabled(features::kBuggyRSAParser)); + // Record metrics about which site isolation flags have been turned on. + SiteIsolationPolicy::RecordSiteIsolationFlagUsage(); + return result_code_; } diff -Nru chromium-browser-63.0.3239.84/content/browser/BUILD.gn chromium-browser-63.0.3239.132/content/browser/BUILD.gn --- chromium-browser-63.0.3239.84/content/browser/BUILD.gn 2017-12-06 20:05:27.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/BUILD.gn 2018-01-04 20:05:49.000000000 +0000 @@ -925,6 +925,8 @@ "leveldb_wrapper_impl.h", "loader/async_resource_handler.cc", "loader/async_resource_handler.h", + "loader/cross_site_document_resource_handler.cc", + "loader/cross_site_document_resource_handler.h", "loader/detachable_resource_handler.cc", "loader/detachable_resource_handler.h", "loader/downloaded_temp_file_impl.cc", diff -Nru chromium-browser-63.0.3239.84/content/browser/frame_host/navigation_request.cc chromium-browser-63.0.3239.132/content/browser/frame_host/navigation_request.cc --- chromium-browser-63.0.3239.84/content/browser/frame_host/navigation_request.cc 2017-12-06 20:05:27.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/frame_host/navigation_request.cc 2018-01-04 20:05:49.000000000 +0000 @@ -1006,14 +1006,25 @@ // Mark the fetch_start (Navigation Timing API). request_params_.navigation_timing.fetch_start = base::TimeTicks::Now(); + GURL base_url; +#if defined(OS_ANDROID) + // On Android, a base URL can be set for the frame. If this the case, it is + // the URL to use for cookies. + NavigationEntry* last_committed_entry = + frame_tree_node_->navigator()->GetController()->GetLastCommittedEntry(); + if (last_committed_entry) + base_url = last_committed_entry->GetBaseURLForDataURL(); +#endif + const GURL& top_document_url = + !base_url.is_empty() + ? base_url + : frame_tree_node_->frame_tree()->root()->current_url(); // TODO(mkwst): This is incorrect. It ought to use the definition from // 'Document::firstPartyForCookies()' in Blink, which walks the ancestor tree // and verifies that all origins are PSL-matches (and special-cases extension // URLs). const GURL& site_for_cookies = - frame_tree_node_->IsMainFrame() - ? common_params_.url - : frame_tree_node_->frame_tree()->root()->current_url(); + frame_tree_node_->IsMainFrame() ? common_params_.url : top_document_url; bool parent_is_main_frame = !frame_tree_node_->parent() ? false : frame_tree_node_->parent()->IsMainFrame(); diff -Nru chromium-browser-63.0.3239.84/content/browser/frame_host/navigator_impl.cc chromium-browser-63.0.3239.132/content/browser/frame_host/navigator_impl.cc --- chromium-browser-63.0.3239.84/content/browser/frame_host/navigator_impl.cc 2017-12-06 20:05:27.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/frame_host/navigator_impl.cc 2018-01-04 20:05:49.000000000 +0000 @@ -1177,9 +1177,12 @@ // a Javascript URL should not interrupt a previous navigation. // Note: The scoped_request will be destroyed at the end of this function. if (dest_url.SchemeIs(url::kJavaScriptScheme)) { + // Don't call frame_tree_node->render_manager()->GetFrameHostForNavigation + // as that might clear the speculative RFH of an ongoing navigation. RenderFrameHostImpl* render_frame_host = - frame_tree_node->render_manager()->GetFrameHostForNavigation( - *scoped_request.get()); + frame_tree_node->current_frame_host(); + frame_tree_node->render_manager()->InitializeRenderFrameIfNecessary( + render_frame_host); render_frame_host->CommitNavigation( nullptr, // response nullptr, // body diff -Nru chromium-browser-63.0.3239.84/content/browser/frame_host/render_frame_host_manager.cc chromium-browser-63.0.3239.132/content/browser/frame_host/render_frame_host_manager.cc --- chromium-browser-63.0.3239.84/content/browser/frame_host/render_frame_host_manager.cc 2017-12-06 20:05:27.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/frame_host/render_frame_host_manager.cc 2018-01-04 20:05:50.000000000 +0000 @@ -1232,6 +1232,36 @@ return new_instance; } +void RenderFrameHostManager::InitializeRenderFrameIfNecessary( + RenderFrameHostImpl* render_frame_host) { + // TODO: this copies some logic inside GetFrameHostForNavigation, which also + // duplicates logic in Navigate. They should all use this method, but that + // involves slight reordering. + if (render_frame_host->IsRenderFrameLive()) + return; + + if (!ReinitializeRenderFrame(render_frame_host)) + return; + + if (render_frame_host != render_frame_host_.get()) + return; + + EnsureRenderFrameHostVisibilityConsistent(); + + // TODO: uncomment this when the method is shared. Not adding the call now + // to make merge to 63 easier. + // EnsureRenderFrameHostPageFocusConsistent(); + + // TODO(nasko): This is a very ugly hack. The Chrome extensions process + // manager still uses NotificationService and expects to see a + // RenderViewHost changed notification after WebContents and + // RenderFrameHostManager are completely initialized. This should be + // removed once the process manager moves away from NotificationService. + // See https://crbug.com/462682. + delegate_->NotifyMainFrameSwappedFromRenderManager( + nullptr, render_frame_host_->render_view_host()); +} + RenderFrameHostManager::SiteInstanceDescriptor RenderFrameHostManager::DetermineSiteInstanceForURL( const GURL& dest_url, @@ -1425,22 +1455,22 @@ if (IsCurrentlySameSite(render_frame_host_.get(), dest_url)) return SiteInstanceDescriptor(render_frame_host_->GetSiteInstance()); - if (SiteIsolationPolicy::IsTopDocumentIsolationEnabled()) { - // TODO(nick): Looking at the main frame and openers is required for TDI - // mode, but should be safe to enable unconditionally. - if (!frame_tree_node_->IsMainFrame()) { - RenderFrameHostImpl* main_frame = - frame_tree_node_->frame_tree()->root()->current_frame_host(); - if (IsCurrentlySameSite(main_frame, dest_url)) - return SiteInstanceDescriptor(main_frame->GetSiteInstance()); - } - - if (frame_tree_node_->opener()) { - RenderFrameHostImpl* opener_frame = - frame_tree_node_->opener()->current_frame_host(); - if (IsCurrentlySameSite(opener_frame, dest_url)) - return SiteInstanceDescriptor(opener_frame->GetSiteInstance()); - } + // Shortcut some common cases for reusing an existing frame's SiteInstance. + // Looking at the main frame and openers is required for TDI mode. It also + // avoids putting same-site iframes into different processes after + // navigations from isolated origins. This matters for some OAuth flows; see + // https://crbug.com/796912. + if (!frame_tree_node_->IsMainFrame()) { + RenderFrameHostImpl* main_frame = + frame_tree_node_->frame_tree()->root()->current_frame_host(); + if (IsCurrentlySameSite(main_frame, dest_url)) + return SiteInstanceDescriptor(main_frame->GetSiteInstance()); + } + if (frame_tree_node_->opener()) { + RenderFrameHostImpl* opener_frame = + frame_tree_node_->opener()->current_frame_host(); + if (IsCurrentlySameSite(opener_frame, dest_url)) + return SiteInstanceDescriptor(opener_frame->GetSiteInstance()); } if (!frame_tree_node_->IsMainFrame() && diff -Nru chromium-browser-63.0.3239.84/content/browser/frame_host/render_frame_host_manager.h chromium-browser-63.0.3239.132/content/browser/frame_host/render_frame_host_manager.h --- chromium-browser-63.0.3239.84/content/browser/frame_host/render_frame_host_manager.h 2017-12-06 20:05:27.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/frame_host/render_frame_host_manager.h 2018-01-04 20:05:50.000000000 +0000 @@ -515,6 +515,9 @@ scoped_refptr GetSiteInstanceForNavigationRequest( const NavigationRequest& navigation_request); + // Helper to initialize the RenderFrame if it's not initialized. + void InitializeRenderFrameIfNecessary(RenderFrameHostImpl* render_frame_host); + private: friend class NavigatorTestWithBrowserSideNavigation; friend class RenderFrameHostManagerTest; diff -Nru chromium-browser-63.0.3239.84/content/browser/isolated_origin_browsertest.cc chromium-browser-63.0.3239.132/content/browser/isolated_origin_browsertest.cc --- chromium-browser-63.0.3239.84/content/browser/isolated_origin_browsertest.cc 2017-12-06 20:05:27.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/isolated_origin_browsertest.cc 2018-01-04 20:05:50.000000000 +0000 @@ -355,6 +355,120 @@ EXPECT_NE(third_shell_instance->GetProcess(), isolated_process); } +// Check that when a cross-site, non-isolated-origin iframe opens a popup, +// navigates it to an isolated origin, and then the popup navigates back to its +// opener iframe's site, the popup and the opener iframe end up in the same +// process and can script each other. See https://crbug.com/796912. +IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, + PopupNavigatesToIsolatedOriginAndBack) { + // Start on a page with same-site iframe. + GURL foo_url( + embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html")); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + FrameTreeNode* child = root->child_at(0); + + // Navigate iframe cross-site, but not to an isolated origin. This should + // stay in the main frame's SiteInstance, unless we're in --site-per-process + // mode. (Note that the bug for which this test is written is exclusive to + // --isolate-origins and does not happen with --site-per-process.) + GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html")); + NavigateIframeToURL(web_contents(), "test_iframe", bar_url); + if (AreAllSitesIsolatedForTesting()) { + EXPECT_NE(root->current_frame_host()->GetSiteInstance(), + child->current_frame_host()->GetSiteInstance()); + } else { + EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), + child->current_frame_host()->GetSiteInstance()); + } + + // Open a blank popup from the iframe. + ShellAddedObserver new_shell_observer; + EXPECT_TRUE(ExecuteScript(child, "window.w = window.open();")); + Shell* new_shell = new_shell_observer.GetShell(); + + // Have the opener iframe navigate the popup to an isolated origin. + GURL isolated_url( + embedded_test_server()->GetURL("isolated.foo.com", "/title1.html")); + { + TestNavigationManager manager(new_shell->web_contents(), isolated_url); + EXPECT_TRUE(ExecuteScript( + child, "window.w.location.href = '" + isolated_url.spec() + "';")); + manager.WaitForNavigationFinished(); + } + + // Simulate the isolated origin in the popup navigating back to bar.com. + GURL bar_url2(embedded_test_server()->GetURL("bar.com", "/title2.html")); + { + TestNavigationManager manager(new_shell->web_contents(), bar_url2); + EXPECT_TRUE( + ExecuteScript(new_shell, "location.href = '" + bar_url2.spec() + "';")); + manager.WaitForNavigationFinished(); + } + + // Check that the popup ended up in the same SiteInstance as its same-site + // opener iframe. + EXPECT_EQ(new_shell->web_contents()->GetMainFrame()->GetSiteInstance(), + child->current_frame_host()->GetSiteInstance()); + + // Check that the opener iframe can script the popup. + std::string popup_location; + EXPECT_TRUE(ExecuteScriptAndExtractString( + child, "domAutomationController.send(window.w.location.href);", + &popup_location)); + EXPECT_EQ(bar_url2.spec(), popup_location); +} + +// Check that with an ABA hierarchy, where B is an isolated origin, the root +// and grandchild frames end up in the same process and can script each other. +// See https://crbug.com/796912. +IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, + IsolatedOriginSubframeCreatesGrandchildInRootSite) { + // Start at foo.com and do a cross-site, renderer-initiated navigation to + // bar.com, which should stay in the same SiteInstance (outside of + // --site-per-process mode). This sets up the main frame such that its + // SiteInstance's site URL does not match its actual origin - a prerequisite + // for https://crbug.com/796912 to happen. + GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + GURL bar_url( + embedded_test_server()->GetURL("bar.com", "/page_with_iframe.html")); + TestNavigationObserver observer(web_contents()); + EXPECT_TRUE( + ExecuteScript(shell(), "location.href = '" + bar_url.spec() + "';")); + observer.Wait(); + + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + FrameTreeNode* child = root->child_at(0); + + // Navigate bar.com's subframe to an isolated origin with its own subframe. + GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com", + "/page_with_iframe.html")); + NavigateIframeToURL(web_contents(), "test_iframe", isolated_url); + EXPECT_EQ(isolated_url, child->current_url()); + FrameTreeNode* grandchild = child->child_at(0); + + // Navigate the isolated origin's subframe back to bar.com, completing the + // ABA hierarchy. + NavigateFrameToURL(grandchild, bar_url); + + // The root and grandchild should be in the same SiteInstance, and the + // middle child should be in a different SiteInstance. + EXPECT_NE(root->current_frame_host()->GetSiteInstance(), + child->current_frame_host()->GetSiteInstance()); + EXPECT_NE(child->current_frame_host()->GetSiteInstance(), + grandchild->current_frame_host()->GetSiteInstance()); + EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), + grandchild->current_frame_host()->GetSiteInstance()); + + // Check that the root frame can script the same-site grandchild frame. + std::string location; + EXPECT_TRUE(ExecuteScriptAndExtractString( + root, "domAutomationController.send(frames[0][0].location.href);", + &location)); + EXPECT_EQ(bar_url.spec(), location); +} + // Check that isolated origins can access cookies. This requires cookie checks // on the IO thread to be aware of isolated origins. IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Cookies) { diff -Nru chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_blocking_browsertest.cc chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_blocking_browsertest.cc --- chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_blocking_browsertest.cc 1970-01-01 00:00:00.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_blocking_browsertest.cc 2018-01-04 20:05:50.000000000 +0000 @@ -0,0 +1,380 @@ +// Copyright 2017 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/command_line.h" +#include "base/macros.h" +#include "base/strings/pattern.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/test/histogram_tester.h" +#include "base/test/scoped_feature_list.h" +#include "content/public/common/content_features.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/resource_type.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace content { + +// These tests verify that the browser process blocks cross-site HTML, XML, +// JSON, and some plain text responses when they are not otherwise permitted +// (e.g., by CORS). This ensures that such responses never end up in the +// renderer process where they might be accessible via a bug. Careful attention +// is paid to allow other cross-site resources necessary for rendering, +// including cases that may be mislabeled as blocked MIME type. +// +// Many of these tests work by turning off the Same Origin Policy in the +// renderer process via --disable-web-security, and then trying to access the +// resource via a cross-origin XHR. If the response is blocked, the XHR should +// see an empty response body. +// +// Note that this BaseTest class does not specify an isolation mode via +// command-line flags. Most of the tests are in the --site-per-process subclass +// below. +class CrossSiteDocumentBlockingBaseTest : public ContentBrowserTest { + public: + CrossSiteDocumentBlockingBaseTest() {} + ~CrossSiteDocumentBlockingBaseTest() override {} + + void SetUpCommandLine(base::CommandLine* command_line) override { + // EmbeddedTestServer::InitializeAndListen() initializes its |base_url_| + // which is required below. This cannot invoke Start() however as that kicks + // off the "EmbeddedTestServer IO Thread" which then races with + // initialization in ContentBrowserTest::SetUp(), http://crbug.com/674545. + ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); + + // Add a host resolver rule to map all outgoing requests to the test server. + // This allows us to use "real" hostnames and standard ports in URLs (i.e., + // without having to inject the port number into all URLs), which we can use + // to create arbitrary SiteInstances. + command_line->AppendSwitchASCII( + switches::kHostResolverRules, + "MAP * " + embedded_test_server()->host_port_pair().ToString() + + ",EXCLUDE localhost"); + + // To test that the renderer process does not receive blocked documents, we + // disable the same origin policy to let it see cross-origin fetches if they + // are received. + command_line->AppendSwitch(switches::kDisableWebSecurity); + } + + void SetUpOnMainThread() override { + // Complete the manual Start() after ContentBrowserTest's own + // initialization, ref. comment on InitializeAndListen() above. + embedded_test_server()->StartAcceptingConnections(); + } + + // Ensure the correct histograms are incremented for blocking events. + // Assumes the resource type is XHR. + void InspectHistograms(const base::HistogramTester& histograms, + bool should_be_blocked, + bool should_be_sniffed, + const std::string& resource_name, + ResourceType resource_type) { + std::string bucket; + if (base::MatchPattern(resource_name, "*.html")) { + bucket = "HTML"; + } else if (base::MatchPattern(resource_name, "*.xml")) { + bucket = "XML"; + } else if (base::MatchPattern(resource_name, "*.json")) { + bucket = "JSON"; + } else if (base::MatchPattern(resource_name, "*.txt")) { + bucket = "Plain"; + } else { + EXPECT_FALSE(should_be_blocked); + bucket = "Other"; + } + + // Determine the appropriate histograms, including a start and end action + // (which are verified in unit tests), a read size if it was sniffed, and + // additional blocked metrics if it was blocked. + base::HistogramTester::CountsMap expected_counts; + std::string base = "SiteIsolation.XSD.Browser"; + expected_counts[base + ".Action"] = 2; + if (should_be_sniffed) + expected_counts[base + ".BytesReadForSniffing"] = 1; + if (should_be_blocked) { + expected_counts[base + ".Blocked"] = 1; + expected_counts[base + ".Blocked." + bucket] = 1; + } + + // Make sure that the expected metrics, and only those metrics, were + // incremented. + EXPECT_THAT(histograms.GetTotalCountsForPrefix("SiteIsolation.XSD.Browser"), + testing::ContainerEq(expected_counts)) + << "For resource_name=" << resource_name + << ", should_be_blocked=" << should_be_blocked; + + // Determine if the bucket for the resource type (XHR) was incremented. + if (should_be_blocked) { + EXPECT_THAT(histograms.GetAllSamples(base + ".Blocked"), + testing::ElementsAre(base::Bucket(resource_type, 1))) + << "The wrong Blocked bucket was incremented."; + EXPECT_THAT(histograms.GetAllSamples(base + ".Blocked." + bucket), + testing::ElementsAre(base::Bucket(resource_type, 1))) + << "The wrong Blocked bucket was incremented."; + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingBaseTest); +}; + +// Most tests here use --site-per-process, which enables document blocking +// everywhere. +class CrossSiteDocumentBlockingTest : public CrossSiteDocumentBlockingBaseTest { + public: + CrossSiteDocumentBlockingTest() {} + ~CrossSiteDocumentBlockingTest() override {} + + void SetUpCommandLine(base::CommandLine* command_line) override { + IsolateAllSitesForTesting(command_line); + CrossSiteDocumentBlockingBaseTest::SetUpCommandLine(command_line); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingTest); +}; + +IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockDocuments) { + // Load a page that issues illegal cross-site document requests to bar.com. + // The page uses XHR to request HTML/XML/JSON documents from bar.com, and + // inspects if any of them were successfully received. This test is only + // possible since we run the browser without the same origin policy, allowing + // it to see the response body if it makes it to the renderer (even if the + // renderer would normally block access to it). + GURL foo_url("http://foo.com/cross_site_document_request.html"); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + + // The following are files under content/test/data/site_isolation. All + // should be disallowed for cross site XHR under the document blocking policy. + // valid.* - Correctly labeled HTML/XML/JSON files. + // *.txt - Plain text that sniffs as HTML, XML, or JSON. + // htmlN_dtd.* - Various HTML templates to test. + const char* blocked_resources[] = { + "valid.html", "valid.xml", "valid.json", "html.txt", + "xml.txt", "json.txt", "comment_valid.html", "html4_dtd.html", + "html4_dtd.txt", "html5_dtd.html", "html5_dtd.txt"}; + for (const char* resource : blocked_resources) { + SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource)); + base::HistogramTester histograms; + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), base::StringPrintf("sendRequest('%s');", resource), + &was_blocked)); + EXPECT_TRUE(was_blocked); + InspectHistograms(histograms, true /* should_be_blocked */, + true /* should_be_sniffed */, resource, + RESOURCE_TYPE_XHR); + } + + // These files should be disallowed without sniffing. + // nosniff.* - Won't sniff correctly, but blocked because of nosniff. + const char* nosniff_blocked_resources[] = {"nosniff.html", "nosniff.xml", + "nosniff.json", "nosniff.txt"}; + for (const char* resource : nosniff_blocked_resources) { + SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource)); + base::HistogramTester histograms; + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), base::StringPrintf("sendRequest('%s');", resource), + &was_blocked)); + EXPECT_TRUE(was_blocked); + InspectHistograms(histograms, true /* should_be_blocked */, + false /* should_be_sniffed */, resource, + RESOURCE_TYPE_XHR); + } + + // These files are allowed for XHR under the document blocking policy because + // the sniffing logic determines they are not actually documents. + // *js.* - JavaScript mislabeled as a document. + // jsonp.* - JSONP (i.e., script) mislabeled as a document. + // img.* - Contents that won't match the document label. + const char* sniff_allowed_resources[] = { + "js.html", "comment_js.html", "js.xml", "js.json", "js.txt", + "jsonp.html", "jsonp.xml", "jsonp.json", "jsonp.txt", "img.html", + "img.xml", "img.json", "img.txt"}; + for (const char* resource : sniff_allowed_resources) { + SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource)); + base::HistogramTester histograms; + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), base::StringPrintf("sendRequest('%s');", resource), + &was_blocked)); + EXPECT_FALSE(was_blocked); + InspectHistograms(histograms, false /* should_be_blocked */, + true /* should_be_sniffed */, resource, + RESOURCE_TYPE_XHR); + } + + // These files should be allowed for XHR under the document blocking policy. + // cors.* - Correctly labeled documents with valid CORS headers. + // valid.* - Correctly labeled responses of non-document types. + const char* allowed_resources[] = {"cors.html", "cors.xml", "cors.json", + "cors.txt", "valid.js"}; + for (const char* resource : allowed_resources) { + SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource)); + base::HistogramTester histograms; + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), base::StringPrintf("sendRequest('%s');", resource), + &was_blocked)); + EXPECT_FALSE(was_blocked); + InspectHistograms(histograms, false /* should_be_blocked */, + false /* should_be_sniffed */, resource, + RESOURCE_TYPE_XHR); + } +} + +// Verify that range requests disable the sniffing logic, so that attackers +// can't cause sniffing to fail to force a response to be allowed. This won't +// be a problem for script files mislabeled as HTML/XML/JSON/text (i.e., the +// reason for sniffing), since script tags won't send Range headers. +IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, RangeRequest) { + GURL foo_url("http://foo.com/cross_site_document_request.html"); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + + { + // Try to skip the first byte using a range request in an attempt to get the + // response to fail sniffing and be allowed through. It should still be + // blocked because sniffing is disabled. + base::HistogramTester histograms; + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), "sendRequest('valid.html', 'bytes=1-24');", &was_blocked)); + EXPECT_TRUE(was_blocked); + InspectHistograms(histograms, true /* should_be_blocked */, + false /* should_be_sniffed */, "valid.html", + RESOURCE_TYPE_XHR); + } + { + // Verify that a response which would have been allowed by MIME type anyway + // is still allowed for range requests. + base::HistogramTester histograms; + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), "sendRequest('valid.js', 'bytes=1-5');", &was_blocked)); + EXPECT_FALSE(was_blocked); + InspectHistograms(histograms, false /* should_be_blocked */, + false /* should_be_sniffed */, "valid.js", + RESOURCE_TYPE_XHR); + } + { + // Verify that a response which would have been allowed by CORS anyway is + // still allowed for range requests. + base::HistogramTester histograms; + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), "sendRequest('cors.json', 'bytes=2-7');", &was_blocked)); + EXPECT_FALSE(was_blocked); + InspectHistograms(histograms, false /* should_be_blocked */, + false /* should_be_sniffed */, "cors.json", + RESOURCE_TYPE_XHR); + } +} + +IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockForVariousTargets) { + // This webpage loads a cross-site HTML page in different targets such as + // ,,, etc. Since the requested document is blocked, and one + // character string (' ') is returned instead, this tests that the renderer + // does not crash even when it receives a response body which is " ", whose + // length is different from what's described in "content-length" for such + // different targets. + + // TODO(nick): Split up these cases, and add positive assertions here about + // what actually happens in these various resource-block cases. + GURL foo("http://foo.com/cross_site_document_request_target.html"); + EXPECT_TRUE(NavigateToURL(shell(), foo)); + WaitForLoadStop(shell()->web_contents()); + + // TODO(creis): Wait for all the subresources to load and ensure renderer + // process is still alive. +} + +class CrossSiteDocumentBlockingKillSwitchTest + : public CrossSiteDocumentBlockingTest { + public: + CrossSiteDocumentBlockingKillSwitchTest() { + // Simulate flipping the kill switch. + scoped_feature_list_.InitAndDisableFeature( + features::kCrossSiteDocumentBlockingIfIsolating); + } + + ~CrossSiteDocumentBlockingKillSwitchTest() override {} + + private: + base::test::ScopedFeatureList scoped_feature_list_; + + DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingKillSwitchTest); +}; + +// After the kill switch is flipped, there should be no document blocking. +IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingKillSwitchTest, + NoBlockingWithKillSwitch) { + // Load a page that issues illegal cross-site document requests to bar.com. + GURL foo_url("http://foo.com/cross_site_document_request.html"); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), "sendRequest(\"valid.html\");", &was_blocked)); + EXPECT_FALSE(was_blocked); +} + +// Without any Site Isolation (in the base test class), there should be no +// document blocking. +IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingBaseTest, + DontBlockDocumentsByDefault) { + if (AreAllSitesIsolatedForTesting()) + return; + + // Load a page that issues illegal cross-site document requests to bar.com. + GURL foo_url("http://foo.com/cross_site_document_request.html"); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), "sendRequest(\"valid.html\");", &was_blocked)); + EXPECT_FALSE(was_blocked); +} + +// Test class to verify that documents are blocked for isolated origins as well. +class CrossSiteDocumentBlockingIsolatedOriginTest + : public CrossSiteDocumentBlockingBaseTest { + public: + CrossSiteDocumentBlockingIsolatedOriginTest() {} + ~CrossSiteDocumentBlockingIsolatedOriginTest() override {} + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitchASCII(switches::kIsolateOrigins, + "http://bar.com"); + CrossSiteDocumentBlockingBaseTest::SetUpCommandLine(command_line); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingIsolatedOriginTest); +}; + +IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingIsolatedOriginTest, + BlockDocumentsFromIsolatedOrigin) { + if (AreAllSitesIsolatedForTesting()) + return; + + // Load a page that issues illegal cross-site document requests to the + // isolated origin. + GURL foo_url("http://foo.com/cross_site_document_request.html"); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + + bool was_blocked; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell(), "sendRequest(\"valid.html\");", &was_blocked)); + EXPECT_TRUE(was_blocked); +} + +} // namespace content diff -Nru chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_resource_handler.cc chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_resource_handler.cc --- chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_resource_handler.cc 1970-01-01 00:00:00.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_resource_handler.cc 2018-01-04 20:05:50.000000000 +0000 @@ -0,0 +1,420 @@ +// Copyright 2017 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 "content/browser/loader/cross_site_document_resource_handler.h" + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/string_piece.h" +#include "base/trace_event/trace_event.h" +#include "content/browser/child_process_security_policy_impl.h" +#include "content/browser/loader/detachable_resource_handler.h" +#include "content/browser/loader/resource_request_info_impl.h" +#include "content/browser/site_instance_impl.h" +#include "content/common/site_isolation_policy.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/resource_context.h" +#include "content/public/common/content_client.h" +#include "net/base/io_buffer.h" +#include "net/base/mime_sniffer.h" +#include "net/url_request/url_request.h" + +namespace content { + +namespace { + +void LogCrossSiteDocumentAction( + CrossSiteDocumentResourceHandler::Action action) { + UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Action", action, + CrossSiteDocumentResourceHandler::Action::kCount); +} + +} // namespace + +// ResourceController used in OnWillRead in cases that sniffing will happen. +// When invoked, it runs the corresponding method on the ResourceHandler. +class CrossSiteDocumentResourceHandler::OnWillReadController + : public ResourceController { + public: + // Keeps track of the addresses of the ResourceLoader's buffer and size, + // which will be populated by the downstream ResourceHandler by the time that + // Resume() is called. + explicit OnWillReadController( + CrossSiteDocumentResourceHandler* document_handler, + scoped_refptr* buf, + int* buf_size) + : document_handler_(document_handler), buf_(buf), buf_size_(buf_size) {} + + ~OnWillReadController() override {} + + // ResourceController implementation: + void Resume() override { + MarkAsUsed(); + + // Now that |buf_| has a buffer written into it by the downstream handler, + // set up sniffing in the CrossSiteDocumentResourceHandler. + document_handler_->ResumeOnWillRead(buf_, buf_size_); + } + + void Cancel() override { + MarkAsUsed(); + document_handler_->Cancel(); + } + + void CancelWithError(int error_code) override { + MarkAsUsed(); + document_handler_->CancelWithError(error_code); + } + + private: + void MarkAsUsed() { +#if DCHECK_IS_ON() + DCHECK(!used_); + used_ = true; +#endif + } + +#if DCHECK_IS_ON() + bool used_ = false; +#endif + + CrossSiteDocumentResourceHandler* document_handler_; + + // Address of the ResourceLoader's buffer, which will be populated by the + // downstream handler before Resume() is called. + scoped_refptr* buf_; + + // Address of the size of |buf_|, similarly populated downstream. + int* buf_size_; + + DISALLOW_COPY_AND_ASSIGN(OnWillReadController); +}; + +CrossSiteDocumentResourceHandler::CrossSiteDocumentResourceHandler( + std::unique_ptr next_handler, + net::URLRequest* request, + bool is_nocors_plugin_request) + : LayeredResourceHandler(request, std::move(next_handler)), + is_nocors_plugin_request_(is_nocors_plugin_request) {} + +CrossSiteDocumentResourceHandler::~CrossSiteDocumentResourceHandler() {} + +void CrossSiteDocumentResourceHandler::OnResponseStarted( + ResourceResponse* response, + std::unique_ptr controller) { + has_response_started_ = true; + LogCrossSiteDocumentAction( + CrossSiteDocumentResourceHandler::Action::kResponseStarted); + + should_block_based_on_headers_ = ShouldBlockBasedOnHeaders(response); + next_handler_->OnResponseStarted(response, std::move(controller)); +} + +void CrossSiteDocumentResourceHandler::OnWillRead( + scoped_refptr* buf, + int* buf_size, + std::unique_ptr controller) { + DCHECK(has_response_started_); + + // On the next read attempt after the response was blocked, either cancel the + // rest of the request or allow it to proceed in a detached state. + if (blocked_read_completed_) { + DCHECK(should_block_based_on_headers_); + DCHECK(!allow_based_on_sniffing_); + const ResourceRequestInfoImpl* info = GetRequestInfo(); + if (info && info->detachable_handler()) { + // Ensure that prefetch, etc, continue to cache the response, without + // sending it to the renderer. + info->detachable_handler()->Detach(); + } else { + // If it's not detachable, cancel the rest of the request. + controller->Cancel(); + } + return; + } + + // If we intended to block the response and haven't yet decided to allow it + // due to sniffing, we will read some of the data to a local buffer to sniff + // it. Since the downstream handler may defer during the OnWillRead call + // below, the values of |buf| and |buf_size| may not be available right away. + // Instead, create an OnWillReadController to start the sniffing after the + // downstream handler has called Resume on it. + if (should_block_based_on_headers_ && !allow_based_on_sniffing_) { + HoldController(std::move(controller)); + controller = std::make_unique(this, buf, buf_size); + } + + // Have the downstream handler(s) allocate the real buffer to use. + next_handler_->OnWillRead(buf, buf_size, std::move(controller)); +} + +void CrossSiteDocumentResourceHandler::ResumeOnWillRead( + scoped_refptr* buf, + int* buf_size) { + // We should only get here in cases that we intend to sniff the data, after + // downstream handler finishes its work from OnWillRead. + DCHECK(should_block_based_on_headers_); + DCHECK(!allow_based_on_sniffing_); + DCHECK(!blocked_read_completed_); + + // For most blocked responses, we need to sniff the data to confirm it looks + // like the claimed MIME type (to avoid blocking mislabeled JavaScript, + // JSONP, etc). Read this data into a separate buffer (not shared with the + // renderer), which we will only copy over if we decide to allow it through. + // This is only done when we suspect the response should be blocked. + // + // Make it as big as the downstream handler's buffer to make it easy to copy + // over in one operation. This will be large, since the MIME sniffing + // handler is downstream. Technically we could use a smaller buffer if + // |needs_sniffing_| is false, but there's no need for the extra complexity. + DCHECK_GE(*buf_size, net::kMaxBytesToSniff); + local_buffer_ = + base::MakeRefCounted(static_cast(*buf_size)); + + // Store the next handler's buffer but don't read into it while sniffing, + // since we probably won't want to send the data to the renderer process. + next_handler_buffer_ = *buf; + next_handler_buffer_size_ = *buf_size; + *buf = local_buffer_; + + Resume(); +} + +void CrossSiteDocumentResourceHandler::OnReadCompleted( + int bytes_read, + std::unique_ptr controller) { + DCHECK(has_response_started_); + DCHECK(!blocked_read_completed_); + + // If we intended to block the response and haven't sniffed yet, try to + // confirm that we should block it. If sniffing is needed, look at the local + // buffer and either report that zero bytes were read (to indicate the + // response is empty and complete), or copy the sniffed data to the next + // handler's buffer and resume the response without blocking. + if (should_block_based_on_headers_ && !allow_based_on_sniffing_) { + bool confirmed_blockable = false; + if (!needs_sniffing_) { + // TODO(creis): Also consider the MIME type confirmed if |bytes_read| is + // too small to do sniffing, or restructure to allow buffering enough. + // For now, responses with small initial reads may be allowed through. + confirmed_blockable = true; + } else { + // Sniff the data to see if it likely matches the MIME type that caused us + // to decide to block it. If it doesn't match, it may be JavaScript, + // JSONP, or another allowable data type and we should let it through. + // Record how many bytes were read to see how often it's too small. (This + // will typically be under 100,000.) + UMA_HISTOGRAM_COUNTS("SiteIsolation.XSD.Browser.BytesReadForSniffing", + bytes_read); + DCHECK_LE(bytes_read, next_handler_buffer_size_); + base::StringPiece data(local_buffer_->data(), bytes_read); + + // Confirm whether the data is HTML, XML, or JSON. + if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_HTML) { + confirmed_blockable = CrossSiteDocumentClassifier::SniffForHTML(data); + } else if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_XML) { + confirmed_blockable = CrossSiteDocumentClassifier::SniffForXML(data); + } else if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_JSON) { + confirmed_blockable = CrossSiteDocumentClassifier::SniffForJSON(data); + } else if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN) { + // For responses labeled as plain text, only block them if the data + // sniffs as one of the formats we would block in the first place. + confirmed_blockable = CrossSiteDocumentClassifier::SniffForHTML(data) || + CrossSiteDocumentClassifier::SniffForXML(data) || + CrossSiteDocumentClassifier::SniffForJSON(data); + } + } + + if (confirmed_blockable) { + // Block the response and throw away the data. Report zero bytes read. + bytes_read = 0; + blocked_read_completed_ = true; + + // Log the blocking event. Inline the Serialize call to avoid it when + // tracing is disabled. + TRACE_EVENT2("navigation", + "CrossSiteDocumentResourceHandler::ShouldBlockResponse", + "initiator", + request()->initiator().has_value() + ? request()->initiator().value().Serialize() + : "null", + "url", request()->url().spec()); + + LogCrossSiteDocumentAction( + needs_sniffing_ + ? CrossSiteDocumentResourceHandler::Action::kBlockedAfterSniffing + : CrossSiteDocumentResourceHandler::Action:: + kBlockedWithoutSniffing); + ResourceType resource_type = GetRequestInfo()->GetResourceType(); + UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked", + resource_type, + content::RESOURCE_TYPE_LAST_TYPE); + switch (canonical_mime_type_) { + case CROSS_SITE_DOCUMENT_MIME_TYPE_HTML: + UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.HTML", + resource_type, + content::RESOURCE_TYPE_LAST_TYPE); + break; + case CROSS_SITE_DOCUMENT_MIME_TYPE_XML: + UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.XML", + resource_type, + content::RESOURCE_TYPE_LAST_TYPE); + break; + case CROSS_SITE_DOCUMENT_MIME_TYPE_JSON: + UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.JSON", + resource_type, + content::RESOURCE_TYPE_LAST_TYPE); + break; + case CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN: + UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.Plain", + resource_type, + content::RESOURCE_TYPE_LAST_TYPE); + break; + default: + NOTREACHED(); + } + } else { + // Allow the response through instead and proceed with reading more. + // Copy sniffed data into the next handler's buffer before proceeding. + // Note that the size of the two buffers is the same (see OnWillRead). + DCHECK_LE(bytes_read, next_handler_buffer_size_); + memcpy(next_handler_buffer_->data(), local_buffer_->data(), bytes_read); + allow_based_on_sniffing_ = true; + } + + // Clean up, whether we'll cancel or proceed from here. + local_buffer_ = nullptr; + next_handler_buffer_ = nullptr; + next_handler_buffer_size_ = 0; + } + + next_handler_->OnReadCompleted(bytes_read, std::move(controller)); +} + +void CrossSiteDocumentResourceHandler::OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr controller) { + if (blocked_read_completed_) { + // Report blocked responses as successful, rather than the cancellation + // from OnWillRead. + next_handler_->OnResponseCompleted(net::URLRequestStatus(), + std::move(controller)); + } else { + LogCrossSiteDocumentAction( + needs_sniffing_ + ? CrossSiteDocumentResourceHandler::Action::kAllowedAfterSniffing + : CrossSiteDocumentResourceHandler::Action:: + kAllowedWithoutSniffing); + + next_handler_->OnResponseCompleted(status, std::move(controller)); + } +} + +bool CrossSiteDocumentResourceHandler::ShouldBlockBasedOnHeaders( + ResourceResponse* response) { + // The checks in this method are ordered to rule out blocking in most cases as + // quickly as possible. Checks that are likely to lead to returning false or + // that are inexpensive should be near the top. + const GURL& url = request()->url(); + + // Check if the response's site needs to have its documents protected. By + // default, this will usually return false. + // TODO(creis): This check can go away once the logic here is made fully + // backward compatible and we can enforce it always, regardless of Site + // Isolation policy. + switch (SiteIsolationPolicy::IsCrossSiteDocumentBlockingEnabled()) { + case SiteIsolationPolicy::XSDB_ENABLED_UNCONDITIONALLY: + break; + case SiteIsolationPolicy::XSDB_ENABLED_IF_ISOLATED: + if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites() && + !ChildProcessSecurityPolicyImpl::GetInstance()->IsIsolatedOrigin( + url::Origin::Create(url))) { + return false; + } + break; + case SiteIsolationPolicy::XSDB_DISABLED: + return false; + } + + // Look up MIME type. If it doesn't claim to be a blockable type (i.e., HTML, + // XML, JSON, or plain text), don't block it. + canonical_mime_type_ = CrossSiteDocumentClassifier::GetCanonicalMimeType( + response->head.mime_type); + if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_OTHERS) + return false; + + // Treat a missing initiator as an empty origin to be safe, though we don't + // expect this to happen. Unfortunately, this requires a copy. + url::Origin initiator; + if (request()->initiator().has_value()) + initiator = request()->initiator().value(); + + // Don't block same-site documents. + if (CrossSiteDocumentClassifier::IsSameSite(initiator, url)) + return false; + + // Only block documents from HTTP(S) schemes. + if (!CrossSiteDocumentClassifier::IsBlockableScheme(url)) + return false; + + // Allow requests from file:// URLs for now. + // TODO(creis): Limit this to when the allow_universal_access_from_file_urls + // preference is set. See https://crbug.com/789781. + if (initiator.scheme() == url::kFileScheme) + return false; + + // Only block if this is a request made from a renderer process. + const ResourceRequestInfoImpl* info = GetRequestInfo(); + if (!info || info->GetChildID() == -1) + return false; + + // Give embedder a chance to skip document blocking for this response. + if (GetContentClient()->browser()->ShouldBypassDocumentBlocking( + initiator, url, info->GetResourceType())) { + return false; + } + + // Allow the response through if it has valid CORS headers. + std::string cors_header; + response->head.headers->GetNormalizedHeader("access-control-allow-origin", + &cors_header); + if (CrossSiteDocumentClassifier::IsValidCorsHeaderSet(initiator, url, + cors_header)) { + return false; + } + + // Don't block plugin requests with universal access (e.g., Flash). Such + // requests are made without CORS, and thus dont have an Origin request + // header. Other plugin requests (e.g., NaCl) are made using CORS and have an + // Origin request header. If they fail the CORS check above, they should be + // blocked. + if (info->GetResourceType() == RESOURCE_TYPE_PLUGIN_RESOURCE && + is_nocors_plugin_request_) { + return false; + } + + // We intend to block the response at this point. However, we will usually + // sniff the contents to confirm the MIME type, to avoid blocking incorrectly + // labeled JavaScript, JSONP, etc files. + // + // Note: only sniff if there isn't a nosniff header, and if it is not a range + // request. Range requests would let an attacker bypass blocking by + // requesting a range that fails to sniff as a protected type. + std::string nosniff_header; + response->head.headers->GetNormalizedHeader("x-content-type-options", + &nosniff_header); + std::string range_header; + response->head.headers->GetNormalizedHeader("content-range", &range_header); + needs_sniffing_ = !base::LowerCaseEqualsASCII(nosniff_header, "nosniff") && + range_header.empty(); + + return true; +} + +} // namespace content diff -Nru chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_resource_handler.h chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_resource_handler.h --- chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_resource_handler.h 1970-01-01 00:00:00.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_resource_handler.h 2018-01-04 20:05:50.000000000 +0000 @@ -0,0 +1,156 @@ +// Copyright 2017 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. + +#ifndef CONTENT_BROWSER_LOADER_CROSS_SITE_DOCUMENT_RESOURCE_HANDLER_H_ +#define CONTENT_BROWSER_LOADER_CROSS_SITE_DOCUMENT_RESOURCE_HANDLER_H_ + +#include + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "content/browser/loader/layered_resource_handler.h" +#include "content/common/cross_site_document_classifier.h" +#include "content/public/common/resource_type.h" + +namespace net { +class URLRequest; +} // namespace net + +namespace content { + +// A ResourceHandler that prevents the renderer process from receiving network +// responses that contain cross-site documents (HTML, XML, some plain text) or +// similar data that should be opaque (JSON), with appropriate exceptions to +// preserve compatibility. Other cross-site resources such as scripts, images, +// stylesheets, etc are still allowed. +// +// This handler is not used for navigations, which create a new security context +// based on the origin of the response. It currently only protects documents +// from sites that require dedicated renderer processes, though it could be +// expanded to apply to all sites. +// +// When a response is blocked, the renderer is sent an empty response body +// instead of seeing a failed request. A failed request would change page- +// visible behavior (e.g., for a blocked XHR). An empty response can generally +// be consumed by the renderer without noticing the difference. +// +// For more details, see: +// http://chromium.org/developers/design-documents/blocking-cross-site-documents +class CONTENT_EXPORT CrossSiteDocumentResourceHandler + : public LayeredResourceHandler { + public: + // This enum backs a histogram. Update enums.xml if you make any updates, and + // put new entries before |kCount|. + enum class Action { + // Logged at OnResponseStarted. + kResponseStarted, + + // Logged when a response is blocked without requiring sniffing. + kBlockedWithoutSniffing, + + // Logged when a response is blocked as a result of sniffing the content. + kBlockedAfterSniffing, + + // Logged when a response is allowed without requiring sniffing. + kAllowedWithoutSniffing, + + // Logged when a response is allowed as a result of sniffing the content. + kAllowedAfterSniffing, + + kCount + }; + + CrossSiteDocumentResourceHandler( + std::unique_ptr next_handler, + net::URLRequest* request, + bool is_nocors_plugin_request); + ~CrossSiteDocumentResourceHandler() override; + + // LayeredResourceHandler overrides: + void OnResponseStarted( + ResourceResponse* response, + std::unique_ptr controller) override; + void OnWillRead(scoped_refptr* buf, + int* buf_size, + std::unique_ptr controller) override; + void OnReadCompleted(int bytes_read, + std::unique_ptr controller) override; + void OnResponseCompleted( + const net::URLRequestStatus& status, + std::unique_ptr controller) override; + + private: + FRIEND_TEST_ALL_PREFIXES(CrossSiteDocumentResourceHandlerTest, + ResponseBlocking); + FRIEND_TEST_ALL_PREFIXES(CrossSiteDocumentResourceHandlerTest, + OnWillReadDefer); + + // ResourceController that manages the read buffer if a downstream handler + // defers during OnWillRead. + class OnWillReadController; + + // Computes whether this response contains a cross-site document that needs to + // be blocked from the renderer process. This is a first approximation based + // on the headers, and may be revised after some of the data is sniffed. + bool ShouldBlockBasedOnHeaders(ResourceResponse* response); + + // Once the downstream handler has allocated the buffer for OnWillRead + // (possibly after deferring), this sets up sniffing into a local buffer. + // Called by the OnWillReadController. + void ResumeOnWillRead(scoped_refptr* buf, int* buf_size); + + // A local buffer for sniffing content and using for throwaway reads. + // This is not shared with the renderer process. + scoped_refptr local_buffer_; + + // The buffer allocated by the next ResourceHandler for reads, which is used + // if sniffing determines that we should proceed with the response. + scoped_refptr next_handler_buffer_; + + // The size of |next_handler_buffer_|. + int next_handler_buffer_size_ = 0; + + // A canonicalization of the specified MIME type, to determine if blocking the + // response is needed, as well as which type of sniffing to perform. + CrossSiteDocumentMimeType canonical_mime_type_ = + CROSS_SITE_DOCUMENT_MIME_TYPE_OTHERS; + + // Indicates whether this request was made by a plugin and was not using CORS. + // Such requests are exempt from blocking, while other plugin requests must be + // blocked if the CORS check fails. + // TODO(creis, nick): Replace this with a plugin process ID check to see if + // the plugin has universal access. + bool is_nocors_plugin_request_; + + // Tracks whether OnResponseStarted has been called, to ensure that it happens + // before OnWillRead and OnReadCompleted. + bool has_response_started_ = false; + + // Whether this response is a cross-site document that should be blocked, + // pending the outcome of sniffing the content. Set in OnResponseStarted and + // should only be read afterwards. + bool should_block_based_on_headers_ = false; + + // Whether the response data should be sniffed before blocking it, to avoid + // blocking mislabeled responses (e.g., JSONP labeled as HTML). This is + // usually true when |should_block_based_on_headers_| is set, unless there is + // a nosniff header or range request. + bool needs_sniffing_ = false; + + // Whether this response will be allowed through despite being flagged for + // blocking (via |should_block_based_on_headers_), because sniffing determined + // it was incorrectly labeled and might be needed for compatibility (e.g., + // in case it is Javascript). + bool allow_based_on_sniffing_ = false; + + // Whether the next ResourceHandler has already been told that the read has + // completed, and thus it is safe to cancel or detach on the next read. + bool blocked_read_completed_ = false; + + DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentResourceHandler); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_CROSS_SITE_DOCUMENT_RESOURCE_HANDLER_H_ diff -Nru chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_resource_handler_unittest.cc chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_resource_handler_unittest.cc --- chromium-browser-63.0.3239.84/content/browser/loader/cross_site_document_resource_handler_unittest.cc 1970-01-01 00:00:00.000000000 +0000 +++ chromium-browser-63.0.3239.132/content/browser/loader/cross_site_document_resource_handler_unittest.cc 2018-01-04 20:05:50.000000000 +0000 @@ -0,0 +1,836 @@ +// Copyright 2017 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 "content/browser/loader/cross_site_document_resource_handler.h" + +#include + +#include +#include +#include + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "base/test/histogram_tester.h" +#include "base/threading/thread_task_runner_handle.h" +#include "content/browser/loader/mock_resource_loader.h" +#include "content/browser/loader/resource_controller.h" +#include "content/browser/loader/test_resource_handler.h" +#include "content/public/browser/resource_request_info.h" +#include "content/public/common/resource_response.h" +#include "content/public/common/resource_type.h" +#include "content/public/common/webplugininfo.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/test_utils.h" +#include "net/base/net_errors.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_status.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace content { + +namespace { + +enum class OriginHeader { kOmit, kInclude }; + +enum class AccessControlAllowOriginHeader { + kOmit, + kAllowAny, + kAllowNull, + kAllowInitiatorOrigin, + kAllowExampleDotCom +}; + +enum class Verdict { + kAllowWithoutSniffing, + kBlockWithoutSniffing, + kAllowAfterSniffing, + kBlockAfterSniffing +}; + +// This struct is used to describe each test case in this file. It's passed as +// a test parameter to each TEST_P test. +struct TestScenario { + // Attributes to make test failure messages useful. + const char* description; + int source_line; + + // Attributes of the HTTP Request. + const char* target_url; + ResourceType resource_type; + const char* initiator_origin; + OriginHeader cors_request; + + // Attributes of the HTTP response. + const char* response_mime_type; + CrossSiteDocumentMimeType canonical_mime_type; + bool include_no_sniff_header; + AccessControlAllowOriginHeader cors_response; + const char* first_chunk; + + // Expected result. + Verdict verdict; +}; + +// Stream operator to let GetParam() print a useful result if any tests fail. +::std::ostream& operator<<(::std::ostream& os, const TestScenario& scenario) { + std::string cors_response; + switch (scenario.cors_response) { + case AccessControlAllowOriginHeader::kOmit: + cors_response = "AccessControlAllowOriginHeader::kOmit"; + break; + case AccessControlAllowOriginHeader::kAllowAny: + cors_response = "AccessControlAllowOriginHeader::kAllowAny"; + break; + case AccessControlAllowOriginHeader::kAllowNull: + cors_response = "AccessControlAllowOriginHeader::kAllowNull"; + break; + case AccessControlAllowOriginHeader::kAllowInitiatorOrigin: + cors_response = "AccessControlAllowOriginHeader::kAllowInitiatorOrigin"; + break; + case AccessControlAllowOriginHeader::kAllowExampleDotCom: + cors_response = "AccessControlAllowOriginHeader::kAllowExampleDotCom"; + break; + default: + NOTREACHED(); + } + + std::string verdict; + switch (scenario.verdict) { + case Verdict::kAllowWithoutSniffing: + verdict = "Verdict::kAllowWithoutSniffing"; + break; + case Verdict::kBlockWithoutSniffing: + verdict = "Verdict::kBlockWithoutSniffing"; + break; + case Verdict::kAllowAfterSniffing: + verdict = "Verdict::kAllowAfterSniffing"; + break; + case Verdict::kBlockAfterSniffing: + verdict = "Verdict::kBlockAfterSniffing"; + break; + default: + NOTREACHED(); + } + + return os << "\n description = " << scenario.description + << "\n target_url = " << scenario.target_url + << "\n resource_type = " << scenario.resource_type + << "\n initiator_origin = " << scenario.initiator_origin + << "\n cors_request = " + << (scenario.cors_request == OriginHeader::kOmit + ? "OriginHeader::kOmit" + : "OriginHeader::kInclude") + << "\n response_mime_type = " << scenario.response_mime_type + << "\n canonical_mime_type = " << scenario.canonical_mime_type + << "\n include_no_sniff = " + << (scenario.include_no_sniff_header ? "true" : "false") + << "\n cors_response = " << cors_response + << "\n first_chunk = " << scenario.first_chunk + << "\n verdict = " << verdict; +} + +// A set of test cases that verify CrossSiteDocumentResourceHandler correctly +// classifies network responses as allowed or blocked. These TestScenarios are +// passed to the TEST_P tests below as test parameters. +const TestScenario kScenarios[] = { + // Allowed responses: + { + "Allowed: Same-site XHR to HTML", __LINE__, + "http://www.a.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site script", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_SCRIPT, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "application/javascript", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_OTHERS, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "var x=3;", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site XHR to HTML with CORS for origin", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kInclude, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kAllowInitiatorOrigin, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site XHR to XML with CORS for any", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kInclude, // cors_request + "application/rss+xml", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_XML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kAllowAny, // cors_response + "", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site XHR to JSON with CORS for null", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kInclude, // cors_request + "text/json", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_JSON, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kAllowNull, // cors_response + "{\"x\" : 3}", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site XHR to HTML over FTP", __LINE__, + "ftp://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site XHR to HTML from file://", __LINE__, + "file:///foo/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site fetch HTML from Flash without CORS", __LINE__, + "http://www.b.com/plugin.html", // target_url + RESOURCE_TYPE_PLUGIN_RESOURCE, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + { + "Allowed: Cross-site fetch HTML from NaCl with CORS response", __LINE__, + "http://www.b.com/plugin.html", // target_url + RESOURCE_TYPE_PLUGIN_RESOURCE, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kInclude, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kAllowInitiatorOrigin, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kAllowWithoutSniffing, // verdict + }, + + // Allowed responses due to sniffing: + { + "Allowed: Cross-site script to JSONP labeled as HTML", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_SCRIPT, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "foo({\"x\" : 3})", // first_chunk + Verdict::kAllowAfterSniffing, // verdict + }, + { + "Allowed: Cross-site script to JavaScript labeled as text", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_SCRIPT, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/plain", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "var x = 3;", // first_chunk + Verdict::kAllowAfterSniffing, // verdict + }, + { + "Allowed: Cross-site XHR to nonsense labeled as XML", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "application/xml", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_XML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "Won't sniff as XML", // first_chunk + Verdict::kAllowAfterSniffing, // verdict + }, + { + "Allowed: Cross-site XHR to nonsense labeled as JSON", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/x-json", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_JSON, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "Won't sniff as JSON", // first_chunk + Verdict::kAllowAfterSniffing, // verdict + }, + // TODO(creis): We should block the following response since there isn't + // enough data to confirm it as HTML by sniffing. + { + "Allowed for now: Cross-site XHR to HTML with small first read", + __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kBlockAfterSniffing, // verdict + }, + { + "Blocked: Cross-site XHR to XML without CORS", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "application/xml", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_XML, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "", // first_chunk + Verdict::kBlockAfterSniffing, // verdict + }, + { + "Blocked: Cross-site XHR to JSON without CORS", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "application/json", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_JSON, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "{\"x\" : 3}", // first_chunk + Verdict::kBlockAfterSniffing, // verdict + }, + { + "Blocked: Cross-site XHR to HTML labeled as text without CORS", + __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/plain", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN, // canonical_mime_type + false, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kBlockAfterSniffing, // verdict + }, + { + "Blocked: Cross-site XHR to nosniff HTML without CORS", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + true, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "this should sniff as HTML", // first_chunk + Verdict::kBlockWithoutSniffing, // verdict + }, + { + "Blocked: Cross-site XHR to nosniff response without CORS", __LINE__, + "http://www.b.com/resource.html", // target_url + RESOURCE_TYPE_XHR, // resource_type + "http://www.a.com/", // initiator_origin + OriginHeader::kOmit, // cors_request + "text/html", // response_mime_type + CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type + true, // include_no_sniff_header + AccessControlAllowOriginHeader::kOmit, // cors_response + "Wouldn't sniff as HTML", // first_chunk + Verdict::kBlockWithoutSniffing, // verdict + }, + { + "Blocked: Cross-site