From 13cddf9d6d4e2f97b7e8f4db20022f553369e747 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Thu, 2 Apr 2020 08:17:34 -0400 Subject: [PATCH] Move TypeProperty format checks to __init__ TypeProperty uses a fixed value, so check() was never called. This way also runs the check at object registration time because the wrapper creates an instance of TypeProperty and doesn't have to wait for the object to be instantiated so clean() can be called. Also fix some tests. --- stix2/parsing.py | 21 ++++++++------ stix2/properties.py | 54 ++++++++++++++++++++--------------- stix2/test/v20/test_custom.py | 13 ++++----- stix2/test/v21/test_custom.py | 18 ++++++------ 4 files changed, 58 insertions(+), 48 deletions(-) diff --git a/stix2/parsing.py b/stix2/parsing.py index b81a07d..db0c693 100644 --- a/stix2/parsing.py +++ b/stix2/parsing.py @@ -200,6 +200,11 @@ def _register_object(new_type, version=None): version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If None, use latest version. + Raises: + ValueError: If the class being registered wasn't created with the + @CustomObject decorator. + DuplicateRegistrationError: If the class has already been registered. + """ if not issubclass(new_type, _DomainObject): @@ -208,8 +213,6 @@ def _register_object(new_type, version=None): new_type.__name__, ) - new_type._properties['type'].clean(new_type._type) - if version: v = 'v' + version.replace('.', '') else: @@ -232,8 +235,11 @@ def _register_marking(new_marking, version=None): """ + mark_type = new_marking._type properties = new_marking._properties + stix2.properties._validate_type(mark_type, version) + if version == "2.1": for prop_name, prop_value in properties.items(): if not re.match(PREFIX_21_REGEX, prop_name): @@ -246,9 +252,9 @@ def _register_marking(new_marking, version=None): v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') OBJ_MAP_MARKING = STIX2_OBJ_MAPS[v]['markings'] - if new_marking._type in OBJ_MAP_MARKING.keys(): - raise DuplicateRegistrationError("STIX Marking", new_marking._type) - OBJ_MAP_MARKING[new_marking._type] = new_marking + if mark_type in OBJ_MAP_MARKING.keys(): + raise DuplicateRegistrationError("STIX Marking", mark_type) + OBJ_MAP_MARKING[mark_type] = new_marking def _register_observable(new_observable, version=None): @@ -261,8 +267,6 @@ def _register_observable(new_observable, version=None): """ - new_observable._properties['type'].clean(new_observable._type) - if version: v = 'v' + version.replace('.', '') else: @@ -296,8 +300,7 @@ def _register_observable_extension( if not issubclass(obs_class, _Observable): raise ValueError("'observable' must be a valid Observable class!") - temp_prop = stix2.properties.TypeProperty(ext_type, spec_version=version) - temp_prop.clean(ext_type) + stix2.properties._validate_type(ext_type, version) if not new_extension._properties: raise ValueError( diff --git a/stix2/properties.py b/stix2/properties.py index 4ccfa89..39c409c 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -84,6 +84,36 @@ def _validate_id(id_, spec_version, required_prefix): raise ValueError(ERROR_INVALID_ID.format(id_)) +def _validate_type(type_, spec_version): + """ + Check the STIX type name for correctness, raise an exception if there are + errors. + + :param type_: The STIX type name + :param spec_version: The STIX specification version to use + :raises ValueError: If there are any errors with the identifier + """ + if spec_version == "2.0": + if not re.match(TYPE_REGEX, type_): + raise ValueError( + "Invalid type name '%s': must only contain the " + "characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % + type_, + ) + else: # 2.1+ + if not re.match(TYPE_21_REGEX, type_): + raise ValueError( + "Invalid type name '%s': must only contain the " + "characters a-z (lowercase ASCII), 0-9, and hyphen (-) " + "and must begin with an a-z character" % type_, + ) + + if len(type_) < 3 or len(type_) > 250: + raise ValueError( + "Invalid type name '%s': must be between 3 and 250 characters." % type_, + ) + + class Property(object): """Represent a property of STIX data type. @@ -236,32 +266,10 @@ class StringProperty(Property): class TypeProperty(Property): def __init__(self, type, spec_version=stix2.DEFAULT_VERSION): + _validate_type(type, spec_version) self.spec_version = spec_version super(TypeProperty, self).__init__(fixed=type) - def clean(self, value): - if self.spec_version == "2.0": - if not re.match(TYPE_REGEX, type): - raise ValueError( - "Invalid type name '%s': must only contain the " - "characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % - type, - ) - else: # 2.1+ - if not re.match(TYPE_21_REGEX, type): - raise ValueError( - "Invalid type name '%s': must only contain the " - "characters a-z (lowercase ASCII), 0-9, and hyphen (-) " - "and must begin with an a-z character" % type, - ) - - if len(type) < 3 or len(type) > 250: - raise ValueError( - "Invalid type name '%s': must be between 3 and 250 characters." % type, - ) - - return value - class IDProperty(Property): diff --git a/stix2/test/v20/test_custom.py b/stix2/test/v20/test_custom.py index 8342fec..57f8aac 100644 --- a/stix2/test/v20/test_custom.py +++ b/stix2/test/v20/test_custom.py @@ -483,7 +483,7 @@ def test_custom_observable_object_invalid_type_name(): ) class NewObs(object): pass # pragma: no cover - assert "Invalid observable type name 'x':" in str(excinfo.value) + assert "Invalid type name 'x':" in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v20.CustomObservable( @@ -493,7 +493,7 @@ def test_custom_observable_object_invalid_type_name(): ) class NewObs2(object): pass # pragma: no cover - assert "Invalid observable type name 'x_new_obs':" in str(excinfo.value) + assert "Invalid type name 'x_new_obs':" in str(excinfo.value) def test_custom_observable_object_invalid_ref_property(): @@ -808,7 +808,7 @@ def test_custom_extension_invalid_type_name(): ) class FooExtension(): pass # pragma: no cover - assert "Invalid extension type name 'x':" in str(excinfo.value) + assert "Invalid type name 'x':" in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v20.CustomExtension( @@ -818,7 +818,7 @@ def test_custom_extension_invalid_type_name(): ) class BlaExtension(): pass # pragma: no cover - assert "Invalid extension type name 'x_new_ext':" in str(excinfo.value) + assert "Invalid type name 'x_new_ext':" in str(excinfo.value) def test_custom_extension_no_properties(): @@ -968,9 +968,8 @@ def test_register_custom_object(): class CustomObject2(object): _type = 'awesome-object' - stix2.parsing._register_object(CustomObject2, version="2.0") - # Note that we will always check against newest OBJ_MAP. - assert (CustomObject2._type, CustomObject2) in stix2.v20.OBJ_MAP.items() + with pytest.raises(ValueError): + stix2.parsing._register_object(CustomObject2, version="2.0") def test_extension_property_location(): diff --git a/stix2/test/v21/test_custom.py b/stix2/test/v21/test_custom.py index 59eb53e..3646f11 100644 --- a/stix2/test/v21/test_custom.py +++ b/stix2/test/v21/test_custom.py @@ -375,7 +375,7 @@ def test_custom_marking_invalid_type_name(): ) class NewObj(object): pass # pragma: no cover - assert "Invalid marking type name 'x': " in str(excinfo.value) + assert "Invalid type name 'x': " in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v21.CustomMarking( @@ -385,7 +385,7 @@ def test_custom_marking_invalid_type_name(): ) class NewObj2(object): pass # pragma: no cover - assert "Invalid marking type name 'x_new_marking':" in str(excinfo.value) + assert "Invalid type name 'x_new_marking':" in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v21.CustomMarking( @@ -395,7 +395,7 @@ def test_custom_marking_invalid_type_name(): ) class NewObj3(object): pass # pragma: no cover - assert "Invalid marking type name '7x-new-marking':" in str(excinfo.value) + assert "Invalid type name '7x-new-marking':" in str(excinfo.value) # Custom Objects @@ -619,7 +619,7 @@ def test_custom_observable_object_invalid_type_name(): ) class NewObs(object): pass # pragma: no cover - assert "Invalid observable type name 'x':" in str(excinfo.value) + assert "Invalid type name 'x':" in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v21.CustomObservable( @@ -629,7 +629,7 @@ def test_custom_observable_object_invalid_type_name(): ) class NewObs2(object): pass # pragma: no cover - assert "Invalid observable type name 'x_new_obs':" in str(excinfo.value) + assert "Invalid type name 'x_new_obs':" in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v21.CustomObservable( @@ -639,7 +639,7 @@ def test_custom_observable_object_invalid_type_name(): ) class NewObs3(object): pass # pragma: no cover - assert "Invalid observable type name '7x-new-obs':" in str(excinfo.value) + assert "Invalid type name '7x-new-obs':" in str(excinfo.value) def test_custom_observable_object_invalid_ref_property(): @@ -1005,7 +1005,7 @@ def test_custom_extension_invalid_type_name(): ) class FooExtension(): pass # pragma: no cover - assert "Invalid extension type name 'x':" in str(excinfo.value) + assert "Invalid type name 'x':" in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v21.CustomExtension( @@ -1015,7 +1015,7 @@ def test_custom_extension_invalid_type_name(): ) class BlaExtension(): pass # pragma: no cover - assert "Invalid extension type name 'x_new_ext':" in str(excinfo.value) + assert "Invalid type name 'x_new_ext':" in str(excinfo.value) with pytest.raises(ValueError) as excinfo: @stix2.v21.CustomExtension( @@ -1025,7 +1025,7 @@ def test_custom_extension_invalid_type_name(): ) class Bla2Extension(): pass # pragma: no cover - assert "Invalid extension type name '7x-new-ext':" in str(excinfo.value) + assert "Invalid type name '7x-new-ext':" in str(excinfo.value) def test_custom_extension_no_properties():