1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/safe_browsing_db/safe_browsing_api_handler_util.h"
#include <stddef.h>
#include <memory>
#include <string>
#include "base/json/json_reader.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "components/safe_browsing_db/metadata.pb.h"
#include "components/safe_browsing_db/util.h"
namespace safe_browsing {
namespace {
// JSON metatdata keys. These are are fixed in the Java-side API.
const char kJsonKeyMatches[] = "matches";
const char kJsonKeyThreatType[] = "threat_type";
// Do not reorder or delete. Make sure changes are reflected in
// SB2RemoteCallThreatSubType.
enum UmaThreatSubType {
UMA_THREAT_SUB_TYPE_NOT_SET = 0,
UMA_THREAT_SUB_TYPE_POTENTIALLY_HALMFUL_APP_LANDING = 1,
UMA_THREAT_SUB_TYPE_POTENTIALLY_HALMFUL_APP_DISTRIBUTION = 2,
UMA_THREAT_SUB_TYPE_UNKNOWN = 3,
UMA_THREAT_SUB_TYPE_SOCIAL_ENGINEERING_ADS = 4,
UMA_THREAT_SUB_TYPE_SOCIAL_ENGINEERING_LANDING = 5,
UMA_THREAT_SUB_TYPE_PHISHING = 6,
UMA_THREAT_SUB_TYPE_MAX_VALUE
};
void ReportUmaThreatSubType(SBThreatType threat_type,
UmaThreatSubType sub_type) {
if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
UMA_HISTOGRAM_ENUMERATION(
"SB2.RemoteCall.ThreatSubType.PotentiallyHarmfulApp", sub_type,
UMA_THREAT_SUB_TYPE_MAX_VALUE);
} else {
UMA_HISTOGRAM_ENUMERATION("SB2.RemoteCall.ThreatSubType.SocialEngineering",
sub_type, UMA_THREAT_SUB_TYPE_MAX_VALUE);
}
}
// Parse the appropriate "*_pattern_type" key from the metadata.
// Returns NONE if no pattern type was found.
ThreatPatternType ParseThreatSubType(
const base::DictionaryValue* match,
SBThreatType threat_type) {
std::string pattern_key;
if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
pattern_key = "pha_pattern_type";
} else {
DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING);
pattern_key = "se_pattern_type";
}
std::string pattern_type;
if (!match->GetString(pattern_key, &pattern_type)) {
ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_NOT_SET);
return ThreatPatternType::NONE;
}
if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
if (pattern_type == "LANDING") {
ReportUmaThreatSubType(
threat_type, UMA_THREAT_SUB_TYPE_POTENTIALLY_HALMFUL_APP_LANDING);
return ThreatPatternType::MALWARE_LANDING;
} else if (pattern_type == "DISTRIBUTION") {
ReportUmaThreatSubType(
threat_type,
UMA_THREAT_SUB_TYPE_POTENTIALLY_HALMFUL_APP_DISTRIBUTION);
return ThreatPatternType::MALWARE_DISTRIBUTION;
} else {
ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_UNKNOWN);
return ThreatPatternType::NONE;
}
} else {
DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING);
if (pattern_type == "SOCIAL_ENGINEERING_ADS") {
ReportUmaThreatSubType(threat_type,
UMA_THREAT_SUB_TYPE_SOCIAL_ENGINEERING_ADS);
return ThreatPatternType::SOCIAL_ENGINEERING_ADS;
} else if (pattern_type == "SOCIAL_ENGINEERING_LANDING") {
ReportUmaThreatSubType(threat_type,
UMA_THREAT_SUB_TYPE_SOCIAL_ENGINEERING_LANDING);
return ThreatPatternType::SOCIAL_ENGINEERING_LANDING;
} else if (pattern_type == "PHISHING") {
ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_PHISHING);
return ThreatPatternType::PHISHING;
} else {
ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_UNKNOWN);
return ThreatPatternType::NONE;
}
}
}
// Parse the optional "UserPopulation" key from the metadata.
// Returns empty string if none was found.
std::string ParseUserPopulation(const base::DictionaryValue* match) {
std::string population_id;
if (!match->GetString("UserPopulation", &population_id))
return std::string();
else
return population_id;
}
int GetThreatSeverity(int java_threat_num) {
// Assign higher numbers to more severe threats.
switch (java_threat_num) {
case JAVA_THREAT_TYPE_POTENTIALLY_HARMFUL_APPLICATION:
return 2;
case JAVA_THREAT_TYPE_SOCIAL_ENGINEERING:
return 1;
default:
// Unknown threat type
return -1;
}
}
SBThreatType JavaToSBThreatType(int java_threat_num) {
switch (java_threat_num) {
case JAVA_THREAT_TYPE_POTENTIALLY_HARMFUL_APPLICATION:
return SB_THREAT_TYPE_URL_MALWARE;
case JAVA_THREAT_TYPE_SOCIAL_ENGINEERING:
return SB_THREAT_TYPE_URL_PHISHING;
default:
// Unknown threat type
return SB_THREAT_TYPE_SAFE;
}
}
} // namespace
// Valid examples:
// {"matches":[{"threat_type":"5"}]}
// or
// {"matches":[{"threat_type":"4", "pha_pattern_type":"LANDING"},
// {"threat_type":"5"}]}
// or
// {"matches":[{"threat_type":"4", "UserPopulation":"YXNvZWZpbmFqO..."}]
UmaRemoteCallResult ParseJsonFromGMSCore(const std::string& metadata_str,
SBThreatType* worst_threat,
ThreatMetadata* metadata) {
*worst_threat = SB_THREAT_TYPE_SAFE; // Default to safe.
*metadata = ThreatMetadata(); // Default values.
if (metadata_str.empty())
return UMA_STATUS_JSON_EMPTY;
// Pick out the "matches" list.
std::unique_ptr<base::Value> value = base::JSONReader::Read(metadata_str);
const base::ListValue* matches = nullptr;
if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY) ||
!(static_cast<base::DictionaryValue*>(value.get()))
->GetList(kJsonKeyMatches, &matches) ||
!matches) {
return UMA_STATUS_JSON_FAILED_TO_PARSE;
}
// Go through each matched threat type and pick the most severe.
int worst_threat_num = -1;
const base::DictionaryValue* worst_match = nullptr;
for (size_t i = 0; i < matches->GetSize(); i++) {
// Get the threat number
const base::DictionaryValue* match;
std::string threat_num_str;
int java_threat_num = -1;
if (!matches->GetDictionary(i, &match) ||
!match->GetString(kJsonKeyThreatType, &threat_num_str) ||
!base::StringToInt(threat_num_str, &java_threat_num)) {
continue; // Skip malformed list entries
}
if (GetThreatSeverity(java_threat_num) >
GetThreatSeverity(worst_threat_num)) {
worst_threat_num = java_threat_num;
worst_match = match;
}
}
*worst_threat = JavaToSBThreatType(worst_threat_num);
if (*worst_threat == SB_THREAT_TYPE_SAFE || !worst_match)
return UMA_STATUS_JSON_UNKNOWN_THREAT;
// Fill in the metadata
metadata->threat_pattern_type =
ParseThreatSubType(worst_match, *worst_threat);
metadata->population_id = ParseUserPopulation(worst_match);
return UMA_STATUS_UNSAFE; // success
}
} // namespace safe_browsing
|