1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/safe_browsing/safe_browsing_api_handler_util.h"
9 #include "base/json/json_reader.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/safe_browsing/metadata.pb.h"
15 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
18 namespace safe_browsing
{
21 // JSON metatdata keys. These are are fixed in the Java-side API.
22 const char kJsonKeyMatches
[] = "matches";
23 const char kJsonKeyThreatType
[] = "threat_type";
25 // Do not reorder or delete. Make sure changes are reflected in
26 // SB2RemoteCallThreatSubType.
27 enum UmaThreatSubType
{
28 UMA_THREAT_SUB_TYPE_NOT_SET
= 0,
29 UMA_THREAT_SUB_TYPE_LANDING
= 1,
30 UMA_THREAT_SUB_TYPE_DISTRIBUTION
= 2,
31 UMA_THREAT_SUB_TYPE_UNKNOWN
= 3,
32 UMA_THREAT_SUB_TYPE_MAX_VALUE
35 void ReportUmaThreatSubType(SBThreatType threat_type
,
36 UmaThreatSubType sub_type
) {
37 if (threat_type
== SB_THREAT_TYPE_URL_MALWARE
) {
38 UMA_HISTOGRAM_ENUMERATION(
39 "SB2.RemoteCall.ThreatSubType.PotentiallyHarmfulApp", sub_type
,
40 UMA_THREAT_SUB_TYPE_MAX_VALUE
);
42 UMA_HISTOGRAM_ENUMERATION(
43 "SB2.RemoteCall.ThreatSubType.SocialEngineering", sub_type
,
44 UMA_THREAT_SUB_TYPE_MAX_VALUE
);
48 // Parse the extra key/value pair(s) added by Safe Browsing backend. To make
49 // it binary compatible with the Pver3 metadata that UIManager expects to
50 // deserialize, we convert it to a MalwarePatternType.
52 // TODO(nparker): When chrome desktop is converted to use Pver4, convert this
53 // to the new proto instead.
54 const std::string
ParseExtraMetadataToPB(const base::DictionaryValue
* match
,
55 SBThreatType threat_type
) {
56 std::string pattern_key
;
57 if (threat_type
== SB_THREAT_TYPE_URL_MALWARE
) {
58 pattern_key
= "pha_pattern_type";
60 DCHECK(threat_type
== SB_THREAT_TYPE_URL_PHISHING
);
61 pattern_key
= "se_pattern_type";
64 std::string pattern_type
;
65 if (!match
->GetString(pattern_key
, &pattern_type
)) {
66 ReportUmaThreatSubType(threat_type
, UMA_THREAT_SUB_TYPE_NOT_SET
);
70 MalwarePatternType pb
;
71 if (pattern_type
== "LANDING") {
72 pb
.set_pattern_type(MalwarePatternType::LANDING
);
73 ReportUmaThreatSubType(threat_type
, UMA_THREAT_SUB_TYPE_LANDING
);
74 } else if (pattern_type
== "DISTRIBUTION") {
75 pb
.set_pattern_type(MalwarePatternType::DISTRIBUTION
);
76 ReportUmaThreatSubType(threat_type
, UMA_THREAT_SUB_TYPE_DISTRIBUTION
);
78 ReportUmaThreatSubType(threat_type
, UMA_THREAT_SUB_TYPE_UNKNOWN
);
82 return pb
.SerializeAsString();
85 int GetThreatSeverity(int java_threat_num
) {
86 // Assign higher numbers to more severe threats.
87 switch (java_threat_num
) {
88 case JAVA_THREAT_TYPE_POTENTIALLY_HARMFUL_APPLICATION
:
90 case JAVA_THREAT_TYPE_SOCIAL_ENGINEERING
:
93 // Unknown threat type
98 SBThreatType
JavaToSBThreatType(int java_threat_num
) {
99 switch (java_threat_num
) {
100 case JAVA_THREAT_TYPE_POTENTIALLY_HARMFUL_APPLICATION
:
101 return SB_THREAT_TYPE_URL_MALWARE
;
102 case JAVA_THREAT_TYPE_SOCIAL_ENGINEERING
:
103 return SB_THREAT_TYPE_URL_PHISHING
;
105 // Unknown threat type
106 return SB_THREAT_TYPE_SAFE
;
114 // {"matches":[{"threat_type":"5"}]}
116 // {"matches":[{"threat_type":"4"},
117 // {"threat_type":"5", "se_pattern_type":"LANDING"}]}
118 UmaRemoteCallResult
ParseJsonToThreatAndPB(const std::string
& metadata_str
,
119 SBThreatType
* worst_threat
,
120 std::string
* metadata_pb_str
) {
121 *worst_threat
= SB_THREAT_TYPE_SAFE
; // Default to safe.
122 *metadata_pb_str
= std::string();
124 if (metadata_str
.empty())
125 return UMA_STATUS_JSON_EMPTY
;
127 // Pick out the "matches" list.
128 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(metadata_str
);
129 const base::ListValue
* matches
;
130 if (!value
.get() || !value
->IsType(base::Value::TYPE_DICTIONARY
) ||
131 !(static_cast<base::DictionaryValue
*>(value
.get()))
132 ->GetList(kJsonKeyMatches
, &matches
)) {
133 return UMA_STATUS_JSON_FAILED_TO_PARSE
;
136 // Go through each matched threat type and pick the most severe.
137 int worst_threat_num
= -1;
138 const base::DictionaryValue
* worst_match
= nullptr;
139 for (size_t i
= 0; i
< matches
->GetSize(); i
++) {
140 // Get the threat number
141 const base::DictionaryValue
* match
;
142 std::string threat_num_str
;
143 int java_threat_num
= -1;
144 if (!matches
->GetDictionary(i
, &match
) ||
145 !match
->GetString(kJsonKeyThreatType
, &threat_num_str
) ||
146 !base::StringToInt(threat_num_str
, &java_threat_num
)) {
147 continue; // Skip malformed list entries
150 if (GetThreatSeverity(java_threat_num
) >
151 GetThreatSeverity(worst_threat_num
)) {
152 worst_threat_num
= java_threat_num
;
157 *worst_threat
= JavaToSBThreatType(worst_threat_num
);
158 if (*worst_threat
== SB_THREAT_TYPE_SAFE
|| !worst_match
)
159 return UMA_STATUS_JSON_UNKNOWN_THREAT
;
160 *metadata_pb_str
= ParseExtraMetadataToPB(worst_match
, *worst_threat
);
162 return UMA_STATUS_UNSAFE
; // success
165 } // namespace safe_browsing