diff -r a64b986abb54 -r fb1f9e681848 WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py --- a/WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py Wed Apr 11 19:57:23 2018 +0200 +++ b/WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py Thu Apr 12 19:12:36 2018 +0200 @@ -62,7 +62,7 @@ # key: URL as string # value: dictionary with these entries: # "validUntil": (QDateTime) - # "threatInfo": (ThreatList) + # "threatInfo": (list of ThreatList) def setApiKey(self, apiKey): """ @@ -77,9 +77,9 @@ """ Public method to retrieve all available threat lists. - @return list of threat lists - @rtype list of dict containing 'threatType', 'platformType' and - 'threatEntryType' + @return tuple containing list of threat lists and an error message + @rtype tuple of (list of dict containing 'threatType', 'platformType' + and 'threatEntryType', bool) """ url = QUrl(self.GsbUrlTemplate.format("threatLists", self.__apiKey)) req = QNetworkRequest(url) @@ -90,14 +90,16 @@ # max. 200 ms processing res = None + error = "" if reply.error() != QNetworkReply.NoError: - self.networkError.emit(reply.errorString()) + error = reply.errorString() + self.networkError.emit(error) else: result = self.__extractData(reply) res = result["threatLists"] reply.deleteLater() - return res + return res, error ####################################################################### ## Methods below implement the 'Update API (v4)' @@ -110,8 +112,9 @@ @param clientStates dictionary of client states with keys like (threatType, platformType, threatEntryType) @type dict - @return list of threat updates - @rtype list of dict + @return tuple containing the list of threat updates and an error + message + @rtype tuple of (list of dict, bool) """ requestBody = { "client": { @@ -147,14 +150,16 @@ # max. 200 ms processing res = None + error = "" if reply.error() != QNetworkReply.NoError: - self.networkError.emit(reply.errorString()) + error = reply.errorString() + self.networkError.emit(error) else: result = self.__extractData(reply) res = result["listUpdateResponses"] reply.deleteLater() - return res + return res, error def getFullHashes(self, prefixes, clientState): """ @@ -212,7 +217,7 @@ QCoreApplication.processEvents(QEventLoop.AllEvents, 200) # max. 200 ms processing - res = None + res = [] if reply.error() != QNetworkReply.NoError: self.networkError.emit(reply.errorString()) else: @@ -281,34 +286,38 @@ @type QUrl @param platforms list of platform types to check against @type list of str - @return list of threat list info objects - @rtype list of ThreatList + @return tuple containing the list of threat list info objects and + an error message + @rtype tuple of (list of ThreatList, str) """ + error = "" + # sanitize the URL by removing user info and query data url = url.adjusted( QUrl.RemoveUserInfo | QUrl.RemoveQuery | QUrl.RemoveFragment ) urlStr = url.toString() + + # check the local cache first if urlStr in self.__lookupApiCache: if self.__lookupApiCache[urlStr]["validUntil"] > \ QDateTime.currentDateTime(): # cached entry is still valid - return self.__lookupApiCache[urlStr]["threatInfo"] + return self.__lookupApiCache[urlStr]["threatInfo"], error else: del self.__lookupApiCache[urlStr] + # no valid entry found, ask the safe browsing server requestBody = { "client": { "clientId": self.ClientId, "clientVersion": self.ClientVersion, }, "threatInfo": { - "threatTypes": [ - "MALWARE", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE", - "POTENTIALLY_HARMFUL_APPLICATION", - ], + "threatTypes": SafeBrowsingAPIClient.definedThreatTypes(), "platformTypes": platforms, - "threatEntryTypes": ["URL", "EXECUTABLE"], + "threatEntryTypes": + SafeBrowsingAPIClient.definedThreatEntryTypes(), "threatEntries": [ {"url": urlStr}, ], @@ -328,7 +337,8 @@ threats = [] if reply.error() != QNetworkReply.NoError: - self.networkError.emit(reply.errorString()) + error = reply.errorString() + self.networkError.emit(error) else: res = json.loads(str(reply.readAll(), "utf-8")) if res and "matches" in res: @@ -355,7 +365,7 @@ } reply.deleteLater() - return threats + return threats, error ####################################################################### ## Methods below implement global (class wide) functionality @@ -449,6 +459,50 @@ return displayString @classmethod + def definedThreatTypes(cls): + """ + Class method to get all threat types defined in API v4. + + @return list of defined threat types + @rtype list of str + """ + return [ + "THREAT_TYPE_UNSPECIFIED", "MALWARE", "SOCIAL_ENGINEERING", + "UNWANTED_SOFTWARE", "POTENTIALLY_HARMFUL_APPLICATION", + ] + + @classmethod + def getThreatEntryString(cls, threatEntry): + """ + Class method to get the threat entry string. + + @param threatEntry threat entry type as defined in the v4 API + @type str + @return threat entry string + @rtype str + """ + if threatEntry == "URL": + return "URL" + elif threatEntry == "EXECUTABLE": + return QCoreApplication.translate( + "SafeBrowsingAPI", "executable program") + else: + return QCoreApplication.translate( + "SafeBrowsingAPI", "unknown type") + + @classmethod + def definedThreatEntryTypes(cls): + """ + Class method to get all threat entry types defined in API v4. + + @return list of all defined threat entry types + @rtype list of str + """ + return [ + "THREAT_ENTRY_TYPE_UNSPECIFIED", "URL", "EXECUTABLE", + ] + + @classmethod def getPlatformString(cls, platformType): """ Class method to get the platform string for a given platform type. @@ -480,25 +534,6 @@ "SafeBrowsingAPI", "unknown platform") @classmethod - def getThreatEntryString(cls, threatEntry): - """ - Class method to get the threat entry string. - - @param threatEntry threat entry type as defined in the v4 API - @type str - @return threat entry string - @rtype str - """ - if threatEntry == "URL": - return "URL" - elif threatEntry == "EXECUTABLE": - return QCoreApplication.translate( - "SafeBrowsingAPI", "executable program") - else: - return QCoreApplication.translate( - "SafeBrowsingAPI", "unknown type") - - @classmethod def getPlatformTypes(cls, platform): """ Class method to get the platform types for a given platform. @@ -523,3 +558,16 @@ raise ValueError("Unsupported platform") return platformTypes + + @classmethod + def definedPlatformTypes(cls): + """ + Class method to get all platform types defined in API v4. + + @return list of all defined platform types + @rtype list of str + """ + return [ + "PLATFORM_TYPE_UNSPECIFIED", "WINDOWS", "LINUX", "ANDROID", "OSX", + "IOS", "ANY_PLATFORM", "ALL_PLATFORMS", "CHROME", + ]