| 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. | 44 const char kBlinkSetAttributeEvent[] = "blinkSetAttribute"; |
| 55 const char* kIframeElementType = "HTMLIFrameElement"; | 45 const char kBlinkAddElementEvent[] = "blinkAddElement"; |
| 56 const char* kEmbedElementType = "HTMLEmbedElement"; | 46 |
| 57 const char* kAnchorElementType = "HTMLAnchorElement"; | 47 const char kIframe[] = "iframe"; |
| 48 const char kAnchor[] = "a"; |
| 49 |
| 50 const char kSrc[] = "src"; |
| 51 const char kHref[] = "href"; |
| 58 | 52 |
| 59 std::string Serialize(const base::Value* value) { | 53 std::string Serialize(const base::Value* value) { |
| 60 std::string value_as_text; | 54 std::string value_as_text; |
| 61 if (!value) { | 55 if (!value) { |
| 62 value_as_text = "null"; | 56 value_as_text = "null"; |
| 63 } else { | 57 } else { |
| 64 JSONStringValueSerializer serializer(&value_as_text); | 58 JSONStringValueSerializer serializer(&value_as_text); |
| 65 serializer.SerializeAndOmitBinaryValues(*value); | 59 serializer.SerializeAndOmitBinaryValues(*value); |
| 66 } | 60 } |
| 67 return value_as_text; | 61 return value_as_text; |
| (...skipping 45 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, | 107 // We should always have an AdNetworkDatabase, but, on the offchance we don't, |
| 114 // don't crash in a release build. | 108 // don't crash in a release build. |
| 115 if (!AdNetworkDatabase::Get()) { | 109 if (!AdNetworkDatabase::Get()) { |
| 116 NOTREACHED(); | 110 NOTREACHED(); |
| 117 return NO_AD_INJECTION; | 111 return NO_AD_INJECTION; |
| 118 } | 112 } |
| 119 | 113 |
| 120 AdType ad_type = AD_TYPE_NONE; | 114 AdType ad_type = AD_TYPE_NONE; |
| 121 InjectionType injection_type = NO_AD_INJECTION; | 115 InjectionType injection_type = NO_AD_INJECTION; |
| 122 | 116 |
| 123 if (EndsWith(api_name_, | 117 if (api_name_ == kBlinkSetAttributeEvent) { |
| 124 ad_injection_constants::kAppendChildApiSuffix, | 118 std::string element_name; |
| 125 true /* case senstive */)) { | 119 std::string attr_name; |
| 126 injection_type = CheckAppendChild(&ad_type); | 120 if (args_.get()) { |
| 127 } else { | 121 args_->GetString(0u, &element_name); |
| 128 // Check if the action modified an element's src/href. | 122 args_->GetString(1u, &attr_name); |
| 129 if (api_name_ == ad_injection_constants::kHtmlIframeSrcApiName) | 123 } |
| 124 if (element_name == kIframe && attr_name == kSrc) |
| 130 ad_type = AD_TYPE_IFRAME; | 125 ad_type = AD_TYPE_IFRAME; |
| 131 else if (api_name_ == ad_injection_constants::kHtmlEmbedSrcApiName) | 126 else if (element_name == kAnchor && attr_name == kHref) |
| 132 ad_type = AD_TYPE_EMBED; | |
| 133 else if (api_name_ == ad_injection_constants::kHtmlAnchorHrefApiName) | |
| 134 ad_type = AD_TYPE_ANCHOR; | 127 ad_type = AD_TYPE_ANCHOR; |
| 135 | 128 |
| 136 if (ad_type != AD_TYPE_NONE) | 129 if (ad_type != AD_TYPE_NONE) |
| 137 injection_type = CheckSrcModification(); | 130 injection_type = CheckAttrModification(); |
| 131 } else if (api_name_ == kBlinkAddElementEvent) { |
| 132 std::string element_name; |
| 133 if (args_.get()) |
| 134 args_->GetString(0u, &element_name); |
| 135 if (element_name == kIframe) |
| 136 ad_type = AD_TYPE_IFRAME; |
| 137 else if (element_name == kAnchor) |
| 138 ad_type = AD_TYPE_ANCHOR; |
| 139 |
| 140 if (ad_type != AD_TYPE_NONE) |
| 141 injection_type = CheckElementAddition(); |
| 138 } | 142 } |
| 139 | 143 |
| 140 if (injection_type != NO_AD_INJECTION) { | 144 if (injection_type != NO_AD_INJECTION) { |
| 141 UMA_HISTOGRAM_ENUMERATION( | 145 UMA_HISTOGRAM_ENUMERATION( |
| 142 "Extensions.AdInjection.AdType", ad_type, Action::NUM_AD_TYPES); | 146 "Extensions.AdInjection.AdType", ad_type, Action::NUM_AD_TYPES); |
| 143 } | 147 } |
| 144 | 148 |
| 145 return injection_type; | 149 return injection_type; |
| 146 } | 150 } |
| 147 | 151 |
| (...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 | 370 // current page should be considered valid use), and aren't local to the |
| 367 // extension. | 371 // extension. |
| 368 return url.is_valid() && | 372 return url.is_valid() && |
| 369 !url.is_empty() && | 373 !url.is_empty() && |
| 370 url.host() != page_url_.host() && | 374 url.host() != page_url_.host() && |
| 371 !url.SchemeIs(kExtensionScheme); | 375 !url.SchemeIs(kExtensionScheme); |
| 372 } | 376 } |
| 373 | 377 |
| 374 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const { | 378 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const { |
| 375 // Don't bother recording if the url is innocuous (or no |rappor_service|). | 379 // Don't bother recording if the url is innocuous (or no |rappor_service|). |
| 376 if (!rappor_service || !UrlCouldBeAd(arg_url_)) | 380 if (!rappor_service) |
| 377 return; | 381 return; |
| 378 | 382 |
| 379 bool can_inject_ads = false; | 383 GURL url; |
| 380 for (size_t i = 0; i < arraysize(kApisForRapporMetric); ++i) { | 384 |
| 381 if (api_name_ == kApisForRapporMetric[i]) { | 385 if (api_name_ == kBlinkSetAttributeEvent) { |
| 382 can_inject_ads = true; | 386 std::string element_name; |
| 383 break; | 387 std::string attr_name; |
| 388 std::string url_string; |
| 389 if (args_.get()) { |
| 390 args_->GetString(0u, &element_name); |
| 391 args_->GetString(1u, &attr_name); |
| 392 } |
| 393 if (element_name == kIframe && attr_name == kSrc) { |
| 394 args_->GetString(3u, &url_string); |
| 395 url = GURL(url_string); |
| 396 } else if (element_name == kAnchor && attr_name == kHref) { |
| 397 args_->GetString(3u, &url_string); |
| 398 url = GURL(url_string); |
| 399 } |
| 400 } else if (api_name_ == kBlinkAddElementEvent) { |
| 401 std::string element_name; |
| 402 std::string url_string; |
| 403 if (args_.get()) |
| 404 args_->GetString(0u, &element_name); |
| 405 if (element_name == kIframe) { |
| 406 args_->GetString(1u, &url_string); |
| 407 url = GURL(url_string); |
| 408 } else if (element_name == kAnchor) { |
| 409 args_->GetString(1u, &url_string); |
| 410 url = GURL(url_string); |
| 384 } | 411 } |
| 385 } | 412 } |
| 386 | 413 |
| 387 if (!can_inject_ads) | 414 if (!UrlCouldBeAd(url)) |
| 388 return; | 415 return; |
| 389 | 416 |
| 390 // Record the URL - an ad *may* have been injected. | 417 // Record the URL - an ad *may* have been injected. |
| 391 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName, | 418 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName, |
| 392 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, | 419 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, |
| 393 arg_url_.host()); | 420 url.host()); |
| 394 } | 421 } |
| 395 | 422 |
| 396 Action::InjectionType Action::CheckSrcModification() const { | 423 Action::InjectionType Action::CheckAttrModification() const { |
| 424 if (api_name_ != kBlinkSetAttributeEvent) |
| 425 return NO_AD_INJECTION; |
| 426 |
| 397 const AdNetworkDatabase* database = AdNetworkDatabase::Get(); | 427 const AdNetworkDatabase* database = AdNetworkDatabase::Get(); |
| 398 | 428 |
| 399 bool arg_url_could_be_ad = UrlCouldBeAd(arg_url_); | |
| 400 | |
| 401 GURL prev_url; | 429 GURL prev_url; |
| 402 std::string prev_url_string; | 430 std::string prev_url_string; |
| 403 if (args_.get() && args_->GetString(1u, &prev_url_string)) | 431 if (args_.get() && args_->GetString(2u, &prev_url_string)) |
| 404 prev_url = GURL(prev_url_string); | 432 prev_url = GURL(prev_url_string); |
| 405 | 433 |
| 434 GURL new_url; |
| 435 std::string new_url_string; |
| 436 if (args_.get() && args_->GetString(3u, &new_url_string)) |
| 437 new_url = GURL(new_url_string); |
| 438 |
| 439 bool new_url_could_be_ad = UrlCouldBeAd(new_url); |
| 406 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty(); | 440 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty(); |
| 407 | 441 |
| 408 bool injected_ad = arg_url_could_be_ad && database->IsAdNetwork(arg_url_); | 442 bool injected_ad = new_url_could_be_ad && database->IsAdNetwork(new_url); |
| 409 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url); | 443 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url); |
| 410 | 444 |
| 411 if (injected_ad && replaced_ad) | 445 if (injected_ad && replaced_ad) |
| 412 return INJECTION_REPLACED_AD; | 446 return INJECTION_REPLACED_AD; |
| 413 if (injected_ad) | 447 if (injected_ad) |
| 414 return INJECTION_NEW_AD; | 448 return INJECTION_NEW_AD; |
| 415 if (replaced_ad) | 449 if (replaced_ad) |
| 416 return INJECTION_REMOVED_AD; | 450 return INJECTION_REMOVED_AD; |
| 417 | 451 |
| 418 // If the extension modified the URL with an external, valid URL then there's | 452 // 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 | 453 // 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. | 454 // us determine the effectiveness of our IsAdNetwork() recognition. |
| 421 if (arg_url_could_be_ad) { | 455 if (new_url_could_be_ad) { |
| 422 if (prev_url_valid) | 456 if (prev_url_valid) |
| 423 return INJECTION_LIKELY_REPLACED_AD; | 457 return INJECTION_LIKELY_REPLACED_AD; |
| 424 return INJECTION_LIKELY_NEW_AD; | 458 return INJECTION_LIKELY_NEW_AD; |
| 425 } | 459 } |
| 426 | 460 |
| 427 return NO_AD_INJECTION; | 461 return NO_AD_INJECTION; |
| 428 } | 462 } |
| 429 | 463 |
| 430 Action::InjectionType Action::CheckAppendChild(AdType* ad_type_out) const { | 464 Action::InjectionType Action::CheckElementAddition() const { |
| 431 const base::DictionaryValue* child = NULL; | 465 if (api_name_ != kBlinkAddElementEvent) |
| 432 if (!args_->GetDictionary(0u, &child)) | |
| 433 return NO_AD_INJECTION; | 466 return NO_AD_INJECTION; |
| 434 | 467 |
| 435 return CheckDomObject(child, ad_type_out); | 468 GURL url; |
| 436 } | 469 std::string url_string; |
| 470 if (args_.get() && args_->GetString(1u, &url_string)) |
| 471 url = GURL(url_string); |
| 437 | 472 |
| 438 Action::InjectionType Action::CheckDomObject( | 473 if (UrlCouldBeAd(url)) { |
| 439 const base::DictionaryValue* object, | 474 if (AdNetworkDatabase::Get()->IsAdNetwork(url)) |
| 440 AdType* ad_type_out) const { | 475 return INJECTION_NEW_AD; |
| 441 DCHECK(ad_type_out); | 476 // If the extension injected an URL which is not local to itself or the |
| 442 std::string type; | 477 // page, there is a good chance it could be a new ad, and our database |
| 443 object->GetString(keys::kType, &type); | 478 // missed it. |
| 444 | 479 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 } | 480 } |
| 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; | 481 return NO_AD_INJECTION; |
| 490 } | 482 } |
| 491 | 483 |
| 492 bool ActionComparator::operator()( | 484 bool ActionComparator::operator()( |
| 493 const scoped_refptr<Action>& lhs, | 485 const scoped_refptr<Action>& lhs, |
| 494 const scoped_refptr<Action>& rhs) const { | 486 const scoped_refptr<Action>& rhs) const { |
| 495 if (lhs->time() != rhs->time()) | 487 if (lhs->time() != rhs->time()) |
| 496 return lhs->time() < rhs->time(); | 488 return lhs->time() < rhs->time(); |
| 497 else if (lhs->action_id() != rhs->action_id()) | 489 else if (lhs->action_id() != rhs->action_id()) |
| 498 return lhs->action_id() < rhs->action_id(); | 490 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()); | 539 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other()); |
| 548 if (lhs_other != rhs_other) | 540 if (lhs_other != rhs_other) |
| 549 return lhs_other < rhs_other; | 541 return lhs_other < rhs_other; |
| 550 } | 542 } |
| 551 | 543 |
| 552 // All fields compare as equal if this point is reached. | 544 // All fields compare as equal if this point is reached. |
| 553 return false; | 545 return false; |
| 554 } | 546 } |
| 555 | 547 |
| 556 } // namespace extensions | 548 } // namespace extensions |
| OLD | NEW |