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.
master
Chris Lenk 2020-04-02 08:17:34 -04:00
parent 03cb225932
commit 13cddf9d6d
4 changed files with 58 additions and 48 deletions

View File

@ -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 version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version. 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): if not issubclass(new_type, _DomainObject):
@ -208,8 +213,6 @@ def _register_object(new_type, version=None):
new_type.__name__, new_type.__name__,
) )
new_type._properties['type'].clean(new_type._type)
if version: if version:
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
@ -232,8 +235,11 @@ def _register_marking(new_marking, version=None):
""" """
mark_type = new_marking._type
properties = new_marking._properties properties = new_marking._properties
stix2.properties._validate_type(mark_type, version)
if version == "2.1": if version == "2.1":
for prop_name, prop_value in properties.items(): for prop_name, prop_value in properties.items():
if not re.match(PREFIX_21_REGEX, prop_name): 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('.', '') v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP_MARKING = STIX2_OBJ_MAPS[v]['markings'] OBJ_MAP_MARKING = STIX2_OBJ_MAPS[v]['markings']
if new_marking._type in OBJ_MAP_MARKING.keys(): if mark_type in OBJ_MAP_MARKING.keys():
raise DuplicateRegistrationError("STIX Marking", new_marking._type) raise DuplicateRegistrationError("STIX Marking", mark_type)
OBJ_MAP_MARKING[new_marking._type] = new_marking OBJ_MAP_MARKING[mark_type] = new_marking
def _register_observable(new_observable, version=None): 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: if version:
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
@ -296,8 +300,7 @@ def _register_observable_extension(
if not issubclass(obs_class, _Observable): if not issubclass(obs_class, _Observable):
raise ValueError("'observable' must be a valid Observable class!") raise ValueError("'observable' must be a valid Observable class!")
temp_prop = stix2.properties.TypeProperty(ext_type, spec_version=version) stix2.properties._validate_type(ext_type, version)
temp_prop.clean(ext_type)
if not new_extension._properties: if not new_extension._properties:
raise ValueError( raise ValueError(

View File

@ -84,6 +84,36 @@ def _validate_id(id_, spec_version, required_prefix):
raise ValueError(ERROR_INVALID_ID.format(id_)) 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): class Property(object):
"""Represent a property of STIX data type. """Represent a property of STIX data type.
@ -236,32 +266,10 @@ class StringProperty(Property):
class TypeProperty(Property): class TypeProperty(Property):
def __init__(self, type, spec_version=stix2.DEFAULT_VERSION): def __init__(self, type, spec_version=stix2.DEFAULT_VERSION):
_validate_type(type, spec_version)
self.spec_version = spec_version self.spec_version = spec_version
super(TypeProperty, self).__init__(fixed=type) 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): class IDProperty(Property):

View File

@ -483,7 +483,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs(object): class NewObs(object):
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v20.CustomObservable( @stix2.v20.CustomObservable(
@ -493,7 +493,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs2(object): class NewObs2(object):
pass # pragma: no cover 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(): def test_custom_observable_object_invalid_ref_property():
@ -808,7 +808,7 @@ def test_custom_extension_invalid_type_name():
) )
class FooExtension(): class FooExtension():
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v20.CustomExtension( @stix2.v20.CustomExtension(
@ -818,7 +818,7 @@ def test_custom_extension_invalid_type_name():
) )
class BlaExtension(): class BlaExtension():
pass # pragma: no cover 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(): def test_custom_extension_no_properties():
@ -968,9 +968,8 @@ def test_register_custom_object():
class CustomObject2(object): class CustomObject2(object):
_type = 'awesome-object' _type = 'awesome-object'
stix2.parsing._register_object(CustomObject2, version="2.0") with pytest.raises(ValueError):
# Note that we will always check against newest OBJ_MAP. stix2.parsing._register_object(CustomObject2, version="2.0")
assert (CustomObject2._type, CustomObject2) in stix2.v20.OBJ_MAP.items()
def test_extension_property_location(): def test_extension_property_location():

View File

@ -375,7 +375,7 @@ def test_custom_marking_invalid_type_name():
) )
class NewObj(object): class NewObj(object):
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomMarking( @stix2.v21.CustomMarking(
@ -385,7 +385,7 @@ def test_custom_marking_invalid_type_name():
) )
class NewObj2(object): class NewObj2(object):
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomMarking( @stix2.v21.CustomMarking(
@ -395,7 +395,7 @@ def test_custom_marking_invalid_type_name():
) )
class NewObj3(object): class NewObj3(object):
pass # pragma: no cover 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 # Custom Objects
@ -619,7 +619,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs(object): class NewObs(object):
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
@ -629,7 +629,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs2(object): class NewObs2(object):
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
@ -639,7 +639,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs3(object): class NewObs3(object):
pass # pragma: no cover 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(): def test_custom_observable_object_invalid_ref_property():
@ -1005,7 +1005,7 @@ def test_custom_extension_invalid_type_name():
) )
class FooExtension(): class FooExtension():
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomExtension( @stix2.v21.CustomExtension(
@ -1015,7 +1015,7 @@ def test_custom_extension_invalid_type_name():
) )
class BlaExtension(): class BlaExtension():
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomExtension( @stix2.v21.CustomExtension(
@ -1025,7 +1025,7 @@ def test_custom_extension_invalid_type_name():
) )
class Bla2Extension(): class Bla2Extension():
pass # pragma: no cover 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(): def test_custom_extension_no_properties():