From b0d250a951d6b39f64aebc9b38ddfe94870e99b6 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Mon, 26 Aug 2013 21:20:45 -0500 Subject: [PATCH 1/2] Cleaned up code for API. Results are now returned in a namedtuple object. --- threatstream-api.py | 49 ++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/threatstream-api.py b/threatstream-api.py index 7688dd8..33156e0 100644 --- a/threatstream-api.py +++ b/threatstream-api.py @@ -4,17 +4,28 @@ # This file is subject to the terms and conditions of the GNU General Public # License version 2. See the file COPYING in the main directory for more # details. + +from collections import namedtuple import urllib2 import json import sys -apiuser='user' -apikey='API_KEY' -query_api_url='https://api.threatstream.com/api/v1/' +apiuser = 'user' +apikey = 'API_KEY' +query_api_url = 'https://api.threatstream.com/api/v1/' + +THREAT_FIELDS = [ + 'domain', 'itype', 'classification', 'lat', + 'maltype', 'source_feed_id', 'date_first', + 'confidence', 'severity', 'lon', 'detail', 'id', 'source', 'email', + 'srcip', 'org', 'asn', 'date_last', 'md5', 'url', 'country', + 'detail2', 'resource_uri'] +Threat = namedtuple('Threat', THREAT_FIELDS) -def query_api(apiuser,apikey,resource,flags): - url = query_api_url+resource+'/'+'?username='+apiuser+'&api_key='+apikey+flags + +def query_api(apiuser, apikey, resource, flags): + url = QUERY_API_URL+resource+'/'+'?username='+apiuser+'&api_key='+apikey+flags http_req = urllib2.Request(url, headers={'ACCEPT': 'application/json, text/html'}) try: resp = urllib2.urlopen(http_req) @@ -26,10 +37,10 @@ def query_api(apiuser,apikey,resource,flags): else: print 'Error code: ', e.code except urllib2.URLError, e: - print 'Failed to contact server' - print 'Check proxy settings or network' - sys.exit() - + print 'Failed to contact server' + print 'Check proxy settings or network' + sys.exit() + def api_decode(api_data): if api_data != None: @@ -39,15 +50,17 @@ def api_decode(api_data): return results -def fetch_intel(apiuser,apikey): - print 'Downloading intelligence: \n' - CORE = { 'c2_domain','bot_ip'} - for itype in CORE: - c = query_api(apiuser,apikey,'intelligence','&limit=0&format=json&itype=%s' % (itype)) - data = api_decode(c) - for i in data: - print i +def fetch_intel(apiuser=APIUSER, apikey=APIKEY): + print 'Downloading intelligence: \n' + CORE = { 'c2_domain'} + threats = [] + for itype in CORE: + c = query_api(apiuser,apikey,'intelligence','&limit=0&format=json&itype=%s' % (itype)) + data = api_decode(c) + for t in data: + threats.append(Threat(**t)) + return threats if __name__ == '__main__': - fetch_intel(apiuser,apikey) + fetch_intel() From 23aebe2dd2e572547b596641ed2263227ff1f0fa Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Wed, 11 Sep 2013 20:06:52 -0500 Subject: [PATCH 2/2] Updated client to new API. Added state attribute to threats, as well as a function to check whether the client is up to date. --- threatstream-api.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/threatstream-api.py b/threatstream-api.py index 33156e0..6808331 100644 --- a/threatstream-api.py +++ b/threatstream-api.py @@ -11,16 +11,17 @@ import sys -apiuser = 'user' -apikey = 'API_KEY' -query_api_url = 'https://api.threatstream.com/api/v1/' +APIUSER = 'user' +APIKEY = 'API_KEY' +QUERY_API_URL = 'https://dev.threatstream.com/api/v1/' THREAT_FIELDS = [ - 'domain', 'itype', 'classification', 'lat', - 'maltype', 'source_feed_id', 'date_first', - 'confidence', 'severity', 'lon', 'detail', 'id', 'source', 'email', - 'srcip', 'org', 'asn', 'date_last', 'md5', 'url', 'country', - 'detail2', 'resource_uri'] + u'domain', u'itype', u'classification', u'lat', + u'maltype', u'source_feed_id', u'date_first', + u'confidence', u'severity', u'lon', u'detail', u'id', u'source', u'email', + u'srcip', u'org', u'asn', u'date_last', u'md5', 'url', u'country', u'state', + u'detail2', u'resource_uri' +] Threat = namedtuple('Threat', THREAT_FIELDS) @@ -50,16 +51,29 @@ def api_decode(api_data): return results +def check_api_version(threat): + api_keys = threat.keys() + api_keys.sort() + client_keys = THREAT_FIELDS[:] + client_keys.sort() + + if api_keys != client_keys: + msg = 'This API client is outdated:\n\nReceived: {}\n\nExpected: {}' + raise AssertionError(msg.format(api_keys, client_keys)) + return True + + def fetch_intel(apiuser=APIUSER, apikey=APIKEY): print 'Downloading intelligence: \n' - CORE = { 'c2_domain'} + CORE = {'c2_domain'} threats = [] for itype in CORE: c = query_api(apiuser,apikey,'intelligence','&limit=0&format=json&itype=%s' % (itype)) data = api_decode(c) for t in data: + if len(threats) == 0: + check_api_version(t) threats.append(Threat(**t)) - return threats if __name__ == '__main__':