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 |