adjusting tests

stix2.0
= 2017-11-29 12:03:10 -05:00
parent 0347bb52fd
commit 2399ee62ec
7 changed files with 231 additions and 182 deletions

View File

@ -1,8 +1,6 @@
"""
Python STIX 2.0 FileSystem Source/Sink
TODO:
Test everything
"""
import json
@ -22,7 +20,12 @@ class FileSystemStore(DataStore):
Args:
stix_dir (str): path to directory of STIX objects
bundlify (bool): Whether to wrap objects in bundles when saving them.
allow_custom (bool): whether to allow custom STIX content to be
pushed/retrieved. Defaults to True for FileSystemSource side(retrieving data)
and False for FileSystemSink side(pushing data). However, when
parameter is supplied, it will be applied to both FileSystemSource
and FileSystemSink.
bundlify (bool): whether to wrap objects in bundles when saving them.
Default: False.
Attributes:
@ -30,10 +33,16 @@ class FileSystemStore(DataStore):
sink (FileSystemSink): FileSystemSink
"""
def __init__(self, stix_dir, bundlify=False):
def __init__(self, stix_dir, allow_custom=None, bundlify=False):
if not allow_custom:
allow_custom_source = True
allow_custom_sink = False
else:
allow_custom_sink = allow_custom_source = allow_custom
super(FileSystemStore, self).__init__(
source=FileSystemSource(stix_dir=stix_dir),
sink=FileSystemSink(stix_dir=stix_dir, bundlify=bundlify)
source=FileSystemSource(stix_dir=stix_dir, allow_custom=allow_custom_source),
sink=FileSystemSink(stix_dir=stix_dir, allow_custom=allow_custom_sink, bundlify=bundlify)
)
@ -46,13 +55,16 @@ class FileSystemSink(DataSink):
Args:
stix_dir (str): path to directory of STIX objects.
allow_custom (bool): Whether to allow custom STIX content to be
added to the FileSystemSource. Default: False
bundlify (bool): Whether to wrap objects in bundles when saving them.
Default: False.
"""
def __init__(self, stix_dir, bundlify=False):
def __init__(self, stix_dir, allow_custom=False, bundlify=False):
super(FileSystemSink, self).__init__()
self._stix_dir = os.path.abspath(stix_dir)
self.allow_custom = allow_custom
self.bundlify = bundlify
if not os.path.exists(self._stix_dir):
@ -71,20 +83,18 @@ class FileSystemSink(DataSink):
os.makedirs(os.path.dirname(path))
if self.bundlify:
stix_obj = Bundle(stix_obj)
stix_obj = Bundle(stix_obj, allow_custom=self.allow_custom)
with open(path, "w") as f:
f.write(str(stix_obj))
def add(self, stix_data=None, allow_custom=False, version=None):
def add(self, stix_data=None, version=None):
"""Add STIX objects to file directory.
Args:
stix_data (STIX object OR dict OR str OR list): valid STIX 2.0 content
in a STIX object (or list of), dict (or list of), or a STIX 2.0
json encoded string.
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -100,24 +110,24 @@ class FileSystemSink(DataSink):
self._check_path_and_write(stix_data)
elif isinstance(stix_data, (str, dict)):
stix_data = parse(stix_data, allow_custom=allow_custom, version=version)
stix_data = parse(stix_data, allow_custom=self.allow_custom, version=version)
if stix_data["type"] == "bundle":
# extract STIX objects
for stix_obj in stix_data.get("objects", []):
self.add(stix_obj, allow_custom=allow_custom, version=version)
self.add(stix_obj, version=version)
else:
# adding json-formatted STIX
self._check_path_and_write(stix_data)
self._check_path_and_write(stix_data,)
elif isinstance(stix_data, Bundle):
# recursively add individual STIX objects
for stix_obj in stix_data.get("objects", []):
self.add(stix_obj, allow_custom=allow_custom, version=version)
self.add(stix_obj, version=version)
elif isinstance(stix_data, list):
# recursively add individual STIX objects
for stix_obj in stix_data:
self.add(stix_obj, allow_custom=allow_custom, version=version)
self.add(stix_obj, version=version)
else:
raise TypeError("stix_data must be a STIX object (or list of), "
@ -134,11 +144,14 @@ class FileSystemSource(DataSource):
Args:
stix_dir (str): path to directory of STIX objects
allow_custom (bool): Whether to allow custom STIX content to be
added to the FileSystemSink. Default: True
"""
def __init__(self, stix_dir):
def __init__(self, stix_dir, allow_custom=True):
super(FileSystemSource, self).__init__()
self._stix_dir = os.path.abspath(stix_dir)
self.allow_custom = allow_custom
if not os.path.exists(self._stix_dir):
raise ValueError("directory path for STIX data does not exist: %s" % self._stix_dir)
@ -147,15 +160,13 @@ class FileSystemSource(DataSource):
def stix_dir(self):
return self._stix_dir
def get(self, stix_id, allow_custom=False, version=None, _composite_filters=None):
def get(self, stix_id, version=None, _composite_filters=None):
"""Retrieve STIX object from file directory via STIX ID.
Args:
stix_id (str): The STIX ID of the STIX object to be retrieved.
_composite_filters (set): set of filters passed from the parent
CompositeDataSource, not user supplied
allow_custom (bool): whether to retrieve custom objects/properties
or not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -167,7 +178,7 @@ class FileSystemSource(DataSource):
"""
query = [Filter("id", "=", stix_id)]
all_data = self.query(query=query, allow_custom=allow_custom, version=version, _composite_filters=_composite_filters)
all_data = self.query(query=query, version=version, _composite_filters=_composite_filters)
if all_data:
stix_obj = sorted(all_data, key=lambda k: k['modified'])[0]
@ -176,7 +187,7 @@ class FileSystemSource(DataSource):
return stix_obj
def all_versions(self, stix_id, allow_custom=False, version=None, _composite_filters=None):
def all_versions(self, stix_id, version=None, _composite_filters=None):
"""Retrieve STIX object from file directory via STIX ID, all versions.
Note: Since FileSystem sources/sinks don't handle multiple versions
@ -186,8 +197,6 @@ class FileSystemSource(DataSource):
stix_id (str): The STIX ID of the STIX objects to be retrieved.
_composite_filters (set): set of filters passed from the parent
CompositeDataSource, not user supplied
allow_custom (bool): whether to retrieve custom objects/properties
or not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -197,9 +206,9 @@ class FileSystemSource(DataSource):
a python STIX objects and then returned
"""
return [self.get(stix_id=stix_id, allow_custom=allow_custom, version=version, _composite_filters=_composite_filters)]
return [self.get(stix_id=stix_id, version=version, _composite_filters=_composite_filters)]
def query(self, query=None, allow_custom=False, version=None, _composite_filters=None):
def query(self, query=None, version=None, _composite_filters=None):
"""Search and retrieve STIX objects based on the complete query.
A "complete query" includes the filters from the query, the filters
@ -210,8 +219,6 @@ class FileSystemSource(DataSource):
query (list): list of filters to search on
_composite_filters (set): set of filters passed from the
CompositeDataSource, not user supplied
allow_custom (bool): whether to retrieve custom objects/properties
or not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -221,6 +228,7 @@ class FileSystemSource(DataSource):
parsed into a python STIX objects and then returned.
"""
all_data = []
if query is None:
@ -304,7 +312,7 @@ class FileSystemSource(DataSource):
all_data = deduplicate(all_data)
# parse python STIX objects from the STIX object dicts
stix_objs = [parse(stix_obj_dict, allow_custom=allow_custom, version=version) for stix_obj_dict in all_data]
stix_objs = [parse(stix_obj_dict, allow_custom=self.allow_custom, version=version) for stix_obj_dict in all_data]
return stix_objs

