diff --git a/stix2/base.py b/stix2/base.py index b2e20de..dba5d71 100644 --- a/stix2/base.py +++ b/stix2/base.py @@ -143,7 +143,7 @@ class _STIXBase(collections.Mapping): def __init__(self, allow_custom=False, **kwargs): cls = self.__class__ - self.__allow_custom = allow_custom + self._allow_custom = allow_custom # Use the same timestamp for any auto-generated datetimes self.__now = get_timestamp() @@ -152,12 +152,12 @@ class _STIXBase(collections.Mapping): custom_props = kwargs.pop('custom_properties', {}) if custom_props and not isinstance(custom_props, dict): raise ValueError("'custom_properties' must be a dictionary") - if not self.__allow_custom: + if not self._allow_custom: extra_kwargs = list(set(kwargs) - set(self._properties)) if extra_kwargs: raise ExtraPropertiesError(cls, extra_kwargs) if custom_props: - self.__allow_custom = True + self._allow_custom = True # Remove any keyword arguments whose value is None or [] (i.e. empty list) setting_kwargs = {} @@ -235,7 +235,7 @@ class _STIXBase(collections.Mapping): if isinstance(self, _Observable): # Assume: valid references in the original object are still valid in the new version new_inner['_valid_refs'] = {'*': '*'} - new_inner['allow_custom'] = self.__allow_custom + new_inner['allow_custom'] = self._allow_custom return cls(**new_inner) def properties_populated(self): @@ -306,7 +306,7 @@ class _Observable(_STIXBase): # the constructor might be called independently of an observed data object self._STIXBase__valid_refs = kwargs.pop('_valid_refs', []) - self.__allow_custom = kwargs.get('allow_custom', False) + self._allow_custom = kwargs.get('allow_custom', False) self._properties['extensions'].allow_custom = kwargs.get('allow_custom', False) try: diff --git a/stix2/properties.py b/stix2/properties.py index 9ddfb82..d236ba2 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -200,7 +200,12 @@ class ListProperty(Property): obj_type = self.contained if isinstance(valid, collections.Mapping): - result.append(obj_type(**valid)) + try: + valid._allow_custom + except AttributeError: + result.append(obj_type(**valid)) + else: + result.append(obj_type(allow_custom=True, **valid)) else: result.append(obj_type(valid)) diff --git a/stix2/test/v21/test_indicator.py b/stix2/test/v21/test_indicator.py index 0562dfd..d786000 100644 --- a/stix2/test/v21/test_indicator.py +++ b/stix2/test/v21/test_indicator.py @@ -207,3 +207,47 @@ def test_invalid_indicator_pattern(): assert excinfo.value.cls == stix2.v21.Indicator assert excinfo.value.prop_name == 'pattern' assert 'mismatched input' in excinfo.value.reason + + +def test_indicator_with_custom_embedded_objs(): + now = dt.datetime(2017, 1, 1, 0, 0, 1, tzinfo=pytz.utc) + epoch = dt.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc) + + ext_ref = stix2.v21.ExternalReference( + source_name="Test", + description="Example Custom Ext Ref", + random_custom_prop="This is a custom property", + allow_custom=True, + ) + + ind = stix2.v21.Indicator( + type="indicator", + id=INDICATOR_ID, + created=now, + modified=now, + pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", + pattern_type="stix", + valid_from=epoch, + indicator_types=['malicious-activity'], + external_references=[ext_ref], + ) + + assert ind.indicator_types == ['malicious-activity'] + assert len(ind.external_references) == 1 + assert ind.external_references[0] == ext_ref + + +def test_indicator_with_custom_embed_objs_extra_props_error(): + ext_ref = stix2.v21.ExternalReference( + source_name="Test", + description="Example Custom Ext Ref", + random_custom_prop="This is a custom property", + allow_custom=True, + ) + + with pytest.raises(stix2.exceptions.ExtraPropertiesError) as excinfo: + stix2.v21.Indicator(external_references=[ext_ref], bad_custom_prop="shouldn't be here", **INDICATOR_KWARGS) + + assert excinfo.value.cls == stix2.v21.Indicator + assert excinfo.value.properties == ['bad_custom_prop'] + assert str(excinfo.value) == "Unexpected properties for Indicator: (bad_custom_prop)." diff --git a/stix2/v20/bundle.py b/stix2/v20/bundle.py index 1383757..ffd70ea 100644 --- a/stix2/v20/bundle.py +++ b/stix2/v20/bundle.py @@ -31,7 +31,7 @@ class Bundle(_STIXBase): else: kwargs['objects'] = list(args) + kwargs.get('objects', []) - self.__allow_custom = kwargs.get('allow_custom', False) + self._allow_custom = kwargs.get('allow_custom', False) self._properties['objects'].contained.allow_custom = kwargs.get('allow_custom', False) super(Bundle, self).__init__(**kwargs) diff --git a/stix2/v20/sdo.py b/stix2/v20/sdo.py index 9ba1860..44fe6fd 100644 --- a/stix2/v20/sdo.py +++ b/stix2/v20/sdo.py @@ -212,7 +212,7 @@ class ObservedData(STIXDomainObject): ]) def __init__(self, *args, **kwargs): - self.__allow_custom = kwargs.get('allow_custom', False) + self._allow_custom = kwargs.get('allow_custom', False) self._properties['objects'].allow_custom = kwargs.get('allow_custom', False) super(ObservedData, self).__init__(*args, **kwargs) diff --git a/stix2/v21/bundle.py b/stix2/v21/bundle.py index fb35be2..168771f 100644 --- a/stix2/v21/bundle.py +++ b/stix2/v21/bundle.py @@ -29,7 +29,7 @@ class Bundle(_STIXBase): else: kwargs['objects'] = list(args) + kwargs.get('objects', []) - self.__allow_custom = kwargs.get('allow_custom', False) + self._allow_custom = kwargs.get('allow_custom', False) self._properties['objects'].contained.allow_custom = kwargs.get('allow_custom', False) super(Bundle, self).__init__(**kwargs) diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index db45ed6..b775833 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -578,7 +578,7 @@ class ObservedData(STIXDomainObject): ]) def __init__(self, *args, **kwargs): - self.__allow_custom = kwargs.get('allow_custom', False) + self._allow_custom = kwargs.get('allow_custom', False) self._properties['objects'].allow_custom = kwargs.get('allow_custom', False) if "objects" in kwargs: