diff --git a/stix2/base.py b/stix2/base.py index ac726bd..3e0d670 100644 --- a/stix2/base.py +++ b/stix2/base.py @@ -119,8 +119,14 @@ class _STIXBase(collections.abc.Mapping): if extra_kwargs and not self._allow_custom: ext_found = False + # This section performs a check on top-level objects that support extensions. + # If extra_kwargs is not empty, allow_custom False, and the extension_type is not + # toplevel then we raise the ExtraPropertiesError regardless. for key_id, ext_def in kwargs.get('extensions', {}).items(): - if key_id.startswith('extension-definition--'): + if ( + key_id.startswith('extension-definition--') and + ext_def.get('extension_type', '') == 'toplevel-property-extension' + ): ext_found = True break if ext_found is False: @@ -150,6 +156,10 @@ class _STIXBase(collections.abc.Mapping): required_properties = set(get_required_properties(self._properties)) missing_kwargs = required_properties - set(setting_kwargs) if missing_kwargs: + # In this scenario, we are inside within the scope of the extension. + # It is possible to check if this is a new Extension Class by + # querying "extension_type". Note: There is an API limitation currently + # because a toplevel-property-extension cannot validate its parent properties new_ext_check = ( bool(getattr(self, "extension_type", None)) and issubclass(cls, stix2.v21._Extension) diff --git a/stix2/properties.py b/stix2/properties.py index 1ec1311..1c0a9d8 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -117,7 +117,7 @@ class Property(object): creating an object with that property. No default value exists for these properties. (Default: ``False``) fixed: This provides a constant default value. Users are free to - provide this value explicity when constructing an object (which + provide this value explicitly when constructing an object (which allows you to copy **all** values from an existing object to a new object), but if the user provides a value other than the ``fixed`` value, it will raise an error. This is semantically equivalent to