View File

@ -21,7 +21,7 @@ from stix2.sources import DataSink, DataSource, DataStore
from stix2.sources.filters import Filter, apply_common_filters
def _add(store, stix_data=None, allow_custom=False, version=None):
def _add(store, stix_data=None, version=None):
"""Add STIX objects to MemoryStore/Sink.
Adds STIX objects to an in-memory dictionary for fast lookup.
@ -29,8 +29,6 @@ def _add(store, stix_data=None, allow_custom=False, version=None):
Args:
stix_data (list OR dict OR STIX object): STIX objects to be added
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -43,28 +41,19 @@ def _add(store, stix_data=None, allow_custom=False, version=None):
if stix_data["type"] == "bundle":
# adding a json bundle - so just grab STIX objects
for stix_obj in stix_data.get("objects", []):
_add(store, stix_obj, allow_custom=allow_custom, version=version)
_add(store, stix_obj, version=version)
else:
# adding a json STIX object
store._data[stix_data["id"]] = stix_data
elif isinstance(stix_data, str):
# adding json encoded string of STIX content
stix_data = parse(stix_data, allow_custom=allow_custom, version=version)
if stix_data["type"] == "bundle":
# recurse on each STIX object in bundle
for stix_obj in stix_data.get("objects", []):
_add(store, stix_obj, allow_custom=allow_custom, version=version)
else:
_add(store, stix_data, allow_custom=allow_custom, version=version)
elif isinstance(stix_data, list):
# STIX objects are in a list- recurse on each object
for stix_obj in stix_data:
_add(store, stix_obj, allow_custom=allow_custom, version=version)
_add(store, stix_obj, version=version)
else:
raise TypeError("stix_data must be a STIX object (or list of), JSON formatted STIX (or list of), or a JSON formatted STIX bundle")
raise TypeError("stix_data expected to be a python-stix2 object (or list of), JSON formatted STIX (or list of),"
" or a JSON formatted STIX bundle. stix_data was of type: " + str(type(stix_data)))
class MemoryStore(DataStore):
@ -78,8 +67,9 @@ class MemoryStore(DataStore):
Args:
stix_data (list OR dict OR STIX object): STIX content to be added
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
allow_custom (bool): whether to allow custom STIX content.
Only applied when export/input functions called, i.e.
load_from_file() and save_to_file(). Defaults to True.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -89,11 +79,11 @@ class MemoryStore(DataStore):
sink (MemorySink): MemorySink
"""
def __init__(self, stix_data=None, allow_custom=False, version=None):
def __init__(self, stix_data=None, allow_custom=True, version=None):
self._data = {}
if stix_data:
_add(self, stix_data, allow_custom=allow_custom, version=version)
_add(self, stix_data, version=version)
super(MemoryStore, self).__init__(
source=MemorySource(stix_data=self._data, allow_custom=allow_custom, version=version, _store=True),
@ -101,31 +91,11 @@ class MemoryStore(DataStore):
)
def save_to_file(self, *args, **kwargs):
"""Write SITX objects from in-memory dictionary to JSON file, as a STIX
Bundle.
Args:
file_path (str): file path to write STIX data to
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
"""
"""See MemorySink.save_to_file() for documentation"""
return self.sink.save_to_file(*args, **kwargs)
def load_from_file(self, *args, **kwargs):
"""Load STIX data from JSON file.
File format is expected to be a single JSON
STIX object or JSON STIX bundle.
Args:
file_path (str): file path to load STIX data from
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
"""
"""See MemorySource.load_from_file() for documentation"""
return self.source.load_from_file(*args, **kwargs)
@ -138,11 +108,12 @@ class MemorySink(DataSink):
Args:
stix_data (dict OR list): valid STIX 2.0 content in
bundle or a list.
_store (bool): if the MemorySink is a part of a DataStore,
_store (bool): whether the MemorySink is a part of a DataStore,
in which case "stix_data" is a direct reference to
shared memory with DataSource. Not user supplied
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
allow_custom (bool): whether to allow custom objects/properties
when exporting STIX content to file.
Default: True.
Attributes:
_data (dict): the in-memory dict that holds STIX objects.
@ -150,25 +121,34 @@ class MemorySink(DataSink):
a MemorySource
"""
def __init__(self, stix_data=None, allow_custom=False, version=None, _store=False):
def __init__(self, stix_data=None, allow_custom=True, version=None, _store=False):
super(MemorySink, self).__init__()
self._data = {}
self.allow_custom = allow_custom
if _store:
self._data = stix_data
elif stix_data:
_add(self, stix_data, allow_custom=allow_custom, version=version)
_add(self, stix_data, version=version)
def add(self, stix_data, allow_custom=False, version=None):
_add(self, stix_data, allow_custom=allow_custom, version=version)
def add(self, stix_data, version=None):
_add(self, stix_data, version=version)
add.__doc__ = _add.__doc__
def save_to_file(self, file_path, allow_custom=False):
def save_to_file(self, file_path):
"""Write SITX objects from in-memory dictionary to JSON file, as a STIX
Bundle.
Args:
file_path (str): file path to write STIX data to
"""
file_path = os.path.abspath(file_path)
if not os.path.exists(os.path.dirname(file_path)):
os.makedirs(os.path.dirname(file_path))
with open(file_path, "w") as f:
f.write(str(Bundle(list(self._data.values()), allow_custom=allow_custom)))
f.write(str(Bundle(list(self._data.values()), allow_custom=self.allow_custom)))
save_to_file.__doc__ = MemoryStore.save_to_file.__doc__
@ -185,8 +165,9 @@ class MemorySource(DataSource):
_store (bool): if the MemorySource is a part of a DataStore,
in which case "stix_data" is a direct reference to shared
memory with DataSink. Not user supplied
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
allow_custom (bool): whether to allow custom objects/properties
when importing STIX content from file.
Default: True.
Attributes:
_data (dict): the in-memory dict that holds STIX objects.
@ -194,14 +175,15 @@ class MemorySource(DataSource):
a MemorySink
"""
def __init__(self, stix_data=None, allow_custom=False, version=None, _store=False):
def __init__(self, stix_data=None, allow_custom=True, version=None, _store=False):
super(MemorySource, self).__init__()
self._data = {}
self.allow_custom = allow_custom
if _store:
self._data = stix_data
elif stix_data:
_add(self, stix_data, allow_custom=allow_custom, version=version)
_add(self, stix_data, version=version)
def get(self, stix_id, _composite_filters=None):
"""Retrieve STIX object from in-memory dict via STIX ID.
@ -257,6 +239,7 @@ class MemorySource(DataSource):
is returned in the same form as it as added
"""
return [self.get(stix_id=stix_id, _composite_filters=_composite_filters)]
def query(self, query=None, _composite_filters=None):
@ -298,15 +281,24 @@ class MemorySource(DataSource):
return all_data
def load_from_file(self, file_path, allow_custom=False, version=None):
""" Load JSON formatted STIX content from file and add to Memory."""
file_path = os.path.abspath(file_path)
def load_from_file(self, file_path, version=None):
"""Load STIX data from JSON file.
# converting the STIX content to JSON encoded string before calling
# _add() so that the STIX content is added as python-stix2 objects
# to the in-memory dict. Otherwise, if you pass a dict to _add(),
# it gets stored as a dict.
stix_data = json.dumps(json.load(open(file_path, "r")))
File format is expected to be a single JSON
STIX object or JSON STIX bundle.
_add(self, stix_data, allow_custom=allow_custom, version=version)
Args:
file_path (str): file path to load STIX data from
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
"""
stix_data = json.load(open(os.path.abspath(file_path), "r"))
if stix_data["type"] == "bundle":
for stix_obj in stix_data["objects"]:
print(stix_obj)
_add(self, stix_data=parse(stix_obj, allow_custom=self.allow_custom, version=stix_data["spec_version"]))
else:
_add(self, stix_data=parse(stix_obj, allow_custom=self.allow_custom, version=version))
load_from_file.__doc__ = MemoryStore.load_from_file.__doc__

