Merge pull request #198 from treyka/stix2.1

update dict key limits per 2.1 spec
stix2.1
Emmanuelle Vargas-Gonzalez 2018-06-26 12:57:44 -04:00 committed by GitHub
commit 9cc74e88b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 467 additions and 74 deletions

View File

@ -56,7 +56,7 @@ class STIXJSONIncludeOptionalDefaultsEncoder(json.JSONEncoder):
elif isinstance(obj, _STIXBase):
return dict(obj)
else:
return super(STIXJSONEncoder, self).default(obj)
return super(STIXJSONIncludeOptionalDefaultsEncoder, self).default(obj)
def get_required_properties(properties):

View File

@ -3,7 +3,7 @@ import pkgutil
import stix2
from . import exceptions
from .exceptions import ParseError
from .utils import _get_dict
STIX2_OBJ_MAPS = {}
@ -74,7 +74,7 @@ def dict_to_stix2(stix_dict, allow_custom=False, version=None):
"""
if 'type' not in stix_dict:
raise exceptions.ParseError("Can't parse object with no 'type' property: %s" % str(stix_dict))
raise ParseError("Can't parse object with no 'type' property: %s" % str(stix_dict))
if "spec_version" in stix_dict:
# For STIX 2.0, applies to bundles only.
@ -87,7 +87,7 @@ def dict_to_stix2(stix_dict, allow_custom=False, version=None):
else:
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
else:
v = 'v20'
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP = STIX2_OBJ_MAPS[v]
@ -98,7 +98,7 @@ def dict_to_stix2(stix_dict, allow_custom=False, version=None):
# flag allows for unknown custom objects too, but will not
# be parsed into STIX object, returned as is
return stix_dict
raise exceptions.ParseError("Can't parse unknown object type '%s'! For custom types, use the CustomObject decorator." % stix_dict['type'])
raise ParseError("Can't parse unknown object type '%s'! For custom types, use the CustomObject decorator." % stix_dict['type'])
return obj_class(allow_custom=allow_custom, **stix_dict)

View File

@ -3,8 +3,8 @@ import json
import pytest
import stix2
import stix2.v20.sdo
import stix2.v21.bundle
import stix2.v21.sdo
EXPECTED_BUNDLE = """{
"type": "bundle",
@ -179,8 +179,7 @@ def test_parse_bundle(version):
assert bundle.type == "bundle"
assert bundle.id.startswith("bundle--")
# TODO: update this to a STIX 2.1 indicator
assert type(bundle.objects[0]) is stix2.v20.sdo.Indicator
assert type(bundle.objects[0]) is stix2.v21.sdo.Indicator
assert bundle.objects[0].type == 'indicator'
assert bundle.objects[1].type == 'malware'
assert bundle.objects[2].type == 'relationship'

View File

@ -2,7 +2,7 @@ import pytest
import stix2
import stix2.base
import stix2.v20.sdo
import stix2.v21.sdo
from .constants import FAKE_TIME, MARKING_DEFINITION_ID
@ -95,8 +95,7 @@ def test_identity_custom_property_allowed():
def test_parse_identity_custom_property(data):
with pytest.raises(stix2.exceptions.ExtraPropertiesError) as excinfo:
identity = stix2.parse(data)
# TODO: update to create and check a STIX 2.1 Identity object
assert excinfo.value.cls == stix2.v20.sdo.Identity
assert excinfo.value.cls == stix2.v21.sdo.Identity
assert excinfo.value.properties == ['foo']
assert "Unexpected properties for" in str(excinfo.value)

View File

@ -2,7 +2,6 @@
import datetime as dt
import pytest
import pytz
import stix2
@ -21,7 +20,7 @@ TEST_CAMPAIGN = """{
"description": "More information about bank attack"
}"""
TEST_LANGUAGE_CONTENT = """{
TEST_LANGUAGE_CONTENT = u"""{
"type": "language-content",
"id": "language-content--b86bd89f-98bb-4fa9-8cb2-9ad421da981d",
"created": "2017-02-08T21:31:22.007Z",
@ -30,18 +29,17 @@ TEST_LANGUAGE_CONTENT = """{
"object_modified": "2017-02-08T21:31:22.007Z",
"contents": {
"de": {
"name": "Bank Angriff 1",
"description": "Weitere Informationen über Banküberfall"
"description": "Weitere Informationen über Banküberfall",
"name": "Bank Angriff 1"
},
"fr": {
"name": "Attaque Bank 1",
"description": "Plus d'informations sur la crise bancaire"
"description": "Plus d'informations sur la crise bancaire",
"name": "Attaque Bank 1"
}
}
}"""
@pytest.mark.xfail(reason="Dictionary keys are too short")
def test_language_content_campaign():
now = dt.datetime(2017, 2, 8, 21, 31, 22, microsecond=7000, tzinfo=pytz.utc)
@ -66,5 +64,8 @@ def test_language_content_campaign():
camp = stix2.parse(TEST_CAMPAIGN)
assert str(lc) in TEST_LANGUAGE_CONTENT
# In order to provide the same representation, we need to disable escaping
# in json.dumps(). https://docs.python.org/3/library/json.html#json.dumps
# or https://docs.python.org/2/library/json.html#json.dumps
assert lc.serialize(pretty=True, ensure_ascii=False) == TEST_LANGUAGE_CONTENT
assert lc.modified == camp.modified

View File

@ -2,12 +2,13 @@ import pytest
from stix2 import CustomObject, EmailMIMEComponent, ExtensionsProperty, TCPExt
from stix2.exceptions import AtLeastOnePropertyError, DictionaryKeyError
from stix2.properties import (BinaryProperty, BooleanProperty,
DictionaryProperty, EmbeddedObjectProperty,
EnumProperty, FloatProperty, HashesProperty,
HexProperty, IDProperty, IntegerProperty,
ListProperty, Property, ReferenceProperty,
StringProperty, TimestampProperty, TypeProperty)
from stix2.v20.properties import (BinaryProperty, BooleanProperty,
DictionaryProperty, EmbeddedObjectProperty,
EnumProperty, FloatProperty, HashesProperty,
HexProperty, IDProperty, IntegerProperty,
ListProperty, Property, ReferenceProperty,
StringProperty, TimestampProperty,
TypeProperty)
from .constants import FAKE_TIME

View File

@ -1,10 +1,10 @@
from collections import OrderedDict
from stix2 import parse
from stix2.base import _STIXBase
from stix2.properties import (IDProperty, ListProperty, Property,
StringProperty, TypeProperty)
from stix2.utils import _get_dict, get_class_hierarchy_names
from ..base import _STIXBase
from ..core import parse
from ..utils import _get_dict, get_class_hierarchy_names
from .properties import (IDProperty, ListProperty, Property, StringProperty,
TypeProperty)
class STIXObjectProperty(Property):

View File

@ -4,10 +4,10 @@ from collections import OrderedDict
from ..base import _STIXBase
from ..markings import _MarkingsMixin
from ..properties import (HashesProperty, IDProperty, ListProperty, Property,
ReferenceProperty, SelectorProperty, StringProperty,
TimestampProperty, TypeProperty)
from ..utils import NOW, _get_dict
from .properties import (HashesProperty, IDProperty, ListProperty, Property,
ReferenceProperty, SelectorProperty, StringProperty,
TimestampProperty, TypeProperty)
class ExternalReference(_STIXBase):

View File

@ -12,12 +12,12 @@ import re
from ..base import _Extension, _Observable, _STIXBase
from ..exceptions import (AtLeastOnePropertyError, CustomContentError,
DependentPropertiesError, ParseError)
from ..properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, EnumProperty, FloatProperty,
HashesProperty, HexProperty, IntegerProperty,
ListProperty, ObjectReferenceProperty, Property,
StringProperty, TimestampProperty, TypeProperty)
from ..utils import TYPE_REGEX, _get_dict
from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, EnumProperty, FloatProperty,
HashesProperty, HexProperty, IntegerProperty,
ListProperty, ObjectReferenceProperty, Property,
StringProperty, TimestampProperty, TypeProperty)
class ObservableProperty(Property):

