finish 365

master
Rich Piazza 2020-03-19 16:11:52 -04:00
parent 844ec2c3bf
commit f60e4170fd
5 changed files with 82 additions and 58 deletions

View File

@ -173,7 +173,7 @@ class _STIXBase(Mapping):
for prop_name, prop_value in custom_props.items(): for prop_name, prop_value in custom_props.items():
if not re.match(PREFIX_21_REGEX, prop_name): if not re.match(PREFIX_21_REGEX, prop_name):
raise InvalidValueError(self.__class__, prop_name, raise InvalidValueError(self.__class__, prop_name,
reason="Property names must begin with an alpha character.") reason="Property name '%s' must begin with an alpha character." % prop_name)
# Remove any keyword arguments whose value is None or [] (i.e. empty list) # Remove any keyword arguments whose value is None or [] (i.e. empty list)
setting_kwargs = {} setting_kwargs = {}

View File

@ -10,7 +10,7 @@ import stix2
from .base import _Observable, _STIXBase from .base import _Observable, _STIXBase
from .exceptions import ParseError from .exceptions import ParseError
from .markings import _MarkingsMixin from .markings import _MarkingsMixin
from .utils import _get_dict, SCO21_EXT_REGEX, TYPE_REGEX from .utils import _get_dict, TYPE_REGEX, PREFIX_21_REGEX, TYPE_21_REGEX
STIX2_OBJ_MAPS = {} STIX2_OBJ_MAPS = {}
@ -230,6 +230,36 @@ def _register_marking(new_marking, version=None):
None, use latest version. None, use latest version.
""" """
type = new_marking._type
if version == "2.0":
if not re.match(TYPE_REGEX, type):
raise ValueError(
"Invalid marking 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 marking 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 marking type name '%s': must be between 3 and 250 characters." % type,
)
properties = new_marking._properties
if version == "2.1":
for prop_name, prop_value in properties.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
if version: if version:
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
@ -276,6 +306,7 @@ def _register_observable_extension(
obs_class = observable if isinstance(observable, type) else \ obs_class = observable if isinstance(observable, type) else \
type(observable) type(observable)
ext_type = new_extension._type ext_type = new_extension._type
properties = new_extension._properties
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!")
@ -288,7 +319,7 @@ def _register_observable_extension(
ext_type, ext_type,
) )
else: # 2.1+ else: # 2.1+
if not re.match(SCO21_EXT_REGEX, ext_type): if not re.match(TYPE_21_REGEX, ext_type):
raise ValueError( raise ValueError(
"Invalid extension type name '%s': must only contain the " "Invalid extension type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, hyphen (-), " "characters a-z (lowercase ASCII), 0-9, hyphen (-), "
@ -308,6 +339,11 @@ def _register_observable_extension(
ext_type, ext_type,
) )
if version == "2.1":
for prop_name, prop_value in properties.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
try: try:

View File

@ -8,7 +8,7 @@ from .core import (
STIXDomainObject, _register_marking, _register_object, STIXDomainObject, _register_marking, _register_object,
_register_observable, _register_observable_extension, _register_observable, _register_observable_extension,
) )
from .utils import get_class_hierarchy_names, SCO21_TYPE_REGEX, TYPE_REGEX, PREFIX_21_REGEX from .utils import get_class_hierarchy_names, TYPE_21_REGEX, TYPE_REGEX, PREFIX_21_REGEX
def _custom_object_builder(cls, type, properties, version): def _custom_object_builder(cls, type, properties, version):
@ -22,7 +22,7 @@ def _custom_object_builder(cls, type, properties, version):
type, type,
) )
else: # 2.1+ else: # 2.1+
if not re.match(SCO21_TYPE_REGEX, type): if not re.match(TYPE_21_REGEX, type):
raise ValueError( raise ValueError(
"Invalid type name '%s': must only contain the " "Invalid type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-) " "characters a-z (lowercase ASCII), 0-9, and hyphen (-) "
@ -40,7 +40,7 @@ def _custom_object_builder(cls, type, properties, version):
if version == "2.1": if version == "2.1":
for prop_name, prop in properties: for prop_name, prop in properties:
if not re.match(PREFIX_21_REGEX, prop_name): if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name %s must begin with an alpha character" % prop_name) raise ValueError("Property name '%s' must begin with an alpha character" % prop_name)
_type = type _type = type
_properties = OrderedDict(properties) _properties = OrderedDict(properties)
@ -55,48 +55,8 @@ def _custom_object_builder(cls, type, properties, version):
def _custom_marking_builder(cls, type, properties, version): def _custom_marking_builder(cls, type, properties, version):
try:
prop_dict = OrderedDict(properties)
except TypeError as e:
six.raise_from(
ValueError(
"Marking properties must be dict-like, e.g. a list "
"containing tuples. For example, "
"[('property1', IntegerProperty())]",
),
e,
)
class _CustomMarking(cls, _STIXBase): class _CustomMarking(cls, _STIXBase):
if version == "2.0":
if not re.match(TYPE_REGEX, type):
raise ValueError(
"Invalid marking type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." %
type,
)
else: # 2.1+
if not re.match(SCO21_TYPE_REGEX, type):
raise ValueError(
"Invalid marking 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 marking type name '%s': must be between 3 and 250 characters." % type,
)
if not properties or not isinstance(properties, list):
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
if version == "2.1 ":
for prop_name, prop_value in prop_dict.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name %s must begin with an alpha character." % prop_name)
_type = type _type = type
_properties = OrderedDict(properties) _properties = OrderedDict(properties)
@ -122,7 +82,7 @@ def _custom_observable_builder(cls, type, properties, version, id_contrib_props=
type, type,
) )
else: # 2.1+ else: # 2.1+
if not re.match(SCO21_TYPE_REGEX, type): if not re.match(TYPE_21_REGEX, type):
raise ValueError( raise ValueError(
"Invalid observable type name '%s': must only contain the " "Invalid observable type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-) " "characters a-z (lowercase ASCII), 0-9, and hyphen (-) "
@ -153,7 +113,7 @@ def _custom_observable_builder(cls, type, properties, version, id_contrib_props=
# If using STIX2.1 (or newer...), check properties ending in "_ref/s" are ReferenceProperties # If using STIX2.1 (or newer...), check properties ending in "_ref/s" are ReferenceProperties
for prop_name, prop in properties: for prop_name, prop in properties:
if not re.match(PREFIX_21_REGEX, prop_name): if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name %s must begin with an alpha character." % prop_name) raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
elif prop_name.endswith('_ref') and ('ReferenceProperty' not in get_class_hierarchy_names(prop)): elif prop_name.endswith('_ref') and ('ReferenceProperty' not in get_class_hierarchy_names(prop)):
raise ValueError( raise ValueError(
"'%s' is named like a reference property but " "'%s' is named like a reference property but "
@ -195,11 +155,6 @@ def _custom_extension_builder(cls, observable, type, properties, version):
class _CustomExtension(cls, _Extension): class _CustomExtension(cls, _Extension):
if version == "2.1":
for prop_name, prop_value in prop_dict.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name %s must begin with an alpha character." % prop_name)
_type = type _type = type
_properties = prop_dict _properties = prop_dict

View File

@ -9,6 +9,8 @@ import stix2.v21
from ...exceptions import InvalidValueError from ...exceptions import InvalidValueError
from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID
# Custom Properties in SDOs
IDENTITY_CUSTOM_PROP = stix2.v21.Identity( IDENTITY_CUSTOM_PROP = stix2.v21.Identity(
name="John Smith", name="John Smith",
identity_class="individual", identity_class="individual",
@ -54,7 +56,7 @@ def test_identity_custom_property():
"7foo": "bar", "7foo": "bar",
}, },
) )
assert "Property names must begin with an alpha character." in str(excinfo.value) assert "must begin with an alpha character." in str(excinfo.value)
identity = stix2.v21.Identity( identity = stix2.v21.Identity(
id=IDENTITY_ID, id=IDENTITY_ID,
@ -178,6 +180,7 @@ def test_custom_properties_dict_in_bundled_object():
assert bundle.objects[0].x_foo == "bar" assert bundle.objects[0].x_foo == "bar"
assert '"x_foo": "bar"' in str(bundle) assert '"x_foo": "bar"' in str(bundle)
# Custom properties in SCOs
def test_custom_property_in_observed_data(): def test_custom_property_in_observed_data():
artifact = stix2.v21.File( artifact = stix2.v21.File(
@ -206,7 +209,7 @@ def test_invalid_custom_property_in_observed_data():
x_foo='bar', x_foo='bar',
) )
assert "Property names must begin with an alpha character." in str(excinfo.value) assert "must begin with an alpha character." in str(excinfo.value)
def test_custom_property_object_in_observable_extension(): def test_custom_property_object_in_observable_extension():
@ -270,6 +273,7 @@ def test_identity_custom_property_revoke():
identity = IDENTITY_CUSTOM_PROP.revoke() identity = IDENTITY_CUSTOM_PROP.revoke()
assert identity.x_foo == "bar" assert identity.x_foo == "bar"
# Custom markings
def test_identity_custom_property_edit_markings(): def test_identity_custom_property_edit_markings():
marking_obj = stix2.v21.MarkingDefinition( marking_obj = stix2.v21.MarkingDefinition(
@ -302,7 +306,7 @@ def test_invalid_custom_property_in_marking():
class NewObj(): class NewObj():
pass pass
assert "Property names must begin with an alpha character." in str(excinfo.value) assert "must begin with an alpha character." in str(excinfo.value)
def test_custom_marking_no_init_1(): def test_custom_marking_no_init_1():
@ -362,6 +366,7 @@ def test_custom_marking_invalid_type_name():
pass # pragma: no cover pass # pragma: no cover
assert "Invalid marking type name '7x-new-marking':" in str(excinfo.value) assert "Invalid marking type name '7x-new-marking':" in str(excinfo.value)
# Custom Objects
@stix2.v21.CustomObject( @stix2.v21.CustomObject(
'x-new-type', [ 'x-new-type', [
@ -492,6 +497,7 @@ def test_parse_unregistered_custom_object_type_w_allow_custom():
custom_obj = stix2.parse(nt_string, version="2.1", allow_custom=True) custom_obj = stix2.parse(nt_string, version="2.1", allow_custom=True)
assert custom_obj["type"] == "x-foobar-observable" assert custom_obj["type"] == "x-foobar-observable"
# Custom SCOs
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
'x-new-observable', [ 'x-new-observable', [
@ -559,6 +565,18 @@ def test_custom_observable_object_no_init_2():
assert no2.property1 == 'something' assert no2.property1 == 'something'
def test_invalid_custom_property_in_custom_observable_object():
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable(
'x-new-sco', [
('5property1', stix2.properties.StringProperty()),
],
)
class NewObs(object):
pass # pragma: no cover
assert "must begin with an alpha character." in str(excinfo.value)
def test_custom_observable_object_invalid_type_name(): def test_custom_observable_object_invalid_type_name():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
@ -826,6 +844,7 @@ def test_custom_observable_object_no_id_contrib_props():
assert uuid_obj.variant == uuid.RFC_4122 assert uuid_obj.variant == uuid.RFC_4122
assert uuid_obj.version == 4 assert uuid_obj.version == 4
# Custom Extensions
@stix2.v21.CustomExtension( @stix2.v21.CustomExtension(
stix2.v21.DomainName, 'x-new-ext', [ stix2.v21.DomainName, 'x-new-ext', [
@ -1023,6 +1042,20 @@ def test_custom_extension_no_init_2():
assert ne2.property1 == "foobar" assert ne2.property1 == "foobar"
def test_invalid_custom_property_in_extension():
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomExtension(
stix2.v21.DomainName, 'x-new3-ext', [
('6property1', stix2.properties.StringProperty(required=True)),
],
)
class NewExt():
pass
assert "must begin with an alpha character." in str(excinfo.value)
def test_parse_observable_with_custom_extension(): def test_parse_observable_with_custom_extension():
input_str = """{ input_str = """{
"type": "domain-name", "type": "domain-name",

View File

@ -27,8 +27,8 @@ NOW = object()
STIX_UNMOD_PROPERTIES = ['created', 'created_by_ref', 'id', 'type'] STIX_UNMOD_PROPERTIES = ['created', 'created_by_ref', 'id', 'type']
TYPE_REGEX = re.compile(r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$') TYPE_REGEX = re.compile(r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$')
SCO21_TYPE_REGEX = re.compile(r'^\-?([a-z][a-z0-9]*)+(-[a-z0-9]+)*\-?$') TYPE_21_REGEX = re.compile(r'^\-?([a-z][a-z0-9]*)+(-[a-z0-9]+)*\-?$')
SCO21_EXT_REGEX = re.compile(r'^\-?([a-z][a-z0-9]*)+(-[a-z0-9]+)*\-ext$') EXT_21_REGEX = re.compile(r'^\-?([a-z][a-z0-9]*)+(-[a-z0-9]+)*\-ext$')
PREFIX_21_REGEX = re.compile(r'^[a-z].*') PREFIX_21_REGEX = re.compile(r'^[a-z].*')