Make object attribute access act like dictionary access, raising an
error for any property (including custom or optional) not set on the object.stix2.1
parent
35e973243f
commit
860efcc230
|
@ -73,7 +73,8 @@ class _STIXBase(collections.Mapping):
|
|||
failed_dependency_pairs = []
|
||||
for p in list_of_properties:
|
||||
for dp in list_of_dependent_properties:
|
||||
if not self.__getattr__(p) and self.__getattr__(dp):
|
||||
if ((not hasattr(self, p) or (hasattr(self, p) and not self.__getattr__(p))) and
|
||||
hasattr(self, dp) and self.__getattr__(dp)):
|
||||
failed_dependency_pairs.append((p, dp))
|
||||
if failed_dependency_pairs:
|
||||
raise DependentPropertiesError(self.__class__, failed_dependency_pairs)
|
||||
|
@ -128,10 +129,6 @@ class _STIXBase(collections.Mapping):
|
|||
# Return attribute value.
|
||||
return self.__getitem__(name)
|
||||
except KeyError:
|
||||
# If attribute not found, check if its a property of the object.
|
||||
if name in self._properties:
|
||||
return None
|
||||
|
||||
raise AttributeError("'%s' object has no attribute '%s'" %
|
||||
(self.__class__.__name__, name))
|
||||
|
||||
|
@ -163,7 +160,7 @@ class _STIXBase(collections.Mapping):
|
|||
|
||||
def new_version(self, **kwargs):
|
||||
unchangable_properties = []
|
||||
if self.revoked:
|
||||
if hasattr(self, 'revoked') and self.revoked:
|
||||
raise RevokeError("new_version")
|
||||
new_obj_inner = copy.deepcopy(self._inner)
|
||||
properties_to_change = kwargs.keys()
|
||||
|
@ -183,7 +180,7 @@ class _STIXBase(collections.Mapping):
|
|||
return cls(**new_obj_inner)
|
||||
|
||||
def revoke(self):
|
||||
if self.revoked:
|
||||
if hasattr(self, 'revoked') and self.revoked:
|
||||
raise RevokeError("revoke")
|
||||
return self.new_version(revoked=True)
|
||||
|
||||
|
|
|
@ -443,10 +443,10 @@ class Process(_Observable):
|
|||
super(Process, self)._check_object_constraints()
|
||||
try:
|
||||
self._check_at_least_one_property()
|
||||
if self.extensions and "windows-process-ext" in self.extensions:
|
||||
if hasattr(self, 'extensions') and "windows-process-ext" in self.extensions:
|
||||
self.extensions["windows-process-ext"]._check_at_least_one_property()
|
||||
except AtLeastOnePropertyError as enclosing_exc:
|
||||
if not self.extensions:
|
||||
if not hasattr(self, 'extensions'):
|
||||
raise enclosing_exc
|
||||
else:
|
||||
if "windows-process-ext" in self.extensions:
|
||||
|
|
|
@ -244,6 +244,8 @@ class ObservableProperty(Property):
|
|||
dictified = get_dict(value)
|
||||
except ValueError:
|
||||
raise ValueError("The observable property must contain a dictionary")
|
||||
if dictified == {}:
|
||||
raise ValueError("The dictionary property must contain a non-empty dictionary")
|
||||
|
||||
valid_refs = dict((k, v['type']) for (k, v) in dictified.items())
|
||||
|
||||
|
@ -265,6 +267,8 @@ class DictionaryProperty(Property):
|
|||
dictified = get_dict(value)
|
||||
except ValueError:
|
||||
raise ValueError("The dictionary property must contain a dictionary")
|
||||
if dictified == {}:
|
||||
raise ValueError("The dictionary property must contain a non-empty dictionary")
|
||||
|
||||
for k in dictified.keys():
|
||||
if len(k) < 3:
|
||||
|
@ -418,6 +422,8 @@ class ExtensionsProperty(DictionaryProperty):
|
|||
dictified = get_dict(value)
|
||||
except ValueError:
|
||||
raise ValueError("The extensions property must contain a dictionary")
|
||||
if dictified == {}:
|
||||
raise ValueError("The dictionary property must contain a non-empty dictionary")
|
||||
|
||||
from .__init__ import EXT_MAP # avoid circular import
|
||||
if self.enclosing_type in EXT_MAP:
|
||||
|
|
|
@ -48,7 +48,8 @@ def test_empty_bundle():
|
|||
assert bundle.type == "bundle"
|
||||
assert bundle.id.startswith("bundle--")
|
||||
assert bundle.spec_version == "2.0"
|
||||
assert bundle.objects is None
|
||||
with pytest.raises(AttributeError):
|
||||
assert bundle.objects
|
||||
|
||||
|
||||
def test_bundle_with_wrong_type():
|
||||
|
|
|
@ -199,6 +199,13 @@ def test_parse_artifact_invalid(data):
|
|||
stix2.parse(odata_str)
|
||||
|
||||
|
||||
def test_artifact_example_dependency_error():
|
||||
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
|
||||
stix2.Artifact(url="http://example.com/sirvizio.exe")
|
||||
|
||||
assert excinfo.value.dependencies == [("hashes", "url")]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data", [
|
||||
""""0": {
|
||||
"type": "autonomous-system",
|
||||
|
@ -769,6 +776,10 @@ def test_file_example_encryption_error():
|
|||
assert excinfo.value.cls == stix2.File
|
||||
assert excinfo.value.dependencies == [("is_encrypted", "encryption_algorithm")]
|
||||
|
||||
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
|
||||
stix2.File(name="qwerty.dll",
|
||||
encryption_algorithm="AES128-CBC")
|
||||
|
||||
|
||||
def test_ip4_address_example():
|
||||
ip4 = stix2.IPv4Address(_valid_refs={"4": "mac-addr", "5": "mac-addr"},
|
||||
|
@ -916,14 +927,12 @@ def test_process_example_windows_process_ext_empty():
|
|||
|
||||
|
||||
def test_process_example_extensions_empty():
|
||||
with pytest.raises(stix2.exceptions.AtLeastOnePropertyError) as excinfo:
|
||||
stix2.Process(extensions={
|
||||
})
|
||||
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
||||
stix2.Process(extensions={})
|
||||
|
||||
assert excinfo.value.cls == stix2.Process
|
||||
properties_of_process = list(stix2.Process._properties.keys())
|
||||
properties_of_process.remove("type")
|
||||
assert excinfo.value.properties == sorted(properties_of_process)
|
||||
assert excinfo.value.prop_name == 'extensions'
|
||||
assert 'non-empty dictionary' in excinfo.value.reason
|
||||
|
||||
|
||||
def test_process_example_with_WindowsProcessExt_Object():
|
||||
|
|
|
@ -40,7 +40,8 @@ def test_making_new_version_with_unset():
|
|||
assert campaign_v1.created_by_ref == campaign_v2.created_by_ref
|
||||
assert campaign_v1.created == campaign_v2.created
|
||||
assert campaign_v1.name == campaign_v2.name
|
||||
assert campaign_v2.description is None
|
||||
with pytest.raises(AttributeError):
|
||||
assert campaign_v2.description
|
||||
assert campaign_v1.modified < campaign_v2.modified
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue