Merge branch 'master' into dev-extensions-proposal
commit
2528077eb0
|
@ -2,6 +2,7 @@
|
|||
|
||||
import collections.abc
|
||||
import copy
|
||||
import itertools
|
||||
import re
|
||||
import uuid
|
||||
|
||||
|
@ -13,7 +14,7 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|||
from .exceptions import (
|
||||
AtLeastOnePropertyError, DependentPropertiesError, ExtraPropertiesError,
|
||||
ImmutableError, InvalidObjRefError, InvalidValueError,
|
||||
MissingPropertiesError, MutuallyExclusivePropertiesError,
|
||||
MissingPropertiesError, MutuallyExclusivePropertiesError, STIXError,
|
||||
)
|
||||
from .markings import _MarkingsMixin
|
||||
from .markings.utils import validate
|
||||
|
@ -43,7 +44,7 @@ class _STIXBase(collections.abc.Mapping):
|
|||
|
||||
return all_properties
|
||||
|
||||
def _check_property(self, prop_name, prop, kwargs):
|
||||
def _check_property(self, prop_name, prop, kwargs, allow_custom):
|
||||
if prop_name not in kwargs:
|
||||
if hasattr(prop, 'default'):
|
||||
value = prop.default()
|
||||
|
@ -51,9 +52,12 @@ class _STIXBase(collections.abc.Mapping):
|
|||
value = self.__now
|
||||
kwargs[prop_name] = value
|
||||
|
||||
has_custom = False
|
||||
if prop_name in kwargs:
|
||||
try:
|
||||
kwargs[prop_name] = prop.clean(kwargs[prop_name])
|
||||
kwargs[prop_name], has_custom = prop.clean(
|
||||
kwargs[prop_name], allow_custom,
|
||||
)
|
||||
except InvalidValueError:
|
||||
# No point in wrapping InvalidValueError in another
|
||||
# InvalidValueError... so let those propagate.
|
||||
|
@ -63,7 +67,9 @@ class _STIXBase(collections.abc.Mapping):
|
|||
self.__class__, prop_name, reason=str(exc),
|
||||
) from exc
|
||||
|
||||
# inter-property constraint methods
|
||||
return has_custom
|
||||
|
||||
# interproperty constraint methods
|
||||
|
||||
def _check_mutually_exclusive_properties(self, list_of_properties, at_least_one=True):
|
||||
current_properties = self.properties_populated()
|
||||
|
@ -102,7 +108,6 @@ class _STIXBase(collections.abc.Mapping):
|
|||
|
||||
def __init__(self, allow_custom=False, **kwargs):
|
||||
cls = self.__class__
|
||||
self._allow_custom = allow_custom
|
||||
|
||||
# Use the same timestamp for any auto-generated datetimes
|
||||
self.__now = get_timestamp()
|
||||
|
@ -112,12 +117,11 @@ class _STIXBase(collections.abc.Mapping):
|
|||
if custom_props and not isinstance(custom_props, dict):
|
||||
raise ValueError("'custom_properties' must be a dictionary")
|
||||
|
||||
extra_kwargs = list(set(kwargs) - set(self._properties))
|
||||
extra_kwargs = kwargs.keys() - self._properties.keys()
|
||||
if extra_kwargs and issubclass(cls, stix2.v21._Extension):
|
||||
props_to_remove = ['extension_type']
|
||||
extra_kwargs = [prop for prop in extra_kwargs if prop not in props_to_remove]
|
||||
extra_kwargs = [prop for prop in extra_kwargs if prop != 'extension_type']
|
||||
|
||||
if extra_kwargs and not self._allow_custom:
|
||||
if extra_kwargs and not allow_custom:
|
||||
ext_found = False
|
||||
# This section performs a check on top-level objects that support extensions.
|
||||
# If extra_kwargs is not empty, allow_custom False, and the extension_type is not
|
||||
|
@ -132,11 +136,14 @@ class _STIXBase(collections.abc.Mapping):
|
|||
if ext_found is False:
|
||||
raise ExtraPropertiesError(cls, extra_kwargs)
|
||||
|
||||
if custom_props or extra_kwargs:
|
||||
self._allow_custom = True
|
||||
if isinstance(self, stix2.v21._STIXBase21):
|
||||
all_custom_prop_names = extra_kwargs
|
||||
all_custom_prop_names.extend(list(custom_props.keys()))
|
||||
if custom_props:
|
||||
# loophole for custom_properties...
|
||||
allow_custom = True
|
||||
|
||||
all_custom_prop_names = extra_kwargs | custom_props.keys() - \
|
||||
self._properties.keys()
|
||||
if all_custom_prop_names:
|
||||
if not isinstance(self, stix2.v20._STIXBase20):
|
||||
for prop_name in all_custom_prop_names:
|
||||
if not re.match(PREFIX_21_REGEX, prop_name):
|
||||
raise InvalidValueError(
|
||||
|
@ -145,12 +152,11 @@ class _STIXBase(collections.abc.Mapping):
|
|||
)
|
||||
|
||||
# Remove any keyword arguments whose value is None or [] (i.e. empty list)
|
||||
setting_kwargs = {}
|
||||
props = kwargs.copy()
|
||||
props.update(custom_props)
|
||||
for prop_name, prop_value in props.items():
|
||||
if prop_value is not None and prop_value != []:
|
||||
setting_kwargs[prop_name] = prop_value
|
||||
setting_kwargs = {
|
||||
k: v
|
||||
for k, v in itertools.chain(kwargs.items(), custom_props.items())
|
||||
if v is not None and v != []
|
||||
}
|
||||
|
||||
# Detect any missing required properties
|
||||
required_properties = set(get_required_properties(self._properties))
|
||||
|
@ -167,8 +173,13 @@ class _STIXBase(collections.abc.Mapping):
|
|||
if new_ext_check is False:
|
||||
raise MissingPropertiesError(cls, missing_kwargs)
|
||||
|
||||
has_custom = bool(all_custom_prop_names)
|
||||
for prop_name, prop_metadata in self._properties.items():
|
||||
self._check_property(prop_name, prop_metadata, setting_kwargs)
|
||||
temp_custom = self._check_property(
|
||||
prop_name, prop_metadata, setting_kwargs, allow_custom,
|
||||
)
|
||||
|
||||
has_custom = has_custom or temp_custom
|
||||
|
||||
# Cache defaulted optional properties for serialization
|
||||
defaulted = []
|
||||
|
@ -187,6 +198,22 @@ class _STIXBase(collections.abc.Mapping):
|
|||
|
||||
self._check_object_constraints()
|
||||
|
||||
if allow_custom:
|
||||
self.__has_custom = has_custom
|
||||
|
||||
else:
|
||||
# The simple case: our property cleaners are supposed to do their
|
||||
# job and prevent customizations, so we just set to False. But
|
||||
# this sanity check is helpful for finding bugs in those clean()
|
||||
# methods.
|
||||
if has_custom:
|
||||
raise STIXError(
|
||||
"Internal error: a clean() method did not properly enforce "
|
||||
"allow_custom=False!",
|
||||
)
|
||||
|
||||
self.__has_custom = False
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._inner[key]
|
||||
|
||||
|
@ -231,12 +258,15 @@ class _STIXBase(collections.abc.Mapping):
|
|||
if isinstance(self, _Observable):
|
||||
# Assume: valid references in the original object are still valid in the new version
|
||||
new_inner['_valid_refs'] = {'*': '*'}
|
||||
new_inner['allow_custom'] = self._allow_custom
|
||||
return cls(**new_inner)
|
||||
return cls(allow_custom=True, **new_inner)
|
||||
|
||||
def properties_populated(self):
|
||||
return list(self._inner.keys())
|
||||
|
||||
@property
|
||||
def has_custom(self):
|
||||
return self.__has_custom
|
||||
|
||||
# Versioning API
|
||||
|
||||
def new_version(self, **kwargs):
|
||||
|
@ -346,11 +376,10 @@ class _Observable(_STIXBase):
|
|||
if ref_type not in allowed_types:
|
||||
raise InvalidObjRefError(self.__class__, prop_name, "object reference '%s' is of an invalid type '%s'" % (ref, ref_type))
|
||||
|
||||
def _check_property(self, prop_name, prop, kwargs):
|
||||
super(_Observable, self)._check_property(prop_name, prop, kwargs)
|
||||
if prop_name not in kwargs:
|
||||
return
|
||||
def _check_property(self, prop_name, prop, kwargs, allow_custom):
|
||||
has_custom = super(_Observable, self)._check_property(prop_name, prop, kwargs, allow_custom)
|
||||
|
||||
if prop_name in kwargs:
|
||||
from .properties import ObjectReferenceProperty
|
||||
if prop_name.endswith('_ref'):
|
||||
if isinstance(prop, ObjectReferenceProperty):
|
||||
|
@ -361,6 +390,8 @@ class _Observable(_STIXBase):
|
|||
for ref in kwargs[prop_name]:
|
||||
self._check_ref(ref, prop, prop_name)
|
||||
|
||||
return has_custom
|
||||
|
||||
def _generate_id(self):
|
||||
"""
|
||||
Generate a UUIDv5 for this observable, using its "ID contributing
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
"""
|
||||
Library support for hash algorithms, independent of STIX specs.
|
||||
"""
|
||||
|
||||
import enum
|
||||
import re
|
||||
|
||||
|
||||
class Hash(enum.Enum):
|
||||
"""
|
||||
Instances represent a hash algorithm, independent of STIX spec version.
|
||||
Different spec versions may have different requirements for naming; this
|
||||
allows us to refer to and use hash algorithms in a spec-agnostic way.
|
||||
"""
|
||||
MD5 = 0
|
||||
MD6 = 1
|
||||
RIPEMD160 = 2
|
||||
SHA1 = 3
|
||||
SHA224 = 4
|
||||
SHA256 = 5
|
||||
SHA384 = 6
|
||||
SHA512 = 7
|
||||
SHA3224 = 8
|
||||
SHA3256 = 9
|
||||
SHA3384 = 10
|
||||
SHA3512 = 11
|
||||
SSDEEP = 12
|
||||
WHIRLPOOL = 13
|
||||
TLSH = 14
|
||||
|
||||
|
||||
# Regexes used to sanity check hash values. Could also be combined with the
|
||||
# enum values themselves using enum definition tricks, but... this seems
|
||||
# simpler.
|
||||
_HASH_REGEXES = {
|
||||
Hash.MD5: r"^[a-f0-9]{32}$",
|
||||
Hash.MD6: r"^[a-f0-9]{32}|[a-f0-9]{40}|[a-f0-9]{56}|[a-f0-9]{64}|[a-f0-9]{96}|[a-f0-9]{128}$",
|
||||
Hash.RIPEMD160: r"^[a-f0-9]{40}$",
|
||||
Hash.SHA1: r"^[a-f0-9]{40}$",
|
||||
Hash.SHA224: r"^[a-f0-9]{56}$",
|
||||
Hash.SHA256: r"^[a-f0-9]{64}$",
|
||||
Hash.SHA384: r"^[a-f0-9]{96}$",
|
||||
Hash.SHA512: r"^[a-f0-9]{128}$",
|
||||
Hash.SHA3224: r"^[a-f0-9]{56}$",
|
||||
Hash.SHA3256: r"^[a-f0-9]{64}$",
|
||||
Hash.SHA3384: r"^[a-f0-9]{96}$",
|
||||
Hash.SHA3512: r"^[a-f0-9]{128}$",
|
||||
Hash.SSDEEP: r"^[a-z0-9/+:.]{1,128}$",
|
||||
Hash.WHIRLPOOL: r"^[a-f0-9]{128}$",
|
||||
Hash.TLSH: r"^[a-f0-9]{70}$",
|
||||
}
|
||||
|
||||
|
||||
# compile all the regexes; be case-insensitive
|
||||
for hash_, re_str in _HASH_REGEXES.items():
|
||||
_HASH_REGEXES[hash_] = re.compile(re_str, re.I)
|
||||
|
||||
|
||||
def infer_hash_algorithm(name):
|
||||
"""
|
||||
Given a hash algorithm name, try to figure out which hash algorithm it
|
||||
refers to. This primarily enables some user flexibility in naming hash
|
||||
algorithms when creating STIX content.
|
||||
|
||||
:param name: A hash algorithm name
|
||||
:return: A Hash enum value if the name was recognized, or None if it was
|
||||
not recognized.
|
||||
"""
|
||||
enum_name = name.replace("-", "").upper()
|
||||
|
||||
try:
|
||||
enum_obj = Hash[enum_name]
|
||||
except KeyError:
|
||||
enum_obj = None
|
||||
|
||||
return enum_obj
|
||||
|
||||
|
||||
def check_hash(hash_, value):
|
||||
"""
|
||||
Sanity check the given hash value, against the given hash algorithm.
|
||||
|
||||
:param hash_: The hash algorithm, as one of the Hash enums
|
||||
:param value: A hash value as string
|
||||
:return: True if the value seems okay; False if not
|
||||
"""
|
||||
|
||||
# I guess there's no need to require a regex mapping for the algorithm...
|
||||
# Just assume it's okay if we have no way to check it.
|
||||
result = True
|
||||
regex = _HASH_REGEXES.get(hash_)
|
||||
if regex:
|
||||
result = bool(regex.match(value))
|
||||
|
||||
return result
|
|
@ -8,14 +8,18 @@ import inspect
|
|||
import re
|
||||
import uuid
|
||||
|
||||
from . import registry, version
|
||||
import stix2
|
||||
import stix2.hashes
|
||||
|
||||
from .base import _STIXBase
|
||||
from .exceptions import (
|
||||
CustomContentError, DictionaryKeyError, MissingPropertiesError,
|
||||
MutuallyExclusivePropertiesError, STIXError,
|
||||
)
|
||||
from .exceptions import CustomContentError, DictionaryKeyError, STIXError
|
||||
from .parsing import parse, parse_observable
|
||||
from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime
|
||||
from .registry import STIX2_OBJ_MAPS
|
||||
from .utils import (
|
||||
STIXTypeClass, _get_dict, get_class_hierarchy_names, get_type_from_id,
|
||||
is_object, is_stix_type, parse_into_datetime, to_enum,
|
||||
)
|
||||
from .version import DEFAULT_VERSION
|
||||
|
||||
TYPE_REGEX = re.compile(r'^-?[a-z0-9]+(-[a-z0-9]+)*-?$')
|
||||
TYPE_21_REGEX = re.compile(r'^([a-z][a-z0-9]*)+([a-z0-9-]+)*-?$')
|
||||
|
@ -129,11 +133,23 @@ class Property(object):
|
|||
|
||||
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 an exception.
|
||||
- ``def clean(self, value, allow_custom) -> (any, has_custom):``
|
||||
- Return a value that is valid for this property, and enforce and
|
||||
detect value customization. If ``value`` is not valid for this
|
||||
property, you may attempt to transform it first. If ``value`` is not
|
||||
valid and no such transformation is possible, it must raise an
|
||||
exception. The method is also responsible for enforcing and
|
||||
detecting customizations. If allow_custom is False, no customizations
|
||||
must be allowed. If any are encountered, an exception must be raised
|
||||
(e.g. CustomContentError). If none are encountered, False must be
|
||||
returned for has_custom. If allow_custom is True, then the clean()
|
||||
method is responsible for detecting any customizations in the value
|
||||
(just because the user has elected to allow customizations doesn't
|
||||
mean there actually are any). The method must return an appropriate
|
||||
value for has_custom. Customization may not be applicable/possible
|
||||
for a property. In that case, allow_custom can be ignored, and
|
||||
has_custom must be returned as False.
|
||||
|
||||
- ``def default(self):``
|
||||
- provide a default value for this property.
|
||||
- ``default()`` can return the special value ``NOW`` to use the current
|
||||
|
@ -154,10 +170,10 @@ class Property(object):
|
|||
|
||||
"""
|
||||
|
||||
def _default_clean(self, value):
|
||||
def _default_clean(self, value, allow_custom=False):
|
||||
if value != self._fixed_value:
|
||||
raise ValueError("must equal '{}'.".format(self._fixed_value))
|
||||
return value
|
||||
return value, False
|
||||
|
||||
def __init__(self, required=False, fixed=None, default=None):
|
||||
self.required = required
|
||||
|
@ -175,14 +191,8 @@ class Property(object):
|
|||
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
|
||||
def clean(self, value, allow_custom=False):
|
||||
return value, False
|
||||
|
||||
|
||||
class ListProperty(Property):
|
||||
|
@ -214,7 +224,7 @@ class ListProperty(Property):
|
|||
|
||||
super(ListProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom):
|
||||
try:
|
||||
iter(value)
|
||||
except TypeError:
|
||||
|
@ -223,21 +233,22 @@ class ListProperty(Property):
|
|||
if isinstance(value, (_STIXBase, str)):
|
||||
value = [value]
|
||||
|
||||
result = []
|
||||
has_custom = False
|
||||
if isinstance(self.contained, Property):
|
||||
result = [
|
||||
self.contained.clean(item)
|
||||
for item in value
|
||||
]
|
||||
for item in value:
|
||||
valid, temp_custom = self.contained.clean(item, allow_custom)
|
||||
result.append(valid)
|
||||
has_custom = has_custom or temp_custom
|
||||
|
||||
else: # self.contained must be a _STIXBase subclass
|
||||
result = []
|
||||
for item in value:
|
||||
if isinstance(item, self.contained):
|
||||
valid = item
|
||||
|
||||
elif isinstance(item, collections.abc.Mapping):
|
||||
# attempt a mapping-like usage...
|
||||
valid = self.contained(**item)
|
||||
valid = self.contained(allow_custom=allow_custom, **item)
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
|
@ -247,12 +258,16 @@ class ListProperty(Property):
|
|||
)
|
||||
|
||||
result.append(valid)
|
||||
has_custom = has_custom or valid.has_custom
|
||||
|
||||
if not allow_custom and has_custom:
|
||||
raise CustomContentError("custom content encountered")
|
||||
|
||||
# STIX spec forbids empty lists
|
||||
if len(result) < 1:
|
||||
raise ValueError("must not be empty.")
|
||||
|
||||
return result
|
||||
return result, has_custom
|
||||
|
||||
|
||||
class StringProperty(Property):
|
||||
|
@ -260,15 +275,15 @@ class StringProperty(Property):
|
|||
def __init__(self, **kwargs):
|
||||
super(StringProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
if not isinstance(value, str):
|
||||
return str(value)
|
||||
return value
|
||||
value = str(value)
|
||||
return value, False
|
||||
|
||||
|
||||
class TypeProperty(Property):
|
||||
|
||||
def __init__(self, type, spec_version=version.DEFAULT_VERSION):
|
||||
def __init__(self, type, spec_version=DEFAULT_VERSION):
|
||||
_validate_type(type, spec_version)
|
||||
self.spec_version = spec_version
|
||||
super(TypeProperty, self).__init__(fixed=type)
|
||||
|
@ -276,14 +291,14 @@ class TypeProperty(Property):
|
|||
|
||||
class IDProperty(Property):
|
||||
|
||||
def __init__(self, type, spec_version=version.DEFAULT_VERSION):
|
||||
def __init__(self, type, spec_version=DEFAULT_VERSION):
|
||||
self.required_prefix = type + "--"
|
||||
self.spec_version = spec_version
|
||||
super(IDProperty, self).__init__()
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
_validate_id(value, self.spec_version, self.required_prefix)
|
||||
return value
|
||||
return value, False
|
||||
|
||||
def default(self):
|
||||
return self.required_prefix + str(uuid.uuid4())
|
||||
|
@ -296,7 +311,7 @@ class IntegerProperty(Property):
|
|||
self.max = max
|
||||
super(IntegerProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
try:
|
||||
value = int(value)
|
||||
except Exception:
|
||||
|
@ -310,7 +325,7 @@ class IntegerProperty(Property):
|
|||
msg = "maximum value is {}. received {}".format(self.max, value)
|
||||
raise ValueError(msg)
|
||||
|
||||
return value
|
||||
return value, False
|
||||
|
||||
|
||||
class FloatProperty(Property):
|
||||
|
@ -320,7 +335,7 @@ class FloatProperty(Property):
|
|||
self.max = max
|
||||
super(FloatProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
|
@ -334,30 +349,27 @@ class FloatProperty(Property):
|
|||
msg = "maximum value is {}. received {}".format(self.max, value)
|
||||
raise ValueError(msg)
|
||||
|
||||
return value
|
||||
return value, False
|
||||
|
||||
|
||||
class BooleanProperty(Property):
|
||||
_trues = ['true', 't', '1', 1, True]
|
||||
_falses = ['false', 'f', '0', 0, False]
|
||||
|
||||
def clean(self, value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
def clean(self, value, allow_custom=False):
|
||||
|
||||
trues = ['true', 't', '1']
|
||||
falses = ['false', 'f', '0']
|
||||
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
|
||||
if isinstance(value, str):
|
||||
value = value.lower()
|
||||
|
||||
if value in self._trues:
|
||||
result = True
|
||||
elif value in self._falses:
|
||||
result = False
|
||||
else:
|
||||
raise ValueError("must be a boolean value.")
|
||||
|
||||
return result, False
|
||||
|
||||
|
||||
class TimestampProperty(Property):
|
||||
|
||||
|
@ -367,19 +379,19 @@ class TimestampProperty(Property):
|
|||
|
||||
super(TimestampProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
return parse_into_datetime(
|
||||
value, self.precision, self.precision_constraint,
|
||||
)
|
||||
), False
|
||||
|
||||
|
||||
class DictionaryProperty(Property):
|
||||
|
||||
def __init__(self, spec_version=version.DEFAULT_VERSION, **kwargs):
|
||||
def __init__(self, spec_version=DEFAULT_VERSION, **kwargs):
|
||||
self.spec_version = spec_version
|
||||
super(DictionaryProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
try:
|
||||
dictified = _get_dict(value)
|
||||
except ValueError:
|
||||
|
@ -404,140 +416,202 @@ class DictionaryProperty(Property):
|
|||
if len(dictified) < 1:
|
||||
raise ValueError("must not be empty.")
|
||||
|
||||
return dictified
|
||||
|
||||
|
||||
HASHES_REGEX = {
|
||||
"MD5": (r"^[a-fA-F0-9]{32}$", "MD5"),
|
||||
"MD6": (r"^[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": (r"^[a-fA-F0-9]{40}$", "RIPEMD-160"),
|
||||
"SHA1": (r"^[a-fA-F0-9]{40}$", "SHA-1"),
|
||||
"SHA224": (r"^[a-fA-F0-9]{56}$", "SHA-224"),
|
||||
"SHA256": (r"^[a-fA-F0-9]{64}$", "SHA-256"),
|
||||
"SHA384": (r"^[a-fA-F0-9]{96}$", "SHA-384"),
|
||||
"SHA512": (r"^[a-fA-F0-9]{128}$", "SHA-512"),
|
||||
"SHA3224": (r"^[a-fA-F0-9]{56}$", "SHA3-224"),
|
||||
"SHA3256": (r"^[a-fA-F0-9]{64}$", "SHA3-256"),
|
||||
"SHA3384": (r"^[a-fA-F0-9]{96}$", "SHA3-384"),
|
||||
"SHA3512": (r"^[a-fA-F0-9]{128}$", "SHA3-512"),
|
||||
"SSDEEP": (r"^[a-zA-Z0-9/+:.]{1,128}$", "SSDEEP"),
|
||||
"WHIRLPOOL": (r"^[a-fA-F0-9]{128}$", "WHIRLPOOL"),
|
||||
"TLSH": (r"^[a-fA-F0-9]{70}$", "TLSH"),
|
||||
}
|
||||
return dictified, False
|
||||
|
||||
|
||||
class HashesProperty(DictionaryProperty):
|
||||
|
||||
def clean(self, value):
|
||||
clean_dict = super(HashesProperty, self).clean(value)
|
||||
for k, v in copy.deepcopy(clean_dict).items():
|
||||
key = k.upper().replace('-', '')
|
||||
if key in HASHES_REGEX:
|
||||
vocab_key = HASHES_REGEX[key][1]
|
||||
if vocab_key == "SSDEEP" and self.spec_version == "2.0":
|
||||
vocab_key = vocab_key.lower()
|
||||
if not re.match(HASHES_REGEX[key][0], v):
|
||||
raise ValueError("'{0}' is not a valid {1} hash".format(v, vocab_key))
|
||||
if k != vocab_key:
|
||||
clean_dict[vocab_key] = clean_dict[k]
|
||||
del clean_dict[k]
|
||||
return clean_dict
|
||||
def __init__(self, spec_hash_names, spec_version=DEFAULT_VERSION, **kwargs):
|
||||
super().__init__(spec_version=spec_version, **kwargs)
|
||||
|
||||
self.__spec_hash_names = spec_hash_names
|
||||
|
||||
# Map hash algorithm enum to the given spec mandated name, for those
|
||||
# names which are recognized as hash algorithms by this library.
|
||||
self.__alg_to_spec_name = {}
|
||||
for spec_hash_name in spec_hash_names:
|
||||
alg = stix2.hashes.infer_hash_algorithm(spec_hash_name)
|
||||
if alg:
|
||||
self.__alg_to_spec_name[alg] = spec_hash_name
|
||||
|
||||
def clean(self, value, allow_custom):
|
||||
# ignore the has_custom return value here; there is no customization
|
||||
# of DictionaryProperties.
|
||||
clean_dict, _ = super().clean(value, allow_custom)
|
||||
|
||||
spec_dict = {}
|
||||
|
||||
has_custom = False
|
||||
for hash_k, hash_v in clean_dict.items():
|
||||
hash_alg = stix2.hashes.infer_hash_algorithm(hash_k)
|
||||
|
||||
if hash_alg:
|
||||
# Library-supported hash algorithm: sanity check the value.
|
||||
if not stix2.hashes.check_hash(hash_alg, hash_v):
|
||||
raise ValueError(
|
||||
"'{0}' is not a valid {1} hash".format(
|
||||
hash_v, hash_alg.name,
|
||||
),
|
||||
)
|
||||
|
||||
spec_name = self.__alg_to_spec_name.get(hash_alg)
|
||||
if not spec_name:
|
||||
# There is library support for the hash algorithm, but it's
|
||||
# not in the spec. So it's custom. Just use the user's
|
||||
# name as-is.
|
||||
has_custom = True
|
||||
spec_name = hash_k
|
||||
|
||||
else:
|
||||
# Unrecognized hash algorithm; use as-is. Hash algorithm name
|
||||
# must be an exact match from spec, or it will be considered
|
||||
# custom.
|
||||
spec_name = hash_k
|
||||
if spec_name not in self.__spec_hash_names:
|
||||
has_custom = True
|
||||
|
||||
if not allow_custom and has_custom:
|
||||
raise CustomContentError(
|
||||
"custom hash algorithm: " + hash_k,
|
||||
)
|
||||
|
||||
spec_dict[spec_name] = hash_v
|
||||
|
||||
return spec_dict, has_custom
|
||||
|
||||
|
||||
class BinaryProperty(Property):
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
try:
|
||||
base64.b64decode(value)
|
||||
except (binascii.Error, TypeError):
|
||||
raise ValueError("must contain a base64 encoded string")
|
||||
return value
|
||||
return value, False
|
||||
|
||||
|
||||
class HexProperty(Property):
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
if not re.match(r"^([a-fA-F0-9]{2})+$", value):
|
||||
raise ValueError("must contain an even number of hexadecimal characters")
|
||||
return value
|
||||
return value, False
|
||||
|
||||
|
||||
class ReferenceProperty(Property):
|
||||
|
||||
def __init__(self, valid_types=None, invalid_types=None, spec_version=version.DEFAULT_VERSION, **kwargs):
|
||||
_WHITELIST, _BLACKLIST = range(2)
|
||||
|
||||
def __init__(self, valid_types=None, invalid_types=None, spec_version=DEFAULT_VERSION, **kwargs):
|
||||
"""
|
||||
references sometimes must be to a specific object type
|
||||
"""
|
||||
self.spec_version = spec_version
|
||||
|
||||
# These checks need to be done prior to the STIX object finishing construction
|
||||
# and thus we can't use base.py's _check_mutually_exclusive_properties()
|
||||
# in the typical location of _check_object_constraints() in sdo.py
|
||||
if valid_types and invalid_types:
|
||||
raise MutuallyExclusivePropertiesError(self.__class__, ['invalid_types', 'valid_types'])
|
||||
elif valid_types is None and invalid_types is None:
|
||||
raise MissingPropertiesError(self.__class__, ['invalid_types', 'valid_types'])
|
||||
if (valid_types is not None and invalid_types is not None) or \
|
||||
(valid_types is None and invalid_types is None):
|
||||
raise ValueError(
|
||||
"Exactly one of 'valid_types' and 'invalid_types' must be "
|
||||
"given",
|
||||
)
|
||||
|
||||
if valid_types and type(valid_types) is not list:
|
||||
if valid_types and not isinstance(valid_types, list):
|
||||
valid_types = [valid_types]
|
||||
elif invalid_types and type(invalid_types) is not list:
|
||||
elif invalid_types and not isinstance(invalid_types, list):
|
||||
invalid_types = [invalid_types]
|
||||
|
||||
self.valid_types = valid_types
|
||||
self.invalid_types = invalid_types
|
||||
if valid_types is not None and len(valid_types) == 0:
|
||||
raise ValueError("Impossible type constraint: empty whitelist")
|
||||
|
||||
self.auth_type = self._WHITELIST if valid_types else self._BLACKLIST
|
||||
|
||||
# Divide type requirements into generic type classes and specific
|
||||
# types. With respect to strings, values recognized as STIXTypeClass
|
||||
# enum names are generic; all else are specifics.
|
||||
self.generics = set()
|
||||
self.specifics = set()
|
||||
types = valid_types or invalid_types
|
||||
for type_ in types:
|
||||
try:
|
||||
enum_value = to_enum(type_, STIXTypeClass)
|
||||
except KeyError:
|
||||
self.specifics.add(type_)
|
||||
else:
|
||||
self.generics.add(enum_value)
|
||||
|
||||
super(ReferenceProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom):
|
||||
if isinstance(value, _STIXBase):
|
||||
value = value.id
|
||||
value = str(value)
|
||||
|
||||
possible_prefix = value[:value.index('--')]
|
||||
_validate_id(value, self.spec_version, None)
|
||||
|
||||
if self.valid_types:
|
||||
ref_valid_types = enumerate_types(self.valid_types, self.spec_version)
|
||||
obj_type = get_type_from_id(value)
|
||||
|
||||
# Only comes into play when inverting a hybrid whitelist.
|
||||
# E.g. if the possible generic categories are A, B, C, then the
|
||||
# inversion of whitelist constraint "A or x" (where x is a specific
|
||||
# type) is something like "[not (B or C)] or x". In other words, we
|
||||
# invert the generic categories to produce a blacklist, but leave the
|
||||
# specific categories alone; they essentially become exceptions to our
|
||||
# blacklist.
|
||||
blacklist_exceptions = set()
|
||||
|
||||
generics = self.generics
|
||||
specifics = self.specifics
|
||||
auth_type = self.auth_type
|
||||
if allow_custom and auth_type == self._WHITELIST and generics:
|
||||
# If allowing customization and using a whitelist, and if generic
|
||||
# "category" types were given, we need to allow custom object types
|
||||
# of those categories. Unless registered, it's impossible to know
|
||||
# whether a given type is within a given category. So we take a
|
||||
# permissive approach and allow any type which is not known to be
|
||||
# in the wrong category. I.e. flip the whitelist set to a
|
||||
# blacklist of a complementary set.
|
||||
auth_type = self._BLACKLIST
|
||||
generics = set(STIXTypeClass) - generics
|
||||
blacklist_exceptions, specifics = specifics, blacklist_exceptions
|
||||
|
||||
if auth_type == self._WHITELIST:
|
||||
type_ok = is_stix_type(
|
||||
obj_type, self.spec_version, *generics
|
||||
) or obj_type in specifics
|
||||
|
||||
if possible_prefix in ref_valid_types:
|
||||
required_prefix = possible_prefix
|
||||
else:
|
||||
raise ValueError("The type-specifying prefix '%s' for this property is not valid" % (possible_prefix))
|
||||
elif self.invalid_types:
|
||||
ref_invalid_types = enumerate_types(self.invalid_types, self.spec_version)
|
||||
type_ok = (
|
||||
not is_stix_type(
|
||||
obj_type, self.spec_version, *generics
|
||||
) and obj_type not in specifics
|
||||
) or obj_type in blacklist_exceptions
|
||||
|
||||
if possible_prefix not in ref_invalid_types:
|
||||
required_prefix = possible_prefix
|
||||
# We need to figure out whether the referenced object is custom or
|
||||
# not. No good way to do that at present... just check if
|
||||
# unregistered and for the "x-" type prefix, for now?
|
||||
has_custom = not is_object(obj_type, self.spec_version) \
|
||||
or obj_type.startswith("x-")
|
||||
|
||||
if not type_ok:
|
||||
types = self.specifics.union(self.generics)
|
||||
types = ", ".join(x.name if isinstance(x, STIXTypeClass) else x for x in types)
|
||||
if self.auth_type == self._WHITELIST:
|
||||
msg = "not one of the valid types for this property: %s." % types
|
||||
else:
|
||||
raise ValueError("An invalid type-specifying prefix '%s' was specified for this property" % (possible_prefix))
|
||||
msg = "one of the invalid types for this property: %s." % types
|
||||
if not allow_custom and has_custom:
|
||||
msg += " A custom object type may be allowed with allow_custom=True."
|
||||
raise ValueError(
|
||||
"The type-specifying prefix '%s' for this property is %s"
|
||||
% (obj_type, msg),
|
||||
)
|
||||
|
||||
_validate_id(value, self.spec_version, required_prefix)
|
||||
if not allow_custom and has_custom:
|
||||
raise CustomContentError(
|
||||
"reference to custom object type: " + obj_type,
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def enumerate_types(types, spec_version):
|
||||
"""
|
||||
`types` is meant to be a list; it may contain specific object types and/or
|
||||
the any of the words "SCO", "SDO", or "SRO"
|
||||
|
||||
Since "SCO", "SDO", and "SRO" are general types that encompass various specific object types,
|
||||
once each of those words is being processed, that word will be removed from `return_types`,
|
||||
so as not to mistakenly allow objects to be created of types "SCO", "SDO", or "SRO"
|
||||
"""
|
||||
return_types = []
|
||||
return_types += types
|
||||
|
||||
if "SDO" in types:
|
||||
return_types.remove("SDO")
|
||||
return_types += registry.STIX2_OBJ_MAPS[spec_version]['objects'].keys()
|
||||
if "SCO" in types:
|
||||
return_types.remove("SCO")
|
||||
return_types += registry.STIX2_OBJ_MAPS[spec_version]['observables'].keys()
|
||||
if "SRO" in types:
|
||||
return_types.remove("SRO")
|
||||
return_types += ['relationship', 'sighting']
|
||||
|
||||
return return_types
|
||||
return value, has_custom
|
||||
|
||||
|
||||
SELECTOR_REGEX = re.compile(r"^([a-z0-9_-]{3,250}(\.(\[\d+\]|[a-z0-9_-]{1,250}))*|id)$")
|
||||
|
@ -545,10 +619,10 @@ SELECTOR_REGEX = re.compile(r"^([a-z0-9_-]{3,250}(\.(\[\d+\]|[a-z0-9_-]{1,250}))
|
|||
|
||||
class SelectorProperty(Property):
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
if not SELECTOR_REGEX.match(value):
|
||||
raise ValueError("must adhere to selector syntax.")
|
||||
return value
|
||||
return value, False
|
||||
|
||||
|
||||
class ObjectReferenceProperty(StringProperty):
|
||||
|
@ -566,28 +640,74 @@ class EmbeddedObjectProperty(Property):
|
|||
self.type = type
|
||||
super(EmbeddedObjectProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
if type(value) is dict:
|
||||
value = self.type(**value)
|
||||
def clean(self, value, allow_custom):
|
||||
if isinstance(value, dict):
|
||||
value = self.type(allow_custom=allow_custom, **value)
|
||||
elif not isinstance(value, self.type):
|
||||
raise ValueError("must be of type {}.".format(self.type.__name__))
|
||||
return value
|
||||
|
||||
has_custom = False
|
||||
if isinstance(value, _STIXBase):
|
||||
has_custom = value.has_custom
|
||||
|
||||
if not allow_custom and has_custom:
|
||||
raise CustomContentError("custom content encountered")
|
||||
|
||||
return value, has_custom
|
||||
|
||||
|
||||
class EnumProperty(StringProperty):
|
||||
"""
|
||||
Used for enumeration type properties. Properties of this type do not allow
|
||||
customization.
|
||||
"""
|
||||
|
||||
def __init__(self, allowed, **kwargs):
|
||||
if type(allowed) is not list:
|
||||
allowed = list(allowed)
|
||||
if isinstance(allowed, str):
|
||||
allowed = [allowed]
|
||||
self.allowed = allowed
|
||||
super(EnumProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
cleaned_value = super(EnumProperty, self).clean(value)
|
||||
def clean(self, value, allow_custom):
|
||||
cleaned_value, _ = super(EnumProperty, self).clean(value, allow_custom)
|
||||
|
||||
if cleaned_value not in self.allowed:
|
||||
raise ValueError("value '{}' is not valid for this enumeration.".format(cleaned_value))
|
||||
|
||||
return cleaned_value
|
||||
return cleaned_value, False
|
||||
|
||||
|
||||
class OpenVocabProperty(StringProperty):
|
||||
"""
|
||||
Used for open vocab type properties.
|
||||
"""
|
||||
|
||||
def __init__(self, allowed, **kwargs):
|
||||
super(OpenVocabProperty, self).__init__(**kwargs)
|
||||
|
||||
if isinstance(allowed, str):
|
||||
allowed = [allowed]
|
||||
self.allowed = allowed
|
||||
|
||||
def clean(self, value, allow_custom):
|
||||
cleaned_value, _ = super(OpenVocabProperty, self).clean(
|
||||
value, allow_custom,
|
||||
)
|
||||
|
||||
# Disabled: it was decided that enforcing this is too strict (might
|
||||
# break too much user code). Revisit when we have the capability for
|
||||
# more granular config settings when creating objects.
|
||||
#
|
||||
# has_custom = cleaned_value not in self.allowed
|
||||
#
|
||||
# if not allow_custom and has_custom:
|
||||
# raise CustomContentError(
|
||||
# "custom value in open vocab: '{}'".format(cleaned_value),
|
||||
# )
|
||||
|
||||
has_custom = False
|
||||
|
||||
return cleaned_value, has_custom
|
||||
|
||||
|
||||
class PatternProperty(StringProperty):
|
||||
|
@ -598,12 +718,11 @@ class ObservableProperty(Property):
|
|||
"""Property for holding Cyber Observable Objects.
|
||||
"""
|
||||
|
||||
def __init__(self, spec_version=version.DEFAULT_VERSION, allow_custom=False, *args, **kwargs):
|
||||
self.allow_custom = allow_custom
|
||||
def __init__(self, spec_version=DEFAULT_VERSION, *args, **kwargs):
|
||||
self.spec_version = spec_version
|
||||
super(ObservableProperty, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom):
|
||||
try:
|
||||
dictified = _get_dict(value)
|
||||
# get deep copy since we are going modify the dict and might
|
||||
|
@ -617,27 +736,41 @@ class ObservableProperty(Property):
|
|||
|
||||
valid_refs = {k: v['type'] for (k, v) in dictified.items()}
|
||||
|
||||
has_custom = False
|
||||
for key, obj in dictified.items():
|
||||
parsed_obj = parse_observable(
|
||||
obj,
|
||||
valid_refs,
|
||||
allow_custom=self.allow_custom,
|
||||
allow_custom=allow_custom,
|
||||
version=self.spec_version,
|
||||
)
|
||||
|
||||
if isinstance(parsed_obj, _STIXBase):
|
||||
has_custom = has_custom or parsed_obj.has_custom
|
||||
else:
|
||||
# we get dicts for unregistered custom objects
|
||||
has_custom = True
|
||||
|
||||
if not allow_custom and has_custom:
|
||||
raise CustomContentError(
|
||||
"customized {} observable found".format(
|
||||
parsed_obj["type"],
|
||||
),
|
||||
)
|
||||
|
||||
dictified[key] = parsed_obj
|
||||
|
||||
return dictified
|
||||
return dictified, has_custom
|
||||
|
||||
|
||||
class ExtensionsProperty(DictionaryProperty):
|
||||
"""Property for representing extensions on Observable objects.
|
||||
"""
|
||||
|
||||
def __init__(self, spec_version=version.DEFAULT_VERSION, allow_custom=False, required=False):
|
||||
self.allow_custom = allow_custom
|
||||
def __init__(self, spec_version=DEFAULT_VERSION, required=False):
|
||||
super(ExtensionsProperty, self).__init__(spec_version=spec_version, required=required)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom):
|
||||
try:
|
||||
dictified = _get_dict(value)
|
||||
# get deep copy since we are going modify the dict and might
|
||||
|
@ -647,40 +780,50 @@ class ExtensionsProperty(DictionaryProperty):
|
|||
except ValueError:
|
||||
raise ValueError("The extensions property must contain a dictionary")
|
||||
|
||||
extension_type_map = registry.STIX2_OBJ_MAPS[self.spec_version].get('extensions', {})
|
||||
has_custom = False
|
||||
extension_type_map = STIX2_OBJ_MAPS[self.spec_version].get('extensions', {})
|
||||
for key, subvalue in dictified.items():
|
||||
if key in extension_type_map:
|
||||
cls = extension_type_map[key]
|
||||
if type(subvalue) is dict:
|
||||
if self.allow_custom:
|
||||
subvalue['allow_custom'] = True
|
||||
dictified[key] = cls(**subvalue)
|
||||
else:
|
||||
dictified[key] = cls(**subvalue)
|
||||
ext = cls(allow_custom=allow_custom, **subvalue)
|
||||
elif type(subvalue) is cls:
|
||||
# If already an instance of an _Extension class, assume it's valid
|
||||
dictified[key] = subvalue
|
||||
ext = subvalue
|
||||
else:
|
||||
raise ValueError("Cannot determine extension type.")
|
||||
|
||||
has_custom = has_custom or ext.has_custom
|
||||
|
||||
if not allow_custom and has_custom:
|
||||
raise CustomContentError(
|
||||
"custom content found in {} extension".format(
|
||||
key,
|
||||
),
|
||||
)
|
||||
|
||||
dictified[key] = ext
|
||||
|
||||
else:
|
||||
if self.allow_custom:
|
||||
if allow_custom:
|
||||
has_custom = True
|
||||
dictified[key] = subvalue
|
||||
elif key.startswith('extension-definition--'):
|
||||
_validate_id(key, '2.1', 'extension-definition')
|
||||
dictified[key] = subvalue
|
||||
else:
|
||||
raise CustomContentError("Can't parse unknown extension type: {}".format(key))
|
||||
return dictified
|
||||
|
||||
return dictified, has_custom
|
||||
|
||||
|
||||
class STIXObjectProperty(Property):
|
||||
|
||||
def __init__(self, spec_version=version.DEFAULT_VERSION, allow_custom=False, *args, **kwargs):
|
||||
self.allow_custom = allow_custom
|
||||
def __init__(self, spec_version=DEFAULT_VERSION, *args, **kwargs):
|
||||
self.spec_version = spec_version
|
||||
super(STIXObjectProperty, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom):
|
||||
# Any STIX Object (SDO, SRO, or Marking Definition) can be added to
|
||||
# a bundle with no further checks.
|
||||
stix2_classes = {'_DomainObject', '_RelationshipObject', 'MarkingDefinition'}
|
||||
|
@ -700,7 +843,11 @@ class STIXObjectProperty(Property):
|
|||
"containing objects of a different spec "
|
||||
"version.",
|
||||
)
|
||||
return value
|
||||
|
||||
if not allow_custom and value.has_custom:
|
||||
raise CustomContentError("custom content encountered")
|
||||
|
||||
return value, value.has_custom
|
||||
try:
|
||||
dictified = _get_dict(value)
|
||||
except ValueError:
|
||||
|
@ -716,6 +863,22 @@ class STIXObjectProperty(Property):
|
|||
"containing objects of a different spec version.",
|
||||
)
|
||||
|
||||
parsed_obj = parse(dictified, allow_custom=self.allow_custom)
|
||||
parsed_obj = parse(dictified, allow_custom=allow_custom)
|
||||
|
||||
return parsed_obj
|
||||
if isinstance(parsed_obj, _STIXBase):
|
||||
has_custom = parsed_obj.has_custom
|
||||
else:
|
||||
# we get dicts for unregistered custom objects
|
||||
has_custom = True
|
||||
|
||||
if not allow_custom and has_custom:
|
||||
# parse() will ignore the caller's allow_custom=False request if
|
||||
# the object type is registered and dictified has a
|
||||
# "custom_properties" key. So we have to do another check here.
|
||||
raise CustomContentError(
|
||||
"customized {} object found".format(
|
||||
parsed_obj["type"],
|
||||
),
|
||||
)
|
||||
|
||||
return parsed_obj, has_custom
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import pytest
|
||||
|
||||
from stix2.hashes import Hash, check_hash, infer_hash_algorithm
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hash_name, expected_alg", [
|
||||
("md5", Hash.MD5),
|
||||
("md6", Hash.MD6),
|
||||
("ripemd160", Hash.RIPEMD160),
|
||||
("sha1", Hash.SHA1),
|
||||
("sha224", Hash.SHA224),
|
||||
("sha256", Hash.SHA256),
|
||||
("sha384", Hash.SHA384),
|
||||
("sha512", Hash.SHA512),
|
||||
("sha3224", Hash.SHA3224),
|
||||
("sha3256", Hash.SHA3256),
|
||||
("sha3384", Hash.SHA3384),
|
||||
("sha3512", Hash.SHA3512),
|
||||
("ssdeep", Hash.SSDEEP),
|
||||
("whirlpool", Hash.WHIRLPOOL),
|
||||
("tlsh", Hash.TLSH),
|
||||
("xxxx", None),
|
||||
],
|
||||
)
|
||||
def test_hash_inference(hash_name, expected_alg):
|
||||
alg = infer_hash_algorithm(hash_name)
|
||||
assert alg == expected_alg
|
||||
|
||||
# Try some other name variations
|
||||
alg = infer_hash_algorithm(hash_name[0].upper() + hash_name[1:])
|
||||
assert alg == expected_alg
|
||||
|
||||
alg = infer_hash_algorithm("-"+hash_name)
|
||||
assert alg == expected_alg
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hash_alg, hash_value", [
|
||||
(Hash.MD5, "f9e40b9aa5464f3dae711ca524fceb63"),
|
||||
(Hash.MD6, "f9e40b9aa5464f3dae711ca524fceb63"),
|
||||
(Hash.RIPEMD160, "8ae5d2e6b1f3a514257f2469b637454931844aeb"),
|
||||
(Hash.SHA1, "f2c7d4185880c0adcbb4a01d020a69498b16210e"),
|
||||
(Hash.SHA224, "6743ed70cc26e750ad0108b6b8ad7fc2780c550f7d78adefa04dda05"),
|
||||
(Hash.SHA256, "a2d1c2081aa932fe72307ab076b9739455bc7a21b3bed367bd9a86ae27af5a40"),
|
||||
(Hash.SHA384, "bc846457de707f97bce93cca23b5ea58c0326fd8b79ef7b523ba1d0a792f22868732e53a5dcf2f9e3b89eecca9c9b4e3"),
|
||||
(Hash.SHA512, "896e45c82f9d8ba917d4f95891c967b88304b0a67ccc59aac813ee7ab3bc700bf9ce559e283c35ddba619755f6b70bdff2a07dc9cd337576a143a2aa361d08b1"),
|
||||
(Hash.SHA3224, "37cb283bc9f6ecf0f94e92d5bd4c1e061ae00d7ed85804d18f981f53"),
|
||||
(Hash.SHA3256, "d5fc146e37d4fddaeaa57aa88390be5c9ca6bcb18ae1bf2346cbfc36d3310ea2"),
|
||||
(Hash.SHA3384, "ac97414589b2ef59a87dc5277d156b6cfc8f6b92b7c0e889d8f38a235dd9c1ba4030321beddd13f29519390ba914f70f"),
|
||||
(Hash.SHA3512, "8dc580ad3abc6305ce5ada7c5920c763720c7733c2a94d28dd5351ffbc162b6b6d21371d91d6559124159025172e19896e09889047aac4ef555cc55456e14b0a"),
|
||||
(Hash.SSDEEP, "3:AXGBicFlgVNhBGcL6wCrFQEv:AXGHsNhxLsr2C"),
|
||||
(Hash.WHIRLPOOL, "b752b6eeb497a8bebfc1be1649ca41d57fd1973bffc2261ca196b5474e0f353762f354c1d743581f61c51f4d86921360bc2e8ad35e830578b68b12e884a50894"),
|
||||
(Hash.TLSH, "6FF02BEF718027B0160B4391212923ED7F1A463D563B1549B86CF62973B197AD2731F8"),
|
||||
("foo", "bar"), # unrecognized hash type is accepted as-is
|
||||
],
|
||||
)
|
||||
def test_hash_check(hash_alg, hash_value):
|
||||
assert check_hash(hash_alg, hash_value)
|
||||
assert check_hash(hash_alg, hash_value.upper()) # check case sensitivity
|
||||
|
||||
|
||||
def test_hash_check_fail():
|
||||
for hash_alg in Hash:
|
||||
assert not check_hash(hash_alg, "x"*200)
|
|
@ -3,12 +3,15 @@ import datetime as dt
|
|||
import pytest
|
||||
import pytz
|
||||
|
||||
import stix2
|
||||
from stix2.exceptions import ExtraPropertiesError, STIXError
|
||||
from stix2.base import _STIXBase
|
||||
from stix2.exceptions import (
|
||||
CustomContentError, ExtraPropertiesError, STIXError,
|
||||
)
|
||||
from stix2.properties import (
|
||||
BinaryProperty, BooleanProperty, EmbeddedObjectProperty, EnumProperty,
|
||||
FloatProperty, HexProperty, IntegerProperty, ListProperty, Property,
|
||||
StringProperty, TimestampProperty, TypeProperty,
|
||||
FloatProperty, HashesProperty, HexProperty, IntegerProperty, ListProperty,
|
||||
OpenVocabProperty, Property, StringProperty, TimestampProperty,
|
||||
TypeProperty,
|
||||
)
|
||||
|
||||
|
||||
|
@ -16,8 +19,8 @@ def test_property():
|
|||
p = Property()
|
||||
|
||||
assert p.required is False
|
||||
assert p.clean('foo') == 'foo'
|
||||
assert p.clean(3) == 3
|
||||
assert p.clean('foo') == ('foo', False)
|
||||
assert p.clean(3) == (3, False)
|
||||
|
||||
|
||||
def test_basic_clean():
|
||||
|
@ -47,7 +50,7 @@ def test_property_default():
|
|||
assert p.default() == 77
|
||||
|
||||
|
||||
def test_property_fixed():
|
||||
def test_fixed_property():
|
||||
p = Property(fixed="2.0")
|
||||
|
||||
assert p.clean("2.0")
|
||||
|
@ -65,16 +68,18 @@ def test_property_fixed_and_required():
|
|||
Property(default=lambda: 3, required=True)
|
||||
|
||||
|
||||
def test_list_property():
|
||||
def test_list_property_property_type():
|
||||
p = ListProperty(StringProperty)
|
||||
|
||||
assert p.clean(['abc', 'xyz'])
|
||||
result = p.clean(['abc', 'xyz'], False)
|
||||
assert result == (['abc', 'xyz'], False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean([])
|
||||
p.clean([], False)
|
||||
|
||||
|
||||
def test_list_property_property_type_custom():
|
||||
class TestObj(stix2.base._STIXBase):
|
||||
class TestObj(_STIXBase):
|
||||
_type = "test"
|
||||
_properties = {
|
||||
"foo": StringProperty(),
|
||||
|
@ -86,20 +91,26 @@ def test_list_property_property_type_custom():
|
|||
TestObj(foo="xyz"),
|
||||
]
|
||||
|
||||
assert p.clean(objs_custom)
|
||||
result = p.clean(objs_custom, True)
|
||||
assert result == (objs_custom, True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
p.clean(objs_custom, False)
|
||||
|
||||
dicts_custom = [
|
||||
{"foo": "abc", "bar": 123},
|
||||
{"foo": "xyz"},
|
||||
]
|
||||
|
||||
# no opportunity to set allow_custom=True when using dicts
|
||||
result = p.clean(dicts_custom, True)
|
||||
assert result == (objs_custom, True)
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
p.clean(dicts_custom)
|
||||
p.clean(dicts_custom, False)
|
||||
|
||||
|
||||
def test_list_property_object_type():
|
||||
class TestObj(stix2.base._STIXBase):
|
||||
class TestObj(_STIXBase):
|
||||
_type = "test"
|
||||
_properties = {
|
||||
"foo": StringProperty(),
|
||||
|
@ -107,14 +118,16 @@ def test_list_property_object_type():
|
|||
p = ListProperty(TestObj)
|
||||
|
||||
objs = [TestObj(foo="abc"), TestObj(foo="xyz")]
|
||||
assert p.clean(objs)
|
||||
result = p.clean(objs, False)
|
||||
assert result == (objs, False)
|
||||
|
||||
dicts = [{"foo": "abc"}, {"foo": "xyz"}]
|
||||
assert p.clean(dicts)
|
||||
result = p.clean(dicts, False)
|
||||
assert result == (objs, False)
|
||||
|
||||
|
||||
def test_list_property_object_type_custom():
|
||||
class TestObj(stix2.base._STIXBase):
|
||||
class TestObj(_STIXBase):
|
||||
_type = "test"
|
||||
_properties = {
|
||||
"foo": StringProperty(),
|
||||
|
@ -126,16 +139,22 @@ def test_list_property_object_type_custom():
|
|||
TestObj(foo="xyz"),
|
||||
]
|
||||
|
||||
assert p.clean(objs_custom)
|
||||
result = p.clean(objs_custom, True)
|
||||
assert result == (objs_custom, True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
p.clean(objs_custom, False)
|
||||
|
||||
dicts_custom = [
|
||||
{"foo": "abc", "bar": 123},
|
||||
{"foo": "xyz"},
|
||||
]
|
||||
|
||||
# no opportunity to set allow_custom=True when using dicts
|
||||
result = p.clean(dicts_custom, True)
|
||||
assert result == (objs_custom, True)
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
p.clean(dicts_custom)
|
||||
p.clean(dicts_custom, False)
|
||||
|
||||
|
||||
def test_list_property_bad_element_type():
|
||||
|
@ -144,7 +163,7 @@ def test_list_property_bad_element_type():
|
|||
|
||||
|
||||
def test_list_property_bad_value_type():
|
||||
class TestObj(stix2.base._STIXBase):
|
||||
class TestObj(_STIXBase):
|
||||
_type = "test"
|
||||
_properties = {
|
||||
"foo": StringProperty(),
|
||||
|
@ -152,7 +171,7 @@ def test_list_property_bad_value_type():
|
|||
|
||||
list_prop = ListProperty(TestObj)
|
||||
with pytest.raises(ValueError):
|
||||
list_prop.clean([1])
|
||||
list_prop.clean([1], False)
|
||||
|
||||
|
||||
def test_string_property():
|
||||
|
@ -296,7 +315,7 @@ def test_boolean_property_invalid(value):
|
|||
)
|
||||
def test_timestamp_property_valid(value):
|
||||
ts_prop = TimestampProperty()
|
||||
assert ts_prop.clean(value) == dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc)
|
||||
assert ts_prop.clean(value) == (dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc), False)
|
||||
|
||||
|
||||
def test_timestamp_property_invalid():
|
||||
|
@ -332,15 +351,103 @@ def test_hex_property():
|
|||
)
|
||||
def test_enum_property_valid(value):
|
||||
enum_prop = EnumProperty(value)
|
||||
assert enum_prop.clean('b')
|
||||
assert enum_prop.clean('b', False)
|
||||
|
||||
|
||||
def test_enum_property_clean():
|
||||
enum_prop = EnumProperty(['1'])
|
||||
assert enum_prop.clean(1) == '1'
|
||||
assert enum_prop.clean(1, False) == ('1', False)
|
||||
|
||||
|
||||
def test_enum_property_invalid():
|
||||
enum_prop = EnumProperty(['a', 'b', 'c'])
|
||||
with pytest.raises(ValueError):
|
||||
enum_prop.clean('z')
|
||||
enum_prop.clean('z', False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
enum_prop.clean('z', True)
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
reason="Temporarily disabled custom open vocab enforcement",
|
||||
strict=True,
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"vocab", [
|
||||
['a', 'b', 'c'],
|
||||
('a', 'b', 'c'),
|
||||
'b',
|
||||
],
|
||||
)
|
||||
def test_openvocab_property(vocab):
|
||||
ov_prop = OpenVocabProperty(vocab)
|
||||
|
||||
assert ov_prop.clean("b", False) == ("b", False)
|
||||
assert ov_prop.clean("b", True) == ("b", False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
ov_prop.clean("d", False)
|
||||
|
||||
assert ov_prop.clean("d", True) == ("d", True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", [
|
||||
{"sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b"},
|
||||
[('MD5', '2dfb1bcc980200c6706feee399d41b3f'), ('RIPEMD-160', 'b3a8cd8a27c90af79b3c81754f267780f443dfef')],
|
||||
],
|
||||
)
|
||||
def test_hashes_property_valid(value):
|
||||
hash_prop = HashesProperty(["sha256", "md5", "ripemd160"])
|
||||
_, has_custom = hash_prop.clean(value, False)
|
||||
assert not has_custom
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", [
|
||||
{"MD5": "a"},
|
||||
{"SHA-256": "2dfb1bcc980200c6706feee399d41b3f"},
|
||||
],
|
||||
)
|
||||
def test_hashes_property_invalid(value):
|
||||
hash_prop = HashesProperty(["sha256", "md5"])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
hash_prop.clean(value, False)
|
||||
|
||||
|
||||
def test_hashes_property_custom():
|
||||
value = {
|
||||
"sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b",
|
||||
"abc-123": "aaaaaaaaaaaaaaaaaaaaa",
|
||||
}
|
||||
expected_cleaned_value = {
|
||||
# cleaning transforms recognized hash algorithm names to the spec-
|
||||
# mandated name.
|
||||
"SHA-256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b",
|
||||
"abc-123": "aaaaaaaaaaaaaaaaaaaaa",
|
||||
}
|
||||
|
||||
hash_prop = HashesProperty(["SHA-256"])
|
||||
result = hash_prop.clean(value, True)
|
||||
assert result == (expected_cleaned_value, True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
hash_prop.clean(value, False)
|
||||
|
||||
|
||||
def test_hashes_no_library_support():
|
||||
prop = HashesProperty(["foo"])
|
||||
|
||||
result = prop.clean({"foo": "bar"}, False)
|
||||
assert result == ({"foo": "bar"}, False)
|
||||
|
||||
result = prop.clean({"foo": "bar"}, True)
|
||||
assert result == ({"foo": "bar"}, False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
# require exact name match for unsupported hash algorithms
|
||||
prop.clean({"FOO": "bar"}, False)
|
||||
|
||||
result = prop.clean({"FOO": "bar"}, True)
|
||||
assert result == ({"FOO": "bar"}, True)
|
||||
|
|
|
@ -369,6 +369,7 @@ def test_workbench_custom_property_object_in_observable_extension():
|
|||
x_foo='bar',
|
||||
)
|
||||
artifact = File(
|
||||
allow_custom=True,
|
||||
name='test',
|
||||
extensions={'ntfs-ext': ntfs},
|
||||
)
|
||||
|
@ -390,7 +391,6 @@ def test_workbench_custom_property_dict_in_observable_extension():
|
|||
name='test',
|
||||
extensions={
|
||||
'ntfs-ext': {
|
||||
'allow_custom': True,
|
||||
'sid': 1,
|
||||
'x_foo': 'bar',
|
||||
},
|
||||
|
|
|
@ -55,7 +55,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -67,7 +67,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -79,7 +79,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.936Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -91,7 +91,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -103,7 +103,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -156,7 +156,7 @@ def stix_objs2():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-31T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -168,7 +168,7 @@ def stix_objs2():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -180,7 +180,7 @@ def stix_objs2():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
|
|
@ -223,7 +223,7 @@ def test_stix_object_property():
|
|||
prop = stix2.properties.STIXObjectProperty(spec_version='2.0')
|
||||
|
||||
identity = stix2.v20.Identity(name="test", identity_class="individual")
|
||||
assert prop.clean(identity) is identity
|
||||
assert prop.clean(identity, False) == (identity, False)
|
||||
|
||||
|
||||
def test_bundle_with_different_spec_objects():
|
||||
|
|
|
@ -157,10 +157,11 @@ def test_custom_properties_dict_in_bundled_object():
|
|||
'x_foo': 'bar',
|
||||
},
|
||||
}
|
||||
bundle = stix2.v20.Bundle(custom_identity)
|
||||
|
||||
assert bundle.objects[0].x_foo == "bar"
|
||||
assert '"x_foo": "bar"' in str(bundle)
|
||||
# must not succeed: allow_custom was not set to True when creating
|
||||
# the bundle, so it must reject the customized identity object.
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.v20.Bundle(custom_identity)
|
||||
|
||||
|
||||
def test_custom_property_in_observed_data():
|
||||
|
@ -188,6 +189,7 @@ def test_custom_property_object_in_observable_extension():
|
|||
x_foo='bar',
|
||||
)
|
||||
artifact = stix2.v20.File(
|
||||
allow_custom=True,
|
||||
name='test',
|
||||
extensions={'ntfs-ext': ntfs},
|
||||
)
|
||||
|
@ -220,7 +222,6 @@ def test_custom_property_dict_in_observable_extension():
|
|||
name='test',
|
||||
extensions={
|
||||
'ntfs-ext': {
|
||||
'allow_custom': True,
|
||||
'sid': 1,
|
||||
'x_foo': 'bar',
|
||||
},
|
||||
|
@ -384,6 +385,47 @@ def test_custom_object_invalid_type_name():
|
|||
assert "Invalid type name 'x_new_object':" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_subobject_dict():
|
||||
obj_dict = {
|
||||
"type": "bundle",
|
||||
"spec_version": "2.0",
|
||||
"objects": [
|
||||
{
|
||||
"type": "identity",
|
||||
"name": "alice",
|
||||
"identity_class": "individual",
|
||||
"x_foo": 123,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
obj = stix2.parse(obj_dict, allow_custom=True)
|
||||
assert obj["objects"][0]["x_foo"] == 123
|
||||
assert obj.has_custom
|
||||
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.parse(obj_dict, allow_custom=False)
|
||||
|
||||
|
||||
def test_custom_subobject_obj():
|
||||
ident = stix2.v20.Identity(
|
||||
name="alice", identity_class=123, x_foo=123, allow_custom=True,
|
||||
)
|
||||
|
||||
obj_dict = {
|
||||
"type": "bundle",
|
||||
"spec_version": "2.0",
|
||||
"objects": [ident],
|
||||
}
|
||||
|
||||
obj = stix2.parse(obj_dict, allow_custom=True)
|
||||
assert obj["objects"][0]["x_foo"] == 123
|
||||
assert obj.has_custom
|
||||
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.parse(obj_dict, allow_custom=False)
|
||||
|
||||
|
||||
def test_parse_custom_object_type():
|
||||
nt_string = """{
|
||||
"type": "x-new-type",
|
||||
|
@ -866,6 +908,35 @@ def test_parse_observable_with_custom_extension():
|
|||
assert parsed.extensions['x-new-ext'].property2 == 12
|
||||
|
||||
|
||||
def test_parse_observable_with_custom_extension_property():
|
||||
input_str = """{
|
||||
"type": "observed-data",
|
||||
"first_observed": "1976-09-09T01:50:24.000Z",
|
||||
"last_observed": "1988-01-18T15:22:10.000Z",
|
||||
"number_observed": 5,
|
||||
"objects": {
|
||||
"0": {
|
||||
"type": "file",
|
||||
"name": "cats.png",
|
||||
"extensions": {
|
||||
"raster-image-ext": {
|
||||
"image_height": 1024,
|
||||
"image_width": 768,
|
||||
"x-foo": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
parsed = stix2.parse(input_str, version='2.0', allow_custom=True)
|
||||
assert parsed.has_custom
|
||||
assert parsed["objects"]["0"]["extensions"]["raster-image-ext"]["x-foo"] is False
|
||||
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.parse(input_str, version="2.0", allow_custom=False)
|
||||
|
||||
|
||||
def test_custom_and_spec_extension_mix():
|
||||
"""
|
||||
Try to make sure that when allow_custom=True, encountering a custom
|
||||
|
|
|
@ -20,7 +20,7 @@ stix_objs = [
|
|||
"created": "2014-05-08T09:00:00.000Z",
|
||||
"id": "indicator--a932fcc6-e032-476c-826f-cb970a5a1ade",
|
||||
"labels": [
|
||||
"file-hash-watchlist",
|
||||
"compromised",
|
||||
],
|
||||
"modified": "2014-05-08T09:00:00.000Z",
|
||||
"name": "File hash for Poison Ivy variant",
|
||||
|
|
|
@ -19,7 +19,7 @@ IND1 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -31,7 +31,7 @@ IND2 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -43,7 +43,7 @@ IND3 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.936Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -55,7 +55,7 @@ IND4 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -67,7 +67,7 @@ IND5 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -79,7 +79,7 @@ IND6 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-31T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -91,7 +91,7 @@ IND7 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -103,7 +103,7 @@ IND8 = {
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"labels": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -285,7 +285,7 @@ def test_memory_store_object_creator_of_present(mem_store):
|
|||
iden = Identity(
|
||||
id=IDENTITY_ID,
|
||||
name="Foo Corp.",
|
||||
identity_class="corporation",
|
||||
identity_class="organization",
|
||||
)
|
||||
|
||||
mem_store.add(camp)
|
||||
|
|
|
@ -13,7 +13,7 @@ def test_pickling():
|
|||
id=IDENTITY_ID,
|
||||
name="alice",
|
||||
description="this is a pickle test",
|
||||
identity_class="some_class",
|
||||
identity_class="individual",
|
||||
)
|
||||
|
||||
pickle.loads(pickle.dumps(identity))
|
||||
|
|
|
@ -3,14 +3,14 @@ import uuid
|
|||
import pytest
|
||||
|
||||
import stix2
|
||||
import stix2.base
|
||||
from stix2.exceptions import (
|
||||
AtLeastOnePropertyError, CustomContentError, DictionaryKeyError,
|
||||
ExtraPropertiesError, ParseError,
|
||||
)
|
||||
from stix2.properties import (
|
||||
DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty,
|
||||
HashesProperty, IDProperty, ListProperty, ReferenceProperty,
|
||||
STIXObjectProperty,
|
||||
HashesProperty, IDProperty, ListProperty, ObservableProperty,
|
||||
ReferenceProperty, STIXObjectProperty,
|
||||
)
|
||||
from stix2.v20.common import MarkingProperty
|
||||
|
||||
|
@ -27,7 +27,7 @@ MY_ID = 'my-type--232c9d3f-49fc-4440-bb01-607f638778e7'
|
|||
],
|
||||
)
|
||||
def test_id_property_valid(value):
|
||||
assert ID_PROP.clean(value) == value
|
||||
assert ID_PROP.clean(value) == (value, False)
|
||||
|
||||
|
||||
CONSTANT_IDS = [
|
||||
|
@ -54,7 +54,7 @@ CONSTANT_IDS.extend(constants.RELATIONSHIP_IDS)
|
|||
@pytest.mark.parametrize("value", CONSTANT_IDS)
|
||||
def test_id_property_valid_for_type(value):
|
||||
type = value.split('--', 1)[0]
|
||||
assert IDProperty(type=type, spec_version="2.0").clean(value) == value
|
||||
assert IDProperty(type=type, spec_version="2.0").clean(value) == (value, False)
|
||||
|
||||
|
||||
def test_id_property_wrong_type():
|
||||
|
@ -80,29 +80,208 @@ def test_id_property_not_a_valid_hex_uuid(value):
|
|||
|
||||
def test_id_property_default():
|
||||
default = ID_PROP.default()
|
||||
assert ID_PROP.clean(default) == default
|
||||
assert ID_PROP.clean(default) == (default, False)
|
||||
|
||||
|
||||
def test_reference_property():
|
||||
ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.0")
|
||||
def test_reference_property_whitelist_standard_type():
|
||||
ref_prop = ReferenceProperty(valid_types="identity", spec_version="2.0")
|
||||
result = ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
assert ref_prop.clean("my-type--00000000-0000-4000-8000-000000000000")
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("foo")
|
||||
ref_prop.clean("foo--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
# This is not a valid V4 UUID
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("my-type--00000000-0000-0000-0000-000000000000")
|
||||
ref_prop.clean("foo--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
|
||||
def test_reference_property_specific_type():
|
||||
def test_reference_property_whitelist_custom_type():
|
||||
ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.0")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf")
|
||||
ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
assert ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf") == \
|
||||
"my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf"
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
# This is the whitelisted type, but it's still custom, and
|
||||
# customization is disallowed here.
|
||||
ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_whitelist_generic_type():
|
||||
ref_prop = ReferenceProperty(
|
||||
valid_types=["SCO", "SRO"], spec_version="2.0",
|
||||
)
|
||||
|
||||
result = ref_prop.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
# The prop assumes some-type is a custom type of one of the generic
|
||||
# type categories.
|
||||
result = ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_blacklist_standard_type():
|
||||
ref_prop = ReferenceProperty(invalid_types="identity", spec_version="2.0")
|
||||
result = ref_prop.clean(
|
||||
"malware--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("malware--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"malware--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("malware--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
|
||||
|
||||
def test_reference_property_blacklist_generic_type():
|
||||
ref_prop = ReferenceProperty(
|
||||
invalid_types=["SDO", "SRO"], spec_version="2.0",
|
||||
)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"relationship--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"relationship--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
|
||||
|
||||
def test_reference_property_whitelist_hybrid_type():
|
||||
p = ReferenceProperty(valid_types=["a", "SCO"], spec_version="2.0")
|
||||
|
||||
result = p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
# although whitelisted, "a" is a custom type
|
||||
p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
# should just assume "b" is a custom SCO type.
|
||||
result = p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_blacklist_hybrid_type():
|
||||
p = ReferenceProperty(invalid_types=["a", "SCO"], spec_version="2.0")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
# should just assume "b" is a custom type which is not an SCO
|
||||
result = p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_impossible_constraint():
|
||||
with pytest.raises(ValueError):
|
||||
ReferenceProperty(valid_types=[], spec_version="2.0")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -176,27 +355,29 @@ def test_property_list_of_dictionary():
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", [
|
||||
{"sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b"},
|
||||
[('MD5', '2dfb1bcc980200c6706feee399d41b3f'), ('RIPEMD-160', 'b3a8cd8a27c90af79b3c81754f267780f443dfef')],
|
||||
"key", [
|
||||
"aaa",
|
||||
"a"*256,
|
||||
"a-1_b",
|
||||
],
|
||||
)
|
||||
def test_hashes_property_valid(value):
|
||||
hash_prop = HashesProperty()
|
||||
assert hash_prop.clean(value)
|
||||
def test_hash_property_valid_key(key):
|
||||
p = HashesProperty(["foo"], spec_version="2.0")
|
||||
result = p.clean({key: "bar"}, True)
|
||||
assert result == ({key: "bar"}, True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", [
|
||||
{"MD5": "a"},
|
||||
{"SHA-256": "2dfb1bcc980200c6706feee399d41b3f"},
|
||||
"key", [
|
||||
"aa",
|
||||
"a"*257,
|
||||
"funny%chars?",
|
||||
],
|
||||
)
|
||||
def test_hashes_property_invalid(value):
|
||||
hash_prop = HashesProperty()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
hash_prop.clean(value)
|
||||
def test_hash_property_invalid_key(key):
|
||||
p = HashesProperty(["foo"], spec_version="2.0")
|
||||
with pytest.raises(DictionaryKeyError):
|
||||
p.clean({key: "foo"}, True)
|
||||
|
||||
|
||||
def test_embedded_property():
|
||||
|
@ -206,25 +387,103 @@ def test_embedded_property():
|
|||
content_disposition="inline",
|
||||
body="Cats are funny!",
|
||||
)
|
||||
assert emb_prop.clean(mime)
|
||||
result = emb_prop.clean(mime, False)
|
||||
assert result == (mime, False)
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert result == (mime, False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
emb_prop.clean("string")
|
||||
emb_prop.clean("string", False)
|
||||
|
||||
|
||||
def test_embedded_property_dict():
|
||||
emb_prop = EmbeddedObjectProperty(type=stix2.v20.EmailMIMEComponent)
|
||||
mime = {
|
||||
"content_type": "text/plain; charset=utf-8",
|
||||
"content_disposition": "inline",
|
||||
"body": "Cats are funny!",
|
||||
}
|
||||
|
||||
result = emb_prop.clean(mime, False)
|
||||
assert isinstance(result[0], stix2.v20.EmailMIMEComponent)
|
||||
assert result[0]["body"] == "Cats are funny!"
|
||||
assert not result[1]
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert isinstance(result[0], stix2.v20.EmailMIMEComponent)
|
||||
assert result[0]["body"] == "Cats are funny!"
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_embedded_property_custom():
|
||||
emb_prop = EmbeddedObjectProperty(type=stix2.v20.EmailMIMEComponent)
|
||||
mime = stix2.v20.EmailMIMEComponent(
|
||||
content_type="text/plain; charset=utf-8",
|
||||
content_disposition="inline",
|
||||
body="Cats are funny!",
|
||||
foo=123,
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
emb_prop.clean(mime, False)
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert result == (mime, True)
|
||||
|
||||
|
||||
def test_embedded_property_dict_custom():
|
||||
emb_prop = EmbeddedObjectProperty(type=stix2.v20.EmailMIMEComponent)
|
||||
mime = {
|
||||
"content_type": "text/plain; charset=utf-8",
|
||||
"content_disposition": "inline",
|
||||
"body": "Cats are funny!",
|
||||
"foo": 123,
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
emb_prop.clean(mime, False)
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert isinstance(result[0], stix2.v20.EmailMIMEComponent)
|
||||
assert result[0]["body"] == "Cats are funny!"
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_extension_property_valid():
|
||||
ext_prop = ExtensionsProperty(spec_version="2.0")
|
||||
assert ext_prop({
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
})
|
||||
}, False,
|
||||
)
|
||||
|
||||
assert isinstance(
|
||||
result[0]["windows-pebinary-ext"], stix2.v20.WindowsPEBinaryExt,
|
||||
)
|
||||
assert not result[1]
|
||||
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
}, True,
|
||||
)
|
||||
|
||||
assert isinstance(
|
||||
result[0]["windows-pebinary-ext"], stix2.v20.WindowsPEBinaryExt,
|
||||
)
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_extension_property_invalid1():
|
||||
ext_prop = ExtensionsProperty(spec_version="2.0")
|
||||
with pytest.raises(ValueError):
|
||||
ext_prop.clean(1)
|
||||
ext_prop.clean(1, False)
|
||||
|
||||
|
||||
def test_extension_property_invalid2():
|
||||
|
@ -236,8 +495,61 @@ def test_extension_property_invalid2():
|
|||
'pe_type': 'exe',
|
||||
},
|
||||
},
|
||||
False,
|
||||
)
|
||||
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'foobar-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
}, True,
|
||||
)
|
||||
assert result == ({"foobar-ext": {"pe_type": "exe"}}, True)
|
||||
|
||||
|
||||
def test_extension_property_invalid3():
|
||||
ext_prop = ExtensionsProperty(spec_version="2.0")
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
'abc': 123,
|
||||
},
|
||||
},
|
||||
False,
|
||||
)
|
||||
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
'abc': 123,
|
||||
},
|
||||
}, True,
|
||||
)
|
||||
|
||||
assert isinstance(
|
||||
result[0]["windows-pebinary-ext"], stix2.v20.WindowsPEBinaryExt,
|
||||
)
|
||||
assert result[0]["windows-pebinary-ext"]["abc"] == 123
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_extension_property_invalid_type():
|
||||
ext_prop = ExtensionsProperty(spec_version="2.0")
|
||||
with pytest.raises(CustomContentError) as excinfo:
|
||||
ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
},
|
||||
False,
|
||||
)
|
||||
assert "Can't parse unknown extension" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_extension_at_least_one_property_constraint():
|
||||
with pytest.raises(AtLeastOnePropertyError):
|
||||
|
@ -259,6 +571,116 @@ def test_stix_property_not_compliant_spec():
|
|||
stix_prop = STIXObjectProperty(spec_version="2.0")
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
stix_prop.clean(indicator)
|
||||
stix_prop.clean(indicator, False)
|
||||
|
||||
assert "Spec version 2.0 bundles don't yet support containing objects of a different spec version." in str(excinfo.value)
|
||||
|
||||
|
||||
def test_observable_property_obj():
|
||||
prop = ObservableProperty(spec_version="2.0")
|
||||
|
||||
obs = stix2.v20.File(name="data.dat")
|
||||
obs_dict = {
|
||||
"0": obs,
|
||||
}
|
||||
|
||||
result = prop.clean(obs_dict, False)
|
||||
assert result[0]["0"] == obs
|
||||
assert not result[1]
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert result[0]["0"] == obs
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_observable_property_dict():
|
||||
prop = ObservableProperty(spec_version="2.0")
|
||||
|
||||
obs_dict = {
|
||||
"0": {
|
||||
"type": "file",
|
||||
"name": "data.dat",
|
||||
},
|
||||
}
|
||||
|
||||
result = prop.clean(obs_dict, False)
|
||||
assert isinstance(result[0]["0"], stix2.v20.File)
|
||||
assert result[0]["0"]["name"] == "data.dat"
|
||||
assert not result[1]
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert isinstance(result[0]["0"], stix2.v20.File)
|
||||
assert result[0]["0"]["name"] == "data.dat"
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_observable_property_obj_custom():
|
||||
prop = ObservableProperty(spec_version="2.0")
|
||||
|
||||
obs = stix2.v20.File(name="data.dat", foo=True, allow_custom=True)
|
||||
obs_dict = {
|
||||
"0": obs,
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
prop.clean(obs_dict, False)
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert result[0]["0"] == obs
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_observable_property_dict_custom():
|
||||
prop = ObservableProperty(spec_version="2.0")
|
||||
|
||||
obs_dict = {
|
||||
"0": {
|
||||
"type": "file",
|
||||
"name": "data.dat",
|
||||
"foo": True,
|
||||
},
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
prop.clean(obs_dict, False)
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert isinstance(result[0]["0"], stix2.v20.File)
|
||||
assert result[0]["0"]["foo"]
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_stix_object_property_custom_prop():
|
||||
prop = STIXObjectProperty(spec_version="2.0")
|
||||
|
||||
obj_dict = {
|
||||
"type": "identity",
|
||||
"name": "alice",
|
||||
"identity_class": "supergirl",
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
prop.clean(obj_dict, False)
|
||||
|
||||
result = prop.clean(obj_dict, True)
|
||||
assert isinstance(result[0], stix2.v20.Identity)
|
||||
assert result[0]["foo"] == "bar"
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_stix_object_property_custom_obj():
|
||||
prop = STIXObjectProperty(spec_version="2.0")
|
||||
|
||||
obj_dict = {
|
||||
"type": "something",
|
||||
"abc": 123,
|
||||
"xyz": ["a", 1],
|
||||
}
|
||||
|
||||
with pytest.raises(ParseError):
|
||||
prop.clean(obj_dict, False)
|
||||
|
||||
result = prop.clean(obj_dict, True)
|
||||
assert result[0] == {"type": "something", "abc": 123, "xyz": ["a", 1]}
|
||||
assert result[1]
|
||||
|
|
|
@ -4,6 +4,7 @@ import pytest
|
|||
import pytz
|
||||
|
||||
import stix2
|
||||
from stix2.exceptions import InvalidValueError
|
||||
|
||||
from .constants import (
|
||||
CAMPAIGN_ID, IDENTITY_ID, INDICATOR_ID, INDICATOR_KWARGS, RELATIONSHIP_ID,
|
||||
|
@ -133,3 +134,30 @@ def test_parse_report(data):
|
|||
assert rept.name == "The Black Vine Cyberespionage Group"
|
||||
|
||||
# TODO: Add other examples
|
||||
|
||||
|
||||
def test_report_on_custom():
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.v20.Report(
|
||||
name="my report",
|
||||
labels=["a label"],
|
||||
published="2016-01-20T17:00:00Z",
|
||||
object_refs=[
|
||||
"indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"some-type--2672975a-ce1e-4473-a1c6-0d79868930c7",
|
||||
],
|
||||
)
|
||||
|
||||
report = stix2.v20.Report(
|
||||
name="my report",
|
||||
labels=["a label"],
|
||||
published="2016-01-20T17:00:00Z",
|
||||
object_refs=[
|
||||
"indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"some-type--2672975a-ce1e-4473-a1c6-0d79868930c7",
|
||||
],
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
assert "some-type--2672975a-ce1e-4473-a1c6-0d79868930c7" \
|
||||
in report.object_refs
|
||||
|
|
|
@ -433,7 +433,7 @@ def test_version_marking():
|
|||
|
||||
def test_version_disable_custom():
|
||||
m = stix2.v20.Malware(
|
||||
name="foo", labels=["label"], description="Steals your identity!",
|
||||
name="foo", labels=["spyware"], description="Steals your identity!",
|
||||
x_custom=123, allow_custom=True,
|
||||
)
|
||||
|
||||
|
@ -450,7 +450,7 @@ def test_version_disable_custom():
|
|||
|
||||
def test_version_enable_custom():
|
||||
m = stix2.v20.Malware(
|
||||
name="foo", labels=["label"], description="Steals your identity!",
|
||||
name="foo", labels=["spyware"], description="Steals your identity!",
|
||||
)
|
||||
|
||||
# Add a custom property to an object for which it was previously disallowed
|
||||
|
@ -464,7 +464,7 @@ def test_version_enable_custom():
|
|||
|
||||
def test_version_propagate_custom():
|
||||
m = stix2.v20.Malware(
|
||||
name="foo", labels=["label"],
|
||||
name="foo", labels=["spyware"],
|
||||
)
|
||||
|
||||
# Remember custom-not-allowed setting from original; produce error
|
||||
|
@ -476,7 +476,7 @@ def test_version_propagate_custom():
|
|||
assert m2.description == "Steals your identity!"
|
||||
|
||||
m_custom = stix2.v20.Malware(
|
||||
name="foo", labels=["label"], x_custom=123, allow_custom=True,
|
||||
name="foo", labels=["spyware"], x_custom=123, allow_custom=True,
|
||||
)
|
||||
|
||||
# Remember custom-allowed setting from original; should work
|
||||
|
|
|
@ -66,7 +66,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -80,7 +80,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -94,7 +94,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.936Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -108,7 +108,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -122,7 +122,7 @@ def stix_objs1():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -183,7 +183,7 @@ def stix_objs2():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-31T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -197,7 +197,7 @@ def stix_objs2():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
@ -211,7 +211,7 @@ def stix_objs2():
|
|||
"created": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"indicator_types": [
|
||||
"url-watchlist",
|
||||
"malicious-activity",
|
||||
],
|
||||
"modified": "2017-01-27T13:49:53.935Z",
|
||||
"name": "Malicious site hosting downloader",
|
||||
|
|
|
@ -234,7 +234,7 @@ def test_stix_object_property():
|
|||
prop = stix2.properties.STIXObjectProperty(spec_version='2.1')
|
||||
|
||||
identity = stix2.v21.Identity(name="test", identity_class="individual")
|
||||
assert prop.clean(identity) is identity
|
||||
assert prop.clean(identity, False) == (identity, False)
|
||||
|
||||
|
||||
def test_bundle_obj_id_found():
|
||||
|
|
|
@ -206,8 +206,10 @@ def test_custom_properties_dict_in_bundled_object():
|
|||
'x_foo': 'bar',
|
||||
},
|
||||
}
|
||||
bundle = stix2.v21.Bundle(custom_identity)
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.v21.Bundle(custom_identity)
|
||||
|
||||
bundle = stix2.v21.Bundle(custom_identity, allow_custom=True)
|
||||
assert bundle.objects[0].x_foo == "bar"
|
||||
assert '"x_foo": "bar"' in str(bundle)
|
||||
|
||||
|
@ -251,6 +253,7 @@ def test_custom_property_object_in_observable_extension():
|
|||
x_foo='bar',
|
||||
)
|
||||
artifact = stix2.v21.File(
|
||||
allow_custom=True,
|
||||
name='test',
|
||||
extensions={'ntfs-ext': ntfs},
|
||||
)
|
||||
|
@ -283,7 +286,6 @@ def test_custom_property_dict_in_observable_extension():
|
|||
name='test',
|
||||
extensions={
|
||||
'ntfs-ext': {
|
||||
'allow_custom': True,
|
||||
'sid': 1,
|
||||
'x_foo': 'bar',
|
||||
},
|
||||
|
@ -506,6 +508,48 @@ def test_custom_object_invalid_type_name():
|
|||
assert "Invalid type name '7x-new-object':" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_subobject_dict():
|
||||
obj_dict = {
|
||||
"type": "bundle",
|
||||
"id": "bundle--78d99c4a-4eda-4c59-b264-60807f05d799",
|
||||
"objects": [
|
||||
{
|
||||
"type": "identity",
|
||||
"spec_version": "2.1",
|
||||
"name": "alice",
|
||||
"identity_class": "individual",
|
||||
"x_foo": 123,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
obj = stix2.parse(obj_dict, allow_custom=True)
|
||||
assert obj["objects"][0]["x_foo"] == 123
|
||||
assert obj.has_custom
|
||||
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.parse(obj_dict, allow_custom=False)
|
||||
|
||||
|
||||
def test_custom_subobject_obj():
|
||||
ident = stix2.v21.Identity(
|
||||
name="alice", identity_class=123, x_foo=123, allow_custom=True,
|
||||
)
|
||||
|
||||
obj_dict = {
|
||||
"type": "bundle",
|
||||
"id": "bundle--78d99c4a-4eda-4c59-b264-60807f05d799",
|
||||
"objects": [ident],
|
||||
}
|
||||
|
||||
obj = stix2.parse(obj_dict, allow_custom=True)
|
||||
assert obj["objects"][0]["x_foo"] == 123
|
||||
assert obj.has_custom
|
||||
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.parse(obj_dict, allow_custom=False)
|
||||
|
||||
|
||||
def test_parse_custom_object_type():
|
||||
nt_string = """{
|
||||
"type": "x-new-type",
|
||||
|
@ -1075,6 +1119,37 @@ def test_parse_observable_with_custom_extension():
|
|||
assert parsed.extensions['x-new-ext'].property2 == 12
|
||||
|
||||
|
||||
def test_parse_observable_with_custom_extension_property():
|
||||
input_str = """{
|
||||
"type": "observed-data",
|
||||
"spec_version": "2.1",
|
||||
"first_observed": "1976-09-09T01:50:24.000Z",
|
||||
"last_observed": "1988-01-18T15:22:10.000Z",
|
||||
"number_observed": 5,
|
||||
"objects": {
|
||||
"0": {
|
||||
"type": "file",
|
||||
"spec_version": "2.1",
|
||||
"name": "cats.png",
|
||||
"extensions": {
|
||||
"raster-image-ext": {
|
||||
"image_height": 1024,
|
||||
"image_width": 768,
|
||||
"x-foo": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
parsed = stix2.parse(input_str, version='2.1', allow_custom=True)
|
||||
assert parsed.has_custom
|
||||
assert parsed["objects"]["0"]["extensions"]["raster-image-ext"]["x-foo"] is False
|
||||
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.parse(input_str, version="2.1", allow_custom=False)
|
||||
|
||||
|
||||
def test_custom_and_spec_extension_mix():
|
||||
"""
|
||||
Try to make sure that when allow_custom=True, encountering a custom
|
||||
|
@ -1563,3 +1638,30 @@ def test_registered_new_extension_marking_allow_custom_false():
|
|||
marking_serialized = marking_object.serialize(sort_keys=True)
|
||||
assert '"extensions": {"extension-definition--a932fcc6-e032-176c-126f-cb970a5a1fff": ' \
|
||||
'{"extension_type": "property-extension", "some_marking_field": "value"}}' in marking_serialized
|
||||
|
||||
|
||||
def test_allow_custom_propagation():
|
||||
obj_dict = {
|
||||
"type": "bundle",
|
||||
"objects": [
|
||||
{
|
||||
"type": "file",
|
||||
"spec_version": "2.1",
|
||||
"name": "data.dat",
|
||||
"extensions": {
|
||||
"archive-ext": {
|
||||
"contains_refs": [
|
||||
"file--3d4da5f6-31d8-4a66-a172-f31af9bf5238",
|
||||
"file--4bb16def-cdfc-40d1-b6a4-815de6c60b74",
|
||||
],
|
||||
"x_foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# allow_custom=False at the top level should catch the custom property way
|
||||
# down in the SCO extension.
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.parse(obj_dict, allow_custom=False)
|
||||
|
|
|
@ -24,7 +24,7 @@ stix_objs = [
|
|||
"created": "2014-05-08T09:00:00.000Z",
|
||||
"id": "indicator--a932fcc6-e032-476c-826f-cb970a5a1ade",
|
||||
"indicator_types": [
|
||||
"file-hash-watchlist",
|
||||
"compromised",
|
||||
],
|
||||
"modified": "2014-05-08T09:00:00.000Z",
|
||||
"name": "File hash for Poison Ivy variant",
|
||||
|
|
|
@ -300,7 +300,7 @@ def test_memory_store_object_creator_of_present(mem_store):
|
|||
iden = Identity(
|
||||
id=IDENTITY_ID,
|
||||
name="Foo Corp.",
|
||||
identity_class="corporation",
|
||||
identity_class="organization",
|
||||
)
|
||||
|
||||
mem_store.add(camp)
|
||||
|
|
|
@ -14,6 +14,7 @@ from stix2.properties import (
|
|||
TypeProperty,
|
||||
)
|
||||
import stix2.v21
|
||||
from stix2.v21.vocab import HASHING_ALGORITHM
|
||||
|
||||
SCO_DET_ID_NAMESPACE = uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7")
|
||||
|
||||
|
@ -155,7 +156,7 @@ def test_empty_hash():
|
|||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('extensions', ExtensionsProperty(spec_version='2.1')),
|
||||
('hashes', HashesProperty()),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM)),
|
||||
))
|
||||
_id_contributing_properties = ['hashes']
|
||||
|
||||
|
|
|
@ -490,7 +490,7 @@ def test_object_similarity_on_same_identity2():
|
|||
IDEN_KWARGS = dict(
|
||||
name="John Smith",
|
||||
identity_class="individual",
|
||||
sectors=["government", "critical-infrastructure"],
|
||||
sectors=["government", "infrastructure"],
|
||||
)
|
||||
iden1 = stix2.v21.Identity(id=IDENTITY_ID, **IDEN_KWARGS)
|
||||
iden2 = stix2.v21.Identity(id=IDENTITY_ID, **IDEN_KWARGS)
|
||||
|
@ -723,7 +723,7 @@ def test_object_similarity_different_spec_version_raises():
|
|||
|
||||
def test_object_similarity_zero_match():
|
||||
IND_KWARGS = dict(
|
||||
indicator_types=["malicious-activity", "bar"],
|
||||
indicator_types=["anomalous-activity"],
|
||||
pattern="[ipv4-addr:value = '192.168.1.1']",
|
||||
pattern_type="stix",
|
||||
valid_from="2019-01-01T12:34:56Z",
|
||||
|
@ -743,14 +743,14 @@ def test_object_similarity_zero_match():
|
|||
ind1 = stix2.v21.Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS)
|
||||
ind2 = stix2.v21.Indicator(id=INDICATOR_ID, **IND_KWARGS)
|
||||
env = stix2.Environment().object_similarity(ind1, ind2, **weights)
|
||||
assert round(env) == 8
|
||||
assert round(env) == 0
|
||||
env = stix2.Environment().object_similarity(ind2, ind1, **weights)
|
||||
assert round(env) == 8
|
||||
assert round(env) == 0
|
||||
|
||||
|
||||
def test_object_similarity_different_spec_version():
|
||||
IND_KWARGS = dict(
|
||||
labels=["APTX"],
|
||||
labels=["malicious-activity"],
|
||||
pattern="[ipv4-addr:value = '192.168.1.1']",
|
||||
)
|
||||
weights = {
|
||||
|
|
|
@ -223,6 +223,7 @@ def test_indicator_with_custom_embedded_objs():
|
|||
valid_from=epoch,
|
||||
indicator_types=['malicious-activity'],
|
||||
external_references=[ext_ref],
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
assert ind.indicator_types == ['malicious-activity']
|
||||
|
|
|
@ -36,7 +36,7 @@ EXPECTED_LOCATION_2 = """{
|
|||
"id": "location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64",
|
||||
"created": "2016-04-06T20:03:00.000Z",
|
||||
"modified": "2016-04-06T20:03:00.000Z",
|
||||
"region": "north-america"
|
||||
"region": "northern-america"
|
||||
}
|
||||
"""
|
||||
|
||||
|
@ -47,7 +47,7 @@ EXPECTED_LOCATION_2_REPR = "Location(" + " ".join(
|
|||
id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64',
|
||||
created='2016-04-06T20:03:00.000Z',
|
||||
modified='2016-04-06T20:03:00.000Z',
|
||||
region='north-america'""".split(),
|
||||
region='northern-america'""".split(),
|
||||
) + ")"
|
||||
|
||||
|
||||
|
@ -76,7 +76,7 @@ def test_location_with_some_required_properties():
|
|||
"id": LOCATION_ID,
|
||||
"created": "2016-04-06T20:03:00.000Z",
|
||||
"modified": "2016-04-06T20:03:00.000Z",
|
||||
"region": "north-america",
|
||||
"region": "northern-america",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
@ -88,7 +88,7 @@ def test_parse_location(data):
|
|||
assert location.id == LOCATION_ID
|
||||
assert location.created == dt.datetime(2016, 4, 6, 20, 3, 0, tzinfo=pytz.utc)
|
||||
assert location.modified == dt.datetime(2016, 4, 6, 20, 3, 0, tzinfo=pytz.utc)
|
||||
assert location.region == 'north-america'
|
||||
assert location.region == 'northern-america'
|
||||
rep = re.sub(r"(\[|=| )u('|\"|\\\'|\\\")", r"\g<1>\g<2>", repr(location))
|
||||
assert rep == EXPECTED_LOCATION_2_REPR
|
||||
|
||||
|
@ -302,6 +302,7 @@ def test_google_map_url_multiple_props_no_long_lat_provided():
|
|||
region="North America",
|
||||
country="United States of America",
|
||||
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
loc_url = loc.to_maps_url()
|
||||
|
@ -312,7 +313,7 @@ def test_google_map_url_multiple_props_and_long_lat_provided():
|
|||
expected_url = "https://www.google.com/maps/search/?api=1&query=41.862401%2C-87.616001"
|
||||
|
||||
loc = stix2.v21.Location(
|
||||
region="North America",
|
||||
region="northern-america",
|
||||
country="United States of America",
|
||||
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
|
||||
latitude=41.862401,
|
||||
|
@ -354,6 +355,7 @@ def test_bing_map_url_multiple_props_no_long_lat_provided():
|
|||
region="North America",
|
||||
country="United States of America",
|
||||
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
loc_url = loc.to_maps_url("Bing Maps")
|
||||
|
@ -364,7 +366,7 @@ def test_bing_map_url_multiple_props_and_long_lat_provided():
|
|||
expected_url = "https://bing.com/maps/default.aspx?where1=41.862401%2C-87.616001&lvl=16"
|
||||
|
||||
loc = stix2.v21.Location(
|
||||
region="North America",
|
||||
region="northern-america",
|
||||
country="United States of America",
|
||||
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
|
||||
latitude=41.862401,
|
||||
|
|
|
@ -181,7 +181,7 @@ def test_malware_family_no_name():
|
|||
"id": MALWARE_ID,
|
||||
"spec_version": "2.1",
|
||||
"is_family": True,
|
||||
"malware_types": ["a type"],
|
||||
"malware_types": ["spyware"],
|
||||
})
|
||||
|
||||
|
||||
|
@ -191,7 +191,7 @@ def test_malware_non_family_no_name():
|
|||
"id": MALWARE_ID,
|
||||
"spec_version": "2.1",
|
||||
"is_family": False,
|
||||
"malware_types": ["something"],
|
||||
"malware_types": ["spyware"],
|
||||
})
|
||||
|
||||
|
||||
|
@ -207,7 +207,7 @@ def test_malware_with_os_refs():
|
|||
"id": MALWARE_ID,
|
||||
"spec_version": "2.1",
|
||||
"is_family": False,
|
||||
"malware_types": ["something"],
|
||||
"malware_types": ["spyware"],
|
||||
"operating_system_refs": [software],
|
||||
})
|
||||
|
||||
|
|
|
@ -84,3 +84,38 @@ def test_malware_analysis_constraint():
|
|||
stix2.v21.MalwareAnalysis(
|
||||
product="Acme Malware Analyzer",
|
||||
)
|
||||
|
||||
|
||||
def test_malware_analysis_custom_sco_refs():
|
||||
ma = stix2.v21.MalwareAnalysis(
|
||||
product="super scanner",
|
||||
analysis_sco_refs=[
|
||||
"file--6e8c78cf-4bcc-4729-9265-86a97bfc91ba",
|
||||
"some-object--f6bfc147-e844-4578-ae01-847979890239",
|
||||
],
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
assert "some-object--f6bfc147-e844-4578-ae01-847979890239" in \
|
||||
ma["analysis_sco_refs"]
|
||||
assert ma.has_custom
|
||||
|
||||
with pytest.raises(stix2.exceptions.InvalidValueError):
|
||||
stix2.v21.MalwareAnalysis(
|
||||
product="super scanner",
|
||||
analysis_sco_refs=[
|
||||
"file--6e8c78cf-4bcc-4729-9265-86a97bfc91ba",
|
||||
"some-object--f6bfc147-e844-4578-ae01-847979890239",
|
||||
],
|
||||
)
|
||||
|
||||
with pytest.raises(stix2.exceptions.InvalidValueError):
|
||||
stix2.v21.MalwareAnalysis(
|
||||
product="super scanner",
|
||||
analysis_sco_refs=[
|
||||
"file--6e8c78cf-4bcc-4729-9265-86a97bfc91ba",
|
||||
# standard object type; wrong category (not SCO)
|
||||
"identity--56977a19-49ef-49d7-b259-f733fa4b7bbc",
|
||||
],
|
||||
allow_custom=True,
|
||||
)
|
||||
|
|
|
@ -208,7 +208,7 @@ def test_observed_data_example_with_bad_refs():
|
|||
|
||||
assert excinfo.value.cls == stix2.v21.Directory
|
||||
assert excinfo.value.prop_name == "contains_refs"
|
||||
assert "The type-specifying prefix 'monkey' for this property is not valid" in excinfo.value.reason
|
||||
assert "The type-specifying prefix 'monkey' for this property is not" in excinfo.value.reason
|
||||
|
||||
|
||||
def test_observed_data_example_with_non_dictionary():
|
||||
|
|
|
@ -13,7 +13,7 @@ def test_pickling():
|
|||
id=IDENTITY_ID,
|
||||
name="alice",
|
||||
description="this is a pickle test",
|
||||
identity_class="some_class",
|
||||
identity_class="individual",
|
||||
)
|
||||
|
||||
pickle.loads(pickle.dumps(identity))
|
||||
|
|
|
@ -3,11 +3,12 @@ import pytest
|
|||
import stix2
|
||||
from stix2.exceptions import (
|
||||
AtLeastOnePropertyError, CustomContentError, DictionaryKeyError,
|
||||
ExtraPropertiesError, ParseError,
|
||||
)
|
||||
from stix2.properties import (
|
||||
DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty,
|
||||
HashesProperty, IDProperty, ListProperty, ReferenceProperty,
|
||||
StringProperty, TypeProperty,
|
||||
HashesProperty, IDProperty, ListProperty, ObservableProperty,
|
||||
ReferenceProperty, STIXObjectProperty, StringProperty,
|
||||
)
|
||||
from stix2.v21.common import MarkingProperty
|
||||
|
||||
|
@ -22,23 +23,6 @@ def test_dictionary_property():
|
|||
p.clean({})
|
||||
|
||||
|
||||
def test_string_property():
|
||||
prop = StringProperty()
|
||||
|
||||
assert prop.clean('foobar')
|
||||
assert prop.clean(1)
|
||||
assert prop.clean([1, 2, 3])
|
||||
|
||||
|
||||
def test_type_property():
|
||||
prop = TypeProperty('my-type')
|
||||
|
||||
assert prop.clean('my-type')
|
||||
with pytest.raises(ValueError):
|
||||
prop.clean('not-my-type')
|
||||
assert prop.clean(prop.default())
|
||||
|
||||
|
||||
ID_PROP = IDProperty('my-type', spec_version="2.1")
|
||||
MY_ID = 'my-type--232c9d3f-49fc-4440-bb01-607f638778e7'
|
||||
|
||||
|
@ -50,7 +34,7 @@ MY_ID = 'my-type--232c9d3f-49fc-4440-bb01-607f638778e7'
|
|||
],
|
||||
)
|
||||
def test_id_property_valid(value):
|
||||
assert ID_PROP.clean(value) == value
|
||||
assert ID_PROP.clean(value) == (value, False)
|
||||
|
||||
|
||||
CONSTANT_IDS = [
|
||||
|
@ -77,7 +61,7 @@ CONSTANT_IDS.extend(constants.RELATIONSHIP_IDS)
|
|||
@pytest.mark.parametrize("value", CONSTANT_IDS)
|
||||
def test_id_property_valid_for_type(value):
|
||||
type = value.split('--', 1)[0]
|
||||
assert IDProperty(type=type, spec_version="2.1").clean(value) == value
|
||||
assert IDProperty(type=type, spec_version="2.1").clean(value) == (value, False)
|
||||
|
||||
|
||||
def test_id_property_wrong_type():
|
||||
|
@ -100,29 +84,229 @@ def test_id_property_not_a_valid_hex_uuid(value):
|
|||
|
||||
def test_id_property_default():
|
||||
default = ID_PROP.default()
|
||||
assert ID_PROP.clean(default) == default
|
||||
assert ID_PROP.clean(default) == (default, False)
|
||||
|
||||
|
||||
def test_reference_property():
|
||||
ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.1")
|
||||
def test_reference_property_whitelist_standard_type():
|
||||
ref_prop = ReferenceProperty(valid_types="identity", spec_version="2.1")
|
||||
result = ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
assert ref_prop.clean("my-type--00000000-0000-4000-8000-000000000000")
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("foo")
|
||||
ref_prop.clean("foo--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
# This is not a valid RFC 4122 UUID
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("my-type--00000000-0000-0000-0000-000000000000")
|
||||
ref_prop.clean("foo--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
|
||||
def test_reference_property_specific_type():
|
||||
def test_reference_property_whitelist_custom_type():
|
||||
ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.1")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf")
|
||||
ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
assert ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf") == \
|
||||
"my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf"
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
# This is the whitelisted type, but it's still custom, and
|
||||
# customization is disallowed here.
|
||||
ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_whitelist_generic_type():
|
||||
ref_prop = ReferenceProperty(
|
||||
valid_types=["SCO", "SRO"], spec_version="2.1",
|
||||
)
|
||||
|
||||
result = ref_prop.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("sighting--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
# The prop assumes some-type is a custom type of one of the generic
|
||||
# type categories.
|
||||
result = ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_blacklist_standard_type():
|
||||
ref_prop = ReferenceProperty(invalid_types="identity", spec_version="2.1")
|
||||
result = ref_prop.clean(
|
||||
"location--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("location--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"location--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("location--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
|
||||
|
||||
def test_reference_property_blacklist_custom_type():
|
||||
ref_prop = ReferenceProperty(invalid_types="my-type", spec_version="2.1")
|
||||
|
||||
result = ref_prop.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean("my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
# This is not the blacklisted type, but it's still custom, and
|
||||
# customization is disallowed here.
|
||||
ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_blacklist_generic_type():
|
||||
ref_prop = ReferenceProperty(
|
||||
invalid_types=["SDO", "SRO"], spec_version="2.1",
|
||||
)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
result = ref_prop.clean(
|
||||
"some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
assert result == ("some-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"identity--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"relationship--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ref_prop.clean(
|
||||
"relationship--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True,
|
||||
)
|
||||
|
||||
|
||||
def test_reference_property_whitelist_hybrid_type():
|
||||
p = ReferenceProperty(valid_types=["a", "SCO"], spec_version="2.1")
|
||||
|
||||
result = p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
# although whitelisted, "a" is a custom type
|
||||
p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
result = p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
# should just assume "b" is a custom SCO type.
|
||||
result = p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_blacklist_hybrid_type():
|
||||
p = ReferenceProperty(invalid_types=["a", "SCO"], spec_version="2.1")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("file--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
p.clean("a--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", False)
|
||||
|
||||
# should just assume "b" is a custom type which is not an SCO
|
||||
result = p.clean("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
assert result == ("b--8a8e8758-f92c-4058-ba38-f061cd42a0cf", True)
|
||||
|
||||
|
||||
def test_reference_property_impossible_constraint():
|
||||
with pytest.raises(ValueError):
|
||||
ReferenceProperty(valid_types=[], spec_version="2.1")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -205,29 +389,29 @@ def test_property_list_of_dictionary():
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", [
|
||||
{"sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b"},
|
||||
[('MD5', '2dfb1bcc980200c6706feee399d41b3f'), ('RIPEMD-160', 'b3a8cd8a27c90af79b3c81754f267780f443dfef')],
|
||||
[('TLSH', '6FF02BEF718027B0160B4391212923ED7F1A463D563B1549B86CF62973B197AD2731F8')],
|
||||
"key", [
|
||||
"a",
|
||||
"a"*250,
|
||||
"a-1_b",
|
||||
],
|
||||
)
|
||||
def test_hashes_property_valid(value):
|
||||
hash_prop = HashesProperty()
|
||||
assert hash_prop.clean(value)
|
||||
def test_hash_property_valid_key(key):
|
||||
p = HashesProperty(["foo"], spec_version="2.1")
|
||||
result = p.clean({key: "bar"}, True)
|
||||
assert result == ({key: "bar"}, True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", [
|
||||
{"MD5": "a"},
|
||||
{"SHA-256": "2dfb1bcc980200c6706feee399d41b3f"},
|
||||
{"TLSH": "6FF02BEF718027B0160B4391212923ED7F1A463D563B1549B86CF62973B197AD2731F"},
|
||||
"key", [
|
||||
"",
|
||||
"a"*251,
|
||||
"funny%chars?",
|
||||
],
|
||||
)
|
||||
def test_hashes_property_invalid(value):
|
||||
hash_prop = HashesProperty()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
hash_prop.clean(value)
|
||||
def test_hash_property_invalid_key(key):
|
||||
p = HashesProperty(["foo"], spec_version="2.1")
|
||||
with pytest.raises(DictionaryKeyError):
|
||||
p.clean({key: "foo"}, True)
|
||||
|
||||
|
||||
def test_embedded_property():
|
||||
|
@ -237,25 +421,103 @@ def test_embedded_property():
|
|||
content_disposition="inline",
|
||||
body="Cats are funny!",
|
||||
)
|
||||
assert emb_prop.clean(mime)
|
||||
result = emb_prop.clean(mime, False)
|
||||
assert result == (mime, False)
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert result == (mime, False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
emb_prop.clean("string")
|
||||
emb_prop.clean("string", False)
|
||||
|
||||
|
||||
def test_embedded_property_dict():
|
||||
emb_prop = EmbeddedObjectProperty(type=stix2.v21.EmailMIMEComponent)
|
||||
mime = {
|
||||
"content_type": "text/plain; charset=utf-8",
|
||||
"content_disposition": "inline",
|
||||
"body": "Cats are funny!",
|
||||
}
|
||||
|
||||
result = emb_prop.clean(mime, False)
|
||||
assert isinstance(result[0], stix2.v21.EmailMIMEComponent)
|
||||
assert result[0]["body"] == "Cats are funny!"
|
||||
assert not result[1]
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert isinstance(result[0], stix2.v21.EmailMIMEComponent)
|
||||
assert result[0]["body"] == "Cats are funny!"
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_embedded_property_custom():
|
||||
emb_prop = EmbeddedObjectProperty(type=stix2.v21.EmailMIMEComponent)
|
||||
mime = stix2.v21.EmailMIMEComponent(
|
||||
content_type="text/plain; charset=utf-8",
|
||||
content_disposition="inline",
|
||||
body="Cats are funny!",
|
||||
foo=123,
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
with pytest.raises(CustomContentError):
|
||||
emb_prop.clean(mime, False)
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert result == (mime, True)
|
||||
|
||||
|
||||
def test_embedded_property_dict_custom():
|
||||
emb_prop = EmbeddedObjectProperty(type=stix2.v21.EmailMIMEComponent)
|
||||
mime = {
|
||||
"content_type": "text/plain; charset=utf-8",
|
||||
"content_disposition": "inline",
|
||||
"body": "Cats are funny!",
|
||||
"foo": 123,
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
emb_prop.clean(mime, False)
|
||||
|
||||
result = emb_prop.clean(mime, True)
|
||||
assert isinstance(result[0], stix2.v21.EmailMIMEComponent)
|
||||
assert result[0]["body"] == "Cats are funny!"
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_extension_property_valid():
|
||||
ext_prop = ExtensionsProperty(spec_version='2.1')
|
||||
assert ext_prop({
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
})
|
||||
}, False,
|
||||
)
|
||||
|
||||
assert isinstance(
|
||||
result[0]["windows-pebinary-ext"], stix2.v21.WindowsPEBinaryExt,
|
||||
)
|
||||
assert not result[1]
|
||||
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
}, True,
|
||||
)
|
||||
|
||||
assert isinstance(
|
||||
result[0]["windows-pebinary-ext"], stix2.v21.WindowsPEBinaryExt,
|
||||
)
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_extension_property_invalid1():
|
||||
ext_prop = ExtensionsProperty(spec_version='2.1')
|
||||
with pytest.raises(ValueError):
|
||||
ext_prop.clean(1)
|
||||
ext_prop.clean(1, False)
|
||||
|
||||
|
||||
def test_extension_property_invalid2():
|
||||
|
@ -267,8 +529,61 @@ def test_extension_property_invalid2():
|
|||
'pe_type': 'exe',
|
||||
},
|
||||
},
|
||||
False,
|
||||
)
|
||||
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'foobar-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
}, True,
|
||||
)
|
||||
assert result == ({"foobar-ext": {"pe_type": "exe"}}, True)
|
||||
|
||||
|
||||
def test_extension_property_invalid3():
|
||||
ext_prop = ExtensionsProperty(spec_version="2.1")
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
'abc': 123,
|
||||
},
|
||||
},
|
||||
False,
|
||||
)
|
||||
|
||||
result = ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
'abc': 123,
|
||||
},
|
||||
}, True,
|
||||
)
|
||||
|
||||
assert isinstance(
|
||||
result[0]["windows-pebinary-ext"], stix2.v21.WindowsPEBinaryExt,
|
||||
)
|
||||
assert result[0]["windows-pebinary-ext"]["abc"] == 123
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_extension_property_invalid_type():
|
||||
ext_prop = ExtensionsProperty(spec_version='2.1', enclosing_type='indicator')
|
||||
with pytest.raises(CustomContentError) as excinfo:
|
||||
ext_prop.clean(
|
||||
{
|
||||
'windows-pebinary-ext': {
|
||||
'pe_type': 'exe',
|
||||
},
|
||||
},
|
||||
False,
|
||||
)
|
||||
assert "Can't parse unknown extension" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_extension_at_least_one_property_constraint():
|
||||
with pytest.raises(AtLeastOnePropertyError):
|
||||
|
@ -282,3 +597,115 @@ def test_marking_property_error():
|
|||
mark_prop.clean('my-marking')
|
||||
|
||||
assert str(excinfo.value) == "must be a Statement, TLP Marking or a registered marking."
|
||||
|
||||
|
||||
def test_observable_property_obj():
|
||||
prop = ObservableProperty(spec_version="2.1")
|
||||
|
||||
obs = stix2.v21.File(name="data.dat")
|
||||
obs_dict = {
|
||||
"0": obs,
|
||||
}
|
||||
|
||||
result = prop.clean(obs_dict, False)
|
||||
assert result[0]["0"] == obs
|
||||
assert not result[1]
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert result[0]["0"] == obs
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_observable_property_dict():
|
||||
prop = ObservableProperty(spec_version="2.1")
|
||||
|
||||
obs_dict = {
|
||||
"0": {
|
||||
"type": "file",
|
||||
"name": "data.dat",
|
||||
},
|
||||
}
|
||||
|
||||
result = prop.clean(obs_dict, False)
|
||||
assert isinstance(result[0]["0"], stix2.v21.File)
|
||||
assert result[0]["0"]["name"] == "data.dat"
|
||||
assert not result[1]
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert isinstance(result[0]["0"], stix2.v21.File)
|
||||
assert result[0]["0"]["name"] == "data.dat"
|
||||
assert not result[1]
|
||||
|
||||
|
||||
def test_observable_property_obj_custom():
|
||||
prop = ObservableProperty(spec_version="2.1")
|
||||
|
||||
obs = stix2.v21.File(name="data.dat", foo=True, allow_custom=True)
|
||||
obs_dict = {
|
||||
"0": obs,
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
prop.clean(obs_dict, False)
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert result[0]["0"] == obs
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_observable_property_dict_custom():
|
||||
prop = ObservableProperty(spec_version="2.1")
|
||||
|
||||
obs_dict = {
|
||||
"0": {
|
||||
"type": "file",
|
||||
"name": "data.dat",
|
||||
"foo": True,
|
||||
},
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
prop.clean(obs_dict, False)
|
||||
|
||||
result = prop.clean(obs_dict, True)
|
||||
assert isinstance(result[0]["0"], stix2.v21.File)
|
||||
assert result[0]["0"]["foo"]
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_stix_object_property_custom_prop():
|
||||
prop = STIXObjectProperty(spec_version="2.1")
|
||||
|
||||
obj_dict = {
|
||||
"type": "identity",
|
||||
"spec_version": "2.1",
|
||||
"name": "alice",
|
||||
"identity_class": "supergirl",
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
with pytest.raises(ExtraPropertiesError):
|
||||
prop.clean(obj_dict, False)
|
||||
|
||||
result = prop.clean(obj_dict, True)
|
||||
assert isinstance(result[0], stix2.v21.Identity)
|
||||
assert result[0].has_custom
|
||||
assert result[0]["foo"] == "bar"
|
||||
assert result[1]
|
||||
|
||||
|
||||
def test_stix_object_property_custom_obj():
|
||||
prop = STIXObjectProperty(spec_version="2.1")
|
||||
|
||||
obj_dict = {
|
||||
"type": "something",
|
||||
"abc": 123,
|
||||
"xyz": ["a", 1],
|
||||
}
|
||||
|
||||
with pytest.raises(ParseError):
|
||||
prop.clean(obj_dict, False)
|
||||
|
||||
result = prop.clean(obj_dict, True)
|
||||
assert result[0] == {"type": "something", "abc": 123, "xyz": ["a", 1]}
|
||||
assert result[1]
|
||||
|
|
|
@ -4,6 +4,7 @@ import pytest
|
|||
import pytz
|
||||
|
||||
import stix2
|
||||
from stix2.exceptions import InvalidValueError
|
||||
|
||||
from .constants import (
|
||||
CAMPAIGN_ID, IDENTITY_ID, INDICATOR_ID, INDICATOR_KWARGS, RELATIONSHIP_ID,
|
||||
|
@ -135,4 +136,27 @@ def test_parse_report(data):
|
|||
assert rept.report_types == ["campaign"]
|
||||
assert rept.name == "The Black Vine Cyberespionage Group"
|
||||
|
||||
# TODO: Add other examples
|
||||
|
||||
def test_report_on_custom():
|
||||
with pytest.raises(InvalidValueError):
|
||||
stix2.v21.Report(
|
||||
name="my report",
|
||||
published="2016-01-20T17:00:00Z",
|
||||
object_refs=[
|
||||
"indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"some-type--2672975a-ce1e-4473-a1c6-0d79868930c7",
|
||||
],
|
||||
)
|
||||
|
||||
report = stix2.v21.Report(
|
||||
name="my report",
|
||||
published="2016-01-20T17:00:00Z",
|
||||
object_refs=[
|
||||
"indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"some-type--2672975a-ce1e-4473-a1c6-0d79868930c7",
|
||||
],
|
||||
allow_custom=True,
|
||||
)
|
||||
|
||||
assert "some-type--2672975a-ce1e-4473-a1c6-0d79868930c7" \
|
||||
in report.object_refs
|
||||
|
|
|
@ -76,7 +76,7 @@ def test_seen_ordering_constraint():
|
|||
with pytest.raises(ValueError):
|
||||
stix2.v21.ThreatActor(
|
||||
name="Bad Person",
|
||||
threat_actor_types=["bad person", "evil person"],
|
||||
threat_actor_types=["hacker", "criminal"],
|
||||
first_seen="2010-04-21T09:31:11Z",
|
||||
last_seen="2009-02-06T03:39:31Z",
|
||||
)
|
||||
|
@ -84,7 +84,7 @@ def test_seen_ordering_constraint():
|
|||
# equal timestamps is okay.
|
||||
stix2.v21.ThreatActor(
|
||||
name="Bad Person",
|
||||
threat_actor_types=["bad person", "evil person"],
|
||||
threat_actor_types=["hacker", "criminal"],
|
||||
first_seen="2010-04-21T09:31:11Z",
|
||||
last_seen="2010-04-21T09:31:11Z",
|
||||
)
|
||||
|
|
|
@ -5,8 +5,8 @@ import pytest
|
|||
|
||||
import stix2
|
||||
from stix2.utils import (
|
||||
Precision, PrecisionConstraint, STIXdatetime, _to_enum, format_datetime,
|
||||
parse_into_datetime,
|
||||
Precision, PrecisionConstraint, STIXdatetime, format_datetime,
|
||||
parse_into_datetime, to_enum,
|
||||
)
|
||||
|
||||
_DT = datetime.datetime.utcnow()
|
||||
|
@ -27,7 +27,7 @@ _DT_STR = _DT.strftime("%Y-%m-%dT%H:%M:%S")
|
|||
],
|
||||
)
|
||||
def test_to_enum(value, enum_type, enum_default, enum_expected):
|
||||
result = _to_enum(value, enum_type, enum_default)
|
||||
result = to_enum(value, enum_type, enum_default)
|
||||
assert result == enum_expected
|
||||
|
||||
|
||||
|
@ -41,7 +41,7 @@ def test_to_enum(value, enum_type, enum_default, enum_expected):
|
|||
)
|
||||
def test_to_enum_errors(value, err_type):
|
||||
with pytest.raises(err_type):
|
||||
_to_enum(value, Precision)
|
||||
to_enum(value, Precision)
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
|
|
|
@ -45,7 +45,7 @@ class PrecisionConstraint(enum.Enum):
|
|||
# no need for a MAX constraint yet
|
||||
|
||||
|
||||
def _to_enum(value, enum_type, enum_default=None):
|
||||
def to_enum(value, enum_type, enum_default=None):
|
||||
"""
|
||||
Detect and convert strings to enums and None to a default enum. This
|
||||
allows use of strings and None in APIs, while enforcing the enum type: if
|
||||
|
@ -88,11 +88,11 @@ class STIXdatetime(dt.datetime):
|
|||
"""
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
precision = _to_enum(
|
||||
precision = to_enum(
|
||||
kwargs.pop("precision", Precision.ANY),
|
||||
Precision,
|
||||
)
|
||||
precision_constraint = _to_enum(
|
||||
precision_constraint = to_enum(
|
||||
kwargs.pop("precision_constraint", PrecisionConstraint.EXACT),
|
||||
PrecisionConstraint,
|
||||
)
|
||||
|
@ -233,8 +233,8 @@ def parse_into_datetime(
|
|||
:return: A STIXdatetime instance, which is a datetime but also carries the
|
||||
precision info necessary to properly JSON-serialize it.
|
||||
"""
|
||||
precision = _to_enum(precision, Precision)
|
||||
precision_constraint = _to_enum(precision_constraint, PrecisionConstraint)
|
||||
precision = to_enum(precision, Precision)
|
||||
precision_constraint = to_enum(precision_constraint, PrecisionConstraint)
|
||||
|
||||
if isinstance(value, dt.date):
|
||||
if hasattr(value, 'hour'):
|
||||
|
|
|
@ -35,9 +35,6 @@ class Bundle(_STIXBase20):
|
|||
|
||||
kwargs['objects'] = obj_list + kwargs.get('objects', [])
|
||||
|
||||
self._allow_custom = kwargs.get('allow_custom', False)
|
||||
self._properties['objects'].contained.allow_custom = kwargs.get('allow_custom', False)
|
||||
|
||||
super(Bundle, self).__init__(**kwargs)
|
||||
|
||||
def get_obj(self, obj_uuid):
|
||||
|
|
|
@ -12,6 +12,7 @@ from ..properties import (
|
|||
)
|
||||
from ..utils import NOW, _get_dict
|
||||
from .base import _STIXBase20
|
||||
from .vocab import HASHING_ALGORITHM
|
||||
|
||||
|
||||
def _should_set_millisecond(cr, marking_type):
|
||||
|
@ -38,7 +39,7 @@ class ExternalReference(_STIXBase20):
|
|||
('source_name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('url', StringProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.0')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version='2.0')),
|
||||
('external_id', StringProperty()),
|
||||
])
|
||||
|
||||
|
@ -103,9 +104,9 @@ class MarkingProperty(Property):
|
|||
marking-definition objects.
|
||||
"""
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
if type(value) in OBJ_MAP_MARKING.values():
|
||||
return value
|
||||
return value, False
|
||||
else:
|
||||
raise ValueError("must be a Statement, TLP Marking or a registered marking.")
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from ..properties import (
|
|||
ObjectReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from .base import _Extension, _Observable, _STIXBase20
|
||||
from .vocab import HASHING_ALGORITHM
|
||||
|
||||
|
||||
class Artifact(_Observable):
|
||||
|
@ -30,7 +31,7 @@ class Artifact(_Observable):
|
|||
('mime_type', StringProperty()),
|
||||
('payload_bin', BinaryProperty()),
|
||||
('url', StringProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.0')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version='2.0')),
|
||||
('extensions', ExtensionsProperty(spec_version='2.0')),
|
||||
])
|
||||
|
||||
|
@ -173,7 +174,7 @@ class AlternateDataStream(_STIXBase20):
|
|||
|
||||
_properties = OrderedDict([
|
||||
('name', StringProperty(required=True)),
|
||||
('hashes', HashesProperty(spec_version='2.0')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.0")),
|
||||
('size', IntegerProperty()),
|
||||
])
|
||||
|
||||
|
@ -256,7 +257,7 @@ class WindowsPEOptionalHeaderType(_STIXBase20):
|
|||
('size_of_heap_commit', IntegerProperty()),
|
||||
('loader_flags_hex', HexProperty()),
|
||||
('number_of_rva_and_sizes', IntegerProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.0')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.0")),
|
||||
])
|
||||
|
||||
def _check_object_constraints(self):
|
||||
|
@ -273,7 +274,7 @@ class WindowsPESection(_STIXBase20):
|
|||
('name', StringProperty(required=True)),
|
||||
('size', IntegerProperty()),
|
||||
('entropy', FloatProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.0')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.0")),
|
||||
])
|
||||
|
||||
|
||||
|
@ -293,7 +294,7 @@ class WindowsPEBinaryExt(_Extension):
|
|||
('number_of_symbols', IntegerProperty()),
|
||||
('size_of_optional_header', IntegerProperty()),
|
||||
('characteristics_hex', HexProperty()),
|
||||
('file_header_hashes', HashesProperty(spec_version='2.0')),
|
||||
('file_header_hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.0")),
|
||||
('optional_header', EmbeddedObjectProperty(type=WindowsPEOptionalHeaderType)),
|
||||
('sections', ListProperty(EmbeddedObjectProperty(type=WindowsPESection))),
|
||||
])
|
||||
|
@ -307,7 +308,7 @@ class File(_Observable):
|
|||
_type = 'file'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('hashes', HashesProperty(spec_version='2.0')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.0")),
|
||||
('size', IntegerProperty()),
|
||||
('name', StringProperty()),
|
||||
('name_enc', StringProperty()),
|
||||
|
@ -771,7 +772,7 @@ class X509Certificate(_Observable):
|
|||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('is_self_signed', BooleanProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.0')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.0")),
|
||||
('version', StringProperty()),
|
||||
('serial_number', StringProperty()),
|
||||
('signature_algorithm', StringProperty()),
|
||||
|
|
|
@ -9,12 +9,17 @@ from ..custom import _custom_object_builder
|
|||
from ..exceptions import InvalidValueError
|
||||
from ..properties import (
|
||||
BooleanProperty, IDProperty, IntegerProperty, ListProperty,
|
||||
ObservableProperty, PatternProperty, ReferenceProperty, StringProperty,
|
||||
TimestampProperty, TypeProperty,
|
||||
ObservableProperty, OpenVocabProperty, PatternProperty, ReferenceProperty,
|
||||
StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW
|
||||
from .base import _DomainObject
|
||||
from .common import ExternalReference, GranularMarking, KillChainPhase
|
||||
from .vocab import (
|
||||
ATTACK_MOTIVATION, ATTACK_RESOURCE_LEVEL, IDENTITY_CLASS, INDICATOR_LABEL,
|
||||
INDUSTRY_SECTOR, MALWARE_LABEL, REPORT_LABEL, THREAT_ACTOR_LABEL,
|
||||
THREAT_ACTOR_ROLE, THREAT_ACTOR_SOPHISTICATION, TOOL_LABEL,
|
||||
)
|
||||
|
||||
|
||||
class AttackPattern(_DomainObject):
|
||||
|
@ -102,8 +107,8 @@ class Identity(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('identity_class', StringProperty(required=True)),
|
||||
('sectors', ListProperty(StringProperty)),
|
||||
('identity_class', OpenVocabProperty(IDENTITY_CLASS, required=True)),
|
||||
('sectors', ListProperty(OpenVocabProperty(INDUSTRY_SECTOR))),
|
||||
('contact_information', StringProperty()),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -132,7 +137,7 @@ class Indicator(_DomainObject):
|
|||
('valid_until', TimestampProperty()),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty, required=True)),
|
||||
('labels', ListProperty(OpenVocabProperty(INDICATOR_LABEL), required=True)),
|
||||
('external_references', ListProperty(ExternalReference)),
|
||||
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
|
||||
('granular_markings', ListProperty(GranularMarking)),
|
||||
|
@ -163,8 +168,8 @@ class IntrusionSet(_DomainObject):
|
|||
('last_seen', TimestampProperty()),
|
||||
('goals', ListProperty(StringProperty)),
|
||||
('resource_level', StringProperty()),
|
||||
('primary_motivation', StringProperty()),
|
||||
('secondary_motivations', ListProperty(StringProperty)),
|
||||
('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)),
|
||||
('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
('external_references', ListProperty(ExternalReference)),
|
||||
|
@ -189,7 +194,7 @@ class Malware(_DomainObject):
|
|||
('description', StringProperty()),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty, required=True)),
|
||||
('labels', ListProperty(OpenVocabProperty(MALWARE_LABEL), required=True)),
|
||||
('external_references', ListProperty(ExternalReference)),
|
||||
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
|
||||
('granular_markings', ListProperty(GranularMarking)),
|
||||
|
@ -219,12 +224,6 @@ class ObservedData(_DomainObject):
|
|||
('granular_markings', ListProperty(GranularMarking)),
|
||||
])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._allow_custom = kwargs.get('allow_custom', False)
|
||||
self._properties['objects'].allow_custom = kwargs.get('allow_custom', False)
|
||||
|
||||
super(ObservedData, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class Report(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
|
@ -243,7 +242,7 @@ class Report(_DomainObject):
|
|||
('published', TimestampProperty(required=True)),
|
||||
('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.0'), required=True)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty, required=True)),
|
||||
('labels', ListProperty(OpenVocabProperty(REPORT_LABEL), required=True)),
|
||||
('external_references', ListProperty(ExternalReference)),
|
||||
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
|
||||
('granular_markings', ListProperty(GranularMarking)),
|
||||
|
@ -265,15 +264,15 @@ class ThreatActor(_DomainObject):
|
|||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('roles', ListProperty(StringProperty)),
|
||||
('roles', ListProperty(OpenVocabProperty(THREAT_ACTOR_ROLE))),
|
||||
('goals', ListProperty(StringProperty)),
|
||||
('sophistication', StringProperty()),
|
||||
('resource_level', StringProperty()),
|
||||
('primary_motivation', StringProperty()),
|
||||
('secondary_motivations', ListProperty(StringProperty)),
|
||||
('personal_motivations', ListProperty(StringProperty)),
|
||||
('sophistication', OpenVocabProperty(THREAT_ACTOR_SOPHISTICATION)),
|
||||
('resource_level', OpenVocabProperty(ATTACK_RESOURCE_LEVEL)),
|
||||
('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)),
|
||||
('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))),
|
||||
('personal_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty, required=True)),
|
||||
('labels', ListProperty(OpenVocabProperty(THREAT_ACTOR_LABEL), required=True)),
|
||||
('external_references', ListProperty(ExternalReference)),
|
||||
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
|
||||
('granular_markings', ListProperty(GranularMarking)),
|
||||
|
@ -297,7 +296,7 @@ class Tool(_DomainObject):
|
|||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('tool_version', StringProperty()),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty, required=True)),
|
||||
('labels', ListProperty(OpenVocabProperty(TOOL_LABEL), required=True)),
|
||||
('external_references', ListProperty(ExternalReference)),
|
||||
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
|
||||
('granular_markings', ListProperty(GranularMarking)),
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
"""
|
||||
STIX 2.0 open vocabularies and enums
|
||||
"""
|
||||
|
||||
ATTACK_MOTIVATION_ACCIDENTAL = "accidental"
|
||||
ATTACK_MOTIVATION_COERCION = "coercion"
|
||||
ATTACK_MOTIVATION_DOMINANCE = "dominance"
|
||||
ATTACK_MOTIVATION_IDEOLOGY = "ideology"
|
||||
ATTACK_MOTIVATION_NOTORIETY = "notoriety"
|
||||
ATTACK_MOTIVATION_ORGANIZATIONAL_GAIN = "organizational-gain"
|
||||
ATTACK_MOTIVATION_PERSONAL_GAIN = "personal-gain"
|
||||
ATTACK_MOTIVATION_PERSONAL_SATISFACTION = "personal-satisfaction"
|
||||
ATTACK_MOTIVATION_REVENGE = "revenge"
|
||||
ATTACK_MOTIVATION_UNPREDICTABLE = "unpredictable"
|
||||
|
||||
|
||||
ATTACK_MOTIVATION = [
|
||||
ATTACK_MOTIVATION_ACCIDENTAL,
|
||||
ATTACK_MOTIVATION_COERCION,
|
||||
ATTACK_MOTIVATION_DOMINANCE,
|
||||
ATTACK_MOTIVATION_IDEOLOGY,
|
||||
ATTACK_MOTIVATION_NOTORIETY,
|
||||
ATTACK_MOTIVATION_ORGANIZATIONAL_GAIN,
|
||||
ATTACK_MOTIVATION_PERSONAL_GAIN,
|
||||
ATTACK_MOTIVATION_PERSONAL_SATISFACTION,
|
||||
ATTACK_MOTIVATION_REVENGE,
|
||||
ATTACK_MOTIVATION_UNPREDICTABLE,
|
||||
]
|
||||
|
||||
|
||||
ATTACK_RESOURCE_LEVEL_INDIVIDUAL = "individual"
|
||||
ATTACK_RESOURCE_LEVEL_CLUB = "club"
|
||||
ATTACK_RESOURCE_LEVEL_CONTEST = "contest"
|
||||
ATTACK_RESOURCE_LEVEL_TEAM = "team"
|
||||
ATTACK_RESOURCE_LEVEL_ORGANIZATION = "organization"
|
||||
ATTACK_RESOURCE_LEVEL_GOVERNMENT = "government"
|
||||
|
||||
|
||||
ATTACK_RESOURCE_LEVEL = [
|
||||
ATTACK_RESOURCE_LEVEL_INDIVIDUAL,
|
||||
ATTACK_RESOURCE_LEVEL_CLUB,
|
||||
ATTACK_RESOURCE_LEVEL_CONTEST,
|
||||
ATTACK_RESOURCE_LEVEL_TEAM,
|
||||
ATTACK_RESOURCE_LEVEL_ORGANIZATION,
|
||||
ATTACK_RESOURCE_LEVEL_GOVERNMENT,
|
||||
]
|
||||
|
||||
|
||||
HASHING_ALGORITHM_MD5 = "MD5"
|
||||
HASHING_ALGORITHM_MD6 = "MD6"
|
||||
HASHING_ALGORITHM_RIPEMD_160 = "RIPEMD-160"
|
||||
HASHING_ALGORITHM_SHA_1 = "SHA-1"
|
||||
HASHING_ALGORITHM_SHA_224 = "SHA-224"
|
||||
HASHING_ALGORITHM_SHA_256 = "SHA-256"
|
||||
HASHING_ALGORITHM_SHA_384 = "SHA-384"
|
||||
HASHING_ALGORITHM_SHA_512 = "SHA-512"
|
||||
HASHING_ALGORITHM_SHA3_224 = "SHA3-224"
|
||||
HASHING_ALGORITHM_SHA3_256 = "SHA3-256"
|
||||
HASHING_ALGORITHM_SHA3_384 = "SHA3-384"
|
||||
HASHING_ALGORITHM_SHA3_512 = "SHA3-512"
|
||||
HASHING_ALGORITHM_SSDEEP = "ssdeep"
|
||||
HASHING_ALGORITHM_WHIRLPOOL = "WHIRLPOOL"
|
||||
|
||||
|
||||
HASHING_ALGORITHM = [
|
||||
HASHING_ALGORITHM_MD5,
|
||||
HASHING_ALGORITHM_MD6,
|
||||
HASHING_ALGORITHM_RIPEMD_160,
|
||||
HASHING_ALGORITHM_SHA_1,
|
||||
HASHING_ALGORITHM_SHA_224,
|
||||
HASHING_ALGORITHM_SHA_256,
|
||||
HASHING_ALGORITHM_SHA_384,
|
||||
HASHING_ALGORITHM_SHA_512,
|
||||
HASHING_ALGORITHM_SHA3_224,
|
||||
HASHING_ALGORITHM_SHA3_256,
|
||||
HASHING_ALGORITHM_SHA3_384,
|
||||
HASHING_ALGORITHM_SHA3_512,
|
||||
HASHING_ALGORITHM_SSDEEP,
|
||||
HASHING_ALGORITHM_WHIRLPOOL,
|
||||
]
|
||||
|
||||
|
||||
IDENTITY_CLASS_INDIVIDUAL = "individual"
|
||||
IDENTITY_CLASS_GROUP = "group"
|
||||
IDENTITY_CLASS_ORGANIZATION = "organization"
|
||||
IDENTITY_CLASS_CLASS = "class"
|
||||
IDENTITY_CLASS_UNKNOWN = "unknown"
|
||||
|
||||
|
||||
IDENTITY_CLASS = [
|
||||
IDENTITY_CLASS_INDIVIDUAL,
|
||||
IDENTITY_CLASS_GROUP,
|
||||
IDENTITY_CLASS_ORGANIZATION,
|
||||
IDENTITY_CLASS_CLASS,
|
||||
IDENTITY_CLASS_UNKNOWN,
|
||||
]
|
||||
|
||||
|
||||
INDICATOR_LABEL_ANOMALOUS_ACTIVITY = "anomalous-activity"
|
||||
INDICATOR_LABEL_ANONYMIZATION = "anonymization"
|
||||
INDICATOR_LABEL_BENIGN = "benign"
|
||||
INDICATOR_LABEL_COMPROMISED = "compromised"
|
||||
INDICATOR_LABEL_MALICIOUS_ACTIVITY = "malicious-activity"
|
||||
INDICATOR_LABEL_ATTRIBUTION = "attribution"
|
||||
|
||||
|
||||
INDICATOR_LABEL = [
|
||||
INDICATOR_LABEL_ANOMALOUS_ACTIVITY,
|
||||
INDICATOR_LABEL_ANONYMIZATION,
|
||||
INDICATOR_LABEL_BENIGN,
|
||||
INDICATOR_LABEL_COMPROMISED,
|
||||
INDICATOR_LABEL_MALICIOUS_ACTIVITY,
|
||||
INDICATOR_LABEL_ATTRIBUTION,
|
||||
]
|
||||
|
||||
|
||||
INDUSTRY_SECTOR_AGRICULTURE = "agriculture"
|
||||
INDUSTRY_SECTOR_AEROSPACE = "aerospace"
|
||||
INDUSTRY_SECTOR_AUTOMOTIVE = "automotive"
|
||||
INDUSTRY_SECTOR_COMMUNICATIONS = "communications"
|
||||
INDUSTRY_SECTOR_CONSTRUCTION = "construction"
|
||||
INDUSTRY_SECTOR_DEFENCE = "defence"
|
||||
INDUSTRY_SECTOR_EDUCATION = "education"
|
||||
INDUSTRY_SECTOR_ENERGY = "energy"
|
||||
INDUSTRY_SECTOR_ENTERTAINMENT = "entertainment"
|
||||
INDUSTRY_SECTOR_FINANCIAL_SERVICES = "financial-services"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_NATIONAL = "government-national"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_REGIONAL = "government-regional"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_LOCAL = "government-local"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_PUBLIC_SERVICES = "government-public-services"
|
||||
INDUSTRY_SECTOR_HEALTHCARE = "healthcare"
|
||||
INDUSTRY_SECTOR_HOSPITALITY_LEISURE = "hospitality-leisure"
|
||||
INDUSTRY_SECTOR_INFRASTRUCTURE = "infrastructure"
|
||||
INDUSTRY_SECTOR_INSURANCE = "insurance"
|
||||
INDUSTRY_SECTOR_MANUFACTURING = "manufacturing"
|
||||
INDUSTRY_SECTOR_MINING = "mining"
|
||||
INDUSTRY_SECTOR_NON_PROFIT = "non-profit"
|
||||
INDUSTRY_SECTOR_PHARMACEUTICALS = "pharmaceuticals"
|
||||
INDUSTRY_SECTOR_RETAIL = "retail"
|
||||
INDUSTRY_SECTOR_TECHNOLOGY = "technology"
|
||||
INDUSTRY_SECTOR_TELECOMMUNICATIONS = "telecommunications"
|
||||
INDUSTRY_SECTOR_TRANSPORTATION = "transportation"
|
||||
INDUSTRY_SECTOR_UTILITIES = "utilities"
|
||||
|
||||
|
||||
INDUSTRY_SECTOR = [
|
||||
INDUSTRY_SECTOR_AGRICULTURE,
|
||||
INDUSTRY_SECTOR_AEROSPACE,
|
||||
INDUSTRY_SECTOR_AUTOMOTIVE,
|
||||
INDUSTRY_SECTOR_COMMUNICATIONS,
|
||||
INDUSTRY_SECTOR_CONSTRUCTION,
|
||||
INDUSTRY_SECTOR_DEFENCE,
|
||||
INDUSTRY_SECTOR_EDUCATION,
|
||||
INDUSTRY_SECTOR_ENERGY,
|
||||
INDUSTRY_SECTOR_ENTERTAINMENT,
|
||||
INDUSTRY_SECTOR_FINANCIAL_SERVICES,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_NATIONAL,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_REGIONAL,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_LOCAL,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_PUBLIC_SERVICES,
|
||||
INDUSTRY_SECTOR_HEALTHCARE,
|
||||
INDUSTRY_SECTOR_HOSPITALITY_LEISURE,
|
||||
INDUSTRY_SECTOR_INFRASTRUCTURE,
|
||||
INDUSTRY_SECTOR_INSURANCE,
|
||||
INDUSTRY_SECTOR_MANUFACTURING,
|
||||
INDUSTRY_SECTOR_MINING,
|
||||
INDUSTRY_SECTOR_NON_PROFIT,
|
||||
INDUSTRY_SECTOR_PHARMACEUTICALS,
|
||||
INDUSTRY_SECTOR_RETAIL,
|
||||
INDUSTRY_SECTOR_TECHNOLOGY,
|
||||
INDUSTRY_SECTOR_TELECOMMUNICATIONS,
|
||||
INDUSTRY_SECTOR_TRANSPORTATION,
|
||||
INDUSTRY_SECTOR_UTILITIES,
|
||||
]
|
||||
|
||||
|
||||
MALWARE_LABEL_ADWARE = "adware"
|
||||
MALWARE_LABEL_BACKDOOR = "backdoor"
|
||||
MALWARE_LABEL_BOT = "bot"
|
||||
MALWARE_LABEL_DDOS = "ddos"
|
||||
MALWARE_LABEL_DROPPER = "dropper"
|
||||
MALWARE_LABEL_EXPLOIT_KIT = "exploit-kit"
|
||||
MALWARE_LABEL_KEYLOGGER = "keylogger"
|
||||
MALWARE_LABEL_RANSOMWARE = "ransomware"
|
||||
MALWARE_LABEL_REMOTE_ACCESS_TROJAN = "remote-access-trojan"
|
||||
MALWARE_LABEL_RESOURCE_EXPLOITATION = "resource-exploitation"
|
||||
MALWARE_LABEL_ROGUE_SECURITY_SOFTWARE = "rogue-security-software"
|
||||
MALWARE_LABEL_ROOTKIT = "rootkit"
|
||||
MALWARE_LABEL_SCREEN_CAPTURE = "screen-capture"
|
||||
MALWARE_LABEL_SPYWARE = "spyware"
|
||||
MALWARE_LABEL_TROJAN = "trojan"
|
||||
MALWARE_LABEL_VIRUS = "virus"
|
||||
MALWARE_LABEL_WORM = "worm"
|
||||
|
||||
|
||||
MALWARE_LABEL = [
|
||||
MALWARE_LABEL_ADWARE,
|
||||
MALWARE_LABEL_BACKDOOR,
|
||||
MALWARE_LABEL_BOT,
|
||||
MALWARE_LABEL_DDOS,
|
||||
MALWARE_LABEL_DROPPER,
|
||||
MALWARE_LABEL_EXPLOIT_KIT,
|
||||
MALWARE_LABEL_KEYLOGGER,
|
||||
MALWARE_LABEL_RANSOMWARE,
|
||||
MALWARE_LABEL_REMOTE_ACCESS_TROJAN,
|
||||
MALWARE_LABEL_RESOURCE_EXPLOITATION,
|
||||
MALWARE_LABEL_ROGUE_SECURITY_SOFTWARE,
|
||||
MALWARE_LABEL_ROOTKIT,
|
||||
MALWARE_LABEL_SCREEN_CAPTURE,
|
||||
MALWARE_LABEL_SPYWARE,
|
||||
MALWARE_LABEL_TROJAN,
|
||||
MALWARE_LABEL_VIRUS,
|
||||
MALWARE_LABEL_WORM,
|
||||
]
|
||||
|
||||
|
||||
REPORT_LABEL_THREAT_REPORT = "threat-report"
|
||||
REPORT_LABEL_ATTACK_PATTERN = "attack-pattern"
|
||||
REPORT_LABEL_CAMPAIGN = "campaign"
|
||||
REPORT_LABEL_IDENTITY = "identity"
|
||||
REPORT_LABEL_INDICATOR = "indicator"
|
||||
REPORT_LABEL_INTRUSION_SET = "intrusion-set"
|
||||
REPORT_LABEL_MALWARE = "malware"
|
||||
REPORT_LABEL_OBSERVED_DATA = "observed-data"
|
||||
REPORT_LABEL_THREAT_ACTOR = "threat-actor"
|
||||
REPORT_LABEL_TOOL = "tool"
|
||||
REPORT_LABEL_VULNERABILITY = "vulnerability"
|
||||
|
||||
|
||||
REPORT_LABEL = [
|
||||
REPORT_LABEL_THREAT_REPORT,
|
||||
REPORT_LABEL_ATTACK_PATTERN,
|
||||
REPORT_LABEL_CAMPAIGN,
|
||||
REPORT_LABEL_IDENTITY,
|
||||
REPORT_LABEL_INDICATOR,
|
||||
REPORT_LABEL_INTRUSION_SET,
|
||||
REPORT_LABEL_MALWARE,
|
||||
REPORT_LABEL_OBSERVED_DATA,
|
||||
REPORT_LABEL_THREAT_ACTOR,
|
||||
REPORT_LABEL_TOOL,
|
||||
REPORT_LABEL_VULNERABILITY,
|
||||
]
|
||||
|
||||
|
||||
THREAT_ACTOR_LABEL_ACTIVIST = "activist"
|
||||
THREAT_ACTOR_LABEL_COMPETITOR = "competitor"
|
||||
THREAT_ACTOR_LABEL_CRIME_SYNDICATE = "crime-syndicate"
|
||||
THREAT_ACTOR_LABEL_CRIMINAL = "criminal"
|
||||
THREAT_ACTOR_LABEL_HACKER = "hacker"
|
||||
THREAT_ACTOR_LABEL_INSIDER_ACCIDENTAL = "insider-accidental"
|
||||
THREAT_ACTOR_LABEL_INSIDER_DISGRUNTLED = "insider-disgruntled"
|
||||
THREAT_ACTOR_LABEL_NATION_STATE = "nation-state"
|
||||
THREAT_ACTOR_LABEL_SENSATIONALIST = "sensationalist"
|
||||
THREAT_ACTOR_LABEL_SPY = "spy"
|
||||
THREAT_ACTOR_LABEL_TERRORIST = "terrorist"
|
||||
|
||||
|
||||
THREAT_ACTOR_LABEL = [
|
||||
THREAT_ACTOR_LABEL_ACTIVIST,
|
||||
THREAT_ACTOR_LABEL_COMPETITOR,
|
||||
THREAT_ACTOR_LABEL_CRIME_SYNDICATE,
|
||||
THREAT_ACTOR_LABEL_CRIMINAL,
|
||||
THREAT_ACTOR_LABEL_HACKER,
|
||||
THREAT_ACTOR_LABEL_INSIDER_ACCIDENTAL,
|
||||
THREAT_ACTOR_LABEL_INSIDER_DISGRUNTLED,
|
||||
THREAT_ACTOR_LABEL_NATION_STATE,
|
||||
THREAT_ACTOR_LABEL_SENSATIONALIST,
|
||||
THREAT_ACTOR_LABEL_SPY,
|
||||
THREAT_ACTOR_LABEL_TERRORIST,
|
||||
]
|
||||
|
||||
|
||||
THREAT_ACTOR_ROLE_AGENT = "agent"
|
||||
THREAT_ACTOR_ROLE_DIRECTOR = "director"
|
||||
THREAT_ACTOR_ROLE_INDEPENDENT = "independent"
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_ARCHITECT = "infrastructure-architect"
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_OPERATOR = "infrastructure-operator"
|
||||
THREAT_ACTOR_ROLE_MALWARE_AUTHOR = "malware-author"
|
||||
THREAT_ACTOR_ROLE_SPONSOR = "sponsor"
|
||||
|
||||
|
||||
THREAT_ACTOR_ROLE = [
|
||||
THREAT_ACTOR_ROLE_AGENT,
|
||||
THREAT_ACTOR_ROLE_DIRECTOR,
|
||||
THREAT_ACTOR_ROLE_INDEPENDENT,
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_ARCHITECT,
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_OPERATOR,
|
||||
THREAT_ACTOR_ROLE_MALWARE_AUTHOR,
|
||||
THREAT_ACTOR_ROLE_SPONSOR,
|
||||
]
|
||||
|
||||
|
||||
THREAT_ACTOR_SOPHISTICATION_NONE = "none"
|
||||
THREAT_ACTOR_SOPHISTICATION_MINIMAL = "minimal"
|
||||
THREAT_ACTOR_SOPHISTICATION_INTERMEDIATE = "intermediate"
|
||||
THREAT_ACTOR_SOPHISTICATION_ADVANCED = "advanced"
|
||||
THREAT_ACTOR_SOPHISTICATION_EXPERT = "expert"
|
||||
THREAT_ACTOR_SOPHISTICATION_INNOVATOR = "innovator"
|
||||
THREAT_ACTOR_SOPHISTICATION_STRATEGIC = "strategic"
|
||||
|
||||
|
||||
THREAT_ACTOR_SOPHISTICATION = [
|
||||
THREAT_ACTOR_SOPHISTICATION_NONE,
|
||||
THREAT_ACTOR_SOPHISTICATION_MINIMAL,
|
||||
THREAT_ACTOR_SOPHISTICATION_INTERMEDIATE,
|
||||
THREAT_ACTOR_SOPHISTICATION_ADVANCED,
|
||||
THREAT_ACTOR_SOPHISTICATION_EXPERT,
|
||||
THREAT_ACTOR_SOPHISTICATION_INNOVATOR,
|
||||
THREAT_ACTOR_SOPHISTICATION_STRATEGIC,
|
||||
]
|
||||
|
||||
|
||||
TOOL_LABEL_DENIAL_OF_SERVICE = "denial-of-service"
|
||||
TOOL_LABEL_EXPLOITATION = "exploitation"
|
||||
TOOL_LABEL_INFORMATION_GATHERING = "information-gathering"
|
||||
TOOL_LABEL_NETWORK_CAPTURE = "network-capture"
|
||||
TOOL_LABEL_CREDENTIAL_EXPLOITATION = "credential-exploitation"
|
||||
TOOL_LABEL_REMOTE_ACCESS = "remote-access"
|
||||
TOOL_LABEL_VULNERABILITY_SCANNING = "vulnerability-scanning"
|
||||
|
||||
|
||||
TOOL_LABEL = [
|
||||
TOOL_LABEL_DENIAL_OF_SERVICE,
|
||||
TOOL_LABEL_EXPLOITATION,
|
||||
TOOL_LABEL_INFORMATION_GATHERING,
|
||||
TOOL_LABEL_NETWORK_CAPTURE,
|
||||
TOOL_LABEL_CREDENTIAL_EXPLOITATION,
|
||||
TOOL_LABEL_REMOTE_ACCESS,
|
||||
TOOL_LABEL_VULNERABILITY_SCANNING,
|
||||
]
|
|
@ -32,9 +32,6 @@ class Bundle(_STIXBase21):
|
|||
|
||||
kwargs['objects'] = obj_list + kwargs.get('objects', [])
|
||||
|
||||
self._allow_custom = kwargs.get('allow_custom', False)
|
||||
self._properties['objects'].contained.allow_custom = kwargs.get('allow_custom', False)
|
||||
|
||||
super(Bundle, self).__init__(**kwargs)
|
||||
|
||||
def get_obj(self, obj_uuid):
|
||||
|
|
|
@ -14,6 +14,7 @@ from ..properties import (
|
|||
)
|
||||
from ..utils import NOW, _get_dict
|
||||
from .base import _STIXBase21
|
||||
from .vocab import HASHING_ALGORITHM
|
||||
|
||||
|
||||
class ExternalReference(_STIXBase21):
|
||||
|
@ -25,7 +26,7 @@ class ExternalReference(_STIXBase21):
|
|||
('source_name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('url', StringProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
('external_id', StringProperty()),
|
||||
])
|
||||
|
||||
|
@ -178,9 +179,9 @@ class MarkingProperty(Property):
|
|||
marking-definition objects.
|
||||
"""
|
||||
|
||||
def clean(self, value):
|
||||
def clean(self, value, allow_custom=False):
|
||||
if type(value) in OBJ_MAP_MARKING.values():
|
||||
return value
|
||||
return value, False
|
||||
else:
|
||||
raise ValueError("must be a Statement, TLP Marking or a registered marking.")
|
||||
|
||||
|
|
|
@ -14,10 +14,17 @@ from ..properties import (
|
|||
BinaryProperty, BooleanProperty, DictionaryProperty,
|
||||
EmbeddedObjectProperty, EnumProperty, ExtensionsProperty, FloatProperty,
|
||||
HashesProperty, HexProperty, IDProperty, IntegerProperty, ListProperty,
|
||||
ReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
|
||||
OpenVocabProperty, ReferenceProperty, StringProperty, TimestampProperty,
|
||||
TypeProperty,
|
||||
)
|
||||
from .base import _Extension, _Observable, _STIXBase21
|
||||
from .common import GranularMarking
|
||||
from .vocab import (
|
||||
ACCOUNT_TYPE, ENCRYPTION_ALGORITHM, HASHING_ALGORITHM,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY, NETWORK_SOCKET_TYPE,
|
||||
WINDOWS_INTEGRITY_LEVEL, WINDOWS_PEBINARY_TYPE, WINDOWS_REGISTRY_DATATYPE,
|
||||
WINDOWS_SERVICE_START_TYPE, WINDOWS_SERVICE_STATUS, WINDOWS_SERVICE_TYPE,
|
||||
)
|
||||
|
||||
|
||||
class Artifact(_Observable):
|
||||
|
@ -33,8 +40,8 @@ class Artifact(_Observable):
|
|||
('mime_type', StringProperty()),
|
||||
('payload_bin', BinaryProperty()),
|
||||
('url', StringProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('encryption_algorithm', StringProperty()),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
('encryption_algorithm', EnumProperty(ENCRYPTION_ALGORITHM)),
|
||||
('decryption_key', StringProperty()),
|
||||
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
|
||||
('granular_markings', ListProperty(GranularMarking)),
|
||||
|
@ -212,7 +219,7 @@ class AlternateDataStream(_STIXBase21):
|
|||
|
||||
_properties = OrderedDict([
|
||||
('name', StringProperty(required=True)),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
('size', IntegerProperty()),
|
||||
])
|
||||
|
||||
|
@ -294,7 +301,7 @@ class WindowsPEOptionalHeaderType(_STIXBase21):
|
|||
('size_of_heap_commit', IntegerProperty()),
|
||||
('loader_flags_hex', HexProperty()),
|
||||
('number_of_rva_and_sizes', IntegerProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
])
|
||||
|
||||
def _check_object_constraints(self):
|
||||
|
@ -311,7 +318,7 @@ class WindowsPESection(_STIXBase21):
|
|||
('name', StringProperty(required=True)),
|
||||
('size', IntegerProperty(min=0)),
|
||||
('entropy', FloatProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
])
|
||||
|
||||
|
||||
|
@ -322,7 +329,7 @@ class WindowsPEBinaryExt(_Extension):
|
|||
|
||||
_type = 'windows-pebinary-ext'
|
||||
_properties = OrderedDict([
|
||||
('pe_type', StringProperty(required=True)), # open_vocab
|
||||
('pe_type', OpenVocabProperty(WINDOWS_PEBINARY_TYPE, required=True)),
|
||||
('imphash', StringProperty()),
|
||||
('machine_hex', HexProperty()),
|
||||
('number_of_sections', IntegerProperty(min=0)),
|
||||
|
@ -331,7 +338,7 @@ class WindowsPEBinaryExt(_Extension):
|
|||
('number_of_symbols', IntegerProperty(min=0)),
|
||||
('size_of_optional_header', IntegerProperty(min=0)),
|
||||
('characteristics_hex', HexProperty()),
|
||||
('file_header_hashes', HashesProperty(spec_version='2.1')),
|
||||
('file_header_hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
('optional_header', EmbeddedObjectProperty(type=WindowsPEOptionalHeaderType)),
|
||||
('sections', ListProperty(EmbeddedObjectProperty(type=WindowsPESection))),
|
||||
])
|
||||
|
@ -347,7 +354,7 @@ class File(_Observable):
|
|||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
('size', IntegerProperty(min=0)),
|
||||
('name', StringProperty()),
|
||||
('name_enc', StringProperty()),
|
||||
|
@ -486,34 +493,11 @@ class SocketExt(_Extension):
|
|||
|
||||
_type = 'socket-ext'
|
||||
_properties = OrderedDict([
|
||||
(
|
||||
'address_family', EnumProperty(
|
||||
allowed=[
|
||||
"AF_UNSPEC",
|
||||
"AF_INET",
|
||||
"AF_IPX",
|
||||
"AF_APPLETALK",
|
||||
"AF_NETBIOS",
|
||||
"AF_INET6",
|
||||
"AF_IRDA",
|
||||
"AF_BTH",
|
||||
], required=True,
|
||||
),
|
||||
),
|
||||
('address_family', EnumProperty(NETWORK_SOCKET_ADDRESS_FAMILY, required=True)),
|
||||
('is_blocking', BooleanProperty()),
|
||||
('is_listening', BooleanProperty()),
|
||||
('options', DictionaryProperty(spec_version='2.1')),
|
||||
(
|
||||
'socket_type', EnumProperty(
|
||||
allowed=[
|
||||
"SOCK_STREAM",
|
||||
"SOCK_DGRAM",
|
||||
"SOCK_RAW",
|
||||
"SOCK_RDM",
|
||||
"SOCK_SEQPACKET",
|
||||
],
|
||||
),
|
||||
),
|
||||
('socket_type', EnumProperty(NETWORK_SOCKET_TYPE)),
|
||||
('socket_descriptor', IntegerProperty(min=0)),
|
||||
('socket_handle', IntegerProperty()),
|
||||
])
|
||||
|
@ -612,16 +596,7 @@ class WindowsProcessExt(_Extension):
|
|||
('owner_sid', StringProperty()),
|
||||
('window_title', StringProperty()),
|
||||
('startup_info', DictionaryProperty(spec_version='2.1')),
|
||||
(
|
||||
'integrity_level', EnumProperty(
|
||||
allowed=[
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"system",
|
||||
],
|
||||
),
|
||||
),
|
||||
('integrity_level', EnumProperty(WINDOWS_INTEGRITY_LEVEL)),
|
||||
])
|
||||
|
||||
|
||||
|
@ -636,41 +611,10 @@ class WindowsServiceExt(_Extension):
|
|||
('descriptions', ListProperty(StringProperty)),
|
||||
('display_name', StringProperty()),
|
||||
('group_name', StringProperty()),
|
||||
(
|
||||
'start_type', EnumProperty(
|
||||
allowed=[
|
||||
"SERVICE_AUTO_START",
|
||||
"SERVICE_BOOT_START",
|
||||
"SERVICE_DEMAND_START",
|
||||
"SERVICE_DISABLED",
|
||||
"SERVICE_SYSTEM_ALERT",
|
||||
],
|
||||
),
|
||||
),
|
||||
('start_type', EnumProperty(WINDOWS_SERVICE_START_TYPE)),
|
||||
('service_dll_refs', ListProperty(ReferenceProperty(valid_types='file', spec_version='2.1'))),
|
||||
(
|
||||
'service_type', EnumProperty(
|
||||
allowed=[
|
||||
"SERVICE_KERNEL_DRIVER",
|
||||
"SERVICE_FILE_SYSTEM_DRIVER",
|
||||
"SERVICE_WIN32_OWN_PROCESS",
|
||||
"SERVICE_WIN32_SHARE_PROCESS",
|
||||
],
|
||||
),
|
||||
),
|
||||
(
|
||||
'service_status', EnumProperty(
|
||||
allowed=[
|
||||
"SERVICE_CONTINUE_PENDING",
|
||||
"SERVICE_PAUSE_PENDING",
|
||||
"SERVICE_PAUSED",
|
||||
"SERVICE_RUNNING",
|
||||
"SERVICE_START_PENDING",
|
||||
"SERVICE_STOP_PENDING",
|
||||
"SERVICE_STOPPED",
|
||||
],
|
||||
),
|
||||
),
|
||||
('service_type', EnumProperty(WINDOWS_SERVICE_TYPE)),
|
||||
('service_status', EnumProperty(WINDOWS_SERVICE_STATUS)),
|
||||
])
|
||||
|
||||
|
||||
|
@ -788,7 +732,7 @@ class UserAccount(_Observable):
|
|||
('user_id', StringProperty()),
|
||||
('credential', StringProperty()),
|
||||
('account_login', StringProperty()),
|
||||
('account_type', StringProperty()), # open vocab
|
||||
('account_type', OpenVocabProperty(ACCOUNT_TYPE)),
|
||||
('display_name', StringProperty()),
|
||||
('is_service_account', BooleanProperty()),
|
||||
('is_privileged', BooleanProperty()),
|
||||
|
@ -816,25 +760,7 @@ class WindowsRegistryValueType(_STIXBase21):
|
|||
_properties = OrderedDict([
|
||||
('name', StringProperty()),
|
||||
('data', StringProperty()),
|
||||
(
|
||||
'data_type', EnumProperty(
|
||||
allowed=[
|
||||
"REG_NONE",
|
||||
"REG_SZ",
|
||||
"REG_EXPAND_SZ",
|
||||
"REG_BINARY",
|
||||
"REG_DWORD",
|
||||
"REG_DWORD_BIG_ENDIAN",
|
||||
"REG_LINK",
|
||||
"REG_MULTI_SZ",
|
||||
"REG_RESOURCE_LIST",
|
||||
"REG_FULL_RESOURCE_DESCRIPTION",
|
||||
"REG_RESOURCE_REQUIREMENTS_LIST",
|
||||
"REG_QWORD",
|
||||
"REG_INVALID_TYPE",
|
||||
],
|
||||
),
|
||||
),
|
||||
('data_type', EnumProperty(WINDOWS_REGISTRY_DATATYPE)),
|
||||
])
|
||||
|
||||
|
||||
|
@ -899,7 +825,7 @@ class X509Certificate(_Observable):
|
|||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('is_self_signed', BooleanProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('hashes', HashesProperty(HASHING_ALGORITHM, spec_version="2.1")),
|
||||
('version', StringProperty()),
|
||||
('serial_number', StringProperty()),
|
||||
('signature_algorithm', StringProperty()),
|
||||
|
|
|
@ -15,12 +15,20 @@ from ..exceptions import (
|
|||
from ..properties import (
|
||||
BooleanProperty, EnumProperty, ExtensionsProperty, FloatProperty,
|
||||
IDProperty, IntegerProperty, ListProperty, ObservableProperty,
|
||||
PatternProperty, ReferenceProperty, StringProperty, TimestampProperty,
|
||||
TypeProperty,
|
||||
OpenVocabProperty, PatternProperty, ReferenceProperty, StringProperty,
|
||||
TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW
|
||||
from .base import _DomainObject
|
||||
from .common import ExternalReference, GranularMarking, KillChainPhase
|
||||
from .vocab import (
|
||||
ATTACK_MOTIVATION, ATTACK_RESOURCE_LEVEL, GROUPING_CONTEXT, IDENTITY_CLASS,
|
||||
IMPLEMENTATION_LANGUAGE, INDICATOR_TYPE, INDUSTRY_SECTOR,
|
||||
INFRASTRUCTURE_TYPE, MALWARE_CAPABILITIES, MALWARE_RESULT, MALWARE_TYPE,
|
||||
OPINION, PATTERN_TYPE, PROCESSOR_ARCHITECTURE, REGION, REPORT_TYPE,
|
||||
THREAT_ACTOR_ROLE, THREAT_ACTOR_SOPHISTICATION, THREAT_ACTOR_TYPE,
|
||||
TOOL_TYPE,
|
||||
)
|
||||
|
||||
|
||||
class AttackPattern(_DomainObject):
|
||||
|
@ -132,7 +140,7 @@ class Grouping(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('context', StringProperty(required=True)),
|
||||
('context', OpenVocabProperty(GROUPING_CONTEXT, required=True)),
|
||||
('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -161,8 +169,8 @@ class Identity(_DomainObject):
|
|||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('roles', ListProperty(StringProperty)),
|
||||
('identity_class', StringProperty()),
|
||||
('sectors', ListProperty(StringProperty)),
|
||||
('identity_class', OpenVocabProperty(IDENTITY_CLASS)),
|
||||
('sectors', ListProperty(OpenVocabProperty(INDUSTRY_SECTOR))),
|
||||
('contact_information', StringProperty()),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -217,9 +225,9 @@ class Indicator(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('indicator_types', ListProperty(StringProperty)),
|
||||
('indicator_types', ListProperty(OpenVocabProperty(INDICATOR_TYPE))),
|
||||
('pattern', PatternProperty(required=True)),
|
||||
('pattern_type', StringProperty(required=True)),
|
||||
('pattern_type', OpenVocabProperty(PATTERN_TYPE, required=True)),
|
||||
('pattern_version', StringProperty()),
|
||||
('valid_from', TimestampProperty(default=lambda: NOW)),
|
||||
('valid_until', TimestampProperty()),
|
||||
|
@ -277,7 +285,7 @@ class Infrastructure(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('infrastructure_types', ListProperty(StringProperty)),
|
||||
('infrastructure_types', ListProperty(OpenVocabProperty(INFRASTRUCTURE_TYPE))),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('first_seen', TimestampProperty()),
|
||||
|
@ -322,9 +330,9 @@ class IntrusionSet(_DomainObject):
|
|||
('first_seen', TimestampProperty()),
|
||||
('last_seen', TimestampProperty()),
|
||||
('goals', ListProperty(StringProperty)),
|
||||
('resource_level', StringProperty()),
|
||||
('primary_motivation', StringProperty()),
|
||||
('secondary_motivations', ListProperty(StringProperty)),
|
||||
('resource_level', OpenVocabProperty(ATTACK_RESOURCE_LEVEL)),
|
||||
('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)),
|
||||
('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
('confidence', IntegerProperty()),
|
||||
|
@ -364,7 +372,7 @@ class Location(_DomainObject):
|
|||
('latitude', FloatProperty(min=-90.0, max=90.0)),
|
||||
('longitude', FloatProperty(min=-180.0, max=180.0)),
|
||||
('precision', FloatProperty(min=0.0)),
|
||||
('region', StringProperty()),
|
||||
('region', OpenVocabProperty(REGION)),
|
||||
('country', StringProperty()),
|
||||
('administrative_area', StringProperty()),
|
||||
('city', StringProperty()),
|
||||
|
@ -469,16 +477,16 @@ class Malware(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('malware_types', ListProperty(StringProperty)),
|
||||
('malware_types', ListProperty(OpenVocabProperty(MALWARE_TYPE))),
|
||||
('is_family', BooleanProperty(required=True)),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('first_seen', TimestampProperty()),
|
||||
('last_seen', TimestampProperty()),
|
||||
('operating_system_refs', ListProperty(ReferenceProperty(valid_types='software', spec_version='2.1'))),
|
||||
('architecture_execution_envs', ListProperty(StringProperty)),
|
||||
('implementation_languages', ListProperty(StringProperty)),
|
||||
('capabilities', ListProperty(StringProperty)),
|
||||
('architecture_execution_envs', ListProperty(OpenVocabProperty(PROCESSOR_ARCHITECTURE))),
|
||||
('implementation_languages', ListProperty(OpenVocabProperty(IMPLEMENTATION_LANGUAGE))),
|
||||
('capabilities', ListProperty(OpenVocabProperty(MALWARE_CAPABILITIES))),
|
||||
('sample_refs', ListProperty(ReferenceProperty(valid_types=['artifact', 'file'], spec_version='2.1'))),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -533,7 +541,7 @@ class MalwareAnalysis(_DomainObject):
|
|||
('analysis_started', TimestampProperty()),
|
||||
('analysis_ended', TimestampProperty()),
|
||||
('result_name', StringProperty()),
|
||||
('result', StringProperty()),
|
||||
('result', OpenVocabProperty(MALWARE_RESULT)),
|
||||
('analysis_sco_refs', ListProperty(ReferenceProperty(valid_types="SCO", spec_version='2.1'))),
|
||||
('sample_ref', ReferenceProperty(valid_types="SCO", spec_version='2.1')),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
|
@ -609,8 +617,6 @@ class ObservedData(_DomainObject):
|
|||
])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._allow_custom = kwargs.get('allow_custom', False)
|
||||
self._properties['objects'].allow_custom = kwargs.get('allow_custom', False)
|
||||
|
||||
if "objects" in kwargs:
|
||||
warnings.warn(
|
||||
|
@ -651,17 +657,7 @@ class Opinion(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('explanation', StringProperty()),
|
||||
('authors', ListProperty(StringProperty)),
|
||||
(
|
||||
'opinion', EnumProperty(
|
||||
allowed=[
|
||||
'strongly-disagree',
|
||||
'disagree',
|
||||
'neutral',
|
||||
'agree',
|
||||
'strongly-agree',
|
||||
], required=True,
|
||||
),
|
||||
),
|
||||
('opinion', EnumProperty(OPINION, required=True)),
|
||||
('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -689,7 +685,7 @@ class Report(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('report_types', ListProperty(StringProperty)),
|
||||
('report_types', ListProperty(OpenVocabProperty(REPORT_TYPE))),
|
||||
('published', TimestampProperty(required=True)),
|
||||
('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
|
@ -718,17 +714,17 @@ class ThreatActor(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('threat_actor_types', ListProperty(StringProperty)),
|
||||
('threat_actor_types', ListProperty(OpenVocabProperty(THREAT_ACTOR_TYPE))),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('first_seen', TimestampProperty()),
|
||||
('last_seen', TimestampProperty()),
|
||||
('roles', ListProperty(StringProperty)),
|
||||
('roles', ListProperty(OpenVocabProperty(THREAT_ACTOR_ROLE))),
|
||||
('goals', ListProperty(StringProperty)),
|
||||
('sophistication', StringProperty()),
|
||||
('resource_level', StringProperty()),
|
||||
('primary_motivation', StringProperty()),
|
||||
('secondary_motivations', ListProperty(StringProperty)),
|
||||
('personal_motivations', ListProperty(StringProperty)),
|
||||
('sophistication', OpenVocabProperty(THREAT_ACTOR_SOPHISTICATION)),
|
||||
('resource_level', OpenVocabProperty(ATTACK_RESOURCE_LEVEL)),
|
||||
('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)),
|
||||
('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))),
|
||||
('personal_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
('confidence', IntegerProperty()),
|
||||
|
@ -765,7 +761,7 @@ class Tool(_DomainObject):
|
|||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('tool_types', ListProperty(StringProperty)),
|
||||
('tool_types', ListProperty(OpenVocabProperty(TOOL_TYPE))),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
('kill_chain_phases', ListProperty(KillChainPhase)),
|
||||
('tool_version', StringProperty()),
|
||||
|
|
|
@ -0,0 +1,841 @@
|
|||
"""
|
||||
STIX 2.1 open vocabularies and enums
|
||||
"""
|
||||
|
||||
|
||||
ACCOUNT_TYPE_FACEBOOK = "facebook"
|
||||
ACCOUNT_TYPE_LDAP = "ldap"
|
||||
ACCOUNT_TYPE_NIS = "nis"
|
||||
ACCOUNT_TYPE_OPENID = "openid"
|
||||
ACCOUNT_TYPE_RADIUS = "radius"
|
||||
ACCOUNT_TYPE_SKYPE = "skype"
|
||||
ACCOUNT_TYPE_TACACS = "tacacs"
|
||||
ACCOUNT_TYPE_TWITTER = "twitter"
|
||||
ACCOUNT_TYPE_UNIX = "unix"
|
||||
ACCOUNT_TYPE_WINDOWS_LOCAL = "windows-local"
|
||||
ACCOUNT_TYPE_WINDOWS_DOMAIN = "windows-domain"
|
||||
|
||||
|
||||
ACCOUNT_TYPE = [
|
||||
ACCOUNT_TYPE_FACEBOOK,
|
||||
ACCOUNT_TYPE_LDAP,
|
||||
ACCOUNT_TYPE_NIS,
|
||||
ACCOUNT_TYPE_OPENID,
|
||||
ACCOUNT_TYPE_RADIUS,
|
||||
ACCOUNT_TYPE_SKYPE,
|
||||
ACCOUNT_TYPE_TACACS,
|
||||
ACCOUNT_TYPE_TWITTER,
|
||||
ACCOUNT_TYPE_UNIX,
|
||||
ACCOUNT_TYPE_WINDOWS_LOCAL,
|
||||
ACCOUNT_TYPE_WINDOWS_DOMAIN,
|
||||
]
|
||||
|
||||
|
||||
ATTACK_MOTIVATION_ACCIDENTAL = "accidental"
|
||||
ATTACK_MOTIVATION_COERCION = "coercion"
|
||||
ATTACK_MOTIVATION_DOMINANCE = "dominance"
|
||||
ATTACK_MOTIVATION_IDEOLOGY = "ideology"
|
||||
ATTACK_MOTIVATION_NOTORIETY = "notoriety"
|
||||
ATTACK_MOTIVATION_ORGANIZATIONAL_GAIN = "organizational-gain"
|
||||
ATTACK_MOTIVATION_PERSONAL_GAIN = "personal-gain"
|
||||
ATTACK_MOTIVATION_PERSONAL_SATISFACTION = "personal-satisfaction"
|
||||
ATTACK_MOTIVATION_REVENGE = "revenge"
|
||||
ATTACK_MOTIVATION_UNPREDICTABLE = "unpredictable"
|
||||
|
||||
|
||||
ATTACK_MOTIVATION = [
|
||||
ATTACK_MOTIVATION_ACCIDENTAL,
|
||||
ATTACK_MOTIVATION_COERCION,
|
||||
ATTACK_MOTIVATION_DOMINANCE,
|
||||
ATTACK_MOTIVATION_IDEOLOGY,
|
||||
ATTACK_MOTIVATION_NOTORIETY,
|
||||
ATTACK_MOTIVATION_ORGANIZATIONAL_GAIN,
|
||||
ATTACK_MOTIVATION_PERSONAL_GAIN,
|
||||
ATTACK_MOTIVATION_PERSONAL_SATISFACTION,
|
||||
ATTACK_MOTIVATION_REVENGE,
|
||||
ATTACK_MOTIVATION_UNPREDICTABLE,
|
||||
]
|
||||
|
||||
|
||||
ATTACK_RESOURCE_LEVEL_INDIVIDUAL = "individual"
|
||||
ATTACK_RESOURCE_LEVEL_CLUB = "club"
|
||||
ATTACK_RESOURCE_LEVEL_CONTEST = "contest"
|
||||
ATTACK_RESOURCE_LEVEL_TEAM = "team"
|
||||
ATTACK_RESOURCE_LEVEL_ORGANIZATION = "organization"
|
||||
ATTACK_RESOURCE_LEVEL_GOVERNMENT = "government"
|
||||
|
||||
|
||||
ATTACK_RESOURCE_LEVEL = [
|
||||
ATTACK_RESOURCE_LEVEL_INDIVIDUAL,
|
||||
ATTACK_RESOURCE_LEVEL_CLUB,
|
||||
ATTACK_RESOURCE_LEVEL_CONTEST,
|
||||
ATTACK_RESOURCE_LEVEL_TEAM,
|
||||
ATTACK_RESOURCE_LEVEL_ORGANIZATION,
|
||||
ATTACK_RESOURCE_LEVEL_GOVERNMENT,
|
||||
]
|
||||
|
||||
|
||||
ENCRYPTION_ALGORITHM_AES_256_GCM = "AES-256-GCM"
|
||||
ENCRYPTION_ALGORITHM_CHACHA20_POLY1305 = "ChaCha20-Poly1305"
|
||||
ENCRYPTION_ALGORITHM_MIME_TYPE_INDICATED = "mime-type-indicated"
|
||||
|
||||
|
||||
ENCRYPTION_ALGORITHM = [
|
||||
ENCRYPTION_ALGORITHM_AES_256_GCM,
|
||||
ENCRYPTION_ALGORITHM_CHACHA20_POLY1305,
|
||||
ENCRYPTION_ALGORITHM_MIME_TYPE_INDICATED,
|
||||
]
|
||||
|
||||
|
||||
GROUPING_CONTEXT_SUSPICIOUS_ACTIVITY = "suspicious-activity"
|
||||
GROUPING_CONTEXT_MALWARE_ANALYSIS = "malware-analysis"
|
||||
GROUPING_CONTEXT_UNSPECIFIED = "unspecified"
|
||||
|
||||
|
||||
GROUPING_CONTEXT = [
|
||||
GROUPING_CONTEXT_SUSPICIOUS_ACTIVITY,
|
||||
GROUPING_CONTEXT_MALWARE_ANALYSIS,
|
||||
GROUPING_CONTEXT_UNSPECIFIED,
|
||||
]
|
||||
|
||||
|
||||
HASHING_ALGORITHM_MD5 = "MD5"
|
||||
HASHING_ALGORITHM_SHA_1 = "SHA-1"
|
||||
HASHING_ALGORITHM_SHA_256 = "SHA-256"
|
||||
HASHING_ALGORITHM_SHA_512 = "SHA-512"
|
||||
HASHING_ALGORITHM_SHA3_256 = "SHA3-256"
|
||||
HASHING_ALGORITHM_SHA3_512 = "SHA3-512"
|
||||
HASHING_ALGORITHM_SSDEEP = "SSDEEP"
|
||||
HASHING_ALGORITHM_TLSH = "TLSH"
|
||||
|
||||
|
||||
HASHING_ALGORITHM = [
|
||||
HASHING_ALGORITHM_MD5,
|
||||
HASHING_ALGORITHM_SHA_1,
|
||||
HASHING_ALGORITHM_SHA_256,
|
||||
HASHING_ALGORITHM_SHA_512,
|
||||
HASHING_ALGORITHM_SHA3_256,
|
||||
HASHING_ALGORITHM_SHA3_512,
|
||||
HASHING_ALGORITHM_SSDEEP,
|
||||
HASHING_ALGORITHM_TLSH,
|
||||
]
|
||||
|
||||
|
||||
IDENTITY_CLASS_INDIVIDUAL = "individual"
|
||||
IDENTITY_CLASS_GROUP = "group"
|
||||
IDENTITY_CLASS_SYSTEM = "system"
|
||||
IDENTITY_CLASS_ORGANIZATION = "organization"
|
||||
IDENTITY_CLASS_CLASS = "class"
|
||||
IDENTITY_CLASS_UNKNOWN = "unknown"
|
||||
|
||||
|
||||
IDENTITY_CLASS = [
|
||||
IDENTITY_CLASS_INDIVIDUAL,
|
||||
IDENTITY_CLASS_GROUP,
|
||||
IDENTITY_CLASS_SYSTEM,
|
||||
IDENTITY_CLASS_ORGANIZATION,
|
||||
IDENTITY_CLASS_CLASS,
|
||||
IDENTITY_CLASS_UNKNOWN,
|
||||
]
|
||||
|
||||
|
||||
IMPLEMENTATION_LANGUAGE_APPLESCRIPT = "applescript"
|
||||
IMPLEMENTATION_LANGUAGE_BASH = "bash"
|
||||
IMPLEMENTATION_LANGUAGE_C = "c"
|
||||
IMPLEMENTATION_LANGUAGE_CPLUSPLUS = "c++"
|
||||
IMPLEMENTATION_LANGUAGE_CSHARP = "c#"
|
||||
IMPLEMENTATION_LANGUAGE_GO = "go"
|
||||
IMPLEMENTATION_LANGUAGE_JAVA = "java"
|
||||
IMPLEMENTATION_LANGUAGE_JAVASCRIPT = "javascript"
|
||||
IMPLEMENTATION_LANGUAGE_LUA = "lua"
|
||||
IMPLEMENTATION_LANGUAGE_OBJECTIVE_C = "objective-c"
|
||||
IMPLEMENTATION_LANGUAGE_PERL = "perl"
|
||||
IMPLEMENTATION_LANGUAGE_PHP = "php"
|
||||
IMPLEMENTATION_LANGUAGE_POWERSHELL = "powershell"
|
||||
IMPLEMENTATION_LANGUAGE_PYTHON = "python"
|
||||
IMPLEMENTATION_LANGUAGE_RUBY = "ruby"
|
||||
IMPLEMENTATION_LANGUAGE_SCALA = "scala"
|
||||
IMPLEMENTATION_LANGUAGE_SWIFT = "swift"
|
||||
IMPLEMENTATION_LANGUAGE_TYPESCRIPT = "typescript"
|
||||
IMPLEMENTATION_LANGUAGE_VISUAL_BASIC = "visual-basic"
|
||||
IMPLEMENTATION_LANGUAGE_X86_32 = "x86-32"
|
||||
IMPLEMENTATION_LANGUAGE_X86_64 = "x86-64"
|
||||
|
||||
|
||||
IMPLEMENTATION_LANGUAGE = [
|
||||
IMPLEMENTATION_LANGUAGE_APPLESCRIPT,
|
||||
IMPLEMENTATION_LANGUAGE_BASH,
|
||||
IMPLEMENTATION_LANGUAGE_C,
|
||||
IMPLEMENTATION_LANGUAGE_CPLUSPLUS,
|
||||
IMPLEMENTATION_LANGUAGE_CSHARP,
|
||||
IMPLEMENTATION_LANGUAGE_GO,
|
||||
IMPLEMENTATION_LANGUAGE_JAVA,
|
||||
IMPLEMENTATION_LANGUAGE_JAVASCRIPT,
|
||||
IMPLEMENTATION_LANGUAGE_LUA,
|
||||
IMPLEMENTATION_LANGUAGE_OBJECTIVE_C,
|
||||
IMPLEMENTATION_LANGUAGE_PERL,
|
||||
IMPLEMENTATION_LANGUAGE_PHP,
|
||||
IMPLEMENTATION_LANGUAGE_POWERSHELL,
|
||||
IMPLEMENTATION_LANGUAGE_PYTHON,
|
||||
IMPLEMENTATION_LANGUAGE_RUBY,
|
||||
IMPLEMENTATION_LANGUAGE_SCALA,
|
||||
IMPLEMENTATION_LANGUAGE_SWIFT,
|
||||
IMPLEMENTATION_LANGUAGE_TYPESCRIPT,
|
||||
IMPLEMENTATION_LANGUAGE_VISUAL_BASIC,
|
||||
IMPLEMENTATION_LANGUAGE_X86_32,
|
||||
IMPLEMENTATION_LANGUAGE_X86_64,
|
||||
]
|
||||
|
||||
|
||||
INDICATOR_TYPE_ANOMALOUS_ACTIVITY = "anomalous-activity"
|
||||
INDICATOR_TYPE_ANONYMIZATION = "anonymization"
|
||||
INDICATOR_TYPE_BENIGN = "benign"
|
||||
INDICATOR_TYPE_COMPROMISED = "compromised"
|
||||
INDICATOR_TYPE_MALICIOUS_ACTIVITY = "malicious-activity"
|
||||
INDICATOR_TYPE_ATTRIBUTION = "attribution"
|
||||
INDICATOR_TYPE_UNKNOWN = "unknown"
|
||||
|
||||
|
||||
INDICATOR_TYPE = [
|
||||
INDICATOR_TYPE_ANOMALOUS_ACTIVITY,
|
||||
INDICATOR_TYPE_ANONYMIZATION,
|
||||
INDICATOR_TYPE_BENIGN,
|
||||
INDICATOR_TYPE_COMPROMISED,
|
||||
INDICATOR_TYPE_MALICIOUS_ACTIVITY,
|
||||
INDICATOR_TYPE_ATTRIBUTION,
|
||||
INDICATOR_TYPE_UNKNOWN,
|
||||
]
|
||||
|
||||
|
||||
INDUSTRY_SECTOR_AGRICULTURE = "agriculture"
|
||||
INDUSTRY_SECTOR_AEROSPACE = "aerospace"
|
||||
INDUSTRY_SECTOR_AUTOMOTIVE = "automotive"
|
||||
INDUSTRY_SECTOR_CHEMICAL = "chemical"
|
||||
INDUSTRY_SECTOR_COMMERCIAL = "commercial"
|
||||
INDUSTRY_SECTOR_COMMUNICATIONS = "communications"
|
||||
INDUSTRY_SECTOR_CONSTRUCTION = "construction"
|
||||
INDUSTRY_SECTOR_DEFENSE = "defense"
|
||||
INDUSTRY_SECTOR_EDUCATION = "education"
|
||||
INDUSTRY_SECTOR_ENERGY = "energy"
|
||||
INDUSTRY_SECTOR_ENTERTAINMENT = "entertainment"
|
||||
INDUSTRY_SECTOR_FINANCIAL_SERVICES = "financial-services"
|
||||
INDUSTRY_SECTOR_GOVERNMENT = "government"
|
||||
INDUSTRY_SECTOR_EMERGENCY_SERVICES = "emergency-services"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_NATIONAL = "government-national"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_REGIONAL = "government-regional"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_LOCAL = "government-local"
|
||||
INDUSTRY_SECTOR_GOVERNMENT_PUBLIC_SERVICES = "government-public-services"
|
||||
INDUSTRY_SECTOR_HEALTHCARE = "healthcare"
|
||||
INDUSTRY_SECTOR_HOSPITALITY_LEISURE = "hospitality-leisure"
|
||||
INDUSTRY_SECTOR_INFRASTRUCTURE = "infrastructure"
|
||||
INDUSTRY_SECTOR_DAMS = "dams"
|
||||
INDUSTRY_SECTOR_NUCLEAR = "nuclear"
|
||||
INDUSTRY_SECTOR_WATER = "water"
|
||||
INDUSTRY_SECTOR_INSURANCE = "insurance"
|
||||
INDUSTRY_SECTOR_MANUFACTURING = "manufacturing"
|
||||
INDUSTRY_SECTOR_MINING = "mining"
|
||||
INDUSTRY_SECTOR_NON_PROFIT = "non-profit"
|
||||
INDUSTRY_SECTOR_PHARMACEUTICALS = "pharmaceuticals"
|
||||
INDUSTRY_SECTOR_RETAIL = "retail"
|
||||
INDUSTRY_SECTOR_TECHNOLOGY = "technology"
|
||||
INDUSTRY_SECTOR_TELECOMMUNICATIONS = "telecommunications"
|
||||
INDUSTRY_SECTOR_TRANSPORTATION = "transportation"
|
||||
INDUSTRY_SECTOR_UTILITIES = "utilities"
|
||||
|
||||
|
||||
INDUSTRY_SECTOR = [
|
||||
INDUSTRY_SECTOR_AGRICULTURE,
|
||||
INDUSTRY_SECTOR_AEROSPACE,
|
||||
INDUSTRY_SECTOR_AUTOMOTIVE,
|
||||
INDUSTRY_SECTOR_CHEMICAL,
|
||||
INDUSTRY_SECTOR_COMMERCIAL,
|
||||
INDUSTRY_SECTOR_COMMUNICATIONS,
|
||||
INDUSTRY_SECTOR_CONSTRUCTION,
|
||||
INDUSTRY_SECTOR_DEFENSE,
|
||||
INDUSTRY_SECTOR_EDUCATION,
|
||||
INDUSTRY_SECTOR_ENERGY,
|
||||
INDUSTRY_SECTOR_ENTERTAINMENT,
|
||||
INDUSTRY_SECTOR_FINANCIAL_SERVICES,
|
||||
INDUSTRY_SECTOR_GOVERNMENT,
|
||||
INDUSTRY_SECTOR_EMERGENCY_SERVICES,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_NATIONAL,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_REGIONAL,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_LOCAL,
|
||||
INDUSTRY_SECTOR_GOVERNMENT_PUBLIC_SERVICES,
|
||||
INDUSTRY_SECTOR_HEALTHCARE,
|
||||
INDUSTRY_SECTOR_HOSPITALITY_LEISURE,
|
||||
INDUSTRY_SECTOR_INFRASTRUCTURE,
|
||||
INDUSTRY_SECTOR_DAMS,
|
||||
INDUSTRY_SECTOR_NUCLEAR,
|
||||
INDUSTRY_SECTOR_WATER,
|
||||
INDUSTRY_SECTOR_INSURANCE,
|
||||
INDUSTRY_SECTOR_MANUFACTURING,
|
||||
INDUSTRY_SECTOR_MINING,
|
||||
INDUSTRY_SECTOR_NON_PROFIT,
|
||||
INDUSTRY_SECTOR_PHARMACEUTICALS,
|
||||
INDUSTRY_SECTOR_RETAIL,
|
||||
INDUSTRY_SECTOR_TECHNOLOGY,
|
||||
INDUSTRY_SECTOR_TELECOMMUNICATIONS,
|
||||
INDUSTRY_SECTOR_TRANSPORTATION,
|
||||
INDUSTRY_SECTOR_UTILITIES,
|
||||
]
|
||||
|
||||
|
||||
INFRASTRUCTURE_TYPE_AMPLIFICATION = "amplification"
|
||||
INFRASTRUCTURE_TYPE_ANONYMIZATION = "anonymization"
|
||||
INFRASTRUCTURE_TYPE_BOTNET = "botnet"
|
||||
INFRASTRUCTURE_TYPE_COMMAND_AND_CONTROL = "command-and-control"
|
||||
INFRASTRUCTURE_TYPE_EXFILTRATION = "exfiltration"
|
||||
INFRASTRUCTURE_TYPE_HOSTING_MALWARE = "hosting-malware"
|
||||
INFRASTRUCTURE_TYPE_HOSTING_TARGET_LISTS = "hosting-target-lists"
|
||||
INFRASTRUCTURE_TYPE_PHISHING = "phishing"
|
||||
INFRASTRUCTURE_TYPE_RECONNAISSANCE = "reconnaissance"
|
||||
INFRASTRUCTURE_TYPE_STAGING = "staging"
|
||||
INFRASTRUCTURE_TYPE_UNKNOWN = "unknown"
|
||||
|
||||
|
||||
INFRASTRUCTURE_TYPE = [
|
||||
INFRASTRUCTURE_TYPE_AMPLIFICATION,
|
||||
INFRASTRUCTURE_TYPE_ANONYMIZATION,
|
||||
INFRASTRUCTURE_TYPE_BOTNET,
|
||||
INFRASTRUCTURE_TYPE_COMMAND_AND_CONTROL,
|
||||
INFRASTRUCTURE_TYPE_EXFILTRATION,
|
||||
INFRASTRUCTURE_TYPE_HOSTING_MALWARE,
|
||||
INFRASTRUCTURE_TYPE_HOSTING_TARGET_LISTS,
|
||||
INFRASTRUCTURE_TYPE_PHISHING,
|
||||
INFRASTRUCTURE_TYPE_RECONNAISSANCE,
|
||||
INFRASTRUCTURE_TYPE_STAGING,
|
||||
INFRASTRUCTURE_TYPE_UNKNOWN,
|
||||
]
|
||||
|
||||
|
||||
MALWARE_RESULT_MALICIOUS = "malicious"
|
||||
MALWARE_RESULT_SUSPICIOUS = "suspicious"
|
||||
MALWARE_RESULT_BENIGN = "benign"
|
||||
MALWARE_RESULT_UNKNOWN = "unknown"
|
||||
|
||||
|
||||
MALWARE_RESULT = [
|
||||
MALWARE_RESULT_MALICIOUS,
|
||||
MALWARE_RESULT_SUSPICIOUS,
|
||||
MALWARE_RESULT_BENIGN,
|
||||
MALWARE_RESULT_UNKNOWN,
|
||||
]
|
||||
|
||||
|
||||
MALWARE_CAPABILITIES_ACCESSES_REMOTE_MACHINES = "accesses-remote-machines"
|
||||
MALWARE_CAPABILITIES_ANTI_DEBUGGING = "anti-debugging"
|
||||
MALWARE_CAPABILITIES_ANTI_DISASSEMBLY = "anti-disassembly"
|
||||
MALWARE_CAPABILITIES_ANTI_EMULATION = "anti-emulation"
|
||||
MALWARE_CAPABILITIES_ANTI_MEMORY_FORENSICS = "anti-memory-forensics"
|
||||
MALWARE_CAPABILITIES_ANTI_SANDBOX = "anti-sandbox"
|
||||
MALWARE_CAPABILITIES_ANTI_VM = "anti-vm"
|
||||
MALWARE_CAPABILITIES_CAPTURES_INPUT_PERIPHERALS = "captures-input-peripherals"
|
||||
MALWARE_CAPABILITIES_CAPTURES_OUTPUT_PERIPHERALS = "captures-output-peripherals"
|
||||
MALWARE_CAPABILITIES_CAPTURES_SYSTEM_STATE_DATA = "captures-system-state-data"
|
||||
MALWARE_CAPABILITIES_CLEANS_TRACES_OF_INFECTION = "cleans-traces-of-infection"
|
||||
MALWARE_CAPABILITIES_COMMITS_FRAUD = "commits-fraud"
|
||||
MALWARE_CAPABILITIES_COMMUNICATES_WITH_C2 = "communicates-with-c2"
|
||||
MALWARE_CAPABILITIES_COMPROMISES_DATA_AVAILABILITY = "compromises-data-availability"
|
||||
MALWARE_CAPABILITIES_COMPROMISES_DATA_INTEGRITY = "compromises-data-integrity"
|
||||
MALWARE_CAPABILITIES_COMPROMISES_SYSTEM_AVAILABILITY = "compromises-system-availability"
|
||||
MALWARE_CAPABILITIES_CONTROLS_LOCAL_MACHINE = "controls-local-machine"
|
||||
MALWARE_CAPABILITIES_DEGRADES_SECURITY_SOFTWARE = "degrades-security-software"
|
||||
MALWARE_CAPABILITIES_DEGRADES_SYSTEM_UPDATES = "degrades-system-updates"
|
||||
MALWARE_CAPABILITIES_DETERMINES_C2_SERVER = "determines-c2-server"
|
||||
MALWARE_CAPABILITIES_EMAILS_SPAM = "emails-spam"
|
||||
MALWARE_CAPABILITIES_ESCALATES_PRIVILEGES = "escalates-privileges"
|
||||
MALWARE_CAPABILITIES_EVADES_AV = "evades-av"
|
||||
MALWARE_CAPABILITIES_EXFILTRATES_DATA = "exfiltrates-data"
|
||||
MALWARE_CAPABILITIES_FINGERPRINTS_HOST = "fingerprints-host"
|
||||
MALWARE_CAPABILITIES_HIDES_ARTIFACTS = "hides-artifacts"
|
||||
MALWARE_CAPABILITIES_HIDES_EXECUTING_CODE = "hides-executing-code"
|
||||
MALWARE_CAPABILITIES_INFECTS_FILES = "infects-files"
|
||||
MALWARE_CAPABILITIES_INFECTS_REMOTE_MACHINES = "infects-remote-machines"
|
||||
MALWARE_CAPABILITIES_INSTALLS_OTHER_COMPONENTS = "installs-other-components"
|
||||
MALWARE_CAPABILITIES_PERSISTS_AFTER_SYSTEM_REBOOT = "persists-after-system-reboot"
|
||||
MALWARE_CAPABILITIES_PREVENTS_ARTIFACT_ACCESS = "prevents-artifact-access"
|
||||
MALWARE_CAPABILITIES_PREVENTS_ARTIFACT_DELETION = "prevents-artifact-deletion"
|
||||
MALWARE_CAPABILITIES_PROBES_NETWORK_ENVIRONMENT = "probes-network-environment"
|
||||
MALWARE_CAPABILITIES_SELF_MODIFIES = "self-modifies"
|
||||
MALWARE_CAPABILITIES_STEALS_AUTHENTICATION_CREDENTIALS = "steals-authentication-credentials"
|
||||
MALWARE_CAPABILITIES_VIOLATES_SYSTEM_OPERATIONAL_INTEGRITY = "violates-system-operational-integrity"
|
||||
|
||||
|
||||
MALWARE_CAPABILITIES = [
|
||||
MALWARE_CAPABILITIES_ACCESSES_REMOTE_MACHINES,
|
||||
MALWARE_CAPABILITIES_ANTI_DEBUGGING,
|
||||
MALWARE_CAPABILITIES_ANTI_DISASSEMBLY,
|
||||
MALWARE_CAPABILITIES_ANTI_EMULATION,
|
||||
MALWARE_CAPABILITIES_ANTI_MEMORY_FORENSICS,
|
||||
MALWARE_CAPABILITIES_ANTI_SANDBOX,
|
||||
MALWARE_CAPABILITIES_ANTI_VM,
|
||||
MALWARE_CAPABILITIES_CAPTURES_INPUT_PERIPHERALS,
|
||||
MALWARE_CAPABILITIES_CAPTURES_OUTPUT_PERIPHERALS,
|
||||
MALWARE_CAPABILITIES_CAPTURES_SYSTEM_STATE_DATA,
|
||||
MALWARE_CAPABILITIES_CLEANS_TRACES_OF_INFECTION,
|
||||
MALWARE_CAPABILITIES_COMMITS_FRAUD,
|
||||
MALWARE_CAPABILITIES_COMMUNICATES_WITH_C2,
|
||||
MALWARE_CAPABILITIES_COMPROMISES_DATA_AVAILABILITY,
|
||||
MALWARE_CAPABILITIES_COMPROMISES_DATA_INTEGRITY,
|
||||
MALWARE_CAPABILITIES_COMPROMISES_SYSTEM_AVAILABILITY,
|
||||
MALWARE_CAPABILITIES_CONTROLS_LOCAL_MACHINE,
|
||||
MALWARE_CAPABILITIES_DEGRADES_SECURITY_SOFTWARE,
|
||||
MALWARE_CAPABILITIES_DEGRADES_SYSTEM_UPDATES,
|
||||
MALWARE_CAPABILITIES_DETERMINES_C2_SERVER,
|
||||
MALWARE_CAPABILITIES_EMAILS_SPAM,
|
||||
MALWARE_CAPABILITIES_ESCALATES_PRIVILEGES,
|
||||
MALWARE_CAPABILITIES_EVADES_AV,
|
||||
MALWARE_CAPABILITIES_EXFILTRATES_DATA,
|
||||
MALWARE_CAPABILITIES_FINGERPRINTS_HOST,
|
||||
MALWARE_CAPABILITIES_HIDES_ARTIFACTS,
|
||||
MALWARE_CAPABILITIES_HIDES_EXECUTING_CODE,
|
||||
MALWARE_CAPABILITIES_INFECTS_FILES,
|
||||
MALWARE_CAPABILITIES_INFECTS_REMOTE_MACHINES,
|
||||
MALWARE_CAPABILITIES_INSTALLS_OTHER_COMPONENTS,
|
||||
MALWARE_CAPABILITIES_PERSISTS_AFTER_SYSTEM_REBOOT,
|
||||
MALWARE_CAPABILITIES_PREVENTS_ARTIFACT_ACCESS,
|
||||
MALWARE_CAPABILITIES_PREVENTS_ARTIFACT_DELETION,
|
||||
MALWARE_CAPABILITIES_PROBES_NETWORK_ENVIRONMENT,
|
||||
MALWARE_CAPABILITIES_SELF_MODIFIES,
|
||||
MALWARE_CAPABILITIES_STEALS_AUTHENTICATION_CREDENTIALS,
|
||||
MALWARE_CAPABILITIES_VIOLATES_SYSTEM_OPERATIONAL_INTEGRITY,
|
||||
]
|
||||
|
||||
|
||||
MALWARE_TYPE_ADWARE = "adware"
|
||||
MALWARE_TYPE_BACKDOOR = "backdoor"
|
||||
MALWARE_TYPE_BOT = "bot"
|
||||
MALWARE_TYPE_BOOTKIT = "bootkit"
|
||||
MALWARE_TYPE_DDOS = "ddos"
|
||||
MALWARE_TYPE_DOWNLOADER = "downloader"
|
||||
MALWARE_TYPE_DROPPER = "dropper"
|
||||
MALWARE_TYPE_EXPLOIT_KIT = "exploit-kit"
|
||||
MALWARE_TYPE_KEYLOGGER = "keylogger"
|
||||
MALWARE_TYPE_RANSOMWARE = "ransomware"
|
||||
MALWARE_TYPE_REMOTE_ACCESS_TROJAN = "remote-access-trojan"
|
||||
MALWARE_TYPE_RESOURCE_EXPLOITATION = "resource-exploitation"
|
||||
MALWARE_TYPE_ROGUE_SECURITY_SOFTWARE = "rogue-security-software"
|
||||
MALWARE_TYPE_ROOTKIT = "rootkit"
|
||||
MALWARE_TYPE_SCREEN_CAPTURE = "screen-capture"
|
||||
MALWARE_TYPE_SPYWARE = "spyware"
|
||||
MALWARE_TYPE_TROJAN = "trojan"
|
||||
MALWARE_TYPE_UNKNOWN = "unknown"
|
||||
MALWARE_TYPE_VIRUS = "virus"
|
||||
MALWARE_TYPE_WEBSHELL = "webshell"
|
||||
MALWARE_TYPE_WIPER = "wiper"
|
||||
MALWARE_TYPE_WORM = "worm"
|
||||
|
||||
|
||||
MALWARE_TYPE = [
|
||||
MALWARE_TYPE_ADWARE,
|
||||
MALWARE_TYPE_BACKDOOR,
|
||||
MALWARE_TYPE_BOT,
|
||||
MALWARE_TYPE_BOOTKIT,
|
||||
MALWARE_TYPE_DDOS,
|
||||
MALWARE_TYPE_DOWNLOADER,
|
||||
MALWARE_TYPE_DROPPER,
|
||||
MALWARE_TYPE_EXPLOIT_KIT,
|
||||
MALWARE_TYPE_KEYLOGGER,
|
||||
MALWARE_TYPE_RANSOMWARE,
|
||||
MALWARE_TYPE_REMOTE_ACCESS_TROJAN,
|
||||
MALWARE_TYPE_RESOURCE_EXPLOITATION,
|
||||
MALWARE_TYPE_ROGUE_SECURITY_SOFTWARE,
|
||||
MALWARE_TYPE_ROOTKIT,
|
||||
MALWARE_TYPE_SCREEN_CAPTURE,
|
||||
MALWARE_TYPE_SPYWARE,
|
||||
MALWARE_TYPE_TROJAN,
|
||||
MALWARE_TYPE_UNKNOWN,
|
||||
MALWARE_TYPE_VIRUS,
|
||||
MALWARE_TYPE_WEBSHELL,
|
||||
MALWARE_TYPE_WIPER,
|
||||
MALWARE_TYPE_WORM,
|
||||
]
|
||||
|
||||
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_UNSPEC = "AF_UNSPEC"
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_INET = "AF_INET"
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_IPX = "AF_IPX"
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_APPLETALK = "AF_APPLETALK"
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_NETBIOS = "AF_NETBIOS"
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_INET6 = "AF_INET6"
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_IRDA = "AF_IRDA"
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_BTH = "AF_BTH"
|
||||
|
||||
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY = [
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_UNSPEC,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_INET,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_IPX,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_APPLETALK,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_NETBIOS,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_INET6,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_IRDA,
|
||||
NETWORK_SOCKET_ADDRESS_FAMILY_AF_BTH,
|
||||
]
|
||||
|
||||
|
||||
NETWORK_SOCKET_TYPE_SOCK_STREAM = "SOCK_STREAM"
|
||||
NETWORK_SOCKET_TYPE_SOCK_DGRAM = "SOCK_DGRAM"
|
||||
NETWORK_SOCKET_TYPE_SOCK_RAW = "SOCK_RAW"
|
||||
NETWORK_SOCKET_TYPE_SOCK_RDM = "SOCK_RDM"
|
||||
NETWORK_SOCKET_TYPE_SOCK_SEQPACKET = "SOCK_SEQPACKET"
|
||||
|
||||
|
||||
NETWORK_SOCKET_TYPE = [
|
||||
NETWORK_SOCKET_TYPE_SOCK_STREAM,
|
||||
NETWORK_SOCKET_TYPE_SOCK_DGRAM,
|
||||
NETWORK_SOCKET_TYPE_SOCK_RAW,
|
||||
NETWORK_SOCKET_TYPE_SOCK_RDM,
|
||||
NETWORK_SOCKET_TYPE_SOCK_SEQPACKET,
|
||||
]
|
||||
|
||||
|
||||
OPINION_STRONGLY_DISAGREE = "strongly-disagree"
|
||||
OPINION_DISAGREE = "disagree"
|
||||
OPINION_NEUTRAL = "neutral"
|
||||
OPINION_AGREE = "agree"
|
||||
OPINION_STRONGLY_AGREE = "strongly-agree"
|
||||
|
||||
|
||||
OPINION = [
|
||||
OPINION_STRONGLY_DISAGREE,
|
||||
OPINION_DISAGREE,
|
||||
OPINION_NEUTRAL,
|
||||
OPINION_AGREE,
|
||||
OPINION_STRONGLY_AGREE,
|
||||
]
|
||||
|
||||
|
||||
PATTERN_TYPE_STIX = "stix"
|
||||
PATTERN_TYPE_PCRE = "pcre"
|
||||
PATTERN_TYPE_SIGMA = "sigma"
|
||||
PATTERN_TYPE_SNORT = "snort"
|
||||
PATTERN_TYPE_SURICATA = "suricata"
|
||||
PATTERN_TYPE_YARA = "yara"
|
||||
|
||||
|
||||
PATTERN_TYPE = [
|
||||
PATTERN_TYPE_STIX,
|
||||
PATTERN_TYPE_PCRE,
|
||||
PATTERN_TYPE_SIGMA,
|
||||
PATTERN_TYPE_SNORT,
|
||||
PATTERN_TYPE_SURICATA,
|
||||
PATTERN_TYPE_YARA,
|
||||
]
|
||||
|
||||
|
||||
PROCESSOR_ARCHITECTURE_ALPHA = "alpha"
|
||||
PROCESSOR_ARCHITECTURE_ARM = "arm"
|
||||
PROCESSOR_ARCHITECTURE_IA_64 = "ia-64"
|
||||
PROCESSOR_ARCHITECTURE_MIPS = "mips"
|
||||
PROCESSOR_ARCHITECTURE_POWERPC = "powerpc"
|
||||
PROCESSOR_ARCHITECTURE_SPARC = "sparc"
|
||||
PROCESSOR_ARCHITECTURE_X86 = "x86"
|
||||
PROCESSOR_ARCHITECTURE_X86_64 = "x86-64"
|
||||
|
||||
|
||||
PROCESSOR_ARCHITECTURE = [
|
||||
PROCESSOR_ARCHITECTURE_ALPHA,
|
||||
PROCESSOR_ARCHITECTURE_ARM,
|
||||
PROCESSOR_ARCHITECTURE_IA_64,
|
||||
PROCESSOR_ARCHITECTURE_MIPS,
|
||||
PROCESSOR_ARCHITECTURE_POWERPC,
|
||||
PROCESSOR_ARCHITECTURE_SPARC,
|
||||
PROCESSOR_ARCHITECTURE_X86,
|
||||
PROCESSOR_ARCHITECTURE_X86_64,
|
||||
]
|
||||
|
||||
|
||||
REGION_AFRICA = "africa"
|
||||
REGION_EASTERN_AFRICA = "eastern-africa"
|
||||
REGION_MIDDLE_AFRICA = "middle-africa"
|
||||
REGION_NORTHERN_AFRICA = "northern-africa"
|
||||
REGION_SOUTHERN_AFRICA = "southern-africa"
|
||||
REGION_WESTERN_AFRICA = "western-africa"
|
||||
REGION_AMERICAS = "americas"
|
||||
REGION_LATIN_AMERICA_CARIBBEAN = "latin-america-caribbean"
|
||||
REGION_SOUTH_AMERICA = "south-america"
|
||||
REGION_CARIBBEAN = "caribbean"
|
||||
REGION_CENTRAL_AMERICA = "central-america"
|
||||
REGION_NORTHERN_AMERICA = "northern-america"
|
||||
REGION_ASIA = "asia"
|
||||
REGION_CENTRAL_ASIA = "central-asia"
|
||||
REGION_EASTERN_ASIA = "eastern-asia"
|
||||
REGION_SOUTHERN_ASIA = "southern-asia"
|
||||
REGION_SOUTH_EASTERN_ASIA = "south-eastern-asia"
|
||||
REGION_WESTERN_ASIA = "western-asia"
|
||||
REGION_EUROPE = "europe"
|
||||
REGION_EASTERN_EUROPE = "eastern-europe"
|
||||
REGION_NORTHERN_EUROPE = "northern-europe"
|
||||
REGION_SOUTHERN_EUROPE = "southern-europe"
|
||||
REGION_WESTERN_EUROPE = "western-europe"
|
||||
REGION_OCEANIA = "oceania"
|
||||
REGION_ANTARCTICA = "antarctica"
|
||||
REGION_AUSTRALIA_NEW_ZEALAND = "australia-new-zealand"
|
||||
REGION_MELANESIA = "melanesia"
|
||||
REGION_MICRONESIA = "micronesia"
|
||||
REGION_POLYNESIA = "polynesia"
|
||||
|
||||
|
||||
REGION = [
|
||||
REGION_AFRICA,
|
||||
REGION_EASTERN_AFRICA,
|
||||
REGION_MIDDLE_AFRICA,
|
||||
REGION_NORTHERN_AFRICA,
|
||||
REGION_SOUTHERN_AFRICA,
|
||||
REGION_WESTERN_AFRICA,
|
||||
REGION_AMERICAS,
|
||||
REGION_LATIN_AMERICA_CARIBBEAN,
|
||||
REGION_SOUTH_AMERICA,
|
||||
REGION_CARIBBEAN,
|
||||
REGION_CENTRAL_AMERICA,
|
||||
REGION_NORTHERN_AMERICA,
|
||||
REGION_ASIA,
|
||||
REGION_CENTRAL_ASIA,
|
||||
REGION_EASTERN_ASIA,
|
||||
REGION_SOUTHERN_ASIA,
|
||||
REGION_SOUTH_EASTERN_ASIA,
|
||||
REGION_WESTERN_ASIA,
|
||||
REGION_EUROPE,
|
||||
REGION_EASTERN_EUROPE,
|
||||
REGION_NORTHERN_EUROPE,
|
||||
REGION_SOUTHERN_EUROPE,
|
||||
REGION_WESTERN_EUROPE,
|
||||
REGION_OCEANIA,
|
||||
REGION_ANTARCTICA,
|
||||
REGION_AUSTRALIA_NEW_ZEALAND,
|
||||
REGION_MELANESIA,
|
||||
REGION_MICRONESIA,
|
||||
REGION_POLYNESIA,
|
||||
]
|
||||
|
||||
|
||||
REPORT_TYPE_ATTACK_PATTERN = "attack-pattern"
|
||||
REPORT_TYPE_CAMPAIGN = "campaign"
|
||||
REPORT_TYPE_IDENTITY = "identity"
|
||||
REPORT_TYPE_INDICATOR = "indicator"
|
||||
REPORT_TYPE_INTRUSION_SET = "intrusion-set"
|
||||
REPORT_TYPE_MALWARE = "malware"
|
||||
REPORT_TYPE_OBSERVED_DATA = "observed-data"
|
||||
REPORT_TYPE_THREAT_ACTOR = "threat-actor"
|
||||
REPORT_TYPE_THREAT_REPORT = "threat-report"
|
||||
REPORT_TYPE_TOOL = "tool"
|
||||
REPORT_TYPE_VULNERABILITY = "vulnerability"
|
||||
|
||||
|
||||
REPORT_TYPE = [
|
||||
REPORT_TYPE_ATTACK_PATTERN,
|
||||
REPORT_TYPE_CAMPAIGN,
|
||||
REPORT_TYPE_IDENTITY,
|
||||
REPORT_TYPE_INDICATOR,
|
||||
REPORT_TYPE_INTRUSION_SET,
|
||||
REPORT_TYPE_MALWARE,
|
||||
REPORT_TYPE_OBSERVED_DATA,
|
||||
REPORT_TYPE_THREAT_ACTOR,
|
||||
REPORT_TYPE_THREAT_REPORT,
|
||||
REPORT_TYPE_TOOL,
|
||||
REPORT_TYPE_VULNERABILITY,
|
||||
]
|
||||
|
||||
|
||||
THREAT_ACTOR_TYPE_ACTIVIST = "activist"
|
||||
THREAT_ACTOR_TYPE_COMPETITOR = "competitor"
|
||||
THREAT_ACTOR_TYPE_CRIME_SYNDICATE = "crime-syndicate"
|
||||
THREAT_ACTOR_TYPE_CRIMINAL = "criminal"
|
||||
THREAT_ACTOR_TYPE_HACKER = "hacker"
|
||||
THREAT_ACTOR_TYPE_INSIDER_ACCIDENTAL = "insider-accidental"
|
||||
THREAT_ACTOR_TYPE_INSIDER_DISGRUNTLED = "insider-disgruntled"
|
||||
THREAT_ACTOR_TYPE_NATION_STATE = "nation-state"
|
||||
THREAT_ACTOR_TYPE_SENSATIONALIST = "sensationalist"
|
||||
THREAT_ACTOR_TYPE_SPY = "spy"
|
||||
THREAT_ACTOR_TYPE_TERRORIST = "terrorist"
|
||||
THREAT_ACTOR_TYPE_UNKNOWN = "unknown"
|
||||
|
||||
|
||||
THREAT_ACTOR_TYPE = [
|
||||
THREAT_ACTOR_TYPE_ACTIVIST,
|
||||
THREAT_ACTOR_TYPE_COMPETITOR,
|
||||
THREAT_ACTOR_TYPE_CRIME_SYNDICATE,
|
||||
THREAT_ACTOR_TYPE_CRIMINAL,
|
||||
THREAT_ACTOR_TYPE_HACKER,
|
||||
THREAT_ACTOR_TYPE_INSIDER_ACCIDENTAL,
|
||||
THREAT_ACTOR_TYPE_INSIDER_DISGRUNTLED,
|
||||
THREAT_ACTOR_TYPE_NATION_STATE,
|
||||
THREAT_ACTOR_TYPE_SENSATIONALIST,
|
||||
THREAT_ACTOR_TYPE_SPY,
|
||||
THREAT_ACTOR_TYPE_TERRORIST,
|
||||
THREAT_ACTOR_TYPE_UNKNOWN,
|
||||
]
|
||||
|
||||
|
||||
THREAT_ACTOR_ROLE_AGENT = "agent"
|
||||
THREAT_ACTOR_ROLE_DIRECTOR = "director"
|
||||
THREAT_ACTOR_ROLE_INDEPENDENT = "independent"
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_ARCHITECT = "infrastructure-architect"
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_OPERATOR = "infrastructure-operator"
|
||||
THREAT_ACTOR_ROLE_MALWARE_AUTHOR = "malware-author"
|
||||
THREAT_ACTOR_ROLE_SPONSOR = "sponsor"
|
||||
|
||||
|
||||
THREAT_ACTOR_ROLE = [
|
||||
THREAT_ACTOR_ROLE_AGENT,
|
||||
THREAT_ACTOR_ROLE_DIRECTOR,
|
||||
THREAT_ACTOR_ROLE_INDEPENDENT,
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_ARCHITECT,
|
||||
THREAT_ACTOR_ROLE_INFRASTRUCTURE_OPERATOR,
|
||||
THREAT_ACTOR_ROLE_MALWARE_AUTHOR,
|
||||
THREAT_ACTOR_ROLE_SPONSOR,
|
||||
]
|
||||
|
||||
|
||||
THREAT_ACTOR_SOPHISTICATION_NONE = "none"
|
||||
THREAT_ACTOR_SOPHISTICATION_MINIMAL = "minimal"
|
||||
THREAT_ACTOR_SOPHISTICATION_INTERMEDIATE = "intermediate"
|
||||
THREAT_ACTOR_SOPHISTICATION_ADVANCED = "advanced"
|
||||
THREAT_ACTOR_SOPHISTICATION_EXPERT = "expert"
|
||||
THREAT_ACTOR_SOPHISTICATION_INNOVATOR = "innovator"
|
||||
THREAT_ACTOR_SOPHISTICATION_STRATEGIC = "strategic"
|
||||
|
||||
|
||||
THREAT_ACTOR_SOPHISTICATION = [
|
||||
THREAT_ACTOR_SOPHISTICATION_NONE,
|
||||
THREAT_ACTOR_SOPHISTICATION_MINIMAL,
|
||||
THREAT_ACTOR_SOPHISTICATION_INTERMEDIATE,
|
||||
THREAT_ACTOR_SOPHISTICATION_ADVANCED,
|
||||
THREAT_ACTOR_SOPHISTICATION_EXPERT,
|
||||
THREAT_ACTOR_SOPHISTICATION_INNOVATOR,
|
||||
THREAT_ACTOR_SOPHISTICATION_STRATEGIC,
|
||||
]
|
||||
|
||||
|
||||
TOOL_TYPE_DENIAL_OF_SERVICE = "denial-of-service"
|
||||
TOOL_TYPE_EXPLOITATION = "exploitation"
|
||||
TOOL_TYPE_INFORMATION_GATHERING = "information-gathering"
|
||||
TOOL_TYPE_NETWORK_CAPTURE = "network-capture"
|
||||
TOOL_TYPE_CREDENTIAL_EXPLOITATION = "credential-exploitation"
|
||||
TOOL_TYPE_REMOTE_ACCESS = "remote-access"
|
||||
TOOL_TYPE_VULNERABILITY_SCANNING = "vulnerability-scanning"
|
||||
TOOL_TYPE_UNKNOWN = "unknown"
|
||||
|
||||
|
||||
TOOL_TYPE = [
|
||||
TOOL_TYPE_DENIAL_OF_SERVICE,
|
||||
TOOL_TYPE_EXPLOITATION,
|
||||
TOOL_TYPE_INFORMATION_GATHERING,
|
||||
TOOL_TYPE_NETWORK_CAPTURE,
|
||||
TOOL_TYPE_CREDENTIAL_EXPLOITATION,
|
||||
TOOL_TYPE_REMOTE_ACCESS,
|
||||
TOOL_TYPE_VULNERABILITY_SCANNING,
|
||||
TOOL_TYPE_UNKNOWN,
|
||||
]
|
||||
|
||||
|
||||
WINDOWS_INTEGRITY_LEVEL_LOW = "low"
|
||||
WINDOWS_INTEGRITY_LEVEL_MEDIUM = "medium"
|
||||
WINDOWS_INTEGRITY_LEVEL_HIGH = "high"
|
||||
WINDOWS_INTEGRITY_LEVEL_SYSTEM = "system"
|
||||
|
||||
|
||||
WINDOWS_INTEGRITY_LEVEL = [
|
||||
WINDOWS_INTEGRITY_LEVEL_LOW,
|
||||
WINDOWS_INTEGRITY_LEVEL_MEDIUM,
|
||||
WINDOWS_INTEGRITY_LEVEL_HIGH,
|
||||
WINDOWS_INTEGRITY_LEVEL_SYSTEM,
|
||||
]
|
||||
|
||||
|
||||
WINDOWS_PEBINARY_TYPE_DLL = "dll"
|
||||
WINDOWS_PEBINARY_TYPE_EXE = "exe"
|
||||
WINDOWS_PEBINARY_TYPE_SYS = "sys"
|
||||
|
||||
|
||||
WINDOWS_PEBINARY_TYPE = [
|
||||
WINDOWS_PEBINARY_TYPE_DLL,
|
||||
WINDOWS_PEBINARY_TYPE_EXE,
|
||||
WINDOWS_PEBINARY_TYPE_SYS,
|
||||
]
|
||||
|
||||
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_NONE = "REG_NONE"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_SZ = "REG_SZ"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_EXPAND_SZ = "REG_EXPAND_SZ"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_BINARY = "REG_BINARY"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_DWORD = "REG_DWORD"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_DWORD_BIG_ENDIAN = "REG_DWORD_BIG_ENDIAN"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_DWORD_LITTLE_ENDIAN = "REG_DWORD_LITTLE_ENDIAN"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_LINK = "REG_LINK"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_MULTI_SZ = "REG_MULTI_SZ"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_RESOURCE_LIST = "REG_RESOURCE_LIST"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_FULL_RESOURCE_DESCRIPTION = "REG_FULL_RESOURCE_DESCRIPTION"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_RESOURCE_REQUIREMENTS_LIST = "REG_RESOURCE_REQUIREMENTS_LIST"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_QWORD = "REG_QWORD"
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_INVALID_TYPE = "REG_INVALID_TYPE"
|
||||
|
||||
|
||||
WINDOWS_REGISTRY_DATATYPE = [
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_NONE,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_SZ,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_EXPAND_SZ,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_BINARY,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_DWORD,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_DWORD_BIG_ENDIAN,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_DWORD_LITTLE_ENDIAN,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_LINK,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_MULTI_SZ,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_RESOURCE_LIST,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_FULL_RESOURCE_DESCRIPTION,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_RESOURCE_REQUIREMENTS_LIST,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_QWORD,
|
||||
WINDOWS_REGISTRY_DATATYPE_REG_INVALID_TYPE,
|
||||
]
|
||||
|
||||
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_AUTO_START = "SERVICE_AUTO_START"
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_BOOT_START = "SERVICE_BOOT_START"
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_DEMAND_START = "SERVICE_DEMAND_START"
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_DISABLED = "SERVICE_DISABLED"
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_SYSTEM_ALERT = "SERVICE_SYSTEM_ALERT"
|
||||
|
||||
|
||||
WINDOWS_SERVICE_START_TYPE = [
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_AUTO_START,
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_BOOT_START,
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_DEMAND_START,
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_DISABLED,
|
||||
WINDOWS_SERVICE_START_TYPE_SERVICE_SYSTEM_ALERT,
|
||||
]
|
||||
|
||||
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_KERNEL_DRIVER = "SERVICE_KERNEL_DRIVER"
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_FILE_SYSTEM_DRIVER = "SERVICE_FILE_SYSTEM_DRIVER"
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_WIN32_OWN_PROCESS = "SERVICE_WIN32_OWN_PROCESS"
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_WIN32_SHARE_PROCESS = "SERVICE_WIN32_SHARE_PROCESS"
|
||||
|
||||
|
||||
WINDOWS_SERVICE_TYPE = [
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_KERNEL_DRIVER,
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_FILE_SYSTEM_DRIVER,
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_WIN32_OWN_PROCESS,
|
||||
WINDOWS_SERVICE_TYPE_SERVICE_WIN32_SHARE_PROCESS,
|
||||
]
|
||||
|
||||
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_CONTINUE_PENDING = "SERVICE_CONTINUE_PENDING"
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_PAUSE_PENDING = "SERVICE_PAUSE_PENDING"
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_PAUSED = "SERVICE_PAUSED"
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_RUNNING = "SERVICE_RUNNING"
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_START_PENDING = "SERVICE_START_PENDING"
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_STOP_PENDING = "SERVICE_STOP_PENDING"
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_STOPPED = "SERVICE_STOPPED"
|
||||
|
||||
|
||||
WINDOWS_SERVICE_STATUS = [
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_CONTINUE_PENDING,
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_PAUSE_PENDING,
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_PAUSED,
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_RUNNING,
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_START_PENDING,
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_STOP_PENDING,
|
||||
WINDOWS_SERVICE_STATUS_SERVICE_STOPPED,
|
||||
]
|
|
@ -192,8 +192,9 @@ def new_version(data, allow_custom=None, **kwargs):
|
|||
or dict.
|
||||
:param allow_custom: Whether to allow custom properties on the new object.
|
||||
If True, allow them (regardless of whether the original had custom
|
||||
properties); if False disallow them; if None, propagate the preference
|
||||
from the original object.
|
||||
properties); if False disallow them; if None, auto-detect from the
|
||||
object: if it has custom properties, allow them in the new version,
|
||||
otherwise don't allow them.
|
||||
:param kwargs: The properties to change. Setting to None requests property
|
||||
removal.
|
||||
:return: The new object.
|
||||
|
@ -271,7 +272,7 @@ def new_version(data, allow_custom=None, **kwargs):
|
|||
# it for dicts.
|
||||
if isinstance(data, stix2.base._STIXBase):
|
||||
if allow_custom is None:
|
||||
new_obj_inner["allow_custom"] = data._allow_custom
|
||||
new_obj_inner["allow_custom"] = data.has_custom
|
||||
else:
|
||||
new_obj_inner["allow_custom"] = allow_custom
|
||||
|
||||
|
|
Loading…
Reference in New Issue