// 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 "chrome/browser/safe_browsing/safe_browsing_api_handler_util.h" #include #include #include "base/json/json_reader.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "chrome/browser/safe_browsing/metadata.pb.h" #include "chrome/browser/safe_browsing/safe_browsing_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_LANDING = 1, UMA_THREAT_SUB_TYPE_DISTRIBUTION = 2, UMA_THREAT_SUB_TYPE_UNKNOWN = 3, 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 extra key/value pair(s) added by Safe Browsing backend. To make // it binary compatible with the Pver3 metadata that UIManager expects to // deserialize, we convert it to a MalwarePatternType. // // TODO(nparker): When chrome desktop is converted to use Pver4, convert this // to the new proto instead. const std::string ParseExtraMetadataToPB(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 std::string(); } MalwarePatternType pb; if (pattern_type == "LANDING") { pb.set_pattern_type(MalwarePatternType::LANDING); ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_LANDING); } else if (pattern_type == "DISTRIBUTION") { pb.set_pattern_type(MalwarePatternType::DISTRIBUTION); ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_DISTRIBUTION); } else { ReportUmaThreatSubType(threat_type, UMA_THREAT_SUB_TYPE_UNKNOWN); return std::string(); } return pb.SerializeAsString(); } 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"}, // {"threat_type":"5", "se_pattern_type":"LANDING"}]} UmaRemoteCallResult ParseJsonToThreatAndPB(const std::string& metadata_str, SBThreatType* worst_threat, std::string* metadata_pb_str) { *worst_threat = SB_THREAT_TYPE_SAFE; // Default to safe. *metadata_pb_str = std::string(); if (metadata_str.empty()) return UMA_STATUS_JSON_EMPTY; // Pick out the "matches" list. scoped_ptr value = base::JSONReader::Read(metadata_str); const base::ListValue* matches; if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY) || !(static_cast(value.get())) ->GetList(kJsonKeyMatches, &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; *metadata_pb_str = ParseExtraMetadataToPB(worst_match, *worst_threat); return UMA_STATUS_UNSAFE; // success } } // namespace safe_browsing