From a751df32c681e3ddfef92f29b91c071b84e91033 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Tue, 10 Nov 2020 12:55:17 -0500 Subject: [PATCH 01/12] Fix Location semantic equivalence check for Location objects without the latitude and longitude properties (#467) * Fix Location semantic equivalence check for Location objects without the latitude and longitude properties. Uses contribution from @zrush-mitre (#464). Fixes #462. * Remove a line --- stix2/equivalence/object/__init__.py | 7 +++++-- stix2/test/v21/test_environment.py | 26 +++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/stix2/equivalence/object/__init__.py b/stix2/equivalence/object/__init__.py index 8333ceb..ec6cc42 100644 --- a/stix2/equivalence/object/__init__.py +++ b/stix2/equivalence/object/__init__.py @@ -68,7 +68,7 @@ def semantically_equivalent(obj1, obj2, prop_scores={}, **weight_dict): sum_weights = 0.0 for prop in weights[type1]: - if check_property_present(prop, obj1, obj2) or prop == "longitude_latitude": + if check_property_present(prop, obj1, obj2): w = weights[type1][prop][0] comp_funct = weights[type1][prop][1] @@ -117,7 +117,10 @@ def semantically_equivalent(obj1, obj2, prop_scores={}, **weight_dict): def check_property_present(prop, obj1, obj2): """Helper method checks if a property is present on both objects.""" - if prop in obj1 and prop in obj2: + if prop == "longitude_latitude": + if all(x in obj1 and x in obj2 for x in ['latitude', 'longitude']): + return True + elif prop in obj1 and prop in obj2: return True return False diff --git a/stix2/test/v21/test_environment.py b/stix2/test/v21/test_environment.py index 95094fe..0da01d1 100644 --- a/stix2/test/v21/test_environment.py +++ b/stix2/test/v21/test_environment.py @@ -11,9 +11,9 @@ import stix2.exceptions from .constants import ( ATTACK_PATTERN_ID, ATTACK_PATTERN_KWARGS, CAMPAIGN_ID, CAMPAIGN_KWARGS, FAKE_TIME, IDENTITY_ID, IDENTITY_KWARGS, INDICATOR_ID, INDICATOR_KWARGS, - LOCATION_ID, MALWARE_ID, MALWARE_KWARGS, RELATIONSHIP_IDS, REPORT_ID, - REPORT_KWARGS, THREAT_ACTOR_ID, THREAT_ACTOR_KWARGS, TOOL_ID, TOOL_KWARGS, - VULNERABILITY_ID, VULNERABILITY_KWARGS, + LOCATION_ID, LOCATION_KWARGS, MALWARE_ID, MALWARE_KWARGS, RELATIONSHIP_IDS, + REPORT_ID, REPORT_KWARGS, THREAT_ACTOR_ID, THREAT_ACTOR_KWARGS, TOOL_ID, + TOOL_KWARGS, VULNERABILITY_ID, VULNERABILITY_KWARGS, ) FS_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "stix2_data") @@ -495,26 +495,34 @@ def test_semantic_equivalence_on_same_indicator(): def test_semantic_equivalence_on_same_location1(): - LOCATION_KWARGS = dict(latitude=45, longitude=179) - loc1 = stix2.v21.Location(id=LOCATION_ID, **LOCATION_KWARGS) - loc2 = stix2.v21.Location(id=LOCATION_ID, **LOCATION_KWARGS) + location_kwargs = dict(latitude=45, longitude=179) + loc1 = stix2.v21.Location(id=LOCATION_ID, **location_kwargs) + loc2 = stix2.v21.Location(id=LOCATION_ID, **location_kwargs) env = stix2.Environment().semantically_equivalent(loc1, loc2) assert round(env) == 100 def test_semantic_equivalence_on_same_location2(): - LOCATION_KWARGS = dict( + location_kwargs = dict( latitude=38.889, longitude=-77.023, region="northern-america", country="us", ) - loc1 = stix2.v21.Location(id=LOCATION_ID, **LOCATION_KWARGS) - loc2 = stix2.v21.Location(id=LOCATION_ID, **LOCATION_KWARGS) + loc1 = stix2.v21.Location(id=LOCATION_ID, **location_kwargs) + loc2 = stix2.v21.Location(id=LOCATION_ID, **location_kwargs) env = stix2.Environment().semantically_equivalent(loc1, loc2) assert round(env) == 100 +def test_semantic_equivalence_location_with_no_latlong(): + loc_kwargs = dict(country="US", administrative_area="US-DC") + loc1 = stix2.v21.Location(id=LOCATION_ID, **LOCATION_KWARGS) + loc2 = stix2.v21.Location(id=LOCATION_ID, **loc_kwargs) + env = stix2.Environment().semantically_equivalent(loc1, loc2) + assert round(env) != 100 + + def test_semantic_equivalence_on_same_malware(): malw1 = stix2.v21.Malware(id=MALWARE_ID, **MALWARE_KWARGS) malw2 = stix2.v21.Malware(id=MALWARE_ID, **MALWARE_KWARGS) From f5bbbe7a3bc7516ebde254c6fa484936dc802e0a Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Mon, 16 Nov 2020 15:04:24 -0500 Subject: [PATCH 02/12] Fix bug with reference type enforcement on property where_sighted_refs on Sighting. --- stix2/test/v21/test_sighting.py | 10 ++++++---- stix2/v21/sro.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/stix2/test/v21/test_sighting.py b/stix2/test/v21/test_sighting.py index 0493b71..082d46d 100644 --- a/stix2/test/v21/test_sighting.py +++ b/stix2/test/v21/test_sighting.py @@ -5,7 +5,7 @@ import pytz import stix2 -from .constants import IDENTITY_ID, INDICATOR_ID, SIGHTING_ID, SIGHTING_KWARGS +from .constants import IDENTITY_ID, INDICATOR_ID, LOCATION_ID, SIGHTING_ID, SIGHTING_KWARGS EXPECTED_SIGHTING = """{ "type": "sighting", @@ -15,7 +15,8 @@ EXPECTED_SIGHTING = """{ "modified": "2016-04-06T20:06:37.000Z", "sighting_of_ref": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7", "where_sighted_refs": [ - "identity--311b2d2d-f010-4473-83ec-1edf84858f4c" + "identity--311b2d2d-f010-4473-83ec-1edf84858f4c", + "location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64" ] }""" @@ -41,7 +42,7 @@ def test_sighting_all_required_properties(): created=now, modified=now, sighting_of_ref=INDICATOR_ID, - where_sighted_refs=[IDENTITY_ID], + where_sighted_refs=[IDENTITY_ID, LOCATION_ID], ) assert str(s) == EXPECTED_SIGHTING @@ -101,6 +102,7 @@ def test_create_sighting_from_objects_rather_than_ids(malware): # noqa: F811 "type": "sighting", "where_sighted_refs": [ IDENTITY_ID, + LOCATION_ID ], }, ], @@ -114,4 +116,4 @@ def test_parse_sighting(data): assert sighting.created == dt.datetime(2016, 4, 6, 20, 6, 37, tzinfo=pytz.utc) assert sighting.modified == dt.datetime(2016, 4, 6, 20, 6, 37, tzinfo=pytz.utc) assert sighting.sighting_of_ref == INDICATOR_ID - assert sighting.where_sighted_refs == [IDENTITY_ID] + assert sighting.where_sighted_refs == [IDENTITY_ID, LOCATION_ID] diff --git a/stix2/v21/sro.py b/stix2/v21/sro.py index d287373..e6eada6 100644 --- a/stix2/v21/sro.py +++ b/stix2/v21/sro.py @@ -86,7 +86,7 @@ class Sighting(_RelationshipObject): ('count', IntegerProperty(min=0, max=999999999)), ('sighting_of_ref', ReferenceProperty(valid_types="SDO", spec_version='2.1', required=True)), ('observed_data_refs', ListProperty(ReferenceProperty(valid_types='observed-data', spec_version='2.1'))), - ('where_sighted_refs', ListProperty(ReferenceProperty(valid_types='identity', spec_version='2.1'))), + ('where_sighted_refs', ListProperty(ReferenceProperty(valid_types=['identity', 'location'], spec_version='2.1'))), ('summary', BooleanProperty()), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), From 169ef87bc20e1e52a8eeaf31e45e3edb01d7945f Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Mon, 16 Nov 2020 15:10:06 -0500 Subject: [PATCH 03/12] pre-commit stylistic fixes --- stix2/test/v21/test_sighting.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stix2/test/v21/test_sighting.py b/stix2/test/v21/test_sighting.py index 082d46d..0ef5faa 100644 --- a/stix2/test/v21/test_sighting.py +++ b/stix2/test/v21/test_sighting.py @@ -5,7 +5,9 @@ import pytz import stix2 -from .constants import IDENTITY_ID, INDICATOR_ID, LOCATION_ID, SIGHTING_ID, SIGHTING_KWARGS +from .constants import ( + IDENTITY_ID, INDICATOR_ID, LOCATION_ID, SIGHTING_ID, SIGHTING_KWARGS, +) EXPECTED_SIGHTING = """{ "type": "sighting", @@ -102,7 +104,7 @@ def test_create_sighting_from_objects_rather_than_ids(malware): # noqa: F811 "type": "sighting", "where_sighted_refs": [ IDENTITY_ID, - LOCATION_ID + LOCATION_ID, ], }, ], From c9bafaf5ae1c3d58e62a36ebb74327574e6bfc38 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Wed, 18 Nov 2020 18:08:57 -0500 Subject: [PATCH 04/12] fix typo for Class X509V3ExtensionsType --- stix2/v20/__init__.py | 4 ++-- stix2/v20/observables.py | 4 ++-- stix2/v21/__init__.py | 4 ++-- stix2/v21/observables.py | 4 ++-- stix2/workbench.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stix2/v20/__init__.py b/stix2/v20/__init__.py index 36d09be..002343b 100644 --- a/stix2/v20/__init__.py +++ b/stix2/v20/__init__.py @@ -32,7 +32,7 @@ from .observables import ( UNIXAccountExt, UserAccount, WindowsPEBinaryExt, WindowsPEOptionalHeaderType, WindowsPESection, WindowsProcessExt, WindowsRegistryKey, WindowsRegistryValueType, WindowsServiceExt, - X509Certificate, X509V3ExtenstionsType, + X509Certificate, X509V3ExtensionsType, ) from .sdo import ( AttackPattern, Campaign, CourseOfAction, CustomObject, Identity, Indicator, @@ -123,7 +123,7 @@ __all__ = """ UNIXAccountExt, UserAccount, WindowsPEBinaryExt, WindowsPEOptionalHeaderType, WindowsPESection, WindowsProcessExt, WindowsRegistryKey, WindowsRegistryValueType, WindowsServiceExt, - X509Certificate, X509V3ExtenstionsType, + X509Certificate, X509V3ExtensionsType, AttackPattern, Campaign, CourseOfAction, CustomObject, Identity, Indicator, IntrusionSet, Malware, ObservedData, Report, ThreatActor, Tool, diff --git a/stix2/v20/observables.py b/stix2/v20/observables.py index 79d4e79..cec979e 100644 --- a/stix2/v20/observables.py +++ b/stix2/v20/observables.py @@ -724,7 +724,7 @@ class WindowsRegistryKey(_Observable): ]) -class X509V3ExtenstionsType(_STIXBase20): +class X509V3ExtensionsType(_STIXBase20): """For more detailed information on this object's properties, see `the STIX 2.0 specification `__. """ # noqa @@ -770,7 +770,7 @@ class X509Certificate(_Observable): ('subject_public_key_algorithm', StringProperty()), ('subject_public_key_modulus', StringProperty()), ('subject_public_key_exponent', IntegerProperty()), - ('x509_v3_extensions', EmbeddedObjectProperty(type=X509V3ExtenstionsType)), + ('x509_v3_extensions', EmbeddedObjectProperty(type=X509V3ExtensionsType)), ('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)), ]) diff --git a/stix2/v21/__init__.py b/stix2/v21/__init__.py index 77a28ee..15b2fb9 100644 --- a/stix2/v21/__init__.py +++ b/stix2/v21/__init__.py @@ -32,7 +32,7 @@ from .observables import ( UNIXAccountExt, UserAccount, WindowsPEBinaryExt, WindowsPEOptionalHeaderType, WindowsPESection, WindowsProcessExt, WindowsRegistryKey, WindowsRegistryValueType, WindowsServiceExt, - X509Certificate, X509V3ExtenstionsType, + X509Certificate, X509V3ExtensionsType, ) from .sdo import ( AttackPattern, Campaign, CourseOfAction, CustomObject, Grouping, Identity, @@ -131,7 +131,7 @@ __all__ = """ UNIXAccountExt, UserAccount, WindowsPEBinaryExt, WindowsPEOptionalHeaderType, WindowsPESection, WindowsProcessExt, WindowsRegistryKey, WindowsRegistryValueType, WindowsServiceExt, - X509Certificate, X509V3ExtenstionsType, + X509Certificate, X509V3ExtensionsType, AttackPattern, Campaign, CourseOfAction, CustomObject, Grouping, Identity, Indicator, Infrastructure, IntrusionSet, Location, Malware, diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index 55224cd..8413050 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -851,7 +851,7 @@ class WindowsRegistryKey(_Observable): _id_contributing_properties = ["key", "values"] -class X509V3ExtenstionsType(_STIXBase21): +class X509V3ExtensionsType(_STIXBase21): """For more detailed information on this object's properties, see `the STIX 2.1 specification `__. """ @@ -898,7 +898,7 @@ class X509Certificate(_Observable): ('subject_public_key_algorithm', StringProperty()), ('subject_public_key_modulus', StringProperty()), ('subject_public_key_exponent', IntegerProperty()), - ('x509_v3_extensions', EmbeddedObjectProperty(type=X509V3ExtenstionsType)), + ('x509_v3_extensions', EmbeddedObjectProperty(type=X509V3ExtensionsType)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), diff --git a/stix2/workbench.py b/stix2/workbench.py index 3724bdb..ff5d680 100644 --- a/stix2/workbench.py +++ b/stix2/workbench.py @@ -55,7 +55,7 @@ from . import ( # noqa: F401 WindowsPEBinaryExt, WindowsPEOptionalHeaderType, WindowsPESection, WindowsProcessExt, WindowsRegistryKey, WindowsRegistryValueType, WindowsServiceExt, X509Certificate, - X509V3ExtenstionsType + X509V3ExtensionsType ) from .datastore.filters import FilterSet From 49985cc51d291493e1701290ef098f1459ebb913 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Wed, 18 Nov 2020 19:01:12 -0500 Subject: [PATCH 05/12] fix ordering problem with Class definitions --- stix2/test/v21/test_malware_analysis.py | 12 ++--- stix2/v20/common.py | 4 +- stix2/v21/common.py | 8 +-- stix2/v21/observables.py | 72 ++++++++++++------------- stix2/v21/sdo.py | 42 +++++++-------- 5 files changed, 69 insertions(+), 69 deletions(-) diff --git a/stix2/test/v21/test_malware_analysis.py b/stix2/test/v21/test_malware_analysis.py index 22f4171..bf4bbe6 100644 --- a/stix2/test/v21/test_malware_analysis.py +++ b/stix2/test/v21/test_malware_analysis.py @@ -10,13 +10,9 @@ MALWARE_ANALYSIS_JSON = """{ "type": "malware-analysis", "spec_version": "2.1", "id": "malware-analysis--f8afc020-f92f-4906-a971-88ee5882eb46", + "created_by_ref": "identity--e0353ed3-991e-4f71-a332-114c2f10b84f", "created": "2017-11-28T09:44:58.418Z", "modified": "2017-12-31T21:27:49.754Z", - "created_by_ref": "identity--e0353ed3-991e-4f71-a332-114c2f10b84f", - "labels": [ - "label1", - "label2" - ], "product": "Acme Malware Analyzer", "version": "2.5", "host_vm_ref": "software--1bda7336-fe67-469f-a8ca-ab6268b0449b", @@ -40,7 +36,11 @@ MALWARE_ANALYSIS_JSON = """{ "file--fc27e371-6c88-4c5c-868a-4dda0e60b167", "url--6f7a74cd-8eb2-4b88-a4da-aa878e50ac2e" ], - "sample_ref": "email-addr--499a32d7-74c1-4276-ace9-725ac933e243" + "sample_ref": "email-addr--499a32d7-74c1-4276-ace9-725ac933e243", + "labels": [ + "label1", + "label2" + ] }""" diff --git a/stix2/v20/common.py b/stix2/v20/common.py index f2a371e..720f14f 100644 --- a/stix2/v20/common.py +++ b/stix2/v20/common.py @@ -124,11 +124,11 @@ class MarkingDefinition(_STIXBase20, _MarkingsMixin): ('id', IDProperty(_type, spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created', TimestampProperty(default=lambda: NOW)), + ('definition_type', StringProperty(required=True)), + ('definition', MarkingProperty(required=True)), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), ('granular_markings', ListProperty(GranularMarking)), - ('definition_type', StringProperty(required=True)), - ('definition', MarkingProperty(required=True)), ]) def __init__(self, **kwargs): diff --git a/stix2/v21/common.py b/stix2/v21/common.py index 1f228af..2980276 100644 --- a/stix2/v21/common.py +++ b/stix2/v21/common.py @@ -94,7 +94,7 @@ class LanguageContent(_STIXBase21): ('object_modified', TimestampProperty(precision='millisecond')), # TODO: 'contents' https://docs.google.com/document/d/1ShNq4c3e1CkfANmD9O--mdZ5H0O_GLnjN28a_yrEaco/edit#heading=h.cfz5hcantmvx ('contents', DictionaryProperty(spec_version='2.1', required=True)), - ('revoked', BooleanProperty()), + ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), ('confidence', IntegerProperty()), ('external_references', ListProperty(ExternalReference)), @@ -156,12 +156,12 @@ class MarkingDefinition(_STIXBase21, _MarkingsMixin): ('id', IDProperty(_type)), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), - ('external_references', ListProperty(ExternalReference)), - ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), - ('granular_markings', ListProperty(GranularMarking)), ('definition_type', StringProperty(required=True)), ('name', StringProperty()), ('definition', MarkingProperty(required=True)), + ('external_references', ListProperty(ExternalReference)), + ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), + ('granular_markings', ListProperty(GranularMarking)), ]) def __init__(self, **kwargs): diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index 55224cd..7919383 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -28,6 +28,7 @@ class Artifact(_Observable): _type = 'artifact' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('mime_type', StringProperty()), ('payload_bin', BinaryProperty()), @@ -35,11 +36,10 @@ class Artifact(_Observable): ('hashes', HashesProperty(spec_version='2.1')), ('encryption_algorithm', StringProperty()), ('decryption_key', StringProperty()), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["hashes", "payload_bin"] @@ -57,15 +57,15 @@ class AutonomousSystem(_Observable): _type = 'autonomous-system' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('number', IntegerProperty(required=True)), ('name', StringProperty()), ('rir', StringProperty()), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["number"] @@ -78,6 +78,7 @@ class Directory(_Observable): _type = 'directory' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('path', StringProperty(required=True)), ('path_enc', StringProperty()), @@ -86,11 +87,10 @@ class Directory(_Observable): ('mtime', TimestampProperty()), ('atime', TimestampProperty()), ('contains_refs', ListProperty(ReferenceProperty(valid_types=['file', 'directory'], spec_version='2.1'))), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["path"] @@ -103,14 +103,14 @@ class DomainName(_Observable): _type = 'domain-name' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('value', StringProperty(required=True)), ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name'], spec_version='2.1'))), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["value"] @@ -123,15 +123,15 @@ class EmailAddress(_Observable): _type = 'email-addr' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('value', StringProperty(required=True)), ('display_name', StringProperty()), ('belongs_to_ref', ReferenceProperty(valid_types='user-account', spec_version='2.1')), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["value"] @@ -161,6 +161,7 @@ class EmailMessage(_Observable): _type = 'email-message' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('is_multipart', BooleanProperty(required=True)), ('date', TimestampProperty()), @@ -177,11 +178,10 @@ class EmailMessage(_Observable): ('body', StringProperty()), ('body_multipart', ListProperty(EmbeddedObjectProperty(type=EmailMIMEComponent))), ('raw_email_ref', ReferenceProperty(valid_types='artifact', spec_version='2.1')), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["from_ref", "subject", "body"] @@ -345,6 +345,7 @@ class File(_Observable): _type = 'file' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('hashes', HashesProperty(spec_version='2.1')), ('size', IntegerProperty(min=0)), @@ -358,11 +359,10 @@ class File(_Observable): ('parent_directory_ref', ReferenceProperty(valid_types='directory', spec_version='2.1')), ('contains_refs', ListProperty(ReferenceProperty(valid_types=["SCO"], spec_version='2.1'))), ('content_ref', ReferenceProperty(valid_types='artifact', spec_version='2.1')), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["hashes", "name", "parent_directory_ref", "extensions"] @@ -379,15 +379,15 @@ class IPv4Address(_Observable): _type = 'ipv4-addr' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('value', StringProperty(required=True)), ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))), ('belongs_to_refs', ListProperty(ReferenceProperty(valid_types='autonomous-system', spec_version='2.1'))), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["value"] @@ -400,15 +400,15 @@ class IPv6Address(_Observable): _type = 'ipv6-addr' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('value', StringProperty(required=True)), ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))), ('belongs_to_refs', ListProperty(ReferenceProperty(valid_types='autonomous-system', spec_version='2.1'))), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["value"] @@ -421,13 +421,13 @@ class MACAddress(_Observable): _type = 'mac-addr' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('value', StringProperty(required=True)), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["value"] @@ -440,13 +440,13 @@ class Mutex(_Observable): _type = 'mutex' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('name', StringProperty(required=True)), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["name"] @@ -551,6 +551,7 @@ class NetworkTraffic(_Observable): _type = 'network-traffic' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('start', TimestampProperty()), ('end', TimestampProperty()), @@ -569,11 +570,10 @@ class NetworkTraffic(_Observable): ('dst_payload_ref', ReferenceProperty(valid_types='artifact', spec_version='2.1')), ('encapsulates_refs', ListProperty(ReferenceProperty(valid_types='network-traffic', spec_version='2.1'))), ('encapsulated_by_ref', ReferenceProperty(valid_types='network-traffic', spec_version='2.1')), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["start", "src_ref", "dst_ref", "src_port", "dst_port", "protocols"] @@ -673,6 +673,7 @@ class Process(_Observable): _type = 'process' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('is_hidden', BooleanProperty()), ('pid', IntegerProperty()), @@ -686,11 +687,10 @@ class Process(_Observable): ('image_ref', ReferenceProperty(valid_types='file', spec_version='2.1')), ('parent_ref', ReferenceProperty(valid_types='process', spec_version='2.1')), ('child_refs', ListProperty(ReferenceProperty(valid_types='process', spec_version='2.1'))), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = [] @@ -717,6 +717,7 @@ class Software(_Observable): _type = 'software' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('name', StringProperty(required=True)), ('cpe', StringProperty()), @@ -724,11 +725,10 @@ class Software(_Observable): ('languages', ListProperty(StringProperty)), ('vendor', StringProperty()), ('version', StringProperty()), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["name", "cpe", "swid", "vendor", "version"] @@ -741,13 +741,13 @@ class URL(_Observable): _type = 'url' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('value', StringProperty(required=True)), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["value"] @@ -774,6 +774,7 @@ class UserAccount(_Observable): _type = 'user-account' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('user_id', StringProperty()), ('credential', StringProperty()), @@ -789,11 +790,10 @@ class UserAccount(_Observable): ('credential_last_changed', TimestampProperty()), ('account_first_login', TimestampProperty()), ('account_last_login', TimestampProperty()), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["account_type", "user_id", "account_login"] @@ -835,6 +835,7 @@ class WindowsRegistryKey(_Observable): _type = 'windows-registry-key' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('key', StringProperty()), ('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))), @@ -842,11 +843,10 @@ class WindowsRegistryKey(_Observable): ('modified_time', TimestampProperty()), ('creator_user_ref', ReferenceProperty(valid_types='user-account', spec_version='2.1')), ('number_of_subkeys', IntegerProperty()), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["key", "values"] @@ -885,6 +885,7 @@ class X509Certificate(_Observable): _type = 'x509-certificate' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.1')), + ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('is_self_signed', BooleanProperty()), ('hashes', HashesProperty(spec_version='2.1')), @@ -899,11 +900,10 @@ class X509Certificate(_Observable): ('subject_public_key_modulus', StringProperty()), ('subject_public_key_exponent', IntegerProperty()), ('x509_v3_extensions', EmbeddedObjectProperty(type=X509V3ExtenstionsType)), - ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), - ('spec_version', StringProperty(fixed='2.1')), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), + ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ]) _id_contributing_properties = ["hashes", "serial_number"] diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index decbf07..4108001 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -122,9 +122,13 @@ class Grouping(_DomainObject): ('type', TypeProperty(_type, spec_version='2.1')), ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), + ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), - ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), + ('name', StringProperty()), + ('description', StringProperty()), + ('context', StringProperty(required=True)), + ('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), ('confidence', IntegerProperty()), @@ -132,10 +136,6 @@ class Grouping(_DomainObject): ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), - ('name', StringProperty()), - ('description', StringProperty()), - ('context', StringProperty(required=True)), - ('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)), ]) @@ -240,13 +240,6 @@ class Infrastructure(_DomainObject): ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), - ('revoked', BooleanProperty(default=lambda: False)), - ('labels', ListProperty(StringProperty)), - ('confidence', IntegerProperty()), - ('lang', StringProperty()), - ('external_references', ListProperty(ExternalReference)), - ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), - ('granular_markings', ListProperty(GranularMarking)), ('name', StringProperty(required=True)), ('description', StringProperty()), ('infrastructure_types', ListProperty(StringProperty)), @@ -254,6 +247,13 @@ class Infrastructure(_DomainObject): ('kill_chain_phases', ListProperty(KillChainPhase)), ('first_seen', TimestampProperty()), ('last_seen', TimestampProperty()), + ('revoked', BooleanProperty(default=lambda: False)), + ('labels', ListProperty(StringProperty)), + ('confidence', IntegerProperty()), + ('lang', StringProperty()), + ('external_references', ListProperty(ExternalReference)), + ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), + ('granular_markings', ListProperty(GranularMarking)), ]) def _check_object_constraints(self): @@ -478,16 +478,9 @@ class MalwareAnalysis(_DomainObject): ('type', TypeProperty(_type, spec_version='2.1')), ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), + ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), - ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), - ('revoked', BooleanProperty(default=lambda: False)), - ('labels', ListProperty(StringProperty)), - ('confidence', IntegerProperty()), - ('lang', StringProperty()), - ('external_references', ListProperty(ExternalReference)), - ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), - ('granular_markings', ListProperty(GranularMarking)), ('product', StringProperty(required=True)), ('version', StringProperty()), ('host_vm_ref', ReferenceProperty(valid_types='software', spec_version='2.1')), @@ -503,7 +496,14 @@ class MalwareAnalysis(_DomainObject): ('result_name', StringProperty()), ('result', StringProperty()), ('analysis_sco_refs', ListProperty(ReferenceProperty(valid_types="SCO", spec_version='2.1'))), - ('sample_ref', ReferenceProperty(valid_types="SCO", spec_version="2.1")), + ('sample_ref', ReferenceProperty(valid_types="SCO", spec_version='2.1')), + ('revoked', BooleanProperty(default=lambda: False)), + ('labels', ListProperty(StringProperty)), + ('confidence', IntegerProperty()), + ('lang', StringProperty()), + ('external_references', ListProperty(ExternalReference)), + ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), + ('granular_markings', ListProperty(GranularMarking)), ]) def _check_object_constraints(self): From e7242c9ae6a4b6f82f8d8245a6c643f228eca006 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Thu, 19 Nov 2020 08:48:57 -0500 Subject: [PATCH 06/12] forgot one... --- stix2/v21/observables.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index 7919383..616de0e 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -937,6 +937,7 @@ def CustomObservable(type='x-custom-observable', properties=None, id_contrib_pro def wrapper(cls): _properties = list(itertools.chain.from_iterable([ [('type', TypeProperty(type, spec_version='2.1'))], + [('spec_version', StringProperty(fixed='2.1'))], [('id', IDProperty(type, spec_version='2.1'))], properties, [('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=type))], From 53f451b0977f7b3cd5362e20a3c49d10c81271a7 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 20 Nov 2020 09:33:56 -0500 Subject: [PATCH 07/12] Use pattern equivalence in indicators --- stix2/equivalence/object/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stix2/equivalence/object/__init__.py b/stix2/equivalence/object/__init__.py index ec6cc42..0225788 100644 --- a/stix2/equivalence/object/__init__.py +++ b/stix2/equivalence/object/__init__.py @@ -4,6 +4,7 @@ import time from ...datastore import Filter from ...utils import STIXdatetime, parse_into_datetime +from ..pattern import equivalent_patterns logger = logging.getLogger(__name__) @@ -211,8 +212,7 @@ def custom_pattern_based(pattern1, pattern2): float: Number between 0.0 and 1.0 depending on match criteria. """ - logger.warning("Indicator pattern equivalence is not fully defined; will default to zero if not completely identical") - return exact_match(pattern1, pattern2) # TODO: Implement pattern based equivalence + return equivalent_patterns(pattern1, pattern2) def partial_external_reference_based(refs1, refs2): From b55c3bb1dfe076bc97b7fcaf4b45900fb50ca484 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 20 Nov 2020 15:59:55 -0500 Subject: [PATCH 08/12] Fix pattern equivalence doc style for consistency --- stix2/equivalence/pattern/__init__.py | 30 +++-- stix2/equivalence/pattern/compare/__init__.py | 37 +++--- .../equivalence/pattern/compare/comparison.py | 106 ++++++++++++------ .../pattern/compare/observation.py | 9 +- .../pattern/transform/comparison.py | 37 ++++-- .../pattern/transform/observation.py | 34 ++++-- .../equivalence/pattern/transform/specials.py | 25 +++-- 7 files changed, 181 insertions(+), 97 deletions(-) diff --git a/stix2/equivalence/pattern/__init__.py b/stix2/equivalence/pattern/__init__.py index b2e5421..4b80dc2 100644 --- a/stix2/equivalence/pattern/__init__.py +++ b/stix2/equivalence/pattern/__init__.py @@ -30,7 +30,8 @@ def _get_pattern_canonicalizer(): """ Get a canonicalization transformer for STIX patterns. - :return: The transformer + Returns: + The transformer """ # The transformers are either stateless or contain no state which changes @@ -64,11 +65,14 @@ def equivalent_patterns(pattern1, pattern2, stix_version=stix2.DEFAULT_VERSION): """ Determine whether two STIX patterns are semantically equivalent. - :param pattern1: The first STIX pattern - :param pattern2: The second STIX pattern - :param stix_version: The STIX version to use for pattern parsing, as a - string ("2.0", "2.1", etc). Defaults to library-wide default version. - :return: True if the patterns are semantically equivalent; False if not + Args: + pattern1: The first STIX pattern + pattern2: The second STIX pattern + stix_version: The STIX version to use for pattern parsing, as a string + ("2.0", "2.1", etc). Defaults to library-wide default version. + + Returns: + True if the patterns are semantically equivalent; False if not """ patt_ast1 = stix2.pattern_visitor.create_pattern_object( pattern1, version=stix_version, @@ -96,12 +100,14 @@ def find_equivalent_patterns( on an input iterable and is implemented as a generator of matches. So you can "stream" patterns in and matching patterns will be streamed out. - :param search_pattern: A search pattern as a string - :param patterns: An iterable over patterns as strings - :param stix_version: The STIX version to use for pattern parsing, as a - string ("2.0", "2.1", etc). Defaults to library-wide default version. - :return: A generator iterator producing the semantically equivalent - patterns + Args: + search_pattern: A search pattern as a string + patterns: An iterable over patterns as strings + stix_version: The STIX version to use for pattern parsing, as a string + ("2.0", "2.1", etc). Defaults to library-wide default version. + + Returns: + A generator iterator producing the semantically equivalent patterns """ search_pattern_ast = stix2.pattern_visitor.create_pattern_object( search_pattern, version=stix_version, diff --git a/stix2/equivalence/pattern/compare/__init__.py b/stix2/equivalence/pattern/compare/__init__.py index e9d7ec9..8ee3562 100644 --- a/stix2/equivalence/pattern/compare/__init__.py +++ b/stix2/equivalence/pattern/compare/__init__.py @@ -16,9 +16,12 @@ def generic_cmp(value1, value2): Generic comparator of values which uses the builtin '<' and '>' operators. Assumes the values can be compared that way. - :param value1: The first value - :param value2: The second value - :return: -1, 0, or 1 depending on whether value1 is less, equal, or greater + Args: + value1: The first value + value2: The second value + + Returns: + -1, 0, or 1 depending on whether value1 is less, equal, or greater than value2 """ @@ -30,12 +33,15 @@ def iter_lex_cmp(seq1, seq2, cmp): Generic lexicographical compare function, which works on two iterables and a comparator function. - :param seq1: The first iterable - :param seq2: The second iterable - :param cmp: a two-arg callable comparator for values iterated over. It - must behave analogously to this function, returning <0, 0, or >0 to - express the ordering of the two values. - :return: <0 if seq1 < seq2; >0 if seq1 > seq2; 0 if they're equal + Args: + seq1: The first iterable + seq2: The second iterable + cmp: a two-arg callable comparator for values iterated over. It + must behave analogously to this function, returning <0, 0, or >0 to + express the ordering of the two values. + + Returns: + <0 if seq1 < seq2; >0 if seq1 > seq2; 0 if they're equal """ it1 = iter(seq1) @@ -84,11 +90,14 @@ def iter_in(value, seq, cmp): a comparator function. This function checks whether the given value is contained in the given iterable. - :param value: A value - :param seq: An iterable - :param cmp: A 2-arg comparator function which must return 0 if the args - are equal - :return: True if the value is found in the iterable, False if it is not + Args: + value: A value + seq: An iterable + cmp: A 2-arg comparator function which must return 0 if the args + are equal + + Returns: + True if the value is found in the iterable, False if it is not """ result = False for seq_val in seq: diff --git a/stix2/equivalence/pattern/compare/comparison.py b/stix2/equivalence/pattern/compare/comparison.py index e412705..07df36a 100644 --- a/stix2/equivalence/pattern/compare/comparison.py +++ b/stix2/equivalence/pattern/compare/comparison.py @@ -32,9 +32,12 @@ def generic_constant_cmp(const1, const2): Generic comparator for most _Constant instances. They must have a "value" attribute whose value supports the builtin comparison operators. - :param const1: The first _Constant instance - :param const2: The second _Constant instance - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + const1: The first _Constant instance + const2: The second _Constant instance + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ return generic_cmp(const1.value, const2.value) @@ -44,9 +47,12 @@ def bool_cmp(value1, value2): """ Compare two boolean constants. - :param value1: The first BooleanConstant instance - :param value2: The second BooleanConstant instance - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + value1: The first BooleanConstant instance + value2: The second BooleanConstant instance + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ @@ -72,9 +78,12 @@ def hex_cmp(value1, value2): Compare two STIX "hex" values. This decodes to bytes and compares that. It does *not* do a string compare on the hex representations. - :param value1: The first HexConstant - :param value2: The second HexConstant - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + value1: The first HexConstant + value2: The second HexConstant + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ bytes1 = bytes.fromhex(value1.value) @@ -88,9 +97,12 @@ def bin_cmp(value1, value2): Compare two STIX "binary" values. This decodes to bytes and compares that. It does *not* do a string compare on the base64 representations. - :param value1: The first BinaryConstant - :param value2: The second BinaryConstant - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + value1: The first BinaryConstant + value2: The second BinaryConstant + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ bytes1 = base64.standard_b64decode(value1.value) @@ -103,9 +115,12 @@ def list_cmp(value1, value2): """ Compare lists order-insensitively. - :param value1: The first ListConstant - :param value2: The second ListConstant - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + value1: The first ListConstant + value2: The second ListConstant + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ @@ -144,9 +159,12 @@ def object_path_component_cmp(comp1, comp2): Ints and strings compare as usual to each other; ints compare less than strings. - :param comp1: An object path component (string or int) - :param comp2: An object path component (string or int) - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + comp1: An object path component (string or int) + comp2: An object path component (string or int) + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ @@ -172,8 +190,11 @@ def object_path_to_raw_values(path): properties; "*" index steps become that string; and numeric index steps become integers. - :param path: An ObjectPath instance - :return: A generator iterator over the values + Args: + path: An ObjectPath instance + + Returns: + A generator iterator over the values """ for comp in path.property_path: @@ -195,9 +216,12 @@ def object_path_cmp(path1, path2): """ Compare two object paths. - :param path1: The first ObjectPath instance - :param path2: The second ObjectPath instance - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + path1: The first ObjectPath instance + path2: The second ObjectPath instance + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ if path1.object_type_name < path2.object_type_name: @@ -224,9 +248,12 @@ def comparison_operator_cmp(op1, op2): """ Compare two comparison operators. - :param op1: The first comparison operator (a string) - :param op2: The second comparison operator (a string) - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + op1: The first comparison operator (a string) + op2: The second comparison operator (a string) + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ op1_idx = _COMPARISON_OP_ORDER.index(op1) @@ -241,9 +268,12 @@ def constant_cmp(value1, value2): """ Compare two constants. - :param value1: The first _Constant instance - :param value2: The second _Constant instance - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + value1: The first _Constant instance + value2: The second _Constant instance + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ @@ -284,9 +314,12 @@ def simple_comparison_expression_cmp(expr1, expr2): Compare "simple" comparison expressions: those which aren't AND/OR combinations, just comparisons. - :param expr1: first _ComparisonExpression instance - :param expr2: second _ComparisonExpression instance - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + expr1: first _ComparisonExpression instance + expr2: second _ComparisonExpression instance + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ @@ -315,9 +348,12 @@ def comparison_expression_cmp(expr1, expr2): expressions' sub-components. To achieve an order-insensitive comparison, the ASTs must be canonically ordered first. - :param expr1: The first comparison expression - :param expr2: The second comparison expression - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + expr1: The first comparison expression + expr2: The second comparison expression + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ if isinstance(expr1, _ComparisonExpression) \ diff --git a/stix2/equivalence/pattern/compare/observation.py b/stix2/equivalence/pattern/compare/observation.py index 8df9e3f..eff03c0 100644 --- a/stix2/equivalence/pattern/compare/observation.py +++ b/stix2/equivalence/pattern/compare/observation.py @@ -64,9 +64,12 @@ def observation_expression_cmp(expr1, expr2): the expressions' sub-components. To achieve an order-insensitive comparison, the ASTs must be canonically ordered first. - :param expr1: The first observation expression - :param expr2: The second observation expression - :return: <0, 0, or >0 depending on whether the first arg is less, equal or + Args: + expr1: The first observation expression + expr2: The second observation expression + + Returns: + <0, 0, or >0 depending on whether the first arg is less, equal or greater than the second """ type1 = type(expr1) diff --git a/stix2/equivalence/pattern/transform/comparison.py b/stix2/equivalence/pattern/transform/comparison.py index d0f431b..6db1055 100644 --- a/stix2/equivalence/pattern/transform/comparison.py +++ b/stix2/equivalence/pattern/transform/comparison.py @@ -22,13 +22,17 @@ def _dupe_ast(ast): """ Create a duplicate of the given AST. - Note: the comparison expression "leaves", i.e. simple - comparisons are currently not duplicated. I don't think it's necessary as - of this writing; they are never changed. But revisit this if/when - necessary. + Note: + The comparison expression "leaves", i.e. simple + comparisons are currently not duplicated. I don't think it's necessary + as of this writing; they are never changed. But revisit this if/when + necessary. - :param ast: The AST to duplicate - :return: The duplicate AST + Args: + ast: The AST to duplicate + + Returns: + The duplicate AST """ if isinstance(ast, AndBooleanExpression): result = AndBooleanExpression([ @@ -108,8 +112,11 @@ class ComparisonExpressionTransformer(Transformer): Invoke a transformer callback method based on the given ast root node type. - :param ast: The AST - :return: The callback's result + Args: + ast: The AST + + Returns: + The callback's result """ if isinstance(ast, AndBooleanExpression): @@ -153,8 +160,11 @@ class OrderDedupeTransformer( """ Sort/dedupe children. AND and OR can be treated identically. - :param ast: The comparison expression AST - :return: The same AST node, but with sorted children + Args: + ast: The comparison expression AST + + Returns: + The same AST node, but with sorted children """ sorted_children = sorted( ast.operands, key=functools.cmp_to_key(comparison_expression_cmp), @@ -201,8 +211,11 @@ class FlattenTransformer(ComparisonExpressionTransformer): little difference is that we can absorb AND children if we're an AND ourselves; and OR for OR. - :param ast: The comparison expression AST - :return: The same AST node, but with flattened children + Args: + ast: The comparison expression AST + + Returns: + The same AST node, but with flattened children """ changed = False diff --git a/stix2/equivalence/pattern/transform/observation.py b/stix2/equivalence/pattern/transform/observation.py index a8982cf..8e2a4d2 100644 --- a/stix2/equivalence/pattern/transform/observation.py +++ b/stix2/equivalence/pattern/transform/observation.py @@ -38,8 +38,11 @@ def _dupe_ast(ast): observation expressions are currently not duplicated. I don't think it's necessary as of this writing. But revisit this if/when necessary. - :param ast: The AST to duplicate - :return: The duplicate AST + Args: + ast: The AST to duplicate + + Returns: + The duplicate AST """ if isinstance(ast, AndObservationExpression): result = AndObservationExpression([ @@ -160,8 +163,11 @@ class ObservationExpressionTransformer(Transformer): Invoke a transformer callback method based on the given ast root node type. - :param ast: The AST - :return: The callback's result + Args: + ast: The AST + + Returns: + The callback's result """ dispatch_name = self._DISPATCH_NAME_MAP.get(type(ast)) @@ -292,10 +298,12 @@ class AbsorptionTransformer( the right does not "contain" the left. You would need two A's on the right. - :param exprs_containee: The expressions we want to check for containment - :param exprs_container: The expressions acting as the "container" - :return: True if the containee is contained in the container; False if - not + Args: + exprs_containee: The expressions we want to check for containment + exprs_container: The expressions acting as the "container" + + Returns: + True if the containee is contained in the container; False if not """ # make our own list we are free to manipulate without affecting the @@ -336,10 +344,12 @@ class AbsorptionTransformer( in the container (rhs), B follows A, so it "contains" the lhs even though there is other stuff mixed in. - :param exprs_containee: The expressions we want to check for containment - :param exprs_container: The expressions acting as the "container" - :return: True if the containee is contained in the container; False if - not + Args: + exprs_containee: The expressions we want to check for containment + exprs_container: The expressions acting as the "container" + + Returns: + True if the containee is contained in the container; False if not """ ee_iter = iter(exprs_containee) diff --git a/stix2/equivalence/pattern/transform/specials.py b/stix2/equivalence/pattern/transform/specials.py index d3611f3..e0b82f5 100644 --- a/stix2/equivalence/pattern/transform/specials.py +++ b/stix2/equivalence/pattern/transform/specials.py @@ -25,9 +25,12 @@ def _path_is(object_path, path_pattern): index path step; _ANY_KEY matches any key path step, and _ANY matches any path step. - :param object_path: An ObjectPath instance - :param path_pattern: An iterable giving the pattern path steps - :return: True if the path matches the pattern; False if not + Args: + object_path: An ObjectPath instance + path_pattern: An iterable giving the pattern path steps + + Returns: + True if the path matches the pattern; False if not """ path_values = object_path_to_raw_values(object_path) @@ -70,8 +73,9 @@ def _mask_bytes(ip_bytes, prefix_size): Retain the high-order 'prefix_size' bits from ip_bytes, and zero out the remaining low-order bits. This side-effects ip_bytes. - :param ip_bytes: A mutable byte sequence (e.g. a bytearray) - :param prefix_size: An integer prefix size + Args: + ip_bytes: A mutable byte sequence (e.g. a bytearray) + prefix_size: An integer prefix size """ addr_size_bytes = len(ip_bytes) addr_size_bits = 8 * addr_size_bytes @@ -99,8 +103,9 @@ def windows_reg_key(comp_expr): being compared. This enables case-insensitive comparisons between two patterns, for those values. This side-effects the given AST. - :param comp_expr: A _ComparisonExpression object whose type is - windows-registry-key + Args: + comp_expr: A _ComparisonExpression object whose type is + windows-registry-key """ if _path_is(comp_expr.lhs, ("key",)) \ or _path_is(comp_expr.lhs, ("values", _ANY_IDX, "name")): @@ -119,7 +124,8 @@ def ipv4_addr(comp_expr): This side-effects the given AST. - :param comp_expr: A _ComparisonExpression object whose type is ipv4-addr. + Args: + comp_expr: A _ComparisonExpression object whose type is ipv4-addr. """ if _path_is(comp_expr.lhs, ("value",)): value = comp_expr.rhs.value @@ -179,7 +185,8 @@ def ipv6_addr(comp_expr): This side-effects the given AST. - :param comp_expr: A _ComparisonExpression object whose type is ipv6-addr. + Args: + comp_expr: A _ComparisonExpression object whose type is ipv6-addr. """ if _path_is(comp_expr.lhs, ("value",)): value = comp_expr.rhs.value From a19eb54948712f5c10774226ab6f49c696a02bb6 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 20 Nov 2020 17:36:56 -0500 Subject: [PATCH 09/12] Update CHANGELOG for v2.1.0 --- CHANGELOG | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 87d3c77..9c2e65f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,32 @@ CHANGELOG ========= +2.1.0 - 2020-11-20 + +* #337 Switches fuzzywuzzy dependency for rapidfuzz (@maxbachmann) +* #430 Adds ability to mix single objects and lists in the Bundle constructor +* #445, #475 Adds ability to calculate semantic equivalence of indicator patterns +* #449 Adds ability to calculate semantic equivalence of entire graphs of objects +* #427 Fixes protocol_family property on network socket extension +* #436 Fixes pattern visitor to handle expressions with both AND and OR +* #431 Fixes bug when adding custom object to FileSystemSink if the object type + hasn't been registered +* #439 Fixes bug with custom wrapped classes not retaining their name (@maybe-sybr) +* #438 Fixes bug with patterns when the object path contains numeric index steps +* #454 Fixes stix2.patterns.make_constant() to create TimestampConstants +* #455 Fixes bug with AND comparisons in patterns +* #460 Fixes bug when retrieving custom object from TAXIICollectionSource if + the object type hasn't been registered +* #444 Fixes bug so CompositeDataSource and deduplicate() handle unversioned + objects correctly +* #467 Fixes bug in semantic equivalence when Location objects don't have + latitude and longitude properties +* #470 Fixes bug where Sighting's where_sighted_refs property couldn't point to + a Location object +* #473 Fixes typo in name of X509V3ExtensionsType class +* #474 Fixes order of object properties when serialized to match examples from + the STIX specification + 2.0.2 - 2020-07-07 * #423 Fixes issue with six dependency. From 5bddf9321eec47f093b778e213d94ad6c9aee659 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 20 Nov 2020 17:37:58 -0500 Subject: [PATCH 10/12] =?UTF-8?q?Bump=20version:=202.0.2=20=E2=86=92=202.1?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- stix2/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 12d29ce..0114556 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.0.2 +current_version = 2.1.0 commit = True tag = True diff --git a/stix2/version.py b/stix2/version.py index 0309ae2..9aa3f90 100644 --- a/stix2/version.py +++ b/stix2/version.py @@ -1 +1 @@ -__version__ = "2.0.2" +__version__ = "2.1.0" From 20a9bb316c43b7d9faaab686db8d51e5c89416da Mon Sep 17 00:00:00 2001 From: "Desai, Kartikey H" Date: Thu, 10 Dec 2020 22:10:24 -0600 Subject: [PATCH 11/12] Fix #476 --- docs/guide/filesystem.ipynb | 41 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/docs/guide/filesystem.ipynb b/docs/guide/filesystem.ipynb index 845ded4..2e991ba 100644 --- a/docs/guide/filesystem.ipynb +++ b/docs/guide/filesystem.ipynb @@ -66,14 +66,15 @@ "```\n", "stix2_content/\n", " /STIX2 Domain Object type\n", - " STIX2 Domain Object\n", - " STIX2 Domain Object\n", - " .\n", + " /STIX2 Domain Object ID\n", + " STIX2 Domain Object\n", + " /STIX2 Domain Object ID\n", + " STIX2 Domain Object\n", " .\n", " .\n", " /STIX2 Domain Object type\n", - " STIX2 Domain Object\n", - " STIX2 Domain Object\n", + " /STIX2 Domain Object ID\n", + " /STIX2 Domain Object ID\n", " .\n", " .\n", " .\n", @@ -83,25 +84,33 @@ " /STIX2 Domain Object type\n", "```\n", "\n", - "The master STIX 2 content directory contains subdirectories, each of which aligns to a STIX 2 domain object type (i.e. \"attack-pattern\", \"campaign\", \"malware\", etc.). Within each STIX 2 domain object subdirectory are JSON files that are STIX 2 domain objects of the specified type. The name of the json files correspond to the ID of the STIX 2 domain object found within that file. A real example of the FileSystem directory structure:\n", + "The master STIX 2 content directory contains subdirectories, each of which aligns to a STIX 2 domain object type (i.e. \"attack-pattern\", \"campaign\", \"malware\", etc.). Within each STIX 2 domain object type's subdirectory are subfolders containing JSON files that are STIX 2 domain objects of the specified type; the name of each STIX 2 domain object subfolder is the ID of the associated STIX 2 domain object. Inside each STIX 2 domain object subfolder, the name of the contained json file corresponds to the modified timestamp of the STIX 2 domain object found within that file. A real example of the FileSystem directory structure:\n", "\n", "```\n", "stix2_content/\n", " /attack-pattern\n", - " attack-pattern--00d0b012-8a03-410e-95de-5826bf542de6.json\n", - " attack-pattern--0a3ead4e-6d47-4ccb-854c-a6a4f9d96b22.json\n", - " attack-pattern--1b7ba276-eedc-4951-a762-0ceea2c030ec.json\n", + " /attack-pattern--00d0b012-8a03-410e-95de-5826bf542de6\n", + " 20201211035036648071.json\n", + " /attack-pattern--0a3ead4e-6d47-4ccb-854c-a6a4f9d96b22\n", + " 20201210035036648071.json\n", + " /attack-pattern--1b7ba276-eedc-4951-a762-0ceea2c030ec\n", + " 20201111035036648071.json\n", " /campaign\n", " /course-of-action\n", - " course-of-action--2a8de25c-f743-4348-b101-3ee33ab5871b.json\n", - " course-of-action--2c3ce852-06a2-40ee-8fe6-086f6402a739.json\n", + " /course-of-action--2a8de25c-f743-4348-b101-3ee33ab5871b\n", + " 20201011035036648071.json\n", + " /course-of-action--2c3ce852-06a2-40ee-8fe6-086f6402a739\n", + " 20201010035036648071.json\n", " /identity\n", - " identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5.json\n", + " /identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5\n", + " 20201215035036648071.json\n", " /indicator\n", " /intrusion-set\n", " /malware\n", - " malware--1d808f62-cf63-4063-9727-ff6132514c22.json\n", - " malware--2eb9b131-d333-4a48-9eb4-d8dec46c19ee.json\n", + " /malware--1d808f62-cf63-4063-9727-ff6132514c22\n", + " 20201211045036648071.json\n", + " /malware--2eb9b131-d333-4a48-9eb4-d8dec46c19ee\n", + " 20201211035036648072.json\n", " /observed-data\n", " /report\n", " /threat-actor\n", @@ -1408,7 +1417,7 @@ "# add Campaign object to FileSystemSink\n", "fs_sink.add(camp)\n", "\n", - "# can also add STIX objects to FileSystemSink in on call\n", + "# can also add STIX objects to FileSystemSink in one call\n", "fs_sink.add([ind, ind1])" ] } @@ -1429,7 +1438,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.0a6" + "version": "3.6.7" } }, "nbformat": 4, From 71889b173f5b8e8f21d38fedfeacafee0d8c9e30 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 11 Dec 2020 11:28:26 -0500 Subject: [PATCH 12/12] Touch up filesystem docs --- docs/guide/filesystem.ipynb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/guide/filesystem.ipynb b/docs/guide/filesystem.ipynb index 2e991ba..ec09422 100644 --- a/docs/guide/filesystem.ipynb +++ b/docs/guide/filesystem.ipynb @@ -65,26 +65,27 @@ "\n", "```\n", "stix2_content/\n", - " /STIX2 Domain Object type\n", - " /STIX2 Domain Object ID\n", - " STIX2 Domain Object\n", - " /STIX2 Domain Object ID\n", - " STIX2 Domain Object\n", + " STIX2 Domain Object type/\n", + " STIX2 Domain Object ID/\n", + " 'modified' timestamp.json\n", + " 'modified' timestamp.json\n", + " STIX2 Domain Object ID/\n", + " 'modified' timestamp.json\n", " .\n", " .\n", - " /STIX2 Domain Object type\n", - " /STIX2 Domain Object ID\n", - " /STIX2 Domain Object ID\n", + " STIX2 Domain Object type/\n", + " STIX2 Domain Object ID/\n", + " 'modified' timestamp.json\n", " .\n", " .\n", " .\n", " .\n", " .\n", " .\n", - " /STIX2 Domain Object type\n", + " STIX2 Domain Object type/\n", "```\n", "\n", - "The master STIX 2 content directory contains subdirectories, each of which aligns to a STIX 2 domain object type (i.e. \"attack-pattern\", \"campaign\", \"malware\", etc.). Within each STIX 2 domain object type's subdirectory are subfolders containing JSON files that are STIX 2 domain objects of the specified type; the name of each STIX 2 domain object subfolder is the ID of the associated STIX 2 domain object. Inside each STIX 2 domain object subfolder, the name of the contained json file corresponds to the modified timestamp of the STIX 2 domain object found within that file. A real example of the FileSystem directory structure:\n", + "The master STIX 2 content directory contains subdirectories, each of which aligns to a STIX 2 domain object type (i.e. \"attack-pattern\", \"campaign\", \"malware\", etc.). Within each STIX 2 domain object type's subdirectory are further subdirectories containing JSON files that are STIX 2 domain objects of the specified type; the name of each of these subdirectories is the ID of the associated STIX 2 domain object. Inside each of these subdirectories are JSON files, the names of which correspond to the ``modified`` timestamp of the STIX 2 domain object found within that file. A real example of the FileSystem directory structure:\n", "\n", "```\n", "stix2_content/\n",