Merge branch 'master' into dev-extensions-proposal

pull/1/head
Chris Lenk 2021-06-13 01:48:33 -04:00
commit 2528077eb0
44 changed files with 3261 additions and 597 deletions

View File

@ -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,13 +258,16 @@ 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())
# Versioning API
@property
def has_custom(self):
return self.__has_custom
# Versioning API
def new_version(self, **kwargs):
return _new_version(self, **kwargs)
@ -346,20 +376,21 @@ 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)
from .properties import ObjectReferenceProperty
if prop_name.endswith('_ref'):
if isinstance(prop, ObjectReferenceProperty):
ref = kwargs[prop_name]
self._check_ref(ref, prop, prop_name)
elif prop_name.endswith('_refs'):
if isinstance(prop.contained, ObjectReferenceProperty):
for ref in kwargs[prop_name]:
if prop_name in kwargs:
from .properties import ObjectReferenceProperty
if prop_name.endswith('_ref'):
if isinstance(prop, ObjectReferenceProperty):
ref = kwargs[prop_name]
self._check_ref(ref, prop, prop_name)
elif prop_name.endswith('_refs'):
if isinstance(prop.contained, ObjectReferenceProperty):
for ref in kwargs[prop_name]:
self._check_ref(ref, prop, prop_name)
return has_custom
def _generate_id(self):
"""

95
stix2/hashes.py Normal file
View File

@ -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

View File

@ -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,29 +349,26 @@ 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()
raise ValueError("must be a boolean value.")
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)
if possible_prefix in ref_valid_types:
required_prefix = possible_prefix
# 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
else:
type_ok = (
not is_stix_type(
obj_type, self.spec_version, *generics
) and obj_type not in specifics
) or obj_type in blacklist_exceptions
# 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("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)
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),
)
if possible_prefix not in ref_invalid_types:
required_prefix = possible_prefix
else:
raise ValueError("An invalid type-specifying prefix '%s' was specified for this property" % (possible_prefix))
if not allow_custom and has_custom:
raise CustomContentError(
"reference to custom object type: " + obj_type,
)
_validate_id(value, self.spec_version, required_prefix)
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

65
stix2/test/test_hashes.py Normal file
View File

@ -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)

View File

@ -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)

View File

@ -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',
},

View File

@ -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",

View File

@ -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():

View File

@ -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

View File

@ -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",

View File

@ -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)

View File

@ -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))

View File

@ -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({
'windows-pebinary-ext': {
'pe_type': 'exe',
},
})
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]

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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():

View File

@ -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)

View File

@ -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",

View File

@ -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)

View File

@ -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']

View File

@ -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 = {

View File

@ -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']

View File

@ -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,

View File

@ -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],
})

View File

@ -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,
)

View File

@ -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():

View File

@ -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))

View File

@ -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({
'windows-pebinary-ext': {
'pe_type': 'exe',
},
})
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]

View File

@ -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

View File

@ -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",
)

View File

@ -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(

View File

@ -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'):

View File

@ -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):

View File

@ -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.")

View File

@ -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()),

View File

@ -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)),

330
stix2/v20/vocab.py Normal file
View File

@ -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,
]

View File

@ -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):

View File

@ -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.")

View File

@ -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()),

View File

@ -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()),

841
stix2/v21/vocab.py Normal file
View File

@ -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,
]

View File

@ -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