View File

@ -10,9 +10,9 @@ import uuid
from six import string_types, text_type
from stix2patterns.validator import run_validator
from .base import _STIXBase
from .exceptions import DictionaryKeyError
from .utils import _get_dict, parse_into_datetime
from ..base import _STIXBase
from ..exceptions import DictionaryKeyError
from ..utils import _get_dict, parse_into_datetime
class Property(object):

View File

@ -4,16 +4,15 @@
from collections import OrderedDict
import re
import stix2
from ..base import _STIXBase
from ..core import _register_type
from ..markings import _MarkingsMixin
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
ListProperty, PatternProperty, ReferenceProperty,
StringProperty, TimestampProperty, TypeProperty)
from ..utils import NOW, TYPE_REGEX
from .common import ExternalReference, GranularMarking, KillChainPhase
from .observables import ObservableProperty
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
ListProperty, PatternProperty, ReferenceProperty,
StringProperty, TimestampProperty, TypeProperty)
class STIXDomainObject(_STIXBase, _MarkingsMixin):
@ -409,7 +408,7 @@ def CustomObject(type='x-custom-type', properties=None):
return
raise e
stix2._register_type(_Custom, version="2.0")
_register_type(_Custom, version="2.0")
return _Custom
return custom_builder

View File

@ -4,11 +4,11 @@ from collections import OrderedDict
from ..base import _STIXBase
from ..markings import _MarkingsMixin
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
ListProperty, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty)
from ..utils import NOW
from .common import ExternalReference, GranularMarking
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
ListProperty, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty)
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):

