From 31c37a9b122048b38013e7427af54090086c21ae Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Tue, 25 Feb 2020 20:07:47 -0500 Subject: [PATCH 01/20] Changed several *_types properties which were formerly required, to be optional, due to a STIX spec change. Updated unit tests accordingly. --- stix2/test/v21/test_indicator.py | 13 ++----------- stix2/test/v21/test_infrastructure.py | 10 ++-------- stix2/test/v21/test_malware.py | 15 ++++++--------- stix2/v21/sdo.py | 14 +++++++------- 4 files changed, 17 insertions(+), 35 deletions(-) diff --git a/stix2/test/v21/test_indicator.py b/stix2/test/v21/test_indicator.py index 152f253..6965989 100644 --- a/stix2/test/v21/test_indicator.py +++ b/stix2/test/v21/test_indicator.py @@ -14,9 +14,6 @@ EXPECTED_INDICATOR = """{ "id": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7", "created": "2017-01-01T00:00:01.000Z", "modified": "2017-01-01T00:00:01.000Z", - "indicator_types": [ - "malicious-activity" - ], "pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", "pattern_type": "stix", "pattern_version": "2.1", @@ -29,7 +26,6 @@ EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join(""" id='indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7', created='2017-01-01T00:00:01.000Z', modified='2017-01-01T00:00:01.000Z', - indicator_types=['malicious-activity'], pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", pattern_type='stix', pattern_version='2.1', @@ -49,7 +45,6 @@ def test_indicator_with_all_required_properties(): pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", pattern_type="stix", valid_from=epoch, - indicator_types=['malicious-activity'], ) assert ind.revoked is False @@ -103,8 +98,8 @@ def test_indicator_required_properties(): stix2.v21.Indicator() assert excinfo.value.cls == stix2.v21.Indicator - assert excinfo.value.properties == ["indicator_types", "pattern", "pattern_type", "valid_from"] - assert str(excinfo.value) == "No values for required properties for Indicator: (indicator_types, pattern, pattern_type, valid_from)." + assert excinfo.value.properties == ["pattern", "pattern_type", "valid_from"] + assert str(excinfo.value) == "No values for required properties for Indicator: (pattern, pattern_type, valid_from)." def test_indicator_required_property_pattern(): @@ -163,9 +158,6 @@ def test_created_modified_time_are_identical_by_default(): "id": INDICATOR_ID, "created": "2017-01-01T00:00:01Z", "modified": "2017-01-01T00:00:01Z", - "indicator_types": [ - "malicious-activity", - ], "pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", "pattern_type": "stix", "valid_from": "1970-01-01T00:00:01Z", @@ -181,7 +173,6 @@ def test_parse_indicator(data): assert idctr.created == dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc) assert idctr.modified == dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc) assert idctr.valid_from == dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc) - assert idctr.indicator_types[0] == "malicious-activity" assert idctr.pattern == "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']" diff --git a/stix2/test/v21/test_infrastructure.py b/stix2/test/v21/test_infrastructure.py index 30632bb..3e9feb7 100644 --- a/stix2/test/v21/test_infrastructure.py +++ b/stix2/test/v21/test_infrastructure.py @@ -13,10 +13,7 @@ EXPECTED_INFRASTRUCTURE = """{ "id": "infrastructure--3000ae1b-784c-f03d-8abc-0a625b2ff018", "created": "2017-01-01T12:34:56.000Z", "modified": "2017-01-01T12:34:56.000Z", - "name": "Poison Ivy C2", - "infrastructure_types": [ - "command-and-control" - ] + "name": "Poison Ivy C2" }""" @@ -29,7 +26,6 @@ def test_infrastructure_with_all_required_properties(): created=now, modified=now, name="Poison Ivy C2", - infrastructure_types=["command-and-control"], ) assert str(infra) == EXPECTED_INFRASTRUCTURE @@ -76,7 +72,7 @@ def test_infrastructure_required_properties(): stix2.v21.Infrastructure() assert excinfo.value.cls == stix2.v21.Infrastructure - assert excinfo.value.properties == ["infrastructure_types", "name"] + assert excinfo.value.properties == ["name"] def test_infrastructure_required_property_name(): @@ -105,7 +101,6 @@ def test_invalid_kwarg_to_infrastructure(): "id": INFRASTRUCTURE_ID, "created": "2017-01-01T12:34:56.000Z", "modified": "2017-01-01T12:34:56.000Z", - "infrastructure_types": ["command-and-control"], "name": "Poison Ivy C2", }, ], @@ -118,7 +113,6 @@ def test_parse_infrastructure(data): assert infra.id == INFRASTRUCTURE_ID assert infra.created == dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc) assert infra.modified == dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc) - assert infra.infrastructure_types == ['command-and-control'] assert infra.name == 'Poison Ivy C2' diff --git a/stix2/test/v21/test_malware.py b/stix2/test/v21/test_malware.py index 53838c9..98e8d37 100644 --- a/stix2/test/v21/test_malware.py +++ b/stix2/test/v21/test_malware.py @@ -1,4 +1,5 @@ import datetime as dt +import json import re import pytest @@ -16,9 +17,6 @@ EXPECTED_MALWARE = """{ "created": "2016-05-12T08:17:27.000Z", "modified": "2016-05-12T08:17:27.000Z", "name": "Cryptolocker", - "malware_types": [ - "ransomware" - ], "is_family": false }""" @@ -31,7 +29,6 @@ def test_malware_with_all_required_properties(): id=MALWARE_ID, created=now, modified=now, - malware_types=["ransomware"], name="Cryptolocker", is_family=False, ) @@ -80,7 +77,7 @@ def test_malware_required_properties(): stix2.v21.Malware() assert excinfo.value.cls == stix2.v21.Malware - assert excinfo.value.properties == ["is_family", "malware_types"] + assert excinfo.value.properties == ["is_family"] def test_malware_required_property_name(): @@ -116,7 +113,6 @@ def test_invalid_kwarg_to_malware(): "id": MALWARE_ID, "created": "2016-05-12T08:17:27.000Z", "modified": "2016-05-12T08:17:27.000Z", - "malware_types": ["ransomware"], "name": "Cryptolocker", "is_family": False, }, @@ -130,13 +126,14 @@ def test_parse_malware(data): assert mal.id == MALWARE_ID assert mal.created == dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc) assert mal.modified == dt.datetime(2016, 5, 12, 8, 17, 27, tzinfo=pytz.utc) - assert mal.malware_types == ['ransomware'] assert mal.name == 'Cryptolocker' assert not mal.is_family -def test_parse_malware_invalid_labels(): - data = re.compile('\\[.+\\]', re.DOTALL).sub('1', EXPECTED_MALWARE) +def test_parse_malware_invalid_types(): + data = json.loads(EXPECTED_MALWARE) + data["malware_types"] = 1 # Oops, not a list + data = json.dumps(data) with pytest.raises(InvalidValueError) as excinfo: stix2.parse(data) assert "Invalid value for Malware 'malware_types'" in str(excinfo.value) diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 1d97261..0f4a5a7 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -173,7 +173,7 @@ class Identity(STIXDomainObject): ('name', StringProperty(required=True)), ('description', StringProperty()), ('roles', ListProperty(StringProperty)), - ('identity_class', StringProperty(required=True)), + ('identity_class', StringProperty()), ('sectors', ListProperty(StringProperty)), ('contact_information', StringProperty()), ('revoked', BooleanProperty(default=lambda: False)), @@ -202,7 +202,7 @@ class Indicator(STIXDomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty()), ('description', StringProperty()), - ('indicator_types', ListProperty(StringProperty, required=True)), + ('indicator_types', ListProperty(StringProperty)), ('pattern', PatternProperty(required=True)), ('pattern_type', StringProperty(required=True)), ('pattern_version', StringProperty()), @@ -269,7 +269,7 @@ class Infrastructure(STIXDomainObject): ('granular_markings', ListProperty(GranularMarking)), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('infrastructure_types', ListProperty(StringProperty, required=True)), + ('infrastructure_types', ListProperty(StringProperty)), ('aliases', ListProperty(StringProperty)), ('kill_chain_phases', ListProperty(KillChainPhase)), ('first_seen', TimestampProperty()), @@ -454,7 +454,7 @@ class Malware(STIXDomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty()), ('description', StringProperty()), - ('malware_types', ListProperty(StringProperty, required=True)), + ('malware_types', ListProperty(StringProperty)), ('is_family', BooleanProperty(required=True)), ('aliases', ListProperty(StringProperty)), ('kill_chain_phases', ListProperty(KillChainPhase)), @@ -672,7 +672,7 @@ class Report(STIXDomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('report_types', ListProperty(StringProperty, required=True)), + ('report_types', ListProperty(StringProperty)), ('published', TimestampProperty(required=True)), ('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)), ('revoked', BooleanProperty(default=lambda: False)), @@ -701,7 +701,7 @@ class ThreatActor(STIXDomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('threat_actor_types', ListProperty(StringProperty, required=True)), + ('threat_actor_types', ListProperty(StringProperty)), ('aliases', ListProperty(StringProperty)), ('first_seen', TimestampProperty()), ('last_seen', TimestampProperty()), @@ -748,7 +748,7 @@ class Tool(STIXDomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('tool_types', ListProperty(StringProperty, required=True)), + ('tool_types', ListProperty(StringProperty)), ('aliases', ListProperty(StringProperty)), ('kill_chain_phases', ListProperty(KillChainPhase)), ('tool_version', StringProperty()), From 93a8caa09d43c45dd81ecfa8ed2f93e120cece51 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Tue, 25 Feb 2020 20:19:30 -0500 Subject: [PATCH 02/20] Remove unused import --- stix2/test/v21/test_malware.py | 1 - 1 file changed, 1 deletion(-) diff --git a/stix2/test/v21/test_malware.py b/stix2/test/v21/test_malware.py index 98e8d37..a9158cf 100644 --- a/stix2/test/v21/test_malware.py +++ b/stix2/test/v21/test_malware.py @@ -1,6 +1,5 @@ import datetime as dt import json -import re import pytest import pytz From 50eb188190f6b279a72420e7e7332a912e0200ab Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Thu, 27 Feb 2020 16:40:56 -0500 Subject: [PATCH 03/20] Add the "sample_ref" property to malware-analysis SDOs, per: https://github.com/oasis-tcs/cti-stix2/issues/210 --- stix2/test/v21/test_malware_analysis.py | 3 ++- stix2/v21/sdo.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/stix2/test/v21/test_malware_analysis.py b/stix2/test/v21/test_malware_analysis.py index bfb4ff4..b5e65aa 100644 --- a/stix2/test/v21/test_malware_analysis.py +++ b/stix2/test/v21/test_malware_analysis.py @@ -38,7 +38,8 @@ MALWARE_ANALYSIS_JSON = """{ "analysis_sco_refs": [ "file--fc27e371-6c88-4c5c-868a-4dda0e60b167", "url--6f7a74cd-8eb2-4b88-a4da-aa878e50ac2e" - ] + ], + "sample_ref": "email-addr--499a32d7-74c1-4276-ace9-725ac933e243" }""" diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 1d97261..2bc02cc 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -526,6 +526,7 @@ class MalwareAnalysis(STIXDomainObject): ('analysis_ended', TimestampProperty()), ('av_result', StringProperty()), ('analysis_sco_refs', ListProperty(ReferenceProperty(valid_types="SCO", spec_version='2.1'))), + ('sample_ref', ReferenceProperty(valid_types="SCO", spec_version="2.1")), ]) def _check_object_constraints(self): From d2bff4d411efeef9c98de561b528ee95c128b5eb Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Thu, 27 Feb 2020 17:26:04 -0500 Subject: [PATCH 04/20] Update malware-analysis SDO's av_result property: replace it with result and result_name properties. Per: https://github.com/oasis-tcs/cti-stix2/issues/213 --- stix2/test/v21/test_malware_analysis.py | 3 ++- stix2/v21/sdo.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/stix2/test/v21/test_malware_analysis.py b/stix2/test/v21/test_malware_analysis.py index bfb4ff4..94301f2 100644 --- a/stix2/test/v21/test_malware_analysis.py +++ b/stix2/test/v21/test_malware_analysis.py @@ -34,7 +34,8 @@ MALWARE_ANALYSIS_JSON = """{ "submitted": "2018-11-23T06:45:55.747Z", "analysis_started": "2018-11-29T07:30:03.895Z", "analysis_ended": "2018-11-29T08:30:03.895Z", - "av_result": "malicious", + "result_name": "MegaRansom", + "result": "malicious", "analysis_sco_refs": [ "file--fc27e371-6c88-4c5c-868a-4dda0e60b167", "url--6f7a74cd-8eb2-4b88-a4da-aa878e50ac2e" diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 1d97261..2e3cb13 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -524,14 +524,15 @@ class MalwareAnalysis(STIXDomainObject): ('submitted', TimestampProperty()), ('analysis_started', TimestampProperty()), ('analysis_ended', TimestampProperty()), - ('av_result', StringProperty()), + ('result_name', StringProperty()), + ('result', StringProperty()), ('analysis_sco_refs', ListProperty(ReferenceProperty(valid_types="SCO", spec_version='2.1'))), ]) def _check_object_constraints(self): super(MalwareAnalysis, self)._check_object_constraints() - self._check_at_least_one_property(["av_result", "analysis_sco_refs"]) + self._check_at_least_one_property(["result", "analysis_sco_refs"]) class Note(STIXDomainObject): From 4e2b018272b8964b2e6acc5772e1701af43f1434 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Mon, 2 Mar 2020 16:57:18 -0500 Subject: [PATCH 05/20] Add a property to the software SCO, due to STIX spec change. --- stix2/test/v21/test_observed_data.py | 1 + stix2/v21/observables.py | 1 + 2 files changed, 2 insertions(+) diff --git a/stix2/test/v21/test_observed_data.py b/stix2/test/v21/test_observed_data.py index 71bad46..1854e40 100644 --- a/stix2/test/v21/test_observed_data.py +++ b/stix2/test/v21/test_observed_data.py @@ -1300,6 +1300,7 @@ def test_software_example(): s = stix2.v21.Software( name="Word", cpe="cpe:2.3:a:microsoft:word:2000:*:*:*:*:*:*:*", + swid="e2489b43-a436-47fd-9ea5-165ff0316114", version="2002", vendor="Microsoft", ) diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index ed560a6..68115a0 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -760,6 +760,7 @@ class Software(_Observable): ('id', IDProperty(_type, spec_version='2.1')), ('name', StringProperty(required=True)), ('cpe', StringProperty()), + ('swid', StringProperty()), ('languages', ListProperty(StringProperty)), ('vendor', StringProperty()), ('version', StringProperty()), From a5dc514403749d0a718f943a0ba775d2aef7143c Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Wed, 4 Mar 2020 20:55:52 -0500 Subject: [PATCH 06/20] Fix external-references to force hash keys to come from hash-algorithm-ov. --- stix2/test/v21/test_external_reference.py | 10 ++++++++++ stix2/v21/common.py | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/stix2/test/v21/test_external_reference.py b/stix2/test/v21/test_external_reference.py index d192a11..f5a3e23 100644 --- a/stix2/test/v21/test_external_reference.py +++ b/stix2/test/v21/test_external_reference.py @@ -120,3 +120,13 @@ def test_external_reference_source_required(): assert excinfo.value.cls == stix2.v21.ExternalReference assert excinfo.value.properties == ["source_name"] + + +def test_external_reference_bad_hash(): + with pytest.raises(stix2.exceptions.InvalidValueError): + stix2.v21.ExternalReference( + source_name="ACME Threat Intel", + hashes={ + "SHA-123": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ) \ No newline at end of file diff --git a/stix2/v21/common.py b/stix2/v21/common.py index cf3a3b3..ac8daf1 100644 --- a/stix2/v21/common.py +++ b/stix2/v21/common.py @@ -4,6 +4,7 @@ from collections import OrderedDict from ..base import _STIXBase from ..custom import _custom_marking_builder +from ..exceptions import InvalidValueError from ..markings import _MarkingsMixin from ..markings.utils import check_tlp_marking from ..properties import ( @@ -28,10 +29,26 @@ class ExternalReference(_STIXBase): ('external_id', StringProperty()), ]) + # This is hash-algorithm-ov + _LEGAL_HASHES = { + "MD5", "SHA-1", "SHA-256", "SHA-512", "SHA3-256", "SHA3-512", "SSDEEP", + "TLSH", + } + def _check_object_constraints(self): super(ExternalReference, self)._check_object_constraints() self._check_at_least_one_property(['description', 'external_id', 'url']) + if "hashes" in self: + if any( + hash_ not in self._LEGAL_HASHES + for hash_ in self["hashes"] + ): + raise InvalidValueError( + ExternalReference, "hashes", + "Hash algorithm names must be members of hash-algorithm-ov", + ) + class KillChainPhase(_STIXBase): # TODO: Add link From a862b930be44159b2acc273219bf6c32bab6ae90 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Thu, 5 Mar 2020 17:18:32 -0500 Subject: [PATCH 07/20] Add parent_directory_ref as an ID contributing property for the file SCO. --- stix2/v21/observables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index ed560a6..acddf12 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -382,7 +382,7 @@ class File(_Observable): ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), ]) - _id_contributing_properties = ["hashes", "name", "extensions"] + _id_contributing_properties = ["hashes", "name", "parent_directory_ref", "extensions"] def _check_object_constraints(self): super(File, self)._check_object_constraints() From 22f2b241a7ab47954ad814c776a63697bd5567b8 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Thu, 5 Mar 2020 17:38:03 -0500 Subject: [PATCH 08/20] Add a missing required property to fix up an external-reference test. --- stix2/test/v21/test_external_reference.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stix2/test/v21/test_external_reference.py b/stix2/test/v21/test_external_reference.py index f5a3e23..03cf928 100644 --- a/stix2/test/v21/test_external_reference.py +++ b/stix2/test/v21/test_external_reference.py @@ -126,6 +126,7 @@ def test_external_reference_bad_hash(): with pytest.raises(stix2.exceptions.InvalidValueError): stix2.v21.ExternalReference( source_name="ACME Threat Intel", + description="Threat report", hashes={ "SHA-123": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } From e32b074bc968add30da778f9b7a3a6998ce17d05 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Thu, 5 Mar 2020 17:39:35 -0500 Subject: [PATCH 09/20] Fix stylistic issues for pre-commit hooks. --- stix2/test/v21/test_external_reference.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stix2/test/v21/test_external_reference.py b/stix2/test/v21/test_external_reference.py index 03cf928..f347191 100644 --- a/stix2/test/v21/test_external_reference.py +++ b/stix2/test/v21/test_external_reference.py @@ -128,6 +128,6 @@ def test_external_reference_bad_hash(): source_name="ACME Threat Intel", description="Threat report", hashes={ - "SHA-123": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - } - ) \ No newline at end of file + "SHA-123": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + }, + ) From 792cc570d7a1272c4a14ef62952a6f10b7a7d8ad Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Fri, 6 Mar 2020 18:48:40 -0500 Subject: [PATCH 10/20] Change the os_execution_envs property of software SCOs to operating_system_refs, and add a test for it. --- stix2/test/v21/test_malware.py | 19 +++++++++++++++++++ stix2/v21/sdo.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/stix2/test/v21/test_malware.py b/stix2/test/v21/test_malware.py index 53838c9..412d740 100644 --- a/stix2/test/v21/test_malware.py +++ b/stix2/test/v21/test_malware.py @@ -197,3 +197,22 @@ def test_malware_non_family_no_name(): "is_family": False, "malware_types": ["something"], }) + + +def test_malware_with_os_refs(): + software = stix2.parse({ + "type": "software", + "name": "SuperOS", + "spec_version": "2.1", + }) + + malware = stix2.parse({ + "type": "malware", + "id": MALWARE_ID, + "spec_version": "2.1", + "is_family": False, + "malware_types": ["something"], + "operating_system_refs": [software], + }) + + assert malware["operating_system_refs"][0] == software["id"] diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 1d97261..5c87515 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -460,7 +460,7 @@ class Malware(STIXDomainObject): ('kill_chain_phases', ListProperty(KillChainPhase)), ('first_seen', TimestampProperty()), ('last_seen', TimestampProperty()), - ('os_execution_envs', ListProperty(StringProperty)), + ('operating_system_refs', ListProperty(ReferenceProperty(valid_types='software', spec_version='2.1'))), ('architecture_execution_envs', ListProperty(StringProperty)), ('implementation_languages', ListProperty(StringProperty)), ('capabilities', ListProperty(StringProperty)), From d708537b85f99b2b9bb96757929c7fbefc268f6a Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Tue, 10 Mar 2020 20:24:53 -0400 Subject: [PATCH 11/20] Add enforcement of a new STIX 2.1 SCO extension name requirement: that it must end with "-ext". --- stix2/core.py | 58 +++++++++++++++++++++++++++-------- stix2/custom.py | 29 +++++++++--------- stix2/test/v20/test_custom.py | 9 ++---- stix2/test/v21/test_custom.py | 29 ++++++++---------- stix2/utils.py | 1 + 5 files changed, 77 insertions(+), 49 deletions(-) diff --git a/stix2/core.py b/stix2/core.py index 0d1fee5..c4d7628 100644 --- a/stix2/core.py +++ b/stix2/core.py @@ -7,10 +7,10 @@ import re import stix2 -from .base import _STIXBase +from .base import _Observable, _STIXBase from .exceptions import ParseError from .markings import _MarkingsMixin -from .utils import _get_dict +from .utils import SCO21_EXT_REGEX, TYPE_REGEX, _get_dict STIX2_OBJ_MAPS = {} @@ -258,22 +258,54 @@ def _register_observable(new_observable, version=None): OBJ_MAP_OBSERVABLE[new_observable._type] = new_observable -def _register_observable_extension(observable, new_extension, version=None): +def _register_observable_extension( + observable, new_extension, version=stix2.DEFAULT_VERSION +): """Register a custom extension to a STIX Cyber Observable type. Args: - observable: An observable object + observable: An observable class or instance new_extension (class): A class to register in the Observables Extensions map. - version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If - None, use latest version. + version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). + Defaults to the latest supported version. """ - if version: - v = 'v' + version.replace('.', '') - else: - # Use default version (latest) if no version was provided. - v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') + obs_class = observable if isinstance(observable, type) else \ + type(observable) + ext_type = new_extension._type + + if not issubclass(obs_class, _Observable): + raise ValueError("'observable' must be a valid Observable class!") + + if version == "2.0": + if not re.match(TYPE_REGEX, ext_type): + raise ValueError( + "Invalid extension type name '%s': must only contain the " + "characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % + ext_type, + ) + else: # 2.1+ + if not re.match(SCO21_EXT_REGEX, ext_type): + raise ValueError( + "Invalid extension type name '%s': must only contain the " + "characters a-z (lowercase ASCII), 0-9, hyphen (-), and end " + "with '-ext'." % ext_type, + ) + + if len(ext_type) < 3 or len(ext_type) > 250: + raise ValueError( + "Invalid extension type name '%s': must be between 3 and 250" + " characters." % ext_type, + ) + + if not new_extension._properties: + raise ValueError( + "Invalid extension: must define at least one property: " + + ext_type, + ) + + v = 'v' + version.replace('.', '') try: observable_type = observable._type @@ -287,7 +319,7 @@ def _register_observable_extension(observable, new_extension, version=None): EXT_MAP = STIX2_OBJ_MAPS[v]['observable-extensions'] try: - EXT_MAP[observable_type][new_extension._type] = new_extension + EXT_MAP[observable_type][ext_type] = new_extension except KeyError: if observable_type not in OBJ_MAP_OBSERVABLE: raise ValueError( @@ -296,7 +328,7 @@ def _register_observable_extension(observable, new_extension, version=None): % observable_type, ) else: - EXT_MAP[observable_type] = {new_extension._type: new_extension} + EXT_MAP[observable_type] = {ext_type: new_extension} def _collect_stix2_mappings(): diff --git a/stix2/custom.py b/stix2/custom.py index 802fd07..f3c89cf 100644 --- a/stix2/custom.py +++ b/stix2/custom.py @@ -1,6 +1,8 @@ from collections import OrderedDict import re +import six + from .base import _cls_init, _Extension, _Observable, _STIXBase from .core import ( STIXDomainObject, _register_marking, _register_object, @@ -113,24 +115,23 @@ def _custom_observable_builder(cls, type, properties, version, id_contrib_props= def _custom_extension_builder(cls, observable, type, properties, version): - if not observable or not issubclass(observable, _Observable): - raise ValueError("'observable' must be a valid Observable class!") + + try: + prop_dict = OrderedDict(properties) + except TypeError as e: + six.raise_from( + ValueError( + "Extension properties must be dict-like, e.g. a list " + "containing tuples. For example, " + "[('property1', IntegerProperty())]", + ), + e, + ) class _CustomExtension(cls, _Extension): - if not re.match(TYPE_REGEX, type): - raise ValueError( - "Invalid extension type name '%s': must only contain the " - "characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % type, - ) - elif len(type) < 3 or len(type) > 250: - raise ValueError("Invalid extension type name '%s': must be between 3 and 250 characters." % type) - - if not properties or not isinstance(properties, list): - raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]") - _type = type - _properties = OrderedDict(properties) + _properties = prop_dict def __init__(self, **kwargs): _Extension.__init__(self, **kwargs) diff --git a/stix2/test/v20/test_custom.py b/stix2/test/v20/test_custom.py index ce1aac3..b986777 100644 --- a/stix2/test/v20/test_custom.py +++ b/stix2/test/v20/test_custom.py @@ -821,27 +821,24 @@ def test_custom_extension_invalid_type_name(): def test_custom_extension_no_properties(): - with pytest.raises(ValueError) as excinfo: + with pytest.raises(ValueError): @stix2.v20.CustomExtension(stix2.v20.DomainName, 'x-new-ext2', None) class BarExtension(): pass - assert "Must supply a list, containing tuples." in str(excinfo.value) def test_custom_extension_empty_properties(): - with pytest.raises(ValueError) as excinfo: + with pytest.raises(ValueError): @stix2.v20.CustomExtension(stix2.v20.DomainName, 'x-new-ext2', []) class BarExtension(): pass - assert "Must supply a list, containing tuples." in str(excinfo.value) def test_custom_extension_dict_properties(): - with pytest.raises(ValueError) as excinfo: + with pytest.raises(ValueError): @stix2.v20.CustomExtension(stix2.v20.DomainName, 'x-new-ext2', {}) class BarExtension(): pass - assert "Must supply a list, containing tuples." in str(excinfo.value) def test_custom_extension_no_init_1(): diff --git a/stix2/test/v21/test_custom.py b/stix2/test/v21/test_custom.py index b46288d..8b1a38c 100644 --- a/stix2/test/v21/test_custom.py +++ b/stix2/test/v21/test_custom.py @@ -800,7 +800,7 @@ def test_custom_extension_wrong_observable_type(): ) def test_custom_extension_with_list_and_dict_properties_observable_type(data): @stix2.v21.CustomExtension( - stix2.v21.UserAccount, 'some-extension', [ + stix2.v21.UserAccount, 'some-extension-ext', [ ('keys', stix2.properties.ListProperty(stix2.properties.DictionaryProperty, required=True)), ], ) @@ -876,32 +876,29 @@ def test_custom_extension_invalid_type_name(): def test_custom_extension_no_properties(): - with pytest.raises(ValueError) as excinfo: - @stix2.v21.CustomExtension(stix2.v21.DomainName, 'x-new-ext2', None) + with pytest.raises(ValueError): + @stix2.v21.CustomExtension(stix2.v21.DomainName, 'x-new2-ext', None) class BarExtension(): pass - assert "Must supply a list, containing tuples." in str(excinfo.value) def test_custom_extension_empty_properties(): - with pytest.raises(ValueError) as excinfo: - @stix2.v21.CustomExtension(stix2.v21.DomainName, 'x-new-ext2', []) + with pytest.raises(ValueError): + @stix2.v21.CustomExtension(stix2.v21.DomainName, 'x-new2-ext', []) class BarExtension(): pass - assert "Must supply a list, containing tuples." in str(excinfo.value) def test_custom_extension_dict_properties(): - with pytest.raises(ValueError) as excinfo: - @stix2.v21.CustomExtension(stix2.v21.DomainName, 'x-new-ext2', {}) + with pytest.raises(ValueError): + @stix2.v21.CustomExtension(stix2.v21.DomainName, 'x-new2-ext', {}) class BarExtension(): pass - assert "Must supply a list, containing tuples." in str(excinfo.value) def test_custom_extension_no_init_1(): @stix2.v21.CustomExtension( - stix2.v21.DomainName, 'x-new-extension', [ + stix2.v21.DomainName, 'x-new-extension-ext', [ ('property1', stix2.properties.StringProperty(required=True)), ], ) @@ -914,7 +911,7 @@ def test_custom_extension_no_init_1(): def test_custom_extension_no_init_2(): @stix2.v21.CustomExtension( - stix2.v21.DomainName, 'x-new-ext2', [ + stix2.v21.DomainName, 'x-new2-ext', [ ('property1', stix2.properties.StringProperty(required=True)), ], ) @@ -949,14 +946,14 @@ def test_custom_and_spec_extension_mix(): file_obs = stix2.v21.File( name="my_file.dat", extensions={ - "x-custom1": { + "custom1-ext": { "a": 1, "b": 2, }, "ntfs-ext": { "sid": "S-1-whatever", }, - "x-custom2": { + "custom2-ext": { "z": 99.9, "y": False, }, @@ -969,8 +966,8 @@ def test_custom_and_spec_extension_mix(): allow_custom=True, ) - assert file_obs.extensions["x-custom1"] == {"a": 1, "b": 2} - assert file_obs.extensions["x-custom2"] == {"y": False, "z": 99.9} + assert file_obs.extensions["custom1-ext"] == {"a": 1, "b": 2} + assert file_obs.extensions["custom2-ext"] == {"y": False, "z": 99.9} assert file_obs.extensions["ntfs-ext"].sid == "S-1-whatever" assert file_obs.extensions["raster-image-ext"].image_height == 1024 diff --git a/stix2/utils.py b/stix2/utils.py index b23b0e4..7b3b6cf 100644 --- a/stix2/utils.py +++ b/stix2/utils.py @@ -26,6 +26,7 @@ NOW = object() STIX_UNMOD_PROPERTIES = ['created', 'created_by_ref', 'id', 'type'] TYPE_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$' +SCO21_EXT_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-ext$' class STIXdatetime(dt.datetime): From 371bf0b9a445ee9d09308d1685bdd6f5aacb3b46 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Tue, 10 Mar 2020 21:21:53 -0400 Subject: [PATCH 12/20] Add trailing commas for git commit hook... --- stix2/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stix2/core.py b/stix2/core.py index c4d7628..b03e3d7 100644 --- a/stix2/core.py +++ b/stix2/core.py @@ -259,7 +259,7 @@ def _register_observable(new_observable, version=None): def _register_observable_extension( - observable, new_extension, version=stix2.DEFAULT_VERSION + observable, new_extension, version=stix2.DEFAULT_VERSION, ): """Register a custom extension to a STIX Cyber Observable type. From 2472af387b0e94aa9a5de76fea4db53eec465203 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Wed, 11 Mar 2020 15:21:34 -0400 Subject: [PATCH 13/20] Change a SWID tagId in a unit test from a UUID to something more plausible. --- stix2/test/v21/test_observed_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stix2/test/v21/test_observed_data.py b/stix2/test/v21/test_observed_data.py index 1854e40..abcbb7b 100644 --- a/stix2/test/v21/test_observed_data.py +++ b/stix2/test/v21/test_observed_data.py @@ -1300,7 +1300,7 @@ def test_software_example(): s = stix2.v21.Software( name="Word", cpe="cpe:2.3:a:microsoft:word:2000:*:*:*:*:*:*:*", - swid="e2489b43-a436-47fd-9ea5-165ff0316114", + swid="com.acme.rms-ce-v4-1-5-0", version="2002", vendor="Microsoft", ) From 36f70357852d949683cb42b05e255d2197fd13ee Mon Sep 17 00:00:00 2001 From: "Desai, Kartikey H" Date: Wed, 19 Feb 2020 09:34:23 -0500 Subject: [PATCH 14/20] Fixes #338 --- stix2/test/v21/test_course_of_action.py | 34 ++----------------------- stix2/v21/sdo.py | 20 +++++---------- 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/stix2/test/v21/test_course_of_action.py b/stix2/test/v21/test_course_of_action.py index 44079fb..ca8a303 100644 --- a/stix2/test/v21/test_course_of_action.py +++ b/stix2/test/v21/test_course_of_action.py @@ -8,42 +8,23 @@ import stix2.utils COA_WITH_BIN_JSON = """{ "type": "course-of-action", - "spec_version": "2.1", "id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", "created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c", "created": "2016-04-06T20:03:48.000Z", "modified": "2016-04-06T20:03:48.000Z", "name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter", - "description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...", - "action_type": "textual:text/plain", - "os_execution_envs": [ - "a", - "b", - "c" - ], - "action_bin": "aGVsbG8gd29ybGQ=" + "description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ..." }""" COA_WITH_REF_JSON = """{ "type": "course-of-action", - "spec_version": "2.1", "id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", "created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c", "created": "2016-04-06T20:03:48.000Z", "modified": "2016-04-06T20:03:48.000Z", "name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter", - "description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...", - "action_type": "textual:text/plain", - "os_execution_envs": [ - "a", - "b", - "c" - ], - "action_reference": { - "source_name": "a source", - "description": "description of a source" - } + "description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ..." }""" @@ -84,15 +65,4 @@ def test_parse_course_of_action(sdo_json, sdo_dict): assert getattr(coa, attr_name) == cmp_value -def test_course_of_action_constraint(): - with pytest.raises(stix2.exceptions.MutuallyExclusivePropertiesError): - stix2.v21.CourseOfAction( - name="Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter", - action_bin="aGVsbG8gd29ybGQ=", - action_reference=stix2.v21.ExternalReference( - source_name="a source", - description="description of a source", - ), - ) - # TODO: Add other examples diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 1d97261..59ef168 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -13,10 +13,9 @@ from ..exceptions import ( InvalidValueError, PropertyPresenceError, STIXDeprecationWarning, ) from ..properties import ( - BinaryProperty, BooleanProperty, EmbeddedObjectProperty, EnumProperty, - FloatProperty, IDProperty, IntegerProperty, ListProperty, - ObservableProperty, PatternProperty, ReferenceProperty, StringProperty, - TimestampProperty, TypeProperty, + BooleanProperty, EnumProperty, FloatProperty, IDProperty, IntegerProperty, + ListProperty, ObservableProperty, PatternProperty, ReferenceProperty, + StringProperty, TimestampProperty, TypeProperty, ) from ..utils import NOW from .common import ExternalReference, GranularMarking, KillChainPhase @@ -99,23 +98,16 @@ class CourseOfAction(STIXDomainObject): _type = 'course-of-action' _properties = OrderedDict([ ('type', TypeProperty(_type)), - ('spec_version', StringProperty(fixed='2.1')), - ('id', IDProperty(_type, spec_version='2.1')), - ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), + ('id', IDProperty(_type, spec_version='2.0')), + ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('action_type', StringProperty()), - ('os_execution_envs', ListProperty(StringProperty)), - ('action_bin', BinaryProperty()), - ('action_reference', EmbeddedObjectProperty(ExternalReference)), ('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'))), + ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), ('granular_markings', ListProperty(GranularMarking)), ]) From 8885a757cb81943ed01f928280bcf04aea4fdbc6 Mon Sep 17 00:00:00 2001 From: "Desai, Kartikey H" Date: Mon, 24 Feb 2020 12:09:11 -0500 Subject: [PATCH 15/20] Fix properties spec version back to 2.1, and re-adjust tests. Fixes #338 --- stix2/test/v21/test_course_of_action.py | 2 ++ stix2/v21/sdo.py | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/stix2/test/v21/test_course_of_action.py b/stix2/test/v21/test_course_of_action.py index ca8a303..5eea908 100644 --- a/stix2/test/v21/test_course_of_action.py +++ b/stix2/test/v21/test_course_of_action.py @@ -8,6 +8,7 @@ import stix2.utils COA_WITH_BIN_JSON = """{ "type": "course-of-action", + "spec_version": "2.1", "id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", "created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c", "created": "2016-04-06T20:03:48.000Z", @@ -19,6 +20,7 @@ COA_WITH_BIN_JSON = """{ COA_WITH_REF_JSON = """{ "type": "course-of-action", + "spec_version": "2.1", "id": "course-of-action--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", "created_by_ref": "identity--311b2d2d-f010-4473-83ec-1edf84858f4c", "created": "2016-04-06T20:03:48.000Z", diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 59ef168..12a83bd 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -98,8 +98,9 @@ class CourseOfAction(STIXDomainObject): _type = 'course-of-action' _properties = OrderedDict([ ('type', TypeProperty(_type)), - ('id', IDProperty(_type, spec_version='2.0')), - ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), + ('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')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty(required=True)), @@ -107,7 +108,7 @@ class CourseOfAction(STIXDomainObject): ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), ('external_references', ListProperty(ExternalReference)), - ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), + ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), ]) From 82517ae28499a9a1029d2ebacfcb65fd0b7612a0 Mon Sep 17 00:00:00 2001 From: "Desai, Kartikey H" Date: Mon, 24 Feb 2020 12:28:07 -0500 Subject: [PATCH 16/20] Fixes #338 --- stix2/v21/sdo.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 12a83bd..7685401 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -112,14 +112,6 @@ class CourseOfAction(STIXDomainObject): ('granular_markings', ListProperty(GranularMarking)), ]) - def _check_object_constraints(self): - super(CourseOfAction, self)._check_object_constraints() - - self._check_mutually_exclusive_properties( - ["action_bin", "action_reference"], - at_least_one=False, - ) - class Grouping(STIXDomainObject): # TODO: Add link From 3dd9351d38f3026233583c1d79f42e1c8989021d Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Thu, 12 Mar 2020 09:18:23 -0400 Subject: [PATCH 17/20] Bring back lang, confidence for Course of Action --- stix2/v21/sdo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 7685401..be6da3a 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -107,6 +107,8 @@ class CourseOfAction(STIXDomainObject): ('description', StringProperty()), ('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)), From 15316e79333578285b7df9116bbaffacb33f01ae Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Thu, 12 Mar 2020 16:20:32 -0400 Subject: [PATCH 18/20] Added "x-" to SCO extension names in unit tests, to illustrate best practice and follow a spec "should" rule. --- stix2/test/v21/test_custom.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stix2/test/v21/test_custom.py b/stix2/test/v21/test_custom.py index 8b1a38c..1e6f629 100644 --- a/stix2/test/v21/test_custom.py +++ b/stix2/test/v21/test_custom.py @@ -800,7 +800,7 @@ def test_custom_extension_wrong_observable_type(): ) def test_custom_extension_with_list_and_dict_properties_observable_type(data): @stix2.v21.CustomExtension( - stix2.v21.UserAccount, 'some-extension-ext', [ + stix2.v21.UserAccount, 'x-some-extension-ext', [ ('keys', stix2.properties.ListProperty(stix2.properties.DictionaryProperty, required=True)), ], ) @@ -946,14 +946,14 @@ def test_custom_and_spec_extension_mix(): file_obs = stix2.v21.File( name="my_file.dat", extensions={ - "custom1-ext": { + "x-custom1-ext": { "a": 1, "b": 2, }, "ntfs-ext": { "sid": "S-1-whatever", }, - "custom2-ext": { + "x-custom2-ext": { "z": 99.9, "y": False, }, @@ -966,8 +966,8 @@ def test_custom_and_spec_extension_mix(): allow_custom=True, ) - assert file_obs.extensions["custom1-ext"] == {"a": 1, "b": 2} - assert file_obs.extensions["custom2-ext"] == {"y": False, "z": 99.9} + assert file_obs.extensions["x-custom1-ext"] == {"a": 1, "b": 2} + assert file_obs.extensions["x-custom2-ext"] == {"y": False, "z": 99.9} assert file_obs.extensions["ntfs-ext"].sid == "S-1-whatever" assert file_obs.extensions["raster-image-ext"].image_height == 1024 From e8035863b8d7300a2cdba4c0737c49cfadb18bda Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Sat, 21 Mar 2020 23:56:09 -0400 Subject: [PATCH 19/20] Make swid an id-contributing property --- stix2/v21/observables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index 8c9a2a1..88f1ff7 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -770,7 +770,7 @@ class Software(_Observable): ('granular_markings', ListProperty(GranularMarking)), ('defanged', BooleanProperty(default=lambda: False)), ]) - _id_contributing_properties = ["name", "cpe", "vendor", "version"] + _id_contributing_properties = ["name", "cpe", "swid", "vendor", "version"] class URL(_Observable): From 46219bf072da0b4d11014ce62565dc61c68d603f Mon Sep 17 00:00:00 2001 From: Rich Piazza Date: Wed, 25 Mar 2020 11:36:29 -0400 Subject: [PATCH 20/20] add 2.1 links --- stix2/v21/bundle.py | 3 +- stix2/v21/common.py | 21 +++----- stix2/v21/observables.py | 107 +++++++++++++-------------------------- stix2/v21/sdo.py | 54 +++++++------------- stix2/v21/sro.py | 6 +-- 5 files changed, 64 insertions(+), 127 deletions(-) diff --git a/stix2/v21/bundle.py b/stix2/v21/bundle.py index 168771f..574ab48 100644 --- a/stix2/v21/bundle.py +++ b/stix2/v21/bundle.py @@ -9,9 +9,8 @@ from ..properties import ( class Bundle(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'bundle' diff --git a/stix2/v21/common.py b/stix2/v21/common.py index ac8daf1..373a7fc 100644 --- a/stix2/v21/common.py +++ b/stix2/v21/common.py @@ -16,9 +16,8 @@ from ..utils import NOW, _get_dict class ExternalReference(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _properties = OrderedDict([ @@ -51,9 +50,8 @@ class ExternalReference(_STIXBase): class KillChainPhase(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _properties = OrderedDict([ @@ -63,9 +61,8 @@ class KillChainPhase(_STIXBase): class GranularMarking(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _properties = OrderedDict([ @@ -80,9 +77,8 @@ class GranularMarking(_STIXBase): class LanguageContent(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'language-content' @@ -108,9 +104,8 @@ class LanguageContent(_STIXBase): class TLPMarking(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'tlp' @@ -120,9 +115,8 @@ class TLPMarking(_STIXBase): class StatementMarking(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'statement' @@ -151,9 +145,8 @@ class MarkingProperty(Property): class MarkingDefinition(_STIXBase, _MarkingsMixin): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'marking-definition' diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index 88f1ff7..89de0fc 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -22,9 +22,8 @@ from .common import GranularMarking class Artifact(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'artifact' @@ -52,9 +51,8 @@ class Artifact(_Observable): class AutonomousSystem(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'autonomous-system' @@ -74,9 +72,8 @@ class AutonomousSystem(_Observable): class Directory(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'directory' @@ -100,9 +97,8 @@ class Directory(_Observable): class DomainName(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'domain-name' @@ -121,9 +117,8 @@ class DomainName(_Observable): class EmailAddress(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'email-addr' @@ -143,9 +138,8 @@ class EmailAddress(_Observable): class EmailMIMEComponent(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _properties = OrderedDict([ @@ -161,9 +155,8 @@ class EmailMIMEComponent(_STIXBase): class EmailMessage(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'email-message' @@ -202,9 +195,8 @@ class EmailMessage(_Observable): class ArchiveExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'archive-ext' @@ -215,9 +207,8 @@ class ArchiveExt(_Extension): class AlternateDataStream(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _properties = OrderedDict([ @@ -228,9 +219,8 @@ class AlternateDataStream(_STIXBase): class NTFSExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'ntfs-ext' @@ -241,9 +231,8 @@ class NTFSExt(_Extension): class PDFExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'pdf-ext' @@ -257,9 +246,8 @@ class PDFExt(_Extension): class RasterImageExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'raster-image-ext' @@ -272,9 +260,8 @@ class RasterImageExt(_Extension): class WindowsPEOptionalHeaderType(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _properties = OrderedDict([ @@ -317,9 +304,8 @@ class WindowsPEOptionalHeaderType(_STIXBase): class WindowsPESection(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _properties = OrderedDict([ @@ -331,9 +317,8 @@ class WindowsPESection(_STIXBase): class WindowsPEBinaryExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'windows-pebinary-ext' @@ -354,9 +339,8 @@ class WindowsPEBinaryExt(_Extension): class File(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'file' @@ -390,9 +374,8 @@ class File(_Observable): class IPv4Address(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'ipv4-addr' @@ -412,9 +395,8 @@ class IPv4Address(_Observable): class IPv6Address(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'ipv6-addr' @@ -434,9 +416,8 @@ class IPv6Address(_Observable): class MACAddress(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'mac-addr' @@ -454,9 +435,8 @@ class MACAddress(_Observable): class Mutex(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'mutex' @@ -474,9 +454,8 @@ class Mutex(_Observable): class HTTPRequestExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'http-request-ext' @@ -493,7 +472,7 @@ class HTTPRequestExt(_Extension): class ICMPExt(_Extension): # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'icmp-ext' @@ -504,9 +483,8 @@ class ICMPExt(_Extension): class SocketExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'socket-ext' @@ -566,9 +544,8 @@ class SocketExt(_Extension): class TCPExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'tcp-ext' @@ -579,9 +556,8 @@ class TCPExt(_Extension): class NetworkTraffic(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'network-traffic' @@ -635,9 +611,8 @@ class NetworkTraffic(_Observable): class WindowsProcessExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'windows-process-ext' @@ -660,9 +635,8 @@ class WindowsProcessExt(_Extension): class WindowsServiceExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'windows-service-ext' @@ -704,9 +678,8 @@ class WindowsServiceExt(_Extension): class Process(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'process' @@ -749,9 +722,8 @@ class Process(_Observable): class Software(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'software' @@ -774,9 +746,8 @@ class Software(_Observable): class URL(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'url' @@ -794,9 +765,8 @@ class URL(_Observable): class UNIXAccountExt(_Extension): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'unix-account-ext' @@ -809,9 +779,8 @@ class UNIXAccountExt(_Extension): class UserAccount(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'user-account' @@ -842,9 +811,8 @@ class UserAccount(_Observable): class WindowsRegistryValueType(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'windows-registry-value-type' @@ -872,9 +840,8 @@ class WindowsRegistryValueType(_STIXBase): class WindowsRegistryKey(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'windows-registry-key' @@ -897,9 +864,8 @@ class WindowsRegistryKey(_Observable): class X509V3ExtenstionsType(_STIXBase): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'x509-v3-extensions-type' @@ -924,9 +890,8 @@ class X509V3ExtenstionsType(_STIXBase): class X509Certificate(_Observable): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'x509-certificate' diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index f2dc0ea..3253a02 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -22,9 +22,8 @@ from .common import ExternalReference, GranularMarking, KillChainPhase class AttackPattern(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'attack-pattern' @@ -50,9 +49,8 @@ class AttackPattern(STIXDomainObject): class Campaign(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'campaign' @@ -90,9 +88,8 @@ class Campaign(STIXDomainObject): class CourseOfAction(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'course-of-action' @@ -116,9 +113,8 @@ class CourseOfAction(STIXDomainObject): class Grouping(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'grouping' @@ -144,9 +140,8 @@ class Grouping(STIXDomainObject): class Identity(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'identity' @@ -174,9 +169,8 @@ class Identity(STIXDomainObject): class Indicator(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'indicator' @@ -234,9 +228,8 @@ class Indicator(STIXDomainObject): class Infrastructure(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'infrastructure' @@ -275,9 +268,8 @@ class Infrastructure(STIXDomainObject): class IntrusionSet(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'intrusion-set' @@ -318,9 +310,8 @@ class IntrusionSet(STIXDomainObject): class Location(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'location' @@ -426,9 +417,8 @@ class Location(STIXDomainObject): class Malware(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'malware' @@ -479,9 +469,8 @@ class Malware(STIXDomainObject): class MalwareAnalysis(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'malware-analysis' @@ -524,9 +513,8 @@ class MalwareAnalysis(STIXDomainObject): class Note(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'note' @@ -552,9 +540,8 @@ class Note(STIXDomainObject): class ObservedData(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'observed-data' @@ -608,9 +595,8 @@ class ObservedData(STIXDomainObject): class Opinion(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'opinion' @@ -646,9 +632,8 @@ class Opinion(STIXDomainObject): class Report(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'report' @@ -675,9 +660,8 @@ class Report(STIXDomainObject): class ThreatActor(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'threat-actor' @@ -722,9 +706,8 @@ class ThreatActor(STIXDomainObject): class Tool(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'tool' @@ -752,9 +735,8 @@ class Tool(STIXDomainObject): class Vulnerability(STIXDomainObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'vulnerability' diff --git a/stix2/v21/sro.py b/stix2/v21/sro.py index 059bb66..30320b3 100644 --- a/stix2/v21/sro.py +++ b/stix2/v21/sro.py @@ -12,9 +12,8 @@ from .common import ExternalReference, GranularMarking class Relationship(STIXRelationshipObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _invalid_source_target_types = ['bundle', 'language-content', 'marking-definition', 'relationship', 'sighting'] @@ -69,9 +68,8 @@ class Relationship(STIXRelationshipObject): class Sighting(STIXRelationshipObject): - # TODO: Add link """For more detailed information on this object's properties, see - `the STIX 2.1 specification `__. + `the STIX 2.1 specification `__. """ _type = 'sighting'