Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/activity_log/activity_actions.h" | 5 #include "chrome/browser/extensions/activity_log/activity_actions.h" |
| 6 | 6 |
| 7 #include <algorithm> // for std::find. | 7 #include <algorithm> // for std::find. |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/format_macros.h" | 11 #include "base/format_macros.h" |
| 12 #include "base/json/json_string_value_serializer.h" | 12 #include "base/json/json_string_value_serializer.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/memory/singleton.h" | 15 #include "base/memory/singleton.h" |
| 16 #include "base/metrics/histogram.h" | 16 #include "base/metrics/histogram.h" |
| 17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 19 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 20 #include "base/values.h" | 20 #include "base/values.h" |
| 21 #include "chrome/browser/extensions/activity_log/activity_action_constants.h" | 21 #include "chrome/browser/extensions/activity_log/activity_action_constants.h" |
| 22 #include "chrome/browser/extensions/activity_log/ad_network_database.h" | 22 #include "chrome/browser/extensions/activity_log/ad_network_database.h" |
| 23 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" | 23 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" |
| 24 #include "chrome/browser/ui/browser.h" | 24 #include "chrome/browser/ui/browser.h" |
| 25 #include "chrome/common/chrome_switches.h" | 25 #include "chrome/common/chrome_switches.h" |
| 26 #include "components/rappor/rappor_service.h" | 26 #include "components/rappor/rappor_service.h" |
| 27 #include "content/public/browser/web_contents.h" | 27 #include "content/public/browser/web_contents.h" |
| 28 #include "extensions/common/ad_injection_constants.h" | |
| 29 #include "extensions/common/constants.h" | 28 #include "extensions/common/constants.h" |
| 30 #include "extensions/common/dom_action_types.h" | 29 #include "extensions/common/dom_action_types.h" |
| 31 #include "sql/statement.h" | 30 #include "sql/statement.h" |
| 32 #include "url/gurl.h" | 31 #include "url/gurl.h" |
| 33 | 32 |
| 34 namespace constants = activity_log_constants; | 33 namespace constants = activity_log_constants; |
| 35 | 34 |
| 36 namespace extensions { | 35 namespace extensions { |
| 37 | 36 |
| 38 namespace { | 37 namespace { |
| 39 | 38 |
| 40 namespace keys = ad_injection_constants::keys; | |
| 41 | |
| 42 // The list of APIs for which we upload the URL to RAPPOR. | |
| 43 const char* kApisForRapporMetric[] = { | |
| 44 ad_injection_constants::kHtmlIframeSrcApiName, | |
| 45 ad_injection_constants::kHtmlEmbedSrcApiName, | |
| 46 ad_injection_constants::kHtmlAnchorHrefApiName | |
| 47 }; | |
| 48 | |
| 49 // The "Extensions.PossibleAdInjection2" metric uses different Rappor | 39 // The "Extensions.PossibleAdInjection2" metric uses different Rappor |
| 50 // parameters than the original metric. | 40 // parameters than the original metric. |
| 51 const char* kExtensionAdInjectionRapporMetricName = | 41 const char* kExtensionAdInjectionRapporMetricName = |
| 52 "Extensions.PossibleAdInjection2"; | 42 "Extensions.PossibleAdInjection2"; |
| 53 | 43 |
| 54 // The names of different types of HTML elements we check for ad injection. | |
| 55 const char* kIframeElementType = "HTMLIFrameElement"; | |
| 56 const char* kEmbedElementType = "HTMLEmbedElement"; | |
| 57 const char* kAnchorElementType = "HTMLAnchorElement"; | |
| 58 | |
| 59 std::string Serialize(const base::Value* value) { | 44 std::string Serialize(const base::Value* value) { |
| 60 std::string value_as_text; | 45 std::string value_as_text; |
| 61 if (!value) { | 46 if (!value) { |
| 62 value_as_text = "null"; | 47 value_as_text = "null"; |
| 63 } else { | 48 } else { |
| 64 JSONStringValueSerializer serializer(&value_as_text); | 49 JSONStringValueSerializer serializer(&value_as_text); |
| 65 serializer.SerializeAndOmitBinaryValues(*value); | 50 serializer.SerializeAndOmitBinaryValues(*value); |
| 66 } | 51 } |
| 67 return value_as_text; | 52 return value_as_text; |
| 68 } | 53 } |
| 69 | 54 |
| 70 } // namespace | 55 } // namespace |
| 71 | 56 |
| 57 namespace ad_injection_constants { | |
|
Devlin
2014/07/15 17:21:52
nit: I think we can just take out the namespace an
pmarch
2014/07/16 10:34:17
Done.
| |
| 58 | |
| 59 const char kBlinkSetAttributeEvent[] = "blinkSetAttribute"; | |
| 60 const char kBlinkAddElementEvent[] = "blinkAddElement"; | |
| 61 | |
| 62 const char kIframe[] = "iframe"; | |
| 63 const char kAnchor[] = "a"; | |
| 64 | |
| 65 const char kSrc[] = "src"; | |
| 66 const char kHref[] = "href"; | |
| 67 | |
| 68 } // namespace ad_injection_constants | |
| 69 | |
| 72 using api::activity_log_private::ExtensionActivity; | 70 using api::activity_log_private::ExtensionActivity; |
| 73 | 71 |
| 74 Action::Action(const std::string& extension_id, | 72 Action::Action(const std::string& extension_id, |
| 75 const base::Time& time, | 73 const base::Time& time, |
| 76 const ActionType action_type, | 74 const ActionType action_type, |
| 77 const std::string& api_name, | 75 const std::string& api_name, |
| 78 int64 action_id) | 76 int64 action_id) |
| 79 : extension_id_(extension_id), | 77 : extension_id_(extension_id), |
| 80 time_(time), | 78 time_(time), |
| 81 action_type_(action_type), | 79 action_type_(action_type), |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 113 // We should always have an AdNetworkDatabase, but, on the offchance we don't, | 111 // We should always have an AdNetworkDatabase, but, on the offchance we don't, |
| 114 // don't crash in a release build. | 112 // don't crash in a release build. |
| 115 if (!AdNetworkDatabase::Get()) { | 113 if (!AdNetworkDatabase::Get()) { |
| 116 NOTREACHED(); | 114 NOTREACHED(); |
| 117 return NO_AD_INJECTION; | 115 return NO_AD_INJECTION; |
| 118 } | 116 } |
| 119 | 117 |
| 120 AdType ad_type = AD_TYPE_NONE; | 118 AdType ad_type = AD_TYPE_NONE; |
| 121 InjectionType injection_type = NO_AD_INJECTION; | 119 InjectionType injection_type = NO_AD_INJECTION; |
| 122 | 120 |
| 123 if (EndsWith(api_name_, | 121 if (api_name_ == ad_injection_constants::kBlinkSetAttributeEvent) { |
| 124 ad_injection_constants::kAppendChildApiSuffix, | 122 std::string element_name; |
| 125 true /* case senstive */)) { | 123 std::string attr_name; |
| 126 injection_type = CheckAppendChild(&ad_type); | 124 if (args_.get()) { |
| 127 } else { | 125 args_->GetString(0u, &element_name); |
| 128 // Check if the action modified an element's src/href. | 126 args_->GetString(1u, &attr_name); |
| 129 if (api_name_ == ad_injection_constants::kHtmlIframeSrcApiName) | 127 } |
| 128 if (element_name == ad_injection_constants::kIframe && | |
| 129 attr_name == ad_injection_constants::kSrc) | |
| 130 ad_type = AD_TYPE_IFRAME; | 130 ad_type = AD_TYPE_IFRAME; |
| 131 else if (api_name_ == ad_injection_constants::kHtmlEmbedSrcApiName) | 131 else if (element_name == ad_injection_constants::kAnchor && |
| 132 ad_type = AD_TYPE_EMBED; | 132 attr_name == ad_injection_constants::kHref) |
| 133 else if (api_name_ == ad_injection_constants::kHtmlAnchorHrefApiName) | |
| 134 ad_type = AD_TYPE_ANCHOR; | 133 ad_type = AD_TYPE_ANCHOR; |
| 135 | 134 |
| 136 if (ad_type != AD_TYPE_NONE) | 135 if (ad_type != AD_TYPE_NONE) |
| 137 injection_type = CheckSrcModification(); | 136 injection_type = CheckAttrModification(); |
| 137 } else if (api_name_ == ad_injection_constants::kBlinkAddElementEvent) { | |
| 138 std::string element_name; | |
| 139 if (args_.get()) | |
| 140 args_->GetString(0u, &element_name); | |
| 141 if (element_name == ad_injection_constants::kIframe) | |
| 142 ad_type = AD_TYPE_IFRAME; | |
| 143 else if (element_name == ad_injection_constants::kAnchor) | |
| 144 ad_type = AD_TYPE_ANCHOR; | |
| 145 | |
| 146 if (ad_type != AD_TYPE_NONE) | |
| 147 injection_type = CheckElementAddition(); | |
| 138 } | 148 } |
| 139 | 149 |
| 140 if (injection_type != NO_AD_INJECTION) { | 150 if (injection_type != NO_AD_INJECTION) { |
| 141 UMA_HISTOGRAM_ENUMERATION( | 151 UMA_HISTOGRAM_ENUMERATION( |
| 142 "Extensions.AdInjection.AdType", ad_type, Action::NUM_AD_TYPES); | 152 "Extensions.AdInjection.AdType", ad_type, Action::NUM_AD_TYPES); |
| 143 } | 153 } |
| 144 | 154 |
| 145 return injection_type; | 155 return injection_type; |
| 146 } | 156 } |
| 147 | 157 |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 366 // current page should be considered valid use), and aren't local to the | 376 // current page should be considered valid use), and aren't local to the |
| 367 // extension. | 377 // extension. |
| 368 return url.is_valid() && | 378 return url.is_valid() && |
| 369 !url.is_empty() && | 379 !url.is_empty() && |
| 370 url.host() != page_url_.host() && | 380 url.host() != page_url_.host() && |
| 371 !url.SchemeIs(kExtensionScheme); | 381 !url.SchemeIs(kExtensionScheme); |
| 372 } | 382 } |
| 373 | 383 |
| 374 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const { | 384 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const { |
| 375 // Don't bother recording if the url is innocuous (or no |rappor_service|). | 385 // Don't bother recording if the url is innocuous (or no |rappor_service|). |
| 376 if (!rappor_service || !UrlCouldBeAd(arg_url_)) | 386 if (!rappor_service) |
| 377 return; | 387 return; |
| 378 | 388 |
| 379 bool can_inject_ads = false; | 389 GURL url; |
| 380 for (size_t i = 0; i < arraysize(kApisForRapporMetric); ++i) { | 390 |
| 381 if (api_name_ == kApisForRapporMetric[i]) { | 391 if (api_name_ == ad_injection_constants::kBlinkSetAttributeEvent) { |
| 382 can_inject_ads = true; | 392 std::string element_name; |
| 383 break; | 393 std::string attr_name; |
| 394 std::string url_string; | |
| 395 if (args_.get()) { | |
| 396 args_->GetString(0u, &element_name); | |
| 397 args_->GetString(1u, &attr_name); | |
| 398 } | |
| 399 if (element_name == ad_injection_constants::kIframe && | |
| 400 attr_name == ad_injection_constants::kSrc) { | |
| 401 args_->GetString(3u, &url_string); | |
| 402 url = GURL(url_string); | |
| 403 } else if (element_name == ad_injection_constants::kAnchor && | |
| 404 attr_name == ad_injection_constants::kHref) { | |
| 405 args_->GetString(3u, &url_string); | |
| 406 url = GURL(url_string); | |
| 407 } | |
| 408 } else if (api_name_ == ad_injection_constants::kBlinkAddElementEvent) { | |
| 409 std::string element_name; | |
| 410 std::string url_string; | |
| 411 if (args_.get()) | |
| 412 args_->GetString(0u, &element_name); | |
| 413 if (element_name == ad_injection_constants::kIframe) { | |
| 414 args_->GetString(1u, &url_string); | |
| 415 url = GURL(url_string); | |
| 416 } else if (element_name == ad_injection_constants::kAnchor) { | |
| 417 args_->GetString(1u, &url_string); | |
| 418 url = GURL(url_string); | |
| 384 } | 419 } |
| 385 } | 420 } |
| 386 | 421 |
| 387 if (!can_inject_ads) | 422 if (!UrlCouldBeAd(url)) |
| 388 return; | 423 return; |
| 389 | 424 |
| 390 // Record the URL - an ad *may* have been injected. | 425 // Record the URL - an ad *may* have been injected. |
| 391 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName, | 426 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName, |
| 392 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, | 427 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, |
| 393 arg_url_.host()); | 428 url.host()); |
| 394 } | 429 } |
| 395 | 430 |
| 396 Action::InjectionType Action::CheckSrcModification() const { | 431 Action::InjectionType Action::CheckAttrModification() const { |
| 432 if (api_name_ != ad_injection_constants::kBlinkSetAttributeEvent) | |
| 433 return NO_AD_INJECTION; | |
| 434 | |
| 397 const AdNetworkDatabase* database = AdNetworkDatabase::Get(); | 435 const AdNetworkDatabase* database = AdNetworkDatabase::Get(); |
| 398 | 436 |
| 399 bool arg_url_could_be_ad = UrlCouldBeAd(arg_url_); | |
| 400 | |
| 401 GURL prev_url; | 437 GURL prev_url; |
| 402 std::string prev_url_string; | 438 std::string prev_url_string; |
| 403 if (args_.get() && args_->GetString(1u, &prev_url_string)) | 439 if (args_.get() && args_->GetString(2u, &prev_url_string)) |
| 404 prev_url = GURL(prev_url_string); | 440 prev_url = GURL(prev_url_string); |
| 405 | 441 |
| 442 GURL url; | |
| 443 std::string url_string; | |
| 444 if (args_.get() && args_->GetString(3u, &url_string)) | |
| 445 url = GURL(url_string); | |
| 446 | |
| 447 bool arg_url_could_be_ad = UrlCouldBeAd(url); | |
|
Devlin
2014/07/15 17:21:52
nit: this is no longer the |arg_url_|, so we shoul
pmarch
2014/07/16 10:34:17
Done.
| |
| 406 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty(); | 448 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty(); |
| 407 | 449 |
| 408 bool injected_ad = arg_url_could_be_ad && database->IsAdNetwork(arg_url_); | 450 bool injected_ad = arg_url_could_be_ad && database->IsAdNetwork(url); |
| 409 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url); | 451 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url); |
| 410 | 452 |
| 411 if (injected_ad && replaced_ad) | 453 if (injected_ad && replaced_ad) |
| 412 return INJECTION_REPLACED_AD; | 454 return INJECTION_REPLACED_AD; |
| 413 if (injected_ad) | 455 if (injected_ad) |
| 414 return INJECTION_NEW_AD; | 456 return INJECTION_NEW_AD; |
| 415 if (replaced_ad) | 457 if (replaced_ad) |
| 416 return INJECTION_REMOVED_AD; | 458 return INJECTION_REMOVED_AD; |
| 417 | 459 |
| 418 // If the extension modified the URL with an external, valid URL then there's | 460 // If the extension modified the URL with an external, valid URL then there's |
| 419 // a good chance it's ad injection. Log it as a likely one, which also helps | 461 // a good chance it's ad injection. Log it as a likely one, which also helps |
| 420 // us determine the effectiveness of our IsAdNetwork() recognition. | 462 // us determine the effectiveness of our IsAdNetwork() recognition. |
| 421 if (arg_url_could_be_ad) { | 463 if (arg_url_could_be_ad) { |
| 422 if (prev_url_valid) | 464 if (prev_url_valid) |
| 423 return INJECTION_LIKELY_REPLACED_AD; | 465 return INJECTION_LIKELY_REPLACED_AD; |
| 424 return INJECTION_LIKELY_NEW_AD; | 466 return INJECTION_LIKELY_NEW_AD; |
| 425 } | 467 } |
| 426 | 468 |
| 427 return NO_AD_INJECTION; | 469 return NO_AD_INJECTION; |
| 428 } | 470 } |
| 429 | 471 |
| 430 Action::InjectionType Action::CheckAppendChild(AdType* ad_type_out) const { | 472 Action::InjectionType Action::CheckElementAddition() const { |
| 431 const base::DictionaryValue* child = NULL; | 473 if (api_name_ != ad_injection_constants::kBlinkAddElementEvent) |
| 432 if (!args_->GetDictionary(0u, &child)) | |
| 433 return NO_AD_INJECTION; | 474 return NO_AD_INJECTION; |
| 434 | 475 |
| 435 return CheckDomObject(child, ad_type_out); | 476 GURL url; |
| 436 } | 477 std::string url_string; |
| 478 if (args_.get() && args_->GetString(1u, &url_string)) | |
| 479 url = GURL(url_string); | |
| 437 | 480 |
| 438 Action::InjectionType Action::CheckDomObject( | 481 if (UrlCouldBeAd(url)) { |
| 439 const base::DictionaryValue* object, | 482 if (AdNetworkDatabase::Get()->IsAdNetwork(url)) |
| 440 AdType* ad_type_out) const { | 483 return INJECTION_NEW_AD; |
| 441 DCHECK(ad_type_out); | 484 // If the extension injected an URL which is not local to itself or the |
| 442 std::string type; | 485 // page, there is a good chance it could be a new ad, and our database |
| 443 object->GetString(keys::kType, &type); | 486 // missed it. |
| 444 | 487 return INJECTION_LIKELY_NEW_AD; |
| 445 AdType ad_type = AD_TYPE_NONE; | |
| 446 std::string url_key; | |
| 447 if (type == kIframeElementType) { | |
| 448 ad_type = AD_TYPE_IFRAME; | |
| 449 url_key = keys::kSrc; | |
| 450 } else if (type == kEmbedElementType) { | |
| 451 ad_type = AD_TYPE_EMBED; | |
| 452 url_key = keys::kSrc; | |
| 453 } else if (type == kAnchorElementType) { | |
| 454 ad_type = AD_TYPE_ANCHOR; | |
| 455 url_key = keys::kHref; | |
| 456 } | 488 } |
| 457 | |
| 458 if (!url_key.empty()) { | |
| 459 std::string url; | |
| 460 if (object->GetString(url_key, &url)) { | |
| 461 GURL gurl(url); | |
| 462 if (UrlCouldBeAd(gurl)) { | |
| 463 *ad_type_out = ad_type; | |
| 464 if (AdNetworkDatabase::Get()->IsAdNetwork(gurl)) | |
| 465 return INJECTION_NEW_AD; | |
| 466 // If the extension injected an URL which is not local to itself or the | |
| 467 // page, there is a good chance it could be a new ad, and our database | |
| 468 // missed it. | |
| 469 return INJECTION_LIKELY_NEW_AD; | |
| 470 } | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 const base::ListValue* children = NULL; | |
| 475 if (object->GetList(keys::kChildren, &children)) { | |
| 476 const base::DictionaryValue* child = NULL; | |
| 477 for (size_t i = 0; | |
| 478 i < children->GetSize() && | |
| 479 i < ad_injection_constants::kMaximumChildrenToCheck; | |
| 480 ++i) { | |
| 481 if (children->GetDictionary(i, &child)) { | |
| 482 InjectionType type = CheckDomObject(child, ad_type_out); | |
| 483 if (type != NO_AD_INJECTION) | |
| 484 return type; | |
| 485 } | |
| 486 } | |
| 487 } | |
| 488 | |
| 489 return NO_AD_INJECTION; | 489 return NO_AD_INJECTION; |
| 490 } | 490 } |
| 491 | 491 |
| 492 bool ActionComparator::operator()( | 492 bool ActionComparator::operator()( |
| 493 const scoped_refptr<Action>& lhs, | 493 const scoped_refptr<Action>& lhs, |
| 494 const scoped_refptr<Action>& rhs) const { | 494 const scoped_refptr<Action>& rhs) const { |
| 495 if (lhs->time() != rhs->time()) | 495 if (lhs->time() != rhs->time()) |
| 496 return lhs->time() < rhs->time(); | 496 return lhs->time() < rhs->time(); |
| 497 else if (lhs->action_id() != rhs->action_id()) | 497 else if (lhs->action_id() != rhs->action_id()) |
| 498 return lhs->action_id() < rhs->action_id(); | 498 return lhs->action_id() < rhs->action_id(); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 547 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other()); | 547 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other()); |
| 548 if (lhs_other != rhs_other) | 548 if (lhs_other != rhs_other) |
| 549 return lhs_other < rhs_other; | 549 return lhs_other < rhs_other; |
| 550 } | 550 } |
| 551 | 551 |
| 552 // All fields compare as equal if this point is reached. | 552 // All fields compare as equal if this point is reached. |
| 553 return false; | 553 return false; |
| 554 } | 554 } |
| 555 | 555 |
| 556 } // namespace extensions | 556 } // namespace extensions |
| OLD | NEW |