Commit b454d587 authored by Tarun Bansal's avatar Tarun Bansal Committed by Commit Bot

Provide client hints only on secure contexts.

Update the http-equiv and preload scanner code path as well. The client
hints are provided only on secure contexts if
ClientHintsPersistentEnabled() feature is enabled.

Bug: 782381
Change-Id: I28d75b6bfd14f36accdba8f87488ae687df049d7
Reviewed-on: https://chromium-review.googlesource.com/887924
Commit-Queue: Tarun Bansal <tbansal@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533109}
parent 78ed387b
...@@ -789,9 +789,10 @@ void TokenPreloadScanner::ScanCommon(const Token& token, ...@@ -789,9 +789,10 @@ void TokenPreloadScanner::ScanCommon(const Token& token,
"accept-ch")) { "accept-ch")) {
const typename Token::Attribute* content_attribute = const typename Token::Attribute* content_attribute =
token.GetAttributeItem(contentAttr); token.GetAttributeItem(contentAttr);
if (content_attribute) if (content_attribute) {
client_hints_preferences_.UpdateFromAcceptClientHintsHeader( client_hints_preferences_.UpdateFromAcceptClientHintsHeader(
content_attribute->Value(), nullptr); content_attribute->Value(), document_url_, nullptr);
}
} }
return; return;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "public/platform/WebClientHintsType.h" #include "public/platform/WebClientHintsType.h"
#include "public/platform/WebURLLoaderMockFactory.h" #include "public/platform/WebURLLoaderMockFactory.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebRuntimeFeatures.h"
namespace blink { namespace blink {
...@@ -208,9 +209,12 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -208,9 +209,12 @@ class HTMLPreloadScannerTest : public PageTestBase {
void RunSetUp( void RunSetUp(
ViewportState viewport_state, ViewportState viewport_state,
PreloadState preload_state = kPreloadEnabled, PreloadState preload_state = kPreloadEnabled,
ReferrerPolicy document_referrer_policy = kReferrerPolicyDefault) { ReferrerPolicy document_referrer_policy = kReferrerPolicyDefault,
bool use_secure_document_url = false) {
HTMLParserOptions options(&GetDocument()); HTMLParserOptions options(&GetDocument());
KURL document_url("http://whatever.test/"); KURL document_url = KURL("http://whatever.test/");
if (use_secure_document_url)
document_url = KURL("https://whatever.test/");
GetDocument().SetURL(document_url); GetDocument().SetURL(document_url);
GetDocument().SetSecurityOrigin(SecurityOrigin::Create(document_url)); GetDocument().SetSecurityOrigin(SecurityOrigin::Create(document_url));
GetDocument().GetSettings()->SetViewportEnabled(viewport_state == GetDocument().GetSettings()->SetViewportEnabled(viewport_state ==
...@@ -594,11 +598,58 @@ TEST_F(HTMLPreloadScannerTest, testMetaAcceptCH) { ...@@ -594,11 +598,58 @@ TEST_F(HTMLPreloadScannerTest, testMetaAcceptCH) {
}; };
for (const auto& test_case : test_cases) { for (const auto& test_case : test_cases) {
RunSetUp(kViewportDisabled); RunSetUp(kViewportDisabled, kPreloadEnabled, kReferrerPolicyDefault,
true /* use_secure_document_url */);
Test(test_case); Test(test_case);
} }
} }
TEST_F(HTMLPreloadScannerTest, testMetaAcceptCHInsecureDocument) {
ClientHintsPreferences all;
all.SetShouldSendForTesting(mojom::WebClientHintsType::kDpr);
all.SetShouldSendForTesting(mojom::WebClientHintsType::kResourceWidth);
all.SetShouldSendForTesting(mojom::WebClientHintsType::kViewportWidth);
const PreloadScannerTestCase expect_no_client_hint = {
"http://example.test",
"<meta http-equiv='accept-ch' content=' viewport-width ,width, "
"wutever, dpr \t'><img sizes='90vw' srcset='bla.gif 320w, blabla.gif "
"640w'>",
"blabla.gif",
"http://example.test/",
Resource::kImage,
450};
const PreloadScannerTestCase expect_client_hint = {
"http://example.test",
"<meta http-equiv='accept-ch' content=' viewport-width ,width, "
"wutever, dpr \t'><img sizes='90vw' srcset='bla.gif 320w, blabla.gif "
"640w'>",
"blabla.gif",
"http://example.test/",
Resource::kImage,
450,
all};
// For an insecure document, client hint should not be attached.
WebRuntimeFeatures::EnableClientHintsPersistent(true);
RunSetUp(kViewportDisabled, kPreloadEnabled, kReferrerPolicyDefault,
false /* use_secure_document_url */);
Test(expect_no_client_hint);
// For a secure document, client hint should be attached.
RunSetUp(kViewportDisabled, kPreloadEnabled, kReferrerPolicyDefault,
true /* use_secure_document_url */);
Test(expect_client_hint);
// For an insecure document, client hint should be attached if the persistent
// client hints are not enabled.
WebRuntimeFeatures::EnableClientHintsPersistent(false);
RunSetUp(kViewportDisabled, kPreloadEnabled, kReferrerPolicyDefault,
false /* use_secure_document_url */);
Test(expect_client_hint);
}
TEST_F(HTMLPreloadScannerTest, testPreconnect) { TEST_F(HTMLPreloadScannerTest, testPreconnect) {
HTMLPreconnectTestCase test_cases[] = { HTMLPreconnectTestCase test_cases[] = {
{"http://example.test", "<link rel=preconnect href=http://example2.test>", {"http://example.test", "<link rel=preconnect href=http://example2.test>",
......
...@@ -178,12 +178,6 @@ mojom::FetchCacheMode DetermineFrameCacheMode(Frame* frame, ...@@ -178,12 +178,6 @@ mojom::FetchCacheMode DetermineFrameCacheMode(Frame* frame,
ResourceType::kIsNotMainResource, load_type); ResourceType::kIsNotMainResource, load_type);
} }
bool IsClientHintsAllowed(const KURL& url) {
return (url.ProtocolIs("http") || url.ProtocolIs("https")) &&
(SecurityOrigin::IsSecure(url) ||
SecurityOrigin::Create(url)->IsLocalhost());
}
} // namespace } // namespace
struct FrameFetchContext::FrozenState final struct FrameFetchContext::FrozenState final
...@@ -528,14 +522,11 @@ void FrameFetchContext::DispatchDidReceiveResponse( ...@@ -528,14 +522,11 @@ void FrameFetchContext::DispatchDidReceiveResponse(
->Loader() ->Loader()
.GetProvisionalDocumentLoader()) { .GetProvisionalDocumentLoader()) {
FrameClientHintsPreferencesContext hints_context(GetFrame()); FrameClientHintsPreferencesContext hints_context(GetFrame());
if (!blink::RuntimeEnabledFeatures::ClientHintsPersistentEnabled() || document_loader_->GetClientHintsPreferences()
IsClientHintsAllowed(response.Url())) { .UpdateFromAcceptClientHintsHeader(
// If the persistent client hint feature is enabled, then client hints response.HttpHeaderField(HTTPNames::Accept_CH), response.Url(),
// should be allowed only on secure URLs. &hints_context);
document_loader_->GetClientHintsPreferences()
.UpdateFromAcceptClientHintsHeader(
response.HttpHeaderField(HTTPNames::Accept_CH), &hints_context);
}
// When response is received with a provisional docloader, the resource // When response is received with a provisional docloader, the resource
// haven't committed yet, and we cannot load resources, only preconnect. // haven't committed yet, and we cannot load resources, only preconnect.
resource_loading_policy = LinkLoader::kDoNotLoadResources; resource_loading_policy = LinkLoader::kDoNotLoadResources;
...@@ -851,7 +842,7 @@ void FrameFetchContext::AddClientHintsIfNecessary( ...@@ -851,7 +842,7 @@ void FrameFetchContext::AddClientHintsIfNecessary(
if (blink::RuntimeEnabledFeatures::ClientHintsPersistentEnabled()) { if (blink::RuntimeEnabledFeatures::ClientHintsPersistentEnabled()) {
// If the feature is enabled, then client hints are allowed only on secure // If the feature is enabled, then client hints are allowed only on secure
// URLs. // URLs.
if (!IsClientHintsAllowed(request.Url())) if (!ClientHintsPreferences::IsClientHintsAllowed(request.Url()))
return; return;
// Check if |url| is allowed to run JavaScript. If not, client hints are not // Check if |url| is allowed to run JavaScript. If not, client hints are not
...@@ -1184,9 +1175,6 @@ bool FrameFetchContext::ShouldSendClientHint( ...@@ -1184,9 +1175,6 @@ bool FrameFetchContext::ShouldSendClientHint(
void FrameFetchContext::ParseAndPersistClientHints( void FrameFetchContext::ParseAndPersistClientHints(
const ResourceResponse& response) { const ResourceResponse& response) {
if (!IsClientHintsAllowed(response.Url()))
return;
ClientHintsPreferences hints_preferences; ClientHintsPreferences hints_preferences;
WebEnabledClientHints enabled_client_hints; WebEnabledClientHints enabled_client_hints;
TimeDelta persist_duration; TimeDelta persist_duration;
......
...@@ -95,7 +95,7 @@ void HttpEquiv::ProcessHttpEquivAcceptCH(Document& document, ...@@ -95,7 +95,7 @@ void HttpEquiv::ProcessHttpEquivAcceptCH(Document& document,
UseCounter::Count(document, WebFeature::kClientHintsMetaAcceptCH); UseCounter::Count(document, WebFeature::kClientHintsMetaAcceptCH);
FrameClientHintsPreferencesContext hints_context(document.GetFrame()); FrameClientHintsPreferencesContext hints_context(document.GetFrame());
document.GetClientHintsPreferences().UpdateFromAcceptClientHintsHeader( document.GetClientHintsPreferences().UpdateFromAcceptClientHintsHeader(
content, &hints_context); content, document.Url(), &hints_context);
} }
void HttpEquiv::ProcessHttpEquivDefaultStyle(Document& document, void HttpEquiv::ProcessHttpEquivDefaultStyle(Document& document,
......
...@@ -52,10 +52,18 @@ void ClientHintsPreferences::UpdateFrom( ...@@ -52,10 +52,18 @@ void ClientHintsPreferences::UpdateFrom(
void ClientHintsPreferences::UpdateFromAcceptClientHintsHeader( void ClientHintsPreferences::UpdateFromAcceptClientHintsHeader(
const String& header_value, const String& header_value,
const KURL& url,
Context* context) { Context* context) {
if (header_value.IsEmpty()) if (header_value.IsEmpty())
return; return;
// If the persistent client hint feature is enabled, then client hints
// should be allowed only on secure URLs.
if (blink::RuntimeEnabledFeatures::ClientHintsPersistentEnabled() &&
!IsClientHintsAllowed(url)) {
return;
}
WebEnabledClientHints new_enabled_types; WebEnabledClientHints new_enabled_types;
ParseAcceptChHeader(header_value, new_enabled_types); ParseAcceptChHeader(header_value, new_enabled_types);
...@@ -101,6 +109,8 @@ void ClientHintsPreferences::UpdatePersistentHintsFromHeaders( ...@@ -101,6 +109,8 @@ void ClientHintsPreferences::UpdatePersistentHintsFromHeaders(
} }
const KURL url = response.Url(); const KURL url = response.Url();
if (!IsClientHintsAllowed(url))
return;
bool conversion_ok = false; bool conversion_ok = false;
int64_t persist_duration_seconds = int64_t persist_duration_seconds =
...@@ -115,4 +125,11 @@ void ClientHintsPreferences::UpdatePersistentHintsFromHeaders( ...@@ -115,4 +125,11 @@ void ClientHintsPreferences::UpdatePersistentHintsFromHeaders(
ParseAcceptChHeader(accept_ch_header_value, enabled_hints); ParseAcceptChHeader(accept_ch_header_value, enabled_hints);
} }
// static
bool ClientHintsPreferences::IsClientHintsAllowed(const KURL& url) {
return (url.ProtocolIs("http") || url.ProtocolIs("https")) &&
(SecurityOrigin::IsSecure(url) ||
SecurityOrigin::Create(url)->IsLocalhost());
}
} // namespace blink } // namespace blink
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
namespace blink { namespace blink {
class KURL;
class ResourceResponse; class ResourceResponse;
// TODO (tbansal): Remove PLATFORM_EXPORT, and pass WebClientHintsType // TODO (tbansal): Remove PLATFORM_EXPORT, and pass WebClientHintsType
...@@ -35,8 +36,12 @@ class PLATFORM_EXPORT ClientHintsPreferences { ...@@ -35,8 +36,12 @@ class PLATFORM_EXPORT ClientHintsPreferences {
void UpdateFrom(const ClientHintsPreferences&); void UpdateFrom(const ClientHintsPreferences&);
// Parses the client hints headers, and populates |this| with the client hint // Parses the client hints headers, and populates |this| with the client hint
// preferences. |context| may be null. // preferences. |url| is the URL of the resource whose response included the
void UpdateFromAcceptClientHintsHeader(const String& header_value, Context*); // |header_value|. |context| may be null. If client hints are not allowed for
// |url|, then |this| would not be updated.
void UpdateFromAcceptClientHintsHeader(const String& header_value,
const KURL&,
Context*);
bool ShouldSend(mojom::WebClientHintsType type) const { bool ShouldSend(mojom::WebClientHintsType type) const {
return enabled_hints_.IsEnabled(type); return enabled_hints_.IsEnabled(type);
...@@ -59,6 +64,10 @@ class PLATFORM_EXPORT ClientHintsPreferences { ...@@ -59,6 +64,10 @@ class PLATFORM_EXPORT ClientHintsPreferences {
WebEnabledClientHints& enabled_hints, WebEnabledClientHints& enabled_hints,
TimeDelta* persist_duration); TimeDelta* persist_duration);
// Returns true if client hints are allowed for the provided KURL. Client
// hints are allowed only on HTTP URLs that belong to secure contexts.
static bool IsClientHintsAllowed(const KURL&);
private: private:
WebEnabledClientHints enabled_hints_; WebEnabledClientHints enabled_hints_;
}; };
......
...@@ -15,7 +15,7 @@ namespace blink { ...@@ -15,7 +15,7 @@ namespace blink {
class ClientHintsPreferencesTest : public ::testing::Test {}; class ClientHintsPreferencesTest : public ::testing::Test {};
TEST_F(ClientHintsPreferencesTest, Basic) { TEST_F(ClientHintsPreferencesTest, BasicSecure) {
struct TestCase { struct TestCase {
const char* header_value; const char* header_value;
bool expectation_resource_width; bool expectation_resource_width;
...@@ -32,7 +32,8 @@ TEST_F(ClientHintsPreferencesTest, Basic) { ...@@ -32,7 +32,8 @@ TEST_F(ClientHintsPreferencesTest, Basic) {
for (const auto& test_case : cases) { for (const auto& test_case : cases) {
ClientHintsPreferences preferences; ClientHintsPreferences preferences;
preferences.UpdateFromAcceptClientHintsHeader(test_case.header_value, const KURL kurl(String::FromUTF8("https://www.google.com/"));
preferences.UpdateFromAcceptClientHintsHeader(test_case.header_value, kurl,
nullptr); nullptr);
EXPECT_EQ( EXPECT_EQ(
test_case.expectation_resource_width, test_case.expectation_resource_width,
...@@ -45,7 +46,7 @@ TEST_F(ClientHintsPreferencesTest, Basic) { ...@@ -45,7 +46,7 @@ TEST_F(ClientHintsPreferencesTest, Basic) {
// Calling UpdateFromAcceptClientHintsHeader with empty header should have // Calling UpdateFromAcceptClientHintsHeader with empty header should have
// no impact on client hint preferences. // no impact on client hint preferences.
preferences.UpdateFromAcceptClientHintsHeader("", nullptr); preferences.UpdateFromAcceptClientHintsHeader("", kurl, nullptr);
EXPECT_EQ( EXPECT_EQ(
test_case.expectation_resource_width, test_case.expectation_resource_width,
preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth)); preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
...@@ -57,7 +58,7 @@ TEST_F(ClientHintsPreferencesTest, Basic) { ...@@ -57,7 +58,7 @@ TEST_F(ClientHintsPreferencesTest, Basic) {
// Calling UpdateFromAcceptClientHintsHeader with an invalid header should // Calling UpdateFromAcceptClientHintsHeader with an invalid header should
// have no impact on client hint preferences. // have no impact on client hint preferences.
preferences.UpdateFromAcceptClientHintsHeader("foobar", nullptr); preferences.UpdateFromAcceptClientHintsHeader("foobar", kurl, nullptr);
EXPECT_EQ( EXPECT_EQ(
test_case.expectation_resource_width, test_case.expectation_resource_width,
preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth)); preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
...@@ -69,6 +70,18 @@ TEST_F(ClientHintsPreferencesTest, Basic) { ...@@ -69,6 +70,18 @@ TEST_F(ClientHintsPreferencesTest, Basic) {
} }
} }
TEST_F(ClientHintsPreferencesTest, Insecure) {
for (const auto& use_secure_url : {false, true}) {
ClientHintsPreferences preferences;
const KURL kurl = use_secure_url
? KURL(String::FromUTF8("https://www.google.com/"))
: KURL(String::FromUTF8("http://www.google.com/"));
preferences.UpdateFromAcceptClientHintsHeader("dpr", kurl, nullptr);
EXPECT_EQ(use_secure_url,
preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
}
}
TEST_F(ClientHintsPreferencesTest, PersistentHints) { TEST_F(ClientHintsPreferencesTest, PersistentHints) {
struct TestCase { struct TestCase {
bool enable_persistent_runtime_feature; bool enable_persistent_runtime_feature;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment