2018-07-10 20:54:17 +02:00
|
|
|
from collections import OrderedDict
|
|
|
|
import re
|
|
|
|
|
2020-03-11 01:24:53 +01:00
|
|
|
import six
|
|
|
|
|
2018-10-17 13:34:15 +02:00
|
|
|
from .base import _cls_init, _Extension, _Observable, _STIXBase
|
2018-07-13 17:10:05 +02:00
|
|
|
from .core import (
|
|
|
|
STIXDomainObject, _register_marking, _register_object,
|
|
|
|
_register_observable, _register_observable_extension,
|
|
|
|
)
|
2020-03-19 21:11:52 +01:00
|
|
|
from .utils import get_class_hierarchy_names, TYPE_21_REGEX, TYPE_REGEX, PREFIX_21_REGEX
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
|
2018-07-11 14:11:47 +02:00
|
|
|
def _custom_object_builder(cls, type, properties, version):
|
2018-07-10 20:54:17 +02:00
|
|
|
class _CustomObject(cls, STIXDomainObject):
|
|
|
|
|
2020-03-19 15:40:35 +01:00
|
|
|
if version == "2.0":
|
|
|
|
if not re.match(TYPE_REGEX, type):
|
|
|
|
raise ValueError(
|
2020-03-19 19:16:48 +01:00
|
|
|
"Invalid type name '%s': must only contain the "
|
2020-03-19 15:40:35 +01:00
|
|
|
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." %
|
|
|
|
type,
|
|
|
|
)
|
|
|
|
else: # 2.1+
|
2020-03-19 21:11:52 +01:00
|
|
|
if not re.match(TYPE_21_REGEX, type):
|
2020-03-19 15:40:35 +01:00
|
|
|
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:
|
2018-07-10 20:54:17 +02:00
|
|
|
raise ValueError(
|
2018-07-13 17:10:05 +02:00
|
|
|
"Invalid type name '%s': must be between 3 and 250 characters." % type,
|
|
|
|
)
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
if not properties or not isinstance(properties, list):
|
|
|
|
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
|
|
|
|
|
2020-03-19 15:40:35 +01:00
|
|
|
if version == "2.1":
|
|
|
|
for prop_name, prop in properties:
|
2020-03-19 19:16:48 +01:00
|
|
|
if not re.match(PREFIX_21_REGEX, prop_name):
|
2020-03-19 21:11:52 +01:00
|
|
|
raise ValueError("Property name '%s' must begin with an alpha character" % prop_name)
|
2020-03-19 15:40:35 +01:00
|
|
|
|
2018-07-10 20:54:17 +02:00
|
|
|
_type = type
|
|
|
|
_properties = OrderedDict(properties)
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
_STIXBase.__init__(self, **kwargs)
|
2018-10-17 13:34:15 +02:00
|
|
|
_cls_init(cls, self, kwargs)
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
_register_object(_CustomObject, version=version)
|
|
|
|
return _CustomObject
|
|
|
|
|
|
|
|
|
2018-07-11 14:11:47 +02:00
|
|
|
def _custom_marking_builder(cls, type, properties, version):
|
2020-03-19 19:16:48 +01:00
|
|
|
|
2018-07-10 20:54:17 +02:00
|
|
|
class _CustomMarking(cls, _STIXBase):
|
|
|
|
|
|
|
|
_type = type
|
|
|
|
_properties = OrderedDict(properties)
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
_STIXBase.__init__(self, **kwargs)
|
2018-10-17 13:34:15 +02:00
|
|
|
_cls_init(cls, self, kwargs)
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
_register_marking(_CustomMarking, version=version)
|
|
|
|
return _CustomMarking
|
|
|
|
|
|
|
|
|
2020-03-04 20:29:35 +01:00
|
|
|
def _custom_observable_builder(cls, type, properties, version, id_contrib_props=None):
|
2020-03-04 20:46:55 +01:00
|
|
|
if id_contrib_props is None:
|
|
|
|
id_contrib_props = []
|
|
|
|
|
2018-07-10 20:54:17 +02:00
|
|
|
class _CustomObservable(cls, _Observable):
|
|
|
|
|
2020-03-19 15:40:35 +01:00
|
|
|
if version == "2.0":
|
|
|
|
if not re.match(TYPE_REGEX, type):
|
|
|
|
raise ValueError(
|
2020-03-19 19:16:48 +01:00
|
|
|
"Invalid observable type name '%s': must only contain the "
|
2020-03-19 15:40:35 +01:00
|
|
|
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." %
|
|
|
|
type,
|
|
|
|
)
|
|
|
|
else: # 2.1+
|
2020-03-19 21:11:52 +01:00
|
|
|
if not re.match(TYPE_21_REGEX, type):
|
2020-03-19 15:40:35 +01:00
|
|
|
raise ValueError(
|
|
|
|
"Invalid observable 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:
|
2018-07-10 20:54:17 +02:00
|
|
|
raise ValueError("Invalid observable 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())]")
|
|
|
|
|
2019-11-06 16:11:12 +01:00
|
|
|
if version == "2.0":
|
|
|
|
# If using STIX2.0, check properties ending in "_ref/s" are ObjectReferenceProperties
|
|
|
|
for prop_name, prop in properties:
|
|
|
|
if prop_name.endswith('_ref') and ('ObjectReferenceProperty' not in get_class_hierarchy_names(prop)):
|
|
|
|
raise ValueError(
|
|
|
|
"'%s' is named like an object reference property but "
|
|
|
|
"is not an ObjectReferenceProperty." % prop_name,
|
|
|
|
)
|
|
|
|
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
|
|
|
|
'ObjectReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
|
|
|
|
raise ValueError(
|
|
|
|
"'%s' is named like an object reference list property but "
|
|
|
|
"is not a ListProperty containing ObjectReferenceProperty." % prop_name,
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
# If using STIX2.1 (or newer...), check properties ending in "_ref/s" are ReferenceProperties
|
|
|
|
for prop_name, prop in properties:
|
2020-03-19 15:40:35 +01:00
|
|
|
if not re.match(PREFIX_21_REGEX, prop_name):
|
2020-03-19 21:11:52 +01:00
|
|
|
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
|
2020-03-19 15:40:35 +01:00
|
|
|
elif prop_name.endswith('_ref') and ('ReferenceProperty' not in get_class_hierarchy_names(prop)):
|
2019-11-06 16:11:12 +01:00
|
|
|
raise ValueError(
|
|
|
|
"'%s' is named like a reference property but "
|
|
|
|
"is not a ReferenceProperty." % prop_name,
|
|
|
|
)
|
|
|
|
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
|
|
|
|
'ReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
|
|
|
|
raise ValueError(
|
|
|
|
"'%s' is named like a reference list property but "
|
|
|
|
"is not a ListProperty containing ReferenceProperty." % prop_name,
|
|
|
|
)
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
_type = type
|
|
|
|
_properties = OrderedDict(properties)
|
2020-02-25 03:11:42 +01:00
|
|
|
if version != '2.0':
|
|
|
|
_id_contributing_properties = id_contrib_props
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
_Observable.__init__(self, **kwargs)
|
2018-10-17 13:34:15 +02:00
|
|
|
_cls_init(cls, self, kwargs)
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
_register_observable(_CustomObservable, version=version)
|
|
|
|
return _CustomObservable
|
|
|
|
|
|
|
|
|
2018-07-11 14:11:47 +02:00
|
|
|
def _custom_extension_builder(cls, observable, type, properties, version):
|
2018-07-10 20:54:17 +02:00
|
|
|
|
2020-03-11 01:24:53 +01:00
|
|
|
try:
|
|
|
|
prop_dict = OrderedDict(properties)
|
|
|
|
except TypeError as e:
|
|
|
|
six.raise_from(
|
|
|
|
ValueError(
|
|
|
|
"Extension properties must be dict-like, e.g. a list "
|
|
|
|
"containing tuples. For example, "
|
|
|
|
"[('property1', IntegerProperty())]",
|
|
|
|
),
|
|
|
|
e,
|
|
|
|
)
|
2018-07-10 20:54:17 +02:00
|
|
|
|
2020-03-11 01:24:53 +01:00
|
|
|
class _CustomExtension(cls, _Extension):
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
_type = type
|
2020-03-11 01:24:53 +01:00
|
|
|
_properties = prop_dict
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
_Extension.__init__(self, **kwargs)
|
2018-10-17 13:34:15 +02:00
|
|
|
_cls_init(cls, self, kwargs)
|
2018-07-10 20:54:17 +02:00
|
|
|
|
|
|
|
_register_observable_extension(observable, _CustomExtension, version=version)
|
|
|
|
return _CustomExtension
|