Extend object serialization options for _STIXBase
parent
8c2af813b2
commit
d6c14139f3
|
@ -153,15 +153,7 @@ class _STIXBase(collections.Mapping):
|
||||||
super(_STIXBase, self).__setattr__(name, value)
|
super(_STIXBase, self).__setattr__(name, value)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
properties = self.object_properties()
|
return self.serialize(pretty=True)
|
||||||
|
|
||||||
def sort_by(element):
|
|
||||||
return find_property_index(self, properties, element)
|
|
||||||
|
|
||||||
# separators kwarg -> don't include spaces after commas.
|
|
||||||
return json.dumps(self, indent=4, cls=STIXJSONEncoder,
|
|
||||||
item_sort_key=sort_by,
|
|
||||||
separators=(",", ": "))
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
props = [(k, self[k]) for k in self.object_properties() if self.get(k)]
|
props = [(k, self[k]) for k in self.object_properties() if self.get(k)]
|
||||||
|
@ -185,6 +177,38 @@ class _STIXBase(collections.Mapping):
|
||||||
def revoke(self):
|
def revoke(self):
|
||||||
return _revoke(self)
|
return _revoke(self)
|
||||||
|
|
||||||
|
def serialize(self, pretty=False, **kwargs):
|
||||||
|
"""
|
||||||
|
Serialize a STIX object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pretty (bool): If True, output properties following the STIX specs
|
||||||
|
formatting. This includes indentation. Refer to notes for more
|
||||||
|
details.
|
||||||
|
**kwargs: The arguments for a json.dumps() call.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The serialized JSON object.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
The argument ``pretty=True`` will output the STIX object following
|
||||||
|
spec order. Using this argument greatly impacts object serialization
|
||||||
|
performance. If your use case is centered across machine-to-machine
|
||||||
|
operation it is recommended to set ``pretty=False``.
|
||||||
|
|
||||||
|
When ``pretty=True`` the following key-value pairs will be added or
|
||||||
|
overridden: indent=4, separators=(",", ": "), item_sort_key=sort_by.
|
||||||
|
"""
|
||||||
|
if pretty:
|
||||||
|
properties = self.object_properties()
|
||||||
|
|
||||||
|
def sort_by(element):
|
||||||
|
return find_property_index(self, properties, element)
|
||||||
|
|
||||||
|
kwargs.update({'indent': 4, 'separators': (",", ": "), 'item_sort_key': sort_by})
|
||||||
|
|
||||||
|
return json.dumps(self, cls=STIXJSONEncoder, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class _Observable(_STIXBase):
|
class _Observable(_STIXBase):
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import stix2
|
import stix2
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_BUNDLE = """{
|
EXPECTED_BUNDLE = """{
|
||||||
"type": "bundle",
|
"type": "bundle",
|
||||||
"id": "bundle--00000000-0000-0000-0000-000000000004",
|
"id": "bundle--00000000-0000-0000-0000-000000000004",
|
||||||
|
@ -41,6 +42,44 @@ EXPECTED_BUNDLE = """{
|
||||||
]
|
]
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
|
EXPECTED_BUNDLE_DICT = {
|
||||||
|
"type": "bundle",
|
||||||
|
"id": "bundle--00000000-0000-0000-0000-000000000004",
|
||||||
|
"spec_version": "2.0",
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"type": "indicator",
|
||||||
|
"id": "indicator--00000000-0000-0000-0000-000000000001",
|
||||||
|
"created": "2017-01-01T12:34:56.000Z",
|
||||||
|
"modified": "2017-01-01T12:34:56.000Z",
|
||||||
|
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||||
|
"valid_from": "2017-01-01T12:34:56Z",
|
||||||
|
"labels": [
|
||||||
|
"malicious-activity"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "malware",
|
||||||
|
"id": "malware--00000000-0000-0000-0000-000000000002",
|
||||||
|
"created": "2017-01-01T12:34:56.000Z",
|
||||||
|
"modified": "2017-01-01T12:34:56.000Z",
|
||||||
|
"name": "Cryptolocker",
|
||||||
|
"labels": [
|
||||||
|
"ransomware"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "relationship",
|
||||||
|
"id": "relationship--00000000-0000-0000-0000-000000000003",
|
||||||
|
"created": "2017-01-01T12:34:56.000Z",
|
||||||
|
"modified": "2017-01-01T12:34:56.000Z",
|
||||||
|
"relationship_type": "indicates",
|
||||||
|
"source_ref": "indicator--01234567-89ab-cdef-0123-456789abcdef",
|
||||||
|
"target_ref": "malware--fedcba98-7654-3210-fedc-ba9876543210"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_empty_bundle():
|
def test_empty_bundle():
|
||||||
bundle = stix2.Bundle()
|
bundle = stix2.Bundle()
|
||||||
|
@ -82,10 +121,18 @@ def test_bundle_with_wrong_spec_version():
|
||||||
assert str(excinfo.value) == "Invalid value for Bundle 'spec_version': must equal '2.0'."
|
assert str(excinfo.value) == "Invalid value for Bundle 'spec_version': must equal '2.0'."
|
||||||
|
|
||||||
|
|
||||||
def test_create_bundle(indicator, malware, relationship):
|
def test_create_bundle1(indicator, malware, relationship):
|
||||||
bundle = stix2.Bundle(objects=[indicator, malware, relationship])
|
bundle = stix2.Bundle(objects=[indicator, malware, relationship])
|
||||||
|
|
||||||
assert str(bundle) == EXPECTED_BUNDLE
|
assert str(bundle) == EXPECTED_BUNDLE
|
||||||
|
assert bundle.serialize(pretty=True) == EXPECTED_BUNDLE
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_bundle2(indicator, malware, relationship):
|
||||||
|
bundle = stix2.Bundle(objects=[indicator, malware, relationship])
|
||||||
|
|
||||||
|
print(repr(bundle))
|
||||||
|
assert json.loads(bundle.serialize()) == EXPECTED_BUNDLE_DICT
|
||||||
|
|
||||||
|
|
||||||
def test_create_bundle_with_positional_args(indicator, malware, relationship):
|
def test_create_bundle_with_positional_args(indicator, malware, relationship):
|
||||||
|
|
Loading…
Reference in New Issue