View File

@ -1,9 +1,9 @@
from collections import OrderedDict
from stix2 import parse
from stix2.base import _STIXBase
from stix2.properties import IDProperty, ListProperty, Property, TypeProperty
from stix2.utils import _get_dict, get_class_hierarchy_names
from ..base import _STIXBase
from ..core import parse
from ..utils import _get_dict, get_class_hierarchy_names
from .properties import IDProperty, ListProperty, Property, TypeProperty
class STIXObjectProperty(Property):

View File

@ -4,11 +4,11 @@ from collections import OrderedDict
from ..base import _STIXBase
from ..markings import _MarkingsMixin
from ..properties import (BooleanProperty, DictionaryProperty, HashesProperty,
IDProperty, ListProperty, Property,
ReferenceProperty, SelectorProperty, StringProperty,
TimestampProperty, TypeProperty)
from ..utils import NOW, _get_dict
from .properties import (BooleanProperty, DictionaryProperty, HashesProperty,
IDProperty, ListProperty, Property, ReferenceProperty,
SelectorProperty, StringProperty, TimestampProperty,
TypeProperty)
class ExternalReference(_STIXBase):

View File

@ -12,12 +12,12 @@ import re
from ..base import _Extension, _Observable, _STIXBase
from ..exceptions import (AtLeastOnePropertyError, CustomContentError,
DependentPropertiesError, ParseError)
from ..properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, EnumProperty, FloatProperty,
HashesProperty, HexProperty, IntegerProperty,
ListProperty, ObjectReferenceProperty, Property,
StringProperty, TimestampProperty, TypeProperty)
from ..utils import TYPE_REGEX, _get_dict
from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, EnumProperty, FloatProperty,
HashesProperty, HexProperty, IntegerProperty,
ListProperty, ObjectReferenceProperty, Property,
StringProperty, TimestampProperty, TypeProperty)
class ObservableProperty(Property):

395
stix2/v21/properties.py Normal file
View File