View File

@ -19,11 +19,23 @@ class TAXIICollectionStore(DataStore):
Args:
collection (taxii2.Collection): TAXII Collection instance
allow_custom (bool): whether to allow custom STIX content to be
pushed/retrieved. Defaults to True for TAXIICollectionSource
side(retrieving data) and False for TAXIICollectionSink
side(pushing data). However, when parameter is supplied, it will
be applied to both TAXIICollectionSource/Sink.
"""
def __init__(self, collection):
def __init__(self, collection, allow_custom=None):
if not allow_custom:
allow_custom_source = True
allow_custom_sink = False
else:
allow_custom_sink = allow_custom_source = allow_custom
super(TAXIICollectionStore, self).__init__(
source=TAXIICollectionSource(collection),
sink=TAXIICollectionSink(collection)
source=TAXIICollectionSource(collection, allow_custom=allow_custom_source),
sink=TAXIICollectionSink(collection, allow_custom=allow_custom_sink)
)
@ -33,48 +45,49 @@ class TAXIICollectionSink(DataSink):
Args:
collection (taxii2.Collection): TAXII2 Collection instance
allow_custom (bool): Whether to allow custom STIX content to be
added to the TAXIICollectionSink. Default: False
"""
def __init__(self, collection):
def __init__(self, collection, allow_custom=False):
super(TAXIICollectionSink, self).__init__()
self.collection = collection
self.allow_custom = allow_custom
def add(self, stix_data, allow_custom=False, version=None):
def add(self, stix_data, version=None):
"""Add/push STIX content to TAXII Collection endpoint
Args:
stix_data (STIX object OR dict OR str OR list): valid STIX 2.0 content
in a STIX object (or Bundle), STIX onject dict (or Bundle dict), or a STIX 2.0
json encoded string, or list of any of the following
allow_custom (bool): whether to allow custom objects/properties or
not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
"""
if isinstance(stix_data, _STIXBase):
# adding python STIX object
bundle = dict(Bundle(stix_data, allow_custom=allow_custom))
bundle = dict(Bundle(stix_data, allow_custom=self.allow_custom))
elif isinstance(stix_data, dict):
# adding python dict (of either Bundle or STIX obj)
if stix_data["type"] == "bundle":
bundle = stix_data
else:
bundle = dict(Bundle(stix_data, allow_custom=allow_custom))
bundle = dict(Bundle(stix_data, allow_custom=self.allow_custom))
elif isinstance(stix_data, list):
# adding list of something - recurse on each
for obj in stix_data:
self.add(obj, allow_custom=allow_custom, version=version)
self.add(obj, version=version)
elif isinstance(stix_data, str):
# adding json encoded string of STIX content
stix_data = parse(stix_data, allow_custom=allow_custom, version=version)
stix_data = parse(stix_data, allow_custom=self.allow_custom, version=version)
if stix_data["type"] == "bundle":
bundle = dict(stix_data)
else:
bundle = dict(Bundle(stix_data, allow_custom=allow_custom))
bundle = dict(Bundle(stix_data, allow_custom=self.allow_custom))
else:
raise TypeError("stix_data must be as STIX object(or list of),json formatted STIX (or list of), or a json formatted STIX bundle")
@ -88,13 +101,16 @@ class TAXIICollectionSource(DataSource):
Args:
collection (taxii2.Collection): TAXII Collection instance
allow_custom (bool): Whether to allow custom STIX content to be
added to the FileSystemSink. Default: True
"""
def __init__(self, collection):
def __init__(self, collection, allow_custom=True):
super(TAXIICollectionSource, self).__init__()
self.collection = collection
self.allow_custom = allow_custom
def get(self, stix_id, allow_custom=False, version=None, _composite_filters=None):
def get(self, stix_id, version=None, _composite_filters=None):
"""Retrieve STIX object from local/remote STIX Collection
endpoint.
@ -102,8 +118,6 @@ class TAXIICollectionSource(DataSource):
stix_id (str): The STIX ID of the STIX object to be retrieved.
_composite_filters (set): set of filters passed from the parent
CompositeDataSource, not user supplied
allow_custom (bool): whether to retrieve custom objects/properties
or not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -131,7 +145,7 @@ class TAXIICollectionSource(DataSource):
stix_obj = []
if len(stix_obj):
stix_obj = parse(stix_obj[0], allow_custom=allow_custom, version=version)
stix_obj = parse(stix_obj[0], allow_custom=self.allow_custom, version=version)
if stix_obj.id != stix_id:
# check - was added to handle erroneous TAXII servers
stix_obj = None
@ -140,7 +154,7 @@ class TAXIICollectionSource(DataSource):
return stix_obj
def all_versions(self, stix_id, allow_custom=False, version=None, _composite_filters=None):
def all_versions(self, stix_id, version=None, _composite_filters=None):
"""Retrieve STIX object from local/remote TAXII Collection
endpoint, all versions of it
@ -148,8 +162,6 @@ class TAXIICollectionSource(DataSource):
stix_id (str): The STIX ID of the STIX objects to be retrieved.
_composite_filters (set): set of filters passed from the parent
CompositeDataSource, not user supplied
allow_custom (bool): whether to retrieve custom objects/properties
or not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -163,17 +175,17 @@ class TAXIICollectionSource(DataSource):
Filter("match[version]", "=", "all")
]
all_data = self.query(query=query, allow_custom=allow_custom, _composite_filters=_composite_filters)
all_data = self.query(query=query, _composite_filters=_composite_filters)
# parse STIX objects from TAXII returned json
all_data = [parse(stix_obj, allow_custom=allow_custom, version=version) for stix_obj in all_data]
all_data = [parse(stix_obj, allow_custom=self.allow_custom, version=version) for stix_obj in all_data]
# check - was added to handle erroneous TAXII servers
all_data_clean = [stix_obj for stix_obj in all_data if stix_obj.id == stix_id]
return all_data_clean
def query(self, query=None, allow_custom=False, version=None, _composite_filters=None):
def query(self, query=None, version=None, _composite_filters=None):
"""Search and retreive STIX objects based on the complete query
A "complete query" includes the filters from the query, the filters
@ -184,8 +196,6 @@ class TAXIICollectionSource(DataSource):
query (list): list of filters to search on
_composite_filters (set): set of filters passed from the
CompositeDataSource, not user supplied
allow_custom (bool): whether to retrieve custom objects/properties
or not. Default: False.
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
@ -228,7 +238,7 @@ class TAXIICollectionSource(DataSource):
all_data = []
# parse python STIX objects from the STIX object dicts
stix_objs = [parse(stix_obj_dict, allow_custom=allow_custom, version=version) for stix_obj_dict in all_data]
stix_objs = [parse(stix_obj_dict, allow_custom=self.allow_custom, version=version) for stix_obj_dict in all_data]
return stix_objs

