cti-python-stix2/stix2/sources/taxii.py

166 lines
4.5 KiB
Python
Raw Normal View History

2017-07-12 16:58:31 +02:00
"""
Python STIX 2.0 TAXII Source/Sink
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
Classes:
TAXIICollectionStore
TAXIICollectionSink
TAXIICollectionSource
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
TODO: Test everything
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
"""
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
import json
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
from stix2.sources import DataSink, DataSource, DataStore, make_id
2017-07-12 16:58:31 +02:00
TAXII_FILTERS = ['added_after', 'id', 'type', 'version']
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
class TAXIICollectionStore(DataStore):
"""
"""
def __init__(self, collection, name="TAXIICollectionStore"):
"""
Create a new TAXII Collection Data store
Args:
collection (taxii2.Collection): Collection instance
2017-07-12 16:58:31 +02:00
"""
super(TAXIICollectionStore, self).__init__(name=name)
self.source = TAXIICollectionSource(collection)
self.sink = TAXIICollectionSink(collection)
2017-07-12 16:58:31 +02:00
class TAXIICollectionSink(DataSink):
"""
"""
def __init__(self, collection, name="TAXIICollectionSink"):
2017-07-12 16:58:31 +02:00
super(TAXIICollectionSink, self).__init__(name=name)
self.collection = collection
2017-07-12 16:58:31 +02:00
def add(self, stix_obj):
2017-07-12 16:58:31 +02:00
"""
"""
self.collection.add_objects(self.create_bundle([json.loads(str(stix_obj))]))
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
@staticmethod
def create_bundle(objects):
return dict(id="bundle--%s" % make_id(),
2017-07-12 16:58:31 +02:00
objects=objects,
spec_version="2.0",
type="bundle")
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
class TAXIICollectionSource(DataSource):
"""
"""
def __init__(self, collection, name="TAXIICollectionSource"):
2017-07-12 16:58:31 +02:00
super(TAXIICollectionSource, self).__init__(name=name)
self.collection = collection
2017-07-12 16:58:31 +02:00
def get(self, stix_id, _composite_filters=None):
"""
"""
# combine all query filters
query = []
if self.filters:
query.extend(self.filters.values())
if _composite_filters:
query.extend(_composite_filters)
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
# separate taxii query terms (can be done remotely)
taxii_filters = self._parse_taxii_filters(query)
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
stix_objs = self.collection.get_object(stix_id, taxii_filters)["objects"]
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
stix_obj = self.apply_common_filters(stix_objs, query)
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
if len(stix_obj) > 0:
stix_obj = stix_obj[0]
else:
stix_obj = None
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
return stix_obj
2017-05-24 17:25:40 +02:00
2017-07-12 16:58:31 +02:00
def all_versions(self, stix_id, _composite_filters=None):
"""
2017-05-26 21:24:33 +02:00
"""
2017-05-24 17:25:40 +02:00
# make query in TAXII query format since 'id' is TAXII field
query = [
{
"field": "match[id]",
"op": "=",
2017-07-12 16:58:31 +02:00
"value": stix_id
},
{
"field": "match[version]",
"op": "=",
"value": "all"
2017-05-24 17:25:40 +02:00
}
]
all_data = self.query(query=query, _composite_filters=_composite_filters)
return all_data
def query(self, query=None, _composite_filters=None):
2017-05-26 21:24:33 +02:00
"""
2017-07-12 16:58:31 +02:00
"""
2017-05-24 17:25:40 +02:00
if query is None:
query = []
# combine all query filters
if self.filters:
2017-07-12 16:58:31 +02:00
query.extend(self.filters.values())
2017-05-24 17:25:40 +02:00
if _composite_filters:
2017-07-12 16:58:31 +02:00
query.extend(_composite_filters)
2017-05-24 17:25:40 +02:00
2017-05-26 21:24:33 +02:00
# separate taxii query terms (can be done remotely)
2017-05-24 17:25:40 +02:00
taxii_filters = self._parse_taxii_filters(query)
2017-07-12 16:58:31 +02:00
# query TAXII collection
all_data = self.collection.get_objects(filters=taxii_filters)["objects"]
2017-05-24 17:25:40 +02:00
# deduplicate data (before filtering as reduces wasted filtering)
all_data = self.deduplicate(all_data)
# apply local (composite and data source filters)
all_data = self.apply_common_filters(all_data, query)
return all_data
def _parse_taxii_filters(self, query):
"""Parse out TAXII filters that the TAXII server can filter on.
2017-05-24 17:25:40 +02:00
Notes:
For instance - "?match[type]=indicator,sighting" should be in a
query dict as follows:
{
"field": "type"
"op": "=",
"value": "indicator,sighting"
}
2017-05-24 17:25:40 +02:00
Args:
2017-05-26 21:24:33 +02:00
query (list): list of filters to extract which ones are TAXII
specific.
2017-05-24 17:25:40 +02:00
Returns:
2017-05-26 21:24:33 +02:00
params (dict): dict of the TAXII filters but in format required
for 'requests.get()'.
2017-05-24 17:25:40 +02:00
"""
2017-05-24 17:25:40 +02:00
params = {}
2017-07-12 16:58:31 +02:00
for filter_ in query:
if filter_["field"] in TAXII_FILTERS:
if filter_["field"] == "added_after":
params[filter_["field"]] = filter_["value"]
else:
taxii_field = "match[%s]" % filter_["field"]
2017-07-12 16:58:31 +02:00
params[taxii_field] = filter_["value"]
2017-05-24 17:25:40 +02:00
return params