From 0c47936ee0c7d10727d403758a1f66c4ca6cf2e7 Mon Sep 17 00:00:00 2001 From: clenk Date: Wed, 12 Jul 2017 11:36:15 -0400 Subject: [PATCH 01/10] Create ObjectFactory class currently only supports created_by_ref --- stix2/__init__.py | 1 + stix2/environment.py | 14 ++++++++++++++ stix2/test/constants.py | 6 ++++++ stix2/test/test_environment.py | 16 ++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 stix2/environment.py create mode 100644 stix2/test/test_environment.py diff --git a/stix2/__init__.py b/stix2/__init__.py index 904af9c..90bcf59 100644 --- a/stix2/__init__.py +++ b/stix2/__init__.py @@ -4,6 +4,7 @@ from . import exceptions from .bundle import Bundle +from .environment import ObjectFactory from .observables import (URL, AlternateDataStream, ArchiveExt, Artifact, AutonomousSystem, Directory, DomainName, EmailAddress, EmailMessage, EmailMIMEComponent, File, diff --git a/stix2/environment.py b/stix2/environment.py new file mode 100644 index 0000000..a753ef0 --- /dev/null +++ b/stix2/environment.py @@ -0,0 +1,14 @@ + +class ObjectFactory(object): + + def __init__(self, created_by=None): + self.created_by = created_by + + def create(self, cls=None, **kwargs): + if cls is None: + raise ValueError('No STIX object class provided') + + if self.created_by is not None: + kwargs['created_by_ref'] = self.created_by + + return cls(**kwargs) diff --git a/stix2/test/constants.py b/stix2/test/constants.py index b631d08..958120b 100644 --- a/stix2/test/constants.py +++ b/stix2/test/constants.py @@ -20,6 +20,12 @@ TOOL_ID = "tool--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f" SIGHTING_ID = "sighting--bfbc19db-ec35-4e45-beed-f8bde2a772fb" VULNERABILITY_ID = "vulnerability--0c7b5b88-8ff7-4a4d-aa9d-feb398cd0061" +# Minimum required args for an Identity instance +IDENTITY_KWARGS = dict( + name="John Smith", + identity_class="individual", +) + # Minimum required args for an Indicator instance INDICATOR_KWARGS = dict( labels=['malicious-activity'], diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py new file mode 100644 index 0000000..08989cf --- /dev/null +++ b/stix2/test/test_environment.py @@ -0,0 +1,16 @@ +import stix2 + +from .constants import IDENTITY_ID, IDENTITY_KWARGS, INDICATOR_KWARGS + + +def test_object_factory_created_by_ref_str(): + factory = stix2.ObjectFactory(created_by=IDENTITY_ID) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert ind.created_by_ref == IDENTITY_ID + + +def test_object_factory_created_by_ref_obj(): + id_obj = stix2.Identity(id=IDENTITY_ID, **IDENTITY_KWARGS) + factory = stix2.ObjectFactory(created_by=id_obj) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert ind.created_by_ref == IDENTITY_ID From 659ad3df8982d9105467bcdd8df35d9160b64fc8 Mon Sep 17 00:00:00 2001 From: clenk Date: Wed, 12 Jul 2017 14:44:52 -0400 Subject: [PATCH 02/10] Add object markings to object factory --- stix2/environment.py | 5 ++++- stix2/other.py | 2 +- stix2/properties.py | 2 +- stix2/test/test_environment.py | 14 +++++++++++++ stix2/test/test_markings.py | 37 +++++++++++++++++++++++++++++++++- 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/stix2/environment.py b/stix2/environment.py index a753ef0..e7693d8 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -1,8 +1,9 @@ class ObjectFactory(object): - def __init__(self, created_by=None): + def __init__(self, created_by=None, markings=None): self.created_by = created_by + self.markings = markings def create(self, cls=None, **kwargs): if cls is None: @@ -10,5 +11,7 @@ class ObjectFactory(object): if self.created_by is not None: kwargs['created_by_ref'] = self.created_by + if self.markings is not None: + kwargs['object_marking_refs'] = self.markings return cls(**kwargs) diff --git a/stix2/other.py b/stix2/other.py index 51663b3..cd75745 100644 --- a/stix2/other.py +++ b/stix2/other.py @@ -69,7 +69,7 @@ class MarkingProperty(Property): class MarkingDefinition(_STIXBase): _type = 'marking-definition' _properties = { - 'created': TimestampProperty(default=lambda: NOW, required=True), + 'created': TimestampProperty(default=lambda: NOW), 'external_references': ListProperty(ExternalReference), 'created_by_ref': ReferenceProperty(type="identity"), 'object_marking_refs': ListProperty(ReferenceProperty(type="marking-definition")), diff --git a/stix2/properties.py b/stix2/properties.py index 80e5345..686d1c3 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -102,7 +102,7 @@ class ListProperty(Property): except TypeError: raise ValueError("must be an iterable.") try: - if isinstance(value, basestring): + if isinstance(value, _STIXBase) or isinstance(value, basestring): value = [value] except NameError: if isinstance(value, str): diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py index 08989cf..e6a522f 100644 --- a/stix2/test/test_environment.py +++ b/stix2/test/test_environment.py @@ -14,3 +14,17 @@ def test_object_factory_created_by_ref_obj(): factory = stix2.ObjectFactory(created_by=id_obj) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert ind.created_by_ref == IDENTITY_ID + + +def test_object_factory_obj_markings(): + stmt_marking = stix2.StatementMarking("Copyright 2016, Example Corp") + mark_def = stix2.MarkingDefinition(definition_type="statement", + definition=stmt_marking) + factory = stix2.ObjectFactory(markings=[mark_def, stix2.TLP_AMBER]) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert mark_def.id in ind.object_marking_refs + assert stix2.TLP_AMBER.id in ind.object_marking_refs + + factory = stix2.ObjectFactory(markings=stix2.TLP_RED) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert stix2.TLP_RED.id in ind.object_marking_refs diff --git a/stix2/test/test_markings.py b/stix2/test/test_markings.py index ebfa480..70d67dd 100644 --- a/stix2/test/test_markings.py +++ b/stix2/test/test_markings.py @@ -29,6 +29,19 @@ EXPECTED_STATEMENT_MARKING_DEFINITION = """{ "type": "marking-definition" }""" +EXPECTED_CAMPAIGN_WITH_OBJECT_MARKING = """{ + "created": "2016-04-06T20:03:00.000Z", + "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + "description": "Campaign by Green Group against a series of targets in the financial services sector.", + "id": "campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", + "modified": "2016-04-06T20:03:00.000Z", + "name": "Green Group Attacks Against Finance", + "object_marking_refs": [ + "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9" + ], + "type": "campaign" +}""" + EXPECTED_GRANULAR_MARKING = """{ "marking_ref": "marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9", "selectors": [ @@ -84,6 +97,29 @@ def test_marking_def_example_with_positional_statement(): assert str(marking_definition) == EXPECTED_STATEMENT_MARKING_DEFINITION +def test_marking_def_invalid_type(): + with pytest.raises(ValueError): + stix2.MarkingDefinition( + id="marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9", + created="2017-01-20T00:00:00.000Z", + definition_type="my-definiition-type", + definition=stix2.StatementMarking("Copyright 2016, Example Corp") + ) + + +def test_campaign_with_markings_example(): + campaign = stix2.Campaign( + id="campaign--8e2e2d2b-17d4-4cbf-938f-98ee46b3cd3f", + created_by_ref="identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + created="2016-04-06T20:03:00Z", + modified="2016-04-06T20:03:00Z", + name="Green Group Attacks Against Finance", + description="Campaign by Green Group against a series of targets in the financial services sector.", + object_marking_refs=TLP_WHITE + ) + assert str(campaign) == EXPECTED_CAMPAIGN_WITH_OBJECT_MARKING + + def test_granular_example(): granular_marking = stix2.GranularMarking( marking_ref="marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9", @@ -119,7 +155,6 @@ def test_campaign_with_granular_markings_example(): marking_ref="marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9", selectors=["description"]) ]) - print(str(campaign)) assert str(campaign) == EXPECTED_CAMPAIGN_WITH_GRANULAR_MARKINGS From 593f16662aa27cc0cebc6754658784942edd9db1 Mon Sep 17 00:00:00 2001 From: clenk Date: Wed, 12 Jul 2017 15:22:50 -0400 Subject: [PATCH 03/10] Add granular markings to object factory --- stix2/environment.py | 16 ++++++++-------- stix2/test/test_environment.py | 13 +++++++++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/stix2/environment.py b/stix2/environment.py index e7693d8..055a0d6 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -1,17 +1,17 @@ class ObjectFactory(object): - def __init__(self, created_by=None, markings=None): + def __init__(self, created_by=None, object_markings=None, granular_markings=None): self.created_by = created_by - self.markings = markings - - def create(self, cls=None, **kwargs): - if cls is None: - raise ValueError('No STIX object class provided') + self.object_markings = object_markings + self.granular_markings = granular_markings + def create(self, cls, **kwargs): if self.created_by is not None: kwargs['created_by_ref'] = self.created_by - if self.markings is not None: - kwargs['object_marking_refs'] = self.markings + if self.object_markings is not None: + kwargs['object_marking_refs'] = self.object_markings + if self.granular_markings is not None: + kwargs['granular_markings'] = self.granular_markings return cls(**kwargs) diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py index e6a522f..8cbdbc7 100644 --- a/stix2/test/test_environment.py +++ b/stix2/test/test_environment.py @@ -20,11 +20,20 @@ def test_object_factory_obj_markings(): stmt_marking = stix2.StatementMarking("Copyright 2016, Example Corp") mark_def = stix2.MarkingDefinition(definition_type="statement", definition=stmt_marking) - factory = stix2.ObjectFactory(markings=[mark_def, stix2.TLP_AMBER]) + factory = stix2.ObjectFactory(object_markings=[mark_def, stix2.TLP_AMBER]) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert mark_def.id in ind.object_marking_refs assert stix2.TLP_AMBER.id in ind.object_marking_refs - factory = stix2.ObjectFactory(markings=stix2.TLP_RED) + factory = stix2.ObjectFactory(object_markings=stix2.TLP_RED) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert stix2.TLP_RED.id in ind.object_marking_refs + + +def test_object_factory_granular_markings(): + marking = stix2.GranularMarking(marking_ref=stix2.TLP_AMBER, + selectors="created_by_ref") + factory = stix2.ObjectFactory(created_by=IDENTITY_ID, + granular_markings=marking) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert "created_by_ref" in ind.granular_markings[0].selectors From 6ddad22810ce2c616ae3c137643e5f5bf01c3c76 Mon Sep 17 00:00:00 2001 From: clenk Date: Wed, 12 Jul 2017 16:11:51 -0400 Subject: [PATCH 04/10] Add external references to object factory --- stix2/environment.py | 6 +++++- stix2/test/test_environment.py | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/stix2/environment.py b/stix2/environment.py index 055a0d6..451e4a3 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -1,10 +1,12 @@ class ObjectFactory(object): - def __init__(self, created_by=None, object_markings=None, granular_markings=None): + def __init__(self, created_by=None, object_markings=None, + granular_markings=None, external_references=None): self.created_by = created_by self.object_markings = object_markings self.granular_markings = granular_markings + self.external_references = external_references def create(self, cls, **kwargs): if self.created_by is not None: @@ -13,5 +15,7 @@ class ObjectFactory(object): kwargs['object_marking_refs'] = self.object_markings if self.granular_markings is not None: kwargs['granular_markings'] = self.granular_markings + if self.external_references is not None: + kwargs['external_references'] = self.external_references return cls(**kwargs) diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py index 8cbdbc7..c94a4a1 100644 --- a/stix2/test/test_environment.py +++ b/stix2/test/test_environment.py @@ -37,3 +37,12 @@ def test_object_factory_granular_markings(): granular_markings=marking) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert "created_by_ref" in ind.granular_markings[0].selectors + + +def test_object_factory_external_resource(): + ext_ref = stix2.ExternalReference(source_name="ACME Threat Intel", + description="Threat report") + factory = stix2.ObjectFactory(external_references=ext_ref) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert ind.external_references[0].source_name == "ACME Threat Intel" + assert ind.external_references[0].description == "Threat report" From 4dba41afc8cc5c0d4d1d2d906f12f48796d757b7 Mon Sep 17 00:00:00 2001 From: clenk Date: Thu, 13 Jul 2017 09:45:43 -0400 Subject: [PATCH 05/10] Add created to object factory, clean up code --- stix2/environment.py | 30 +++++++++++------- stix2/test/test_environment.py | 57 +++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/stix2/environment.py b/stix2/environment.py index 451e4a3..d68e7a0 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -1,21 +1,27 @@ class ObjectFactory(object): - def __init__(self, created_by=None, object_markings=None, - granular_markings=None, external_references=None): - self.created_by = created_by - self.object_markings = object_markings - self.granular_markings = granular_markings + def __init__(self, created_by_ref=None, created=None, + external_references=None, object_marking_refs=None, + granular_markings=None): + self.created_by_ref = created_by_ref + self.created = created self.external_references = external_references + self.object_marking_refs = object_marking_refs + self.granular_markings = granular_markings def create(self, cls, **kwargs): - if self.created_by is not None: - kwargs['created_by_ref'] = self.created_by - if self.object_markings is not None: - kwargs['object_marking_refs'] = self.object_markings - if self.granular_markings is not None: - kwargs['granular_markings'] = self.granular_markings - if self.external_references is not None: + if 'created_by_ref' not in kwargs and self.created_by_ref is not None: + kwargs['created_by_ref'] = self.created_by_ref + if 'created' not in kwargs and self.created is not None: + kwargs['created'] = self.created + if 'modified' not in kwargs: + kwargs['modified'] = self.created + if 'external_references' not in kwargs and self.external_references is not None: kwargs['external_references'] = self.external_references + if 'object_marking_refs' not in kwargs and self.object_marking_refs is not None: + kwargs['object_marking_refs'] = self.object_marking_refs + if 'granular_markings' not in kwargs and self.granular_markings is not None: + kwargs['granular_markings'] = self.granular_markings return cls(**kwargs) diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py index c94a4a1..72990e5 100644 --- a/stix2/test/test_environment.py +++ b/stix2/test/test_environment.py @@ -1,42 +1,34 @@ import stix2 -from .constants import IDENTITY_ID, IDENTITY_KWARGS, INDICATOR_KWARGS +from .constants import (FAKE_TIME, IDENTITY_ID, IDENTITY_KWARGS, + INDICATOR_KWARGS) def test_object_factory_created_by_ref_str(): - factory = stix2.ObjectFactory(created_by=IDENTITY_ID) + factory = stix2.ObjectFactory(created_by_ref=IDENTITY_ID) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert ind.created_by_ref == IDENTITY_ID def test_object_factory_created_by_ref_obj(): id_obj = stix2.Identity(id=IDENTITY_ID, **IDENTITY_KWARGS) - factory = stix2.ObjectFactory(created_by=id_obj) + factory = stix2.ObjectFactory(created_by_ref=id_obj) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert ind.created_by_ref == IDENTITY_ID -def test_object_factory_obj_markings(): - stmt_marking = stix2.StatementMarking("Copyright 2016, Example Corp") - mark_def = stix2.MarkingDefinition(definition_type="statement", - definition=stmt_marking) - factory = stix2.ObjectFactory(object_markings=[mark_def, stix2.TLP_AMBER]) - ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) - assert mark_def.id in ind.object_marking_refs - assert stix2.TLP_AMBER.id in ind.object_marking_refs - - factory = stix2.ObjectFactory(object_markings=stix2.TLP_RED) - ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) - assert stix2.TLP_RED.id in ind.object_marking_refs +def test_object_factory_override_default(): + factory = stix2.ObjectFactory(created_by_ref=IDENTITY_ID) + new_id = "identity--983b3172-44fe-4a80-8091-eb8098841fe8" + ind = factory.create(stix2.Indicator, created_by_ref=new_id, **INDICATOR_KWARGS) + assert ind.created_by_ref == new_id -def test_object_factory_granular_markings(): - marking = stix2.GranularMarking(marking_ref=stix2.TLP_AMBER, - selectors="created_by_ref") - factory = stix2.ObjectFactory(created_by=IDENTITY_ID, - granular_markings=marking) +def test_object_factory_created(): + factory = stix2.ObjectFactory(created=FAKE_TIME) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) - assert "created_by_ref" in ind.granular_markings[0].selectors + assert ind.created == FAKE_TIME + assert ind.modified == FAKE_TIME def test_object_factory_external_resource(): @@ -46,3 +38,26 @@ def test_object_factory_external_resource(): ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert ind.external_references[0].source_name == "ACME Threat Intel" assert ind.external_references[0].description == "Threat report" + + +def test_object_factory_obj_markings(): + stmt_marking = stix2.StatementMarking("Copyright 2016, Example Corp") + mark_def = stix2.MarkingDefinition(definition_type="statement", + definition=stmt_marking) + factory = stix2.ObjectFactory(object_marking_refs=[mark_def, stix2.TLP_AMBER]) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert mark_def.id in ind.object_marking_refs + assert stix2.TLP_AMBER.id in ind.object_marking_refs + + factory = stix2.ObjectFactory(object_marking_refs=stix2.TLP_RED) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert stix2.TLP_RED.id in ind.object_marking_refs + + +def test_object_factory_granular_markings(): + marking = stix2.GranularMarking(marking_ref=stix2.TLP_AMBER, + selectors="created_by_ref") + factory = stix2.ObjectFactory(created_by_ref=IDENTITY_ID, + granular_markings=marking) + ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) + assert "created_by_ref" in ind.granular_markings[0].selectors From c29acfa3ce224650e6df6b1eba037c5a47888663 Mon Sep 17 00:00:00 2001 From: Greg Back Date: Thu, 13 Jul 2017 15:03:31 +0000 Subject: [PATCH 06/10] Alternate implementation of ObjectFactory. --- stix2/environment.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/stix2/environment.py b/stix2/environment.py index d68e7a0..dfd8a03 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -4,24 +4,27 @@ class ObjectFactory(object): def __init__(self, created_by_ref=None, created=None, external_references=None, object_marking_refs=None, granular_markings=None): - self.created_by_ref = created_by_ref - self.created = created - self.external_references = external_references - self.object_marking_refs = object_marking_refs - self.granular_markings = granular_markings + + self._defaults = {} + if created_by_ref: + self._defaults['created_by_ref'] = created_by_ref + if created: + self._defaults['created'] = created + # If the user provides a default "created" time, we also want to use + # that as the modified time. + self._defaults['modified'] = created + if external_references: + self._defaults['external_references'] = external_references + if object_marking_refs: + self._defaults['object_marking_refs'] = object_marking_refs + if granular_markings: + self._defaults['granular_markings'] = granular_markings def create(self, cls, **kwargs): - if 'created_by_ref' not in kwargs and self.created_by_ref is not None: - kwargs['created_by_ref'] = self.created_by_ref - if 'created' not in kwargs and self.created is not None: - kwargs['created'] = self.created - if 'modified' not in kwargs: - kwargs['modified'] = self.created - if 'external_references' not in kwargs and self.external_references is not None: - kwargs['external_references'] = self.external_references - if 'object_marking_refs' not in kwargs and self.object_marking_refs is not None: - kwargs['object_marking_refs'] = self.object_marking_refs - if 'granular_markings' not in kwargs and self.granular_markings is not None: - kwargs['granular_markings'] = self.granular_markings + # Use self.defaults as the base, but update with any explicit args + # provided by the user. + properties = dict(**self._defaults) + if kwargs: + properties.update(**kwargs) - return cls(**kwargs) + return cls(**properties) From e233a424fb0541a72c70f3b0a8f1d41039c1cda9 Mon Sep 17 00:00:00 2001 From: clenk Date: Fri, 14 Jul 2017 16:11:48 -0400 Subject: [PATCH 07/10] Drop granular markings from object factory for now --- stix2/environment.py | 5 +---- stix2/test/test_environment.py | 9 --------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/stix2/environment.py b/stix2/environment.py index dfd8a03..a80cf47 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -2,8 +2,7 @@ class ObjectFactory(object): def __init__(self, created_by_ref=None, created=None, - external_references=None, object_marking_refs=None, - granular_markings=None): + external_references=None, object_marking_refs=None): self._defaults = {} if created_by_ref: @@ -17,8 +16,6 @@ class ObjectFactory(object): self._defaults['external_references'] = external_references if object_marking_refs: self._defaults['object_marking_refs'] = object_marking_refs - if granular_markings: - self._defaults['granular_markings'] = granular_markings def create(self, cls, **kwargs): # Use self.defaults as the base, but update with any explicit args diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py index 72990e5..549a854 100644 --- a/stix2/test/test_environment.py +++ b/stix2/test/test_environment.py @@ -52,12 +52,3 @@ def test_object_factory_obj_markings(): factory = stix2.ObjectFactory(object_marking_refs=stix2.TLP_RED) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert stix2.TLP_RED.id in ind.object_marking_refs - - -def test_object_factory_granular_markings(): - marking = stix2.GranularMarking(marking_ref=stix2.TLP_AMBER, - selectors="created_by_ref") - factory = stix2.ObjectFactory(created_by_ref=IDENTITY_ID, - granular_markings=marking) - ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) - assert "created_by_ref" in ind.granular_markings[0].selectors From 2e45cacd52b22dc27a5df76498c633c8b8301a8b Mon Sep 17 00:00:00 2001 From: clenk Date: Mon, 17 Jul 2017 14:56:13 -0400 Subject: [PATCH 08/10] Add to list properties like `external_references`, but include option to replace instead --- stix2/environment.py | 16 +++++++++++++++- stix2/properties.py | 11 ++++------- stix2/test/test_environment.py | 24 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/stix2/environment.py b/stix2/environment.py index a80cf47..89ce3d4 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -2,7 +2,8 @@ class ObjectFactory(object): def __init__(self, created_by_ref=None, created=None, - external_references=None, object_marking_refs=None): + external_references=None, object_marking_refs=None, + list_append=True): self._defaults = {} if created_by_ref: @@ -16,12 +17,25 @@ class ObjectFactory(object): self._defaults['external_references'] = external_references if object_marking_refs: self._defaults['object_marking_refs'] = object_marking_refs + self._list_append = list_append + self._list_properties = ['external_references', 'object_marking_refs'] def create(self, cls, **kwargs): # Use self.defaults as the base, but update with any explicit args # provided by the user. properties = dict(**self._defaults) if kwargs: + if self._list_append: + # Append provided items to list properties instead of replacing them + for list_prop in set(self._list_properties).intersection(kwargs.keys(), properties.keys()): + kwarg_prop = kwargs.pop(list_prop) + if kwarg_prop is None: + del properties[list_prop] + continue + if not isinstance(properties[list_prop], list): + properties[list_prop] = [properties[list_prop]] + properties[list_prop].append(kwarg_prop) + properties.update(**kwargs) return cls(**properties) diff --git a/stix2/properties.py b/stix2/properties.py index 686d1c3..71e4bd9 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -5,7 +5,7 @@ import inspect import re import uuid -from six import text_type +from six import string_types, text_type from .base import _Observable, _STIXBase from .exceptions import DictionaryKeyError @@ -101,12 +101,9 @@ class ListProperty(Property): iter(value) except TypeError: raise ValueError("must be an iterable.") - try: - if isinstance(value, _STIXBase) or isinstance(value, basestring): - value = [value] - except NameError: - if isinstance(value, str): - value = [value] + + if isinstance(value, (_STIXBase, string_types)): + value = [value] result = [] for item in value: diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py index 549a854..9e5ab98 100644 --- a/stix2/test/test_environment.py +++ b/stix2/test/test_environment.py @@ -39,6 +39,9 @@ def test_object_factory_external_resource(): assert ind.external_references[0].source_name == "ACME Threat Intel" assert ind.external_references[0].description == "Threat report" + ind2 = factory.create(stix2.Indicator, external_references=None, **INDICATOR_KWARGS) + assert 'external_references' not in ind2 + def test_object_factory_obj_markings(): stmt_marking = stix2.StatementMarking("Copyright 2016, Example Corp") @@ -52,3 +55,24 @@ def test_object_factory_obj_markings(): factory = stix2.ObjectFactory(object_marking_refs=stix2.TLP_RED) ind = factory.create(stix2.Indicator, **INDICATOR_KWARGS) assert stix2.TLP_RED.id in ind.object_marking_refs + + +def test_object_factory_list_append(): + ext_ref = stix2.ExternalReference(source_name="ACME Threat Intel", + description="Threat report from ACME") + ext_ref2 = stix2.ExternalReference(source_name="Yet Another Threat Report", + description="Threat report from YATR") + factory = stix2.ObjectFactory(external_references=ext_ref) + ind = factory.create(stix2.Indicator, external_references=ext_ref2, **INDICATOR_KWARGS) + assert ind.external_references[1].source_name == "Yet Another Threat Report" + + +def test_object_factory_list_replace(): + ext_ref = stix2.ExternalReference(source_name="ACME Threat Intel", + description="Threat report from ACME") + ext_ref2 = stix2.ExternalReference(source_name="Yet Another Threat Report", + description="Threat report from YATR") + factory = stix2.ObjectFactory(external_references=ext_ref, list_append=False) + ind = factory.create(stix2.Indicator, external_references=ext_ref2, **INDICATOR_KWARGS) + assert len(ind.external_references) == 1 + assert ind.external_references[0].source_name == "Yet Another Threat Report" From 0a2cda00cc41a7d29ae7c20e0b923c0dd7298330 Mon Sep 17 00:00:00 2001 From: clenk Date: Mon, 17 Jul 2017 15:21:49 -0400 Subject: [PATCH 09/10] Add object factory docstring --- stix2/environment.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/stix2/environment.py b/stix2/environment.py index 89ce3d4..a3d79bc 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -1,5 +1,26 @@ class ObjectFactory(object): + """Object Factory + + Used to easily create STIX objects with default values for certain + properties. + + Args: + created_by_ref: Default created_by_ref value to apply to all + objects created by this factory. + created: Default created value to apply to all + objects created by this factory. + external_references: Default `external_references` value to apply + to all objects created by this factory. + object_marking_refs: Default `object_marking_refs` value to apply + to all objects created by this factory. + list_append: When a default is set for a list property like + `external_references` or `object_marking_refs` and a value for + that property is passed into `create()`, if this is set to True, + that value will be added to the list alongside the default. If + this is set to False, the passed in value will replace the + default. Defaults to True. + """ def __init__(self, created_by_ref=None, created=None, external_references=None, object_marking_refs=None, From 4bdbb4a2f967405ae752aeac33cf480318f9b06e Mon Sep 17 00:00:00 2001 From: clenk Date: Tue, 18 Jul 2017 12:05:19 -0400 Subject: [PATCH 10/10] Deep-copy in Object Factory and allow appending multiple items to list properties in ObjectFactory.create() --- stix2/environment.py | 10 ++++++++-- stix2/test/test_environment.py | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/stix2/environment.py b/stix2/environment.py index a3d79bc..f855755 100644 --- a/stix2/environment.py +++ b/stix2/environment.py @@ -1,3 +1,5 @@ +import copy + class ObjectFactory(object): """Object Factory @@ -44,7 +46,7 @@ class ObjectFactory(object): def create(self, cls, **kwargs): # Use self.defaults as the base, but update with any explicit args # provided by the user. - properties = dict(**self._defaults) + properties = copy.deepcopy(self._defaults) if kwargs: if self._list_append: # Append provided items to list properties instead of replacing them @@ -55,7 +57,11 @@ class ObjectFactory(object): continue if not isinstance(properties[list_prop], list): properties[list_prop] = [properties[list_prop]] - properties[list_prop].append(kwarg_prop) + + if isinstance(kwarg_prop, list): + properties[list_prop].extend(kwarg_prop) + else: + properties[list_prop].append(kwarg_prop) properties.update(**kwargs) diff --git a/stix2/test/test_environment.py b/stix2/test/test_environment.py index 9e5ab98..9be8101 100644 --- a/stix2/test/test_environment.py +++ b/stix2/test/test_environment.py @@ -62,10 +62,15 @@ def test_object_factory_list_append(): description="Threat report from ACME") ext_ref2 = stix2.ExternalReference(source_name="Yet Another Threat Report", description="Threat report from YATR") + ext_ref3 = stix2.ExternalReference(source_name="Threat Report #3", + description="One more threat report") factory = stix2.ObjectFactory(external_references=ext_ref) ind = factory.create(stix2.Indicator, external_references=ext_ref2, **INDICATOR_KWARGS) assert ind.external_references[1].source_name == "Yet Another Threat Report" + ind = factory.create(stix2.Indicator, external_references=[ext_ref2, ext_ref3], **INDICATOR_KWARGS) + assert ind.external_references[2].source_name == "Threat Report #3" + def test_object_factory_list_replace(): ext_ref = stix2.ExternalReference(source_name="ACME Threat Intel",