View File

@ -340,7 +340,7 @@ def test_filesystem_object_with_custom_property(fs_store):
fs_store.add(camp, True)
camp_r = fs_store.get(camp.id, allow_custom=True)
camp_r = fs_store.get(camp.id)
assert camp_r.id == camp.id
assert camp_r.x_empire == camp.x_empire
@ -352,9 +352,9 @@ def test_filesystem_object_with_custom_property_in_bundle(fs_store):
allow_custom=True)
bundle = Bundle(camp, allow_custom=True)
fs_store.add(bundle, allow_custom=True)
fs_store.add(bundle)
camp_r = fs_store.get(camp.id, allow_custom=True)
camp_r = fs_store.get(camp.id)
assert camp_r.id == camp.id
assert camp_r.x_empire == camp.x_empire
@ -367,9 +367,9 @@ def test_filesystem_custom_object(fs_store):
pass
newobj = NewObj(property1='something')
fs_store.add(newobj, allow_custom=True)
fs_store.add(newobj)
newobj_r = fs_store.get(newobj.id, allow_custom=True)
newobj_r = fs_store.get(newobj.id)
assert newobj_r.id == newobj.id
assert newobj_r.property1 == 'something'

View File

@ -184,64 +184,64 @@ def test_memory_store_save_load_file(mem_store):
shutil.rmtree(os.path.dirname(filename))
# Currently deprecated - removed functionality from Memory API
# def test_memory_store_add_stix_object_str(mem_store):
# # add stix object string
# camp_id = "campaign--111111b6-1112-4fb0-111b-b111107ca70a"
# camp_name = "Aurelius"
# camp_alias = "Purple Robes"
# camp = """{
# "name": "%s",
# "type": "campaign",
# "objective": "German and French Intelligence Services",
# "aliases": ["%s"],
# "id": "%s",
# "created": "2017-05-31T21:31:53.197755Z"
# }""" % (camp_name, camp_alias, camp_id)
#
# mem_store.add(camp)
#
# camp_r = mem_store.get(camp_id)
# assert camp_r["id"] == camp_id
# assert camp_r["name"] == camp_name
# assert camp_alias in camp_r["aliases"]
def test_memory_store_add_stix_object_str(mem_store):
# add stix object string
camp_id = "campaign--111111b6-1112-4fb0-111b-b111107ca70a"
camp_name = "Aurelius"
camp_alias = "Purple Robes"
camp = """{
"name": "%s",
"type": "campaign",
"objective": "German and French Intelligence Services",
"aliases": ["%s"],
"id": "%s",
"created": "2017-05-31T21:31:53.197755Z"
}""" % (camp_name, camp_alias, camp_id)
mem_store.add(camp)
camp_r = mem_store.get(camp_id)
assert camp_r["id"] == camp_id
assert camp_r["name"] == camp_name
assert camp_alias in camp_r["aliases"]
def test_memory_store_add_stix_bundle_str(mem_store):
# add stix bundle string
camp_id = "campaign--133111b6-1112-4fb0-111b-b111107ca70a"
camp_name = "Atilla"
camp_alias = "Huns"
bund = """{
"type": "bundle",
"id": "bundle--112211b6-1112-4fb0-111b-b111107ca70a",
"spec_version": "2.0",
"objects": [
{
"name": "%s",
"type": "campaign",
"objective": "Bulgarian, Albanian and Romanian Intelligence Services",
"aliases": ["%s"],
"id": "%s",
"created": "2017-05-31T21:31:53.197755Z"
}
]
}""" % (camp_name, camp_alias, camp_id)
mem_store.add(bund)
camp_r = mem_store.get(camp_id)
assert camp_r["id"] == camp_id
assert camp_r["name"] == camp_name
assert camp_alias in camp_r["aliases"]
# Currently deprecated - removed functionality from Memory API
# def test_memory_store_add_stix_bundle_str(mem_store):
# # add stix bundle string
# camp_id = "campaign--133111b6-1112-4fb0-111b-b111107ca70a"
# camp_name = "Atilla"
# camp_alias = "Huns"
# bund = """{
# "type": "bundle",
# "id": "bundle--112211b6-1112-4fb0-111b-b111107ca70a",
# "spec_version": "2.0",
# "objects": [
# {
# "name": "%s",
# "type": "campaign",
# "objective": "Bulgarian, Albanian and Romanian Intelligence Services",
# "aliases": ["%s"],
# "id": "%s",
# "created": "2017-05-31T21:31:53.197755Z"
# }
# ]
# }""" % (camp_name, camp_alias, camp_id)
#
# mem_store.add(bund)
#
# camp_r = mem_store.get(camp_id)
# assert camp_r["id"] == camp_id
# assert camp_r["name"] == camp_name
# assert camp_alias in camp_r["aliases"]
def test_memory_store_add_invalid_object(mem_store):
ind = ('indicator', IND1) # tuple isn't valid
with pytest.raises(TypeError) as excinfo:
mem_store.add(ind)
assert 'stix_data must be' in str(excinfo.value)
assert 'a STIX object' in str(excinfo.value)
assert 'stix_data expected to be' in str(excinfo.value)
assert 'a python-stix2 object' in str(excinfo.value)
assert 'JSON formatted STIX' in str(excinfo.value)
assert 'JSON formatted STIX bundle' in str(excinfo.value)

