| Index: content/child/site_isolation_policy.cc
|
| diff --git a/content/child/site_isolation_policy.cc b/content/child/site_isolation_policy.cc
|
| index a14e95e6e4fe25a2af7f18911c46059f5d8a9778..d0f2ec4caaf832dc35aaa5e25c3e5feda7ab6f40 100644
|
| --- a/content/child/site_isolation_policy.cc
|
| +++ b/content/child/site_isolation_policy.cc
|
| @@ -9,45 +9,18 @@
|
| #include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| #include "base/metrics/histogram.h"
|
| -#include "base/strings/string_piece.h"
|
| #include "base/strings/string_util.h"
|
| -#include "content/child/child_thread.h"
|
| #include "content/public/common/content_switches.h"
|
| #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
| #include "net/http/http_response_headers.h"
|
| -#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
|
| -#include "third_party/WebKit/public/platform/WebString.h"
|
| -#include "third_party/WebKit/public/platform/WebURL.h"
|
| -#include "third_party/WebKit/public/platform/WebURLRequest.h"
|
| -#include "third_party/WebKit/public/platform/WebURLResponse.h"
|
| -#include "third_party/WebKit/public/web/WebDocument.h"
|
| -#include "third_party/WebKit/public/web/WebFrame.h"
|
| -#include "third_party/WebKit/public/web/WebFrameClient.h"
|
| -#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
|
| +#include "webkit/common/resource_response_info.h"
|
|
|
| using base::StringPiece;
|
| -using blink::WebDocument;
|
| -using blink::WebString;
|
| -using blink::WebURL;
|
| -using blink::WebURLResponse;
|
| -using blink::WebURLRequest;
|
|
|
| namespace content {
|
|
|
| namespace {
|
|
|
| -// Maintain the bookkeeping data between OnReceivedResponse and
|
| -// OnReceivedData. The key is a request id maintained by ResourceDispatcher.
|
| -static base::LazyInstance<SiteIsolationPolicy::RequestIdToMetaDataMap>
|
| - g_metadata_map = LAZY_INSTANCE_INITIALIZER;
|
| -
|
| -// Maintain the bookkeeping data for OnReceivedData. Blocking decision is made
|
| -// when OnReceivedData is called for the first time for a request, and the
|
| -// decision will remain the same for following data. This map maintains the
|
| -// decision. The key is a request id maintained by ResourceDispatcher.
|
| -static base::LazyInstance<SiteIsolationPolicy::RequestIdToResultMap>
|
| - g_result_map = LAZY_INSTANCE_INITIALIZER;
|
| -
|
| // The cross-site document blocking/UMA data collection is deactivated by
|
| // default, and only activated in renderer processes.
|
| static bool g_policy_enabled = false;
|
| @@ -123,7 +96,7 @@ void IncrementHistogramEnum(const std::string& name,
|
|
|
| void HistogramCountBlockedResponse(
|
| const std::string& bucket_prefix,
|
| - const SiteIsolationPolicy::ResponseMetaData& resp_data,
|
| + linked_ptr<SiteIsolationResponseMetaData>& resp_data,
|
| bool nosniff_block) {
|
| std::string block_label(nosniff_block ? ".NoSniffBlocked" : ".Blocked");
|
| IncrementHistogramCount(bucket_prefix + block_label);
|
| @@ -138,12 +111,12 @@ void HistogramCountBlockedResponse(
|
| // (e.g, 404). 3) the renderer is expected not to use the cross-site
|
| // document content for purposes other than JS/CSS (e.g, XHR).
|
| bool renderable_status_code =
|
| - IsRenderableStatusCode(resp_data.http_status_code);
|
| + IsRenderableStatusCode(resp_data->http_status_code);
|
|
|
| if (renderable_status_code) {
|
| IncrementHistogramEnum(
|
| bucket_prefix + block_label + ".RenderableStatusCode",
|
| - resp_data.resource_type,
|
| + resp_data->resource_type,
|
| ResourceType::LAST_TYPE);
|
| } else {
|
| IncrementHistogramCount(bucket_prefix + block_label +
|
| @@ -160,21 +133,21 @@ void HistogramCountNotBlockedResponse(const std::string& bucket_prefix,
|
|
|
| } // namespace
|
|
|
| -SiteIsolationPolicy::ResponseMetaData::ResponseMetaData() {}
|
| +SiteIsolationResponseMetaData::SiteIsolationResponseMetaData() {}
|
|
|
| void SiteIsolationPolicy::SetPolicyEnabled(bool enabled) {
|
| g_policy_enabled = enabled;
|
| }
|
|
|
| -void SiteIsolationPolicy::OnReceivedResponse(
|
| - int request_id,
|
| +linked_ptr<SiteIsolationResponseMetaData>
|
| +SiteIsolationPolicy::OnReceivedResponse(
|
| const GURL& frame_origin,
|
| const GURL& response_url,
|
| ResourceType::Type resource_type,
|
| int origin_pid,
|
| const webkit_glue::ResourceResponseInfo& info) {
|
| if (!g_policy_enabled)
|
| - return;
|
| + return linked_ptr<SiteIsolationResponseMetaData>();
|
|
|
| // if |origin_pid| is non-zero, it means that this response is for a plugin
|
| // spawned from this renderer process. We exclude responses for plugins for
|
| @@ -182,26 +155,26 @@ void SiteIsolationPolicy::OnReceivedResponse(
|
| // the browser process so that we don't apply cross-site document blocking to
|
| // them.
|
| if (origin_pid)
|
| - return;
|
| + return linked_ptr<SiteIsolationResponseMetaData>();
|
|
|
| UMA_HISTOGRAM_COUNTS("SiteIsolation.AllResponses", 1);
|
|
|
| // See if this is for navigation. If it is, don't block it, under the
|
| // assumption that we will put it in an appropriate process.
|
| if (ResourceType::IsFrame(resource_type))
|
| - return;
|
| + return linked_ptr<SiteIsolationResponseMetaData>();
|
|
|
| if (!IsBlockableScheme(response_url))
|
| - return;
|
| + return linked_ptr<SiteIsolationResponseMetaData>();
|
|
|
| if (IsSameSite(frame_origin, response_url))
|
| - return;
|
| + return linked_ptr<SiteIsolationResponseMetaData>();
|
|
|
| - SiteIsolationPolicy::ResponseMetaData::CanonicalMimeType canonical_mime_type =
|
| + SiteIsolationResponseMetaData::CanonicalMimeType canonical_mime_type =
|
| GetCanonicalMimeType(info.mime_type);
|
|
|
| - if (canonical_mime_type == SiteIsolationPolicy::ResponseMetaData::Others)
|
| - return;
|
| + if (canonical_mime_type == SiteIsolationResponseMetaData::Others)
|
| + return linked_ptr<SiteIsolationResponseMetaData>();
|
|
|
| // Every CORS request should have the Access-Control-Allow-Origin header even
|
| // if it is preceded by a pre-flight request. Therefore, if this is a CORS
|
| @@ -213,63 +186,36 @@ void SiteIsolationPolicy::OnReceivedResponse(
|
| info.headers->EnumerateHeader(
|
| NULL, "access-control-allow-origin", &access_control_origin);
|
| if (IsValidCorsHeaderSet(frame_origin, response_url, access_control_origin))
|
| - return;
|
| + return linked_ptr<SiteIsolationResponseMetaData>();
|
|
|
| // Real XSD data collection starts from here.
|
| std::string no_sniff;
|
| info.headers->EnumerateHeader(NULL, "x-content-type-options", &no_sniff);
|
|
|
| - ResponseMetaData resp_data;
|
| - resp_data.frame_origin = frame_origin.spec();
|
| - resp_data.response_url = response_url;
|
| - resp_data.resource_type = resource_type;
|
| - resp_data.canonical_mime_type = canonical_mime_type;
|
| - resp_data.http_status_code = info.headers->response_code();
|
| - resp_data.no_sniff = LowerCaseEqualsASCII(no_sniff, "nosniff");
|
| + linked_ptr<SiteIsolationResponseMetaData> resp_data(
|
| + new SiteIsolationResponseMetaData);
|
| + resp_data->frame_origin = frame_origin.spec();
|
| + resp_data->response_url = response_url;
|
| + resp_data->resource_type = resource_type;
|
| + resp_data->canonical_mime_type = canonical_mime_type;
|
| + resp_data->http_status_code = info.headers->response_code();
|
| + resp_data->no_sniff = LowerCaseEqualsASCII(no_sniff, "nosniff");
|
|
|
| - (g_metadata_map.Get())[request_id] = resp_data;
|
| + return resp_data;
|
| }
|
|
|
| bool SiteIsolationPolicy::ShouldBlockResponse(
|
| - int request_id,
|
| + linked_ptr<SiteIsolationResponseMetaData>& resp_data,
|
| const char* raw_data,
|
| int raw_length,
|
| std::string* alternative_data) {
|
| if (!g_policy_enabled)
|
| return false;
|
|
|
| - RequestIdToMetaDataMap& metadata_map = g_metadata_map.Get();
|
| - RequestIdToResultMap& result_map = g_result_map.Get();
|
| -
|
| - // If there's an entry for |request_id| in blocked_map, this request's first
|
| - // data packet has already been examined. We can return the result here.
|
| - if (result_map.count(request_id) != 0) {
|
| - if (result_map[request_id]) {
|
| - // Here, the blocking result has been set for the previous run of
|
| - // ShouldBlockResponse(), so we set alternative data to an empty string so
|
| - // that ResourceDispatcher doesn't call its peer's onReceivedData() with
|
| - // the alternative data.
|
| - alternative_data->erase();
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - // If result_map doesn't have an entry for |request_id|, we're receiving the
|
| - // first data packet for request_id. If request_id is not registered, this
|
| - // request is identified as a non-target of our policy. So we return true.
|
| - if (metadata_map.count(request_id) == 0) {
|
| - // We set request_id to true so that we always return true for this request.
|
| - result_map[request_id] = false;
|
| - return false;
|
| - }
|
| + DCHECK(resp_data.get());
|
|
|
| StringPiece data(raw_data, raw_length);
|
|
|
| - // We now look at the first data packet received for request_id.
|
| - ResponseMetaData resp_data = metadata_map[request_id];
|
| - metadata_map.erase(request_id);
|
| -
|
| // Record the length of the first received network packet to see if it's
|
| // enough for sniffing.
|
| UMA_HISTOGRAM_COUNTS("SiteIsolation.XSD.DataLength", raw_length);
|
| @@ -278,8 +224,8 @@ bool SiteIsolationPolicy::ShouldBlockResponse(
|
| // type (text/html, text/xml, etc).
|
| UMA_HISTOGRAM_ENUMERATION(
|
| "SiteIsolation.XSD.MimeType",
|
| - resp_data.canonical_mime_type,
|
| - SiteIsolationPolicy::ResponseMetaData::MaxCanonicalMimeType);
|
| + resp_data->canonical_mime_type,
|
| + SiteIsolationResponseMetaData::MaxCanonicalMimeType);
|
|
|
| // Store the result of cross-site document blocking analysis.
|
| bool is_blocked = false;
|
| @@ -289,32 +235,32 @@ bool SiteIsolationPolicy::ShouldBlockResponse(
|
| // type claims it to be. For example, we apply a HTML sniffer for a document
|
| // tagged with text/html here. Whenever this check becomes true, we'll block
|
| // the response.
|
| - if (resp_data.canonical_mime_type !=
|
| - SiteIsolationPolicy::ResponseMetaData::Plain) {
|
| + if (resp_data->canonical_mime_type !=
|
| + SiteIsolationResponseMetaData::Plain) {
|
| std::string bucket_prefix;
|
| bool sniffed_as_target_document = false;
|
| - if (resp_data.canonical_mime_type ==
|
| - SiteIsolationPolicy::ResponseMetaData::HTML) {
|
| + if (resp_data->canonical_mime_type ==
|
| + SiteIsolationResponseMetaData::HTML) {
|
| bucket_prefix = "SiteIsolation.XSD.HTML";
|
| sniffed_as_target_document = SniffForHTML(data);
|
| - } else if (resp_data.canonical_mime_type ==
|
| - SiteIsolationPolicy::ResponseMetaData::XML) {
|
| + } else if (resp_data->canonical_mime_type ==
|
| + SiteIsolationResponseMetaData::XML) {
|
| bucket_prefix = "SiteIsolation.XSD.XML";
|
| sniffed_as_target_document = SniffForXML(data);
|
| - } else if (resp_data.canonical_mime_type ==
|
| - SiteIsolationPolicy::ResponseMetaData::JSON) {
|
| + } else if (resp_data->canonical_mime_type ==
|
| + SiteIsolationResponseMetaData::JSON) {
|
| bucket_prefix = "SiteIsolation.XSD.JSON";
|
| sniffed_as_target_document = SniffForJSON(data);
|
| } else {
|
| NOTREACHED() << "Not a blockable mime type: "
|
| - << resp_data.canonical_mime_type;
|
| + << resp_data->canonical_mime_type;
|
| }
|
|
|
| if (sniffed_as_target_document) {
|
| is_blocked = true;
|
| HistogramCountBlockedResponse(bucket_prefix, resp_data, false);
|
| } else {
|
| - if (resp_data.no_sniff) {
|
| + if (resp_data->no_sniff) {
|
| is_blocked = true;
|
| HistogramCountBlockedResponse(bucket_prefix, resp_data, true);
|
| } else {
|
| @@ -336,7 +282,7 @@ bool SiteIsolationPolicy::ShouldBlockResponse(
|
| if (bucket_prefix.size() > 0) {
|
| is_blocked = true;
|
| HistogramCountBlockedResponse(bucket_prefix, resp_data, false);
|
| - } else if (resp_data.no_sniff) {
|
| + } else if (resp_data->no_sniff) {
|
| is_blocked = true;
|
| HistogramCountBlockedResponse("SiteIsolation.XSD.Plain", resp_data, true);
|
| } else {
|
| @@ -348,48 +294,40 @@ bool SiteIsolationPolicy::ShouldBlockResponse(
|
| if (!CommandLine::ForCurrentProcess()->HasSwitch(
|
| switches::kBlockCrossSiteDocuments))
|
| is_blocked = false;
|
| - result_map[request_id] = is_blocked;
|
|
|
| if (is_blocked) {
|
| alternative_data->erase();
|
| alternative_data->insert(0, " ");
|
| - LOG(ERROR) << resp_data.response_url
|
| + LOG(ERROR) << resp_data->response_url
|
| << " is blocked as an illegal cross-site document from "
|
| - << resp_data.frame_origin;
|
| + << resp_data->frame_origin;
|
| }
|
| return is_blocked;
|
| }
|
|
|
| -void SiteIsolationPolicy::OnRequestComplete(int request_id) {
|
| - if (!g_policy_enabled)
|
| - return;
|
| - g_metadata_map.Get().erase(request_id);
|
| - g_result_map.Get().erase(request_id);
|
| -}
|
| -
|
| -SiteIsolationPolicy::ResponseMetaData::CanonicalMimeType
|
| +SiteIsolationResponseMetaData::CanonicalMimeType
|
| SiteIsolationPolicy::GetCanonicalMimeType(const std::string& mime_type) {
|
| if (LowerCaseEqualsASCII(mime_type, kTextHtml)) {
|
| - return SiteIsolationPolicy::ResponseMetaData::HTML;
|
| + return SiteIsolationResponseMetaData::HTML;
|
| }
|
|
|
| if (LowerCaseEqualsASCII(mime_type, kTextPlain)) {
|
| - return SiteIsolationPolicy::ResponseMetaData::Plain;
|
| + return SiteIsolationResponseMetaData::Plain;
|
| }
|
|
|
| if (LowerCaseEqualsASCII(mime_type, kAppJson) ||
|
| LowerCaseEqualsASCII(mime_type, kTextJson) ||
|
| LowerCaseEqualsASCII(mime_type, kTextXjson)) {
|
| - return SiteIsolationPolicy::ResponseMetaData::JSON;
|
| + return SiteIsolationResponseMetaData::JSON;
|
| }
|
|
|
| if (LowerCaseEqualsASCII(mime_type, kTextXml) ||
|
| LowerCaseEqualsASCII(mime_type, xAppRssXml) ||
|
| LowerCaseEqualsASCII(mime_type, kAppXml)) {
|
| - return SiteIsolationPolicy::ResponseMetaData::XML;
|
| + return SiteIsolationResponseMetaData::XML;
|
| }
|
|
|
| - return SiteIsolationPolicy::ResponseMetaData::Others;
|
| + return SiteIsolationResponseMetaData::Others;
|
| }
|
|
|
| bool SiteIsolationPolicy::IsBlockableScheme(const GURL& url) {
|
|
|