@ -0,0 +1,395 @@
"""Classes for representing properties of STIX Objects and Cyber Observables.
"""
import base64
import binascii
import collections
import inspect
import re
import uuid
from six import string_types, text_type
from stix2patterns.validator import run_validator
from ..base import _STIXBase
from ..exceptions import DictionaryKeyError
from ..utils import _get_dict, parse_into_datetime
class Property(object):
"""Represent a property of STIX data type.
Subclasses can define the following attributes as keyword arguments to
``__init__()``.
Args:
required (bool): If ``True``, the property must be provided when creating an
object with that property. No default value exists for these properties.
(Default: ``False``)
fixed: This provides a constant default value. Users are free to
provide this value explicity when constructing an object (which allows
you to copy **all** values from an existing object to a new object), but
if the user provides a value other than the ``fixed`` value, it will raise
an error. This is semantically equivalent to defining both:
- a ``clean()`` function that checks if the value matches the fixed
value, and
- a ``default()`` function that returns the fixed value.
Subclasses can also define the following functions:
- ``def clean(self, value) -> any:``
- Return a value that is valid for this property. If ``value`` is not
valid for this property, this will attempt to transform it first. If
``value`` is not valid and no such transformation is possible, it should
raise a ValueError.
- ``def default(self):``
- provide a default value for this property.
- ``default()`` can return the special value ``NOW`` to use the current
time. This is useful when several timestamps in the same object need
to use the same default value, so calling now() for each property--
likely several microseconds apart-- does not work.
Subclasses can instead provide a lambda function for ``default`` as a keyword
argument. ``clean`` should not be provided as a lambda since lambdas cannot
raise their own exceptions.
When instantiating Properties, ``required`` and ``default`` should not be used
together. ``default`` implies that the property is required in the specification
so this function will be used to supply a value if none is provided.
``required`` means that the user must provide this; it is required in the
specification and we can't or don't want to create a default value.
"""
def _default_clean(self, value):
if value != self._fixed_value:
raise ValueError("must equal '{0}'.".format(self._fixed_value))
return value
def __init__(self, required=False, fixed=None, default=None, type=None):
self.required = required
self.type = type
if fixed:
self._fixed_value = fixed
self.clean = self._default_clean
self.default = lambda: fixed
if default:
self.default = default
def clean(self, value):
return value
def __call__(self, value=None):
"""Used by ListProperty to handle lists that have been defined with
either a class or an instance.
"""
return value
class ListProperty(Property):
def __init__(self, contained, **kwargs):
"""
``contained`` should be a function which returns an object from the value.
"""
if inspect.isclass(contained) and issubclass(contained, Property):
# If it's a class and not an instance, instantiate it so that
# clean() can be called on it, and ListProperty.clean() will
# use __call__ when it appends the item.
self.contained = contained()
else:
self.contained = contained
super(ListProperty, self).__init__(**kwargs)
def clean(self, value):
try:
iter(value)
except TypeError:
raise ValueError("must be an iterable.")
if isinstance(value, (_STIXBase, string_types)):
value = [value]
result = []
for item in value:
try:
valid = self.contained.clean(item)
except ValueError:
raise
except AttributeError:
# type of list has no clean() function (eg. built in Python types)
# TODO Should we raise an error here?
valid = item
if type(self.contained) is EmbeddedObjectProperty:
obj_type = self.contained.type
elif type(self.contained).__name__ is 'STIXObjectProperty':
# ^ this way of checking doesn't require a circular import
# valid is already an instance of a python-stix2 class; no need
# to turn it into a dictionary and then pass it to the class
# constructor again
result.append(valid)
continue
elif type(self.contained) is DictionaryProperty:
obj_type = dict
else:
obj_type = self.contained
if isinstance(valid, collections.Mapping):
result.append(obj_type(**valid))
else:
result.append(obj_type(valid))
# STIX spec forbids empty lists
if len(result) < 1:
raise ValueError("must not be empty.")
return result
class StringProperty(Property):
def __init__(self, **kwargs):
self.string_type = text_type
super(StringProperty, self).__init__(**kwargs)
def clean(self, value):
return self.string_type(value)
class TypeProperty(Property):
def __init__(self, type):
super(TypeProperty, self).__init__(fixed=type)
class IDProperty(Property):
def __init__(self, type):
self.required_prefix = type + "--"
super(IDProperty, self).__init__()
def clean(self, value):
if not value.startswith(self.required_prefix):
raise ValueError("must start with '{0}'.".format(self.required_prefix))
try:
uuid.UUID(value.split('--', 1)[1])
except Exception:
raise ValueError("must have a valid UUID after the prefix.")
return value
def default(self):
return self.required_prefix + str(uuid.uuid4())
class IntegerProperty(Property):
def clean(self, value):
try:
return int(value)
except Exception:
raise ValueError("must be an integer.")
class FloatProperty(Property):
def clean(self, value):
try:
return float(value)
except Exception:
raise ValueError("must be a float.")
class BooleanProperty(Property):
def clean(self, value):
if isinstance(value, bool):
return value
trues = ['true', 't']
falses = ['false', 'f']
try:
if value.lower() in trues:
return True
if value.lower() in falses:
return False
except AttributeError:
if value == 1:
return True
if value == 0:
return False
raise ValueError("must be a boolean value.")
class TimestampProperty(Property):
def __init__(self, precision=None, **kwargs):
self.precision = precision
super(TimestampProperty, self).__init__(**kwargs)
def clean(self, value):
return parse_into_datetime(value, self.precision)
class DictionaryProperty(Property):
def clean(self, value):
try:
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) > 250:
raise DictionaryKeyError(k, "longer than 250 characters")
if not re.match('^[a-zA-Z0-9_-]+$', k):
raise DictionaryKeyError(k, "contains characters other than"
"lowercase a-z, uppercase A-Z, "
"numerals 0-9, hyphen (-), or "
"underscore (_)")
return dictified
HASHES_REGEX = {
"MD5": ("^[a-fA-F0-9]{32}$", "MD5"),
"MD6": ("^[a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{56}|[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}$", "MD6"),
"RIPEMD160": ("^[a-fA-F0-9]{40}$", "RIPEMD-160"),
"SHA1": ("^[a-fA-F0-9]{40}$", "SHA-1"),
"SHA224": ("^[a-fA-F0-9]{56}$", "SHA-224"),
"SHA256": ("^[a-fA-F0-9]{64}$", "SHA-256"),
"SHA384": ("^[a-fA-F0-9]{96}$", "SHA-384"),
"SHA512": ("^[a-fA-F0-9]{128}$", "SHA-512"),
"SHA3224": ("^[a-fA-F0-9]{56}$", "SHA3-224"),
"SHA3256": ("^[a-fA-F0-9]{64}$", "SHA3-256"),
"SHA3384": ("^[a-fA-F0-9]{96}$", "SHA3-384"),
"SHA3512": ("^[a-fA-F0-9]{128}$", "SHA3-512"),
"SSDEEP": ("^[a-zA-Z0-9/+:.]{1,128}$", "ssdeep"),
"WHIRLPOOL": ("^[a-fA-F0-9]{128}$", "WHIRLPOOL"),
}
class HashesProperty(DictionaryProperty):
def clean(self, value):
clean_dict = super(HashesProperty, self).clean(value)
for k, v in clean_dict.items():
key = k.upper().replace('-', '')
if key in HASHES_REGEX:
vocab_key = HASHES_REGEX[key][1]
if not re.match(HASHES_REGEX[key][0], v):
raise ValueError("'%s' is not a valid %s hash" % (v, vocab_key))
if k != vocab_key:
clean_dict[vocab_key] = clean_dict[k]
del clean_dict[k]
return clean_dict
class BinaryProperty(Property):
def clean(self, value):
try:
base64.b64decode(value)
except (binascii.Error, TypeError):
raise ValueError("must contain a base64 encoded string")
return value
class HexProperty(Property):
def clean(self, value):
if not re.match('^([a-fA-F0-9]{2})+$', value):
raise ValueError("must contain an even number of hexadecimal characters")
return value
REF_REGEX = re.compile("^[a-z][a-z-]+[a-z]--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}"
"-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
class ReferenceProperty(Property):
def __init__(self, required=False, type=None):
"""
references sometimes must be to a specific object type
"""
self.type = type
super(ReferenceProperty, self).__init__(required, type=type)
def clean(self, value):
if isinstance(value, _STIXBase):
value = value.id
value = str(value)
if self.type:
if not value.startswith(self.type):
raise ValueError("must start with '{0}'.".format(self.type))
if not REF_REGEX.match(value):
raise ValueError("must match <object-type>--<guid>.")
return value
SELECTOR_REGEX = re.compile("^[a-z0-9_-]{3,250}(\\.(\\[\\d+\\]|[a-z0-9_-]{1,250}))*$")
class SelectorProperty(Property):
def __init__(self, type=None):
# ignore type
super(SelectorProperty, self).__init__()
def clean(self, value):
if not SELECTOR_REGEX.match(value):
raise ValueError("must adhere to selector syntax.")
return value
class ObjectReferenceProperty(StringProperty):
def __init__(self, valid_types=None, **kwargs):
if valid_types and type(valid_types) is not list:
valid_types = [valid_types]
self.valid_types = valid_types
super(ObjectReferenceProperty, self).__init__(**kwargs)
class EmbeddedObjectProperty(Property):
def __init__(self, type, required=False):
self.type = type
super(EmbeddedObjectProperty, self).__init__(required, type=type)
def clean(self, value):
if type(value) is dict:
value = self.type(**value)
elif not isinstance(value, self.type):
raise ValueError("must be of type %s." % self.type.__name__)
return value
class EnumProperty(StringProperty):
def __init__(self, allowed, **kwargs):
if type(allowed) is not list:
allowed = list(allowed)
self.allowed = allowed
super(EnumProperty, self).__init__(**kwargs)
def clean(self, value):
value = super(EnumProperty, self).clean(value)
if value not in self.allowed:
raise ValueError("value '%s' is not valid for this enumeration." % value)
return self.string_type(value)
class PatternProperty(StringProperty):
def __init__(self, **kwargs):
super(PatternProperty, self).__init__(**kwargs)
def clean(self, value):
str_value = super(PatternProperty, self).clean(value)
errors = run_validator(str_value)
if errors:
raise ValueError(str(errors[0]))
return self.string_type(value)

View File

@ -3,18 +3,17 @@
from collections import OrderedDict
import re
import stix2
from ..base import _STIXBase
from ..core import _register_type
from ..markings import _MarkingsMixin
from ..properties import (BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, EnumProperty, FloatProperty,
IDProperty, IntegerProperty, ListProperty,
PatternProperty, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty)
from ..utils import NOW, TYPE_REGEX
from .common import ExternalReference, GranularMarking, KillChainPhase
from .observables import ObservableProperty
from .properties import (BooleanProperty, DictionaryProperty,
EmbeddedObjectProperty, EnumProperty, FloatProperty,
IDProperty, IntegerProperty, ListProperty,
PatternProperty, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty)
class STIXDomainObject(_STIXBase, _MarkingsMixin):
@ -585,7 +584,7 @@ def CustomObject(type='x-custom-type', properties=None):
return
raise e
stix2._register_type(_Custom, version="2.1")
_register_type(_Custom, version="2.1")
return _Custom
return custom_builder

View File

@ -4,11 +4,11 @@ from collections import OrderedDict
from ..base import _STIXBase
from ..markings import _MarkingsMixin
from ..properties import (BooleanProperty, IDProperty, IntegerProperty,
ListProperty, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty)
from ..utils import NOW
from .common import ExternalReference, GranularMarking
from .properties import (BooleanProperty, IDProperty, IntegerProperty,
ListProperty, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty)
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):