View File

@ -88,11 +88,15 @@ def test_versioning_error_bad_modified_value():
assert excinfo.value.cls == stix2.Campaign
assert excinfo.value.prop_name == "modified"
assert excinfo.value.reason == "The new modified datetime cannot be before the current modified datatime."
assert excinfo.value.reason == "The new modified datetime cannot be before than or equal to the current modified datetime." \
"It cannot be equal, as according to STIX 2 specification, objects that are different " \
"but have the same id and modified timestamp do not have defined consumer behavior."
msg = "Invalid value for {0} '{1}': {2}"
msg = msg.format(stix2.Campaign.__name__, "modified",
"The new modified datetime cannot be before the current modified datatime.")
"The new modified datetime cannot be before than or equal to the current modified datetime."
"It cannot be equal, as according to STIX 2 specification, objects that are different "
"but have the same id and modified timestamp do not have defined consumer behavior.")
assert str(excinfo.value) == msg
@ -153,7 +157,9 @@ def test_versioning_error_dict_bad_modified_value():
assert excinfo.value.cls == dict
assert excinfo.value.prop_name == "modified"
assert excinfo.value.reason == "The new modified datetime cannot be before the current modified datatime."
assert excinfo.value.reason == "The new modified datetime cannot be before than or equal to the current modified datetime." \
"It cannot be equal, as according to STIX 2 specification, objects that are different " \
"but have the same id and modified timestamp do not have defined consumer behavior."
def test_versioning_error_dict_no_modified_value():
@ -206,3 +212,33 @@ def test_revoke_invalid_cls():
stix2.utils.revoke(campaign_v1)
assert 'cannot revoke object of this type' in str(excinfo.value)
def test_remove_custom_stix_property():
mal = stix2.Malware(name="ColePowers",
labels=["rootkit"],
x_custom="armada",
allow_custom=True)
mal_nc = stix2.utils.remove_custom_stix(mal)
assert "x_custom" not in mal_nc
assert stix2.utils.parse_into_datetime(mal["modified"], precision="millisecond") < stix2.utils.parse_into_datetime(mal_nc["modified"],
precision="millisecond")
def test_remove_custom_stix_object():
@stix2.CustomObject("x-animal", [
("species", stix2.properties.StringProperty(required=True)),
("animal_class", stix2.properties.StringProperty()),
])
class Animal(object):
def __init__(self, animal_class=None, **kwargs):
if animal_class and animal_class not in ["mammal", "bird"]:
raise ValueError("Not a recognized class of animal")
animal = Animal(species="lion", animal_class="mammal")
nc = stix2.utils.remove_custom_stix(animal)
assert nc is None

View File

@ -232,9 +232,9 @@ def new_version(data, **kwargs):
new_modified_property = parse_into_datetime(kwargs['modified'], precision='millisecond')
if new_modified_property <= old_modified_property:
raise InvalidValueError(cls, 'modified',
"The new modified datetime cannot be before the current modified datetime. The new modified time can "
"also not be equal to the current modified datetime because if a consumer receives two objects that are "
"different, but have the same id and modified timestamp, it is not defined how the consumer handles the objects.")
"The new modified datetime cannot be before than or equal to the current modified datetime."
"It cannot be equal, as according to STIX 2 specification, objects that are different "
"but have the same id and modified timestamp do not have defined consumer behavior.")
new_obj_inner.update(kwargs)
# Exclude properties with a value of 'None' in case data is not an instance of a _STIXBase subclass
return cls(**{k: v for k, v in new_obj_inner.items() if v is not None})
@ -269,9 +269,12 @@ def remove_custom_stix(stix_obj):
Args:
stix_obj (dict OR python-stix obj): a single python-stix object
or dict of a STIX object
Returns:
A new version of the object with any custom content removed
"""
if stix_obj["type"].startswith("x_"):
if stix_obj["type"].startswith("x-"):
# if entire object is custom, discard
return None