Merge branch 'master' of github.com:oasis-open/cti-python-stix2
commit
8e95dbfce2
|
@ -1,4 +1,4 @@
|
|||
sudo: false
|
||||
os: linux
|
||||
language: python
|
||||
cache: pip
|
||||
dist: xenial
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
1.4.0 - 2020-04-03
|
||||
|
||||
* #347, #355, #356, #357, #358, #360, #362, #369, #370, #379, #374, #384 Updates STIX 2.1 support to CS01
|
||||
* #376 Fixes bug where registering object of same name would overwrite it; will
|
||||
now raise an error
|
||||
|
||||
1.3.1 - 2020-03-06
|
||||
|
||||
* #322 Adds encoding option FileSystemSource and MemorySource
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 1.3.1
|
||||
current_version = 1.4.0
|
||||
commit = True
|
||||
tag = True
|
||||
|
||||
|
|
1
setup.py
1
setup.py
|
@ -51,6 +51,7 @@ setup(
|
|||
keywords='stix stix2 json cti cyber threat intelligence',
|
||||
packages=find_packages(exclude=['*.test', '*.test.*']),
|
||||
install_requires=[
|
||||
'enum34 ; python_version<"3.4"',
|
||||
'python-dateutil',
|
||||
'pytz',
|
||||
'requests',
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
DEFAULT_VERSION = '2.0' # Default version will always be the latest STIX 2.X version
|
||||
|
||||
from .confidence import scales
|
||||
from .core import _collect_stix2_mappings, parse, parse_observable
|
||||
from .datastore import CompositeDataSource
|
||||
from .datastore.filesystem import (
|
||||
FileSystemSink, FileSystemSource, FileSystemStore,
|
||||
|
@ -38,6 +37,7 @@ from .markings import (
|
|||
add_markings, clear_markings, get_markings, is_marked, remove_markings,
|
||||
set_markings,
|
||||
)
|
||||
from .parsing import _collect_stix2_mappings, parse, parse_observable
|
||||
from .patterns import (
|
||||
AndBooleanExpression, AndObservationExpression, BasicObjectPathComponent,
|
||||
BinaryConstant, BooleanConstant, EqualityComparisonExpression,
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
import copy
|
||||
import datetime as dt
|
||||
import re
|
||||
import uuid
|
||||
|
||||
import simplejson as json
|
||||
import six
|
||||
|
||||
import stix2
|
||||
from stix2.canonicalization.Canonicalize import canonicalize
|
||||
|
||||
from .exceptions import (
|
||||
|
@ -14,8 +16,11 @@ from .exceptions import (
|
|||
ImmutableError, InvalidObjRefError, InvalidValueError,
|
||||
MissingPropertiesError, MutuallyExclusivePropertiesError,
|
||||
)
|
||||
from .markings import _MarkingsMixin
|
||||
from .markings.utils import validate
|
||||
from .utils import NOW, find_property_index, format_datetime, get_timestamp
|
||||
from .utils import (
|
||||
NOW, PREFIX_21_REGEX, find_property_index, format_datetime, get_timestamp,
|
||||
)
|
||||
from .utils import new_version as _new_version
|
||||
from .utils import revoke as _revoke
|
||||
|
||||
|
@ -158,12 +163,23 @@ class _STIXBase(Mapping):
|
|||
custom_props = kwargs.pop('custom_properties', {})
|
||||
if custom_props and not isinstance(custom_props, dict):
|
||||
raise ValueError("'custom_properties' must be a dictionary")
|
||||
if not self._allow_custom:
|
||||
extra_kwargs = list(set(kwargs) - set(self._properties))
|
||||
if extra_kwargs:
|
||||
raise ExtraPropertiesError(cls, extra_kwargs)
|
||||
if custom_props:
|
||||
|
||||
extra_kwargs = list(set(kwargs) - set(self._properties))
|
||||
if extra_kwargs and not self._allow_custom:
|
||||
raise ExtraPropertiesError(cls, extra_kwargs)
|
||||
|
||||
# because allow_custom is true, any extra kwargs are custom
|
||||
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()))
|
||||
for prop_name in all_custom_prop_names:
|
||||
if not re.match(PREFIX_21_REGEX, prop_name):
|
||||
raise InvalidValueError(
|
||||
self.__class__, prop_name,
|
||||
reason="Property name '%s' must begin with an alpha character." % prop_name,
|
||||
)
|
||||
|
||||
# Remove any keyword arguments whose value is None or [] (i.e. empty list)
|
||||
setting_kwargs = {}
|
||||
|
@ -307,6 +323,14 @@ class _STIXBase(Mapping):
|
|||
return json.dumps(self, cls=STIXJSONEncoder, **kwargs)
|
||||
|
||||
|
||||
class _DomainObject(_STIXBase, _MarkingsMixin):
|
||||
pass
|
||||
|
||||
|
||||
class _RelationshipObject(_STIXBase, _MarkingsMixin):
|
||||
pass
|
||||
|
||||
|
||||
class _Observable(_STIXBase):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
|
181
stix2/custom.py
181
stix2/custom.py
|
@ -1,140 +1,91 @@
|
|||
from collections import OrderedDict
|
||||
import re
|
||||
|
||||
import six
|
||||
|
||||
from .base import _cls_init, _Extension, _Observable, _STIXBase
|
||||
from .core import (
|
||||
STIXDomainObject, _register_marking, _register_object,
|
||||
_register_observable, _register_observable_extension,
|
||||
from .base import _cls_init
|
||||
from .parsing import (
|
||||
_register_marking, _register_object, _register_observable,
|
||||
_register_observable_extension,
|
||||
)
|
||||
from .utils import TYPE_REGEX, get_class_hierarchy_names
|
||||
|
||||
|
||||
def _custom_object_builder(cls, type, properties, version):
|
||||
class _CustomObject(cls, STIXDomainObject):
|
||||
|
||||
if not re.match(TYPE_REGEX, type):
|
||||
raise ValueError(
|
||||
"Invalid type name '%s': must only contain the "
|
||||
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % type,
|
||||
)
|
||||
elif len(type) < 3 or len(type) > 250:
|
||||
raise ValueError(
|
||||
"Invalid type name '%s': must be between 3 and 250 characters." % type,
|
||||
)
|
||||
|
||||
if not properties or not isinstance(properties, list):
|
||||
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
|
||||
|
||||
_type = type
|
||||
_properties = OrderedDict(properties)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
_STIXBase.__init__(self, **kwargs)
|
||||
_cls_init(cls, self, kwargs)
|
||||
|
||||
_register_object(_CustomObject, version=version)
|
||||
return _CustomObject
|
||||
|
||||
|
||||
def _custom_marking_builder(cls, type, properties, version):
|
||||
class _CustomMarking(cls, _STIXBase):
|
||||
|
||||
if not properties or not isinstance(properties, list):
|
||||
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
|
||||
|
||||
_type = type
|
||||
_properties = OrderedDict(properties)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
_STIXBase.__init__(self, **kwargs)
|
||||
_cls_init(cls, self, kwargs)
|
||||
|
||||
_register_marking(_CustomMarking, version=version)
|
||||
return _CustomMarking
|
||||
|
||||
|
||||
def _custom_observable_builder(cls, type, properties, version, id_contrib_props=None):
|
||||
if id_contrib_props is None:
|
||||
id_contrib_props = []
|
||||
|
||||
class _CustomObservable(cls, _Observable):
|
||||
|
||||
if not re.match(TYPE_REGEX, type):
|
||||
raise ValueError(
|
||||
"Invalid observable type name '%s': must only contain the "
|
||||
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % type,
|
||||
)
|
||||
elif len(type) < 3 or len(type) > 250:
|
||||
raise ValueError("Invalid observable type name '%s': must be between 3 and 250 characters." % type)
|
||||
|
||||
if not properties or not isinstance(properties, list):
|
||||
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
|
||||
|
||||
if version == "2.0":
|
||||
# If using STIX2.0, check properties ending in "_ref/s" are ObjectReferenceProperties
|
||||
for prop_name, prop in properties:
|
||||
if prop_name.endswith('_ref') and ('ObjectReferenceProperty' not in get_class_hierarchy_names(prop)):
|
||||
raise ValueError(
|
||||
"'%s' is named like an object reference property but "
|
||||
"is not an ObjectReferenceProperty." % prop_name,
|
||||
)
|
||||
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
|
||||
'ObjectReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
|
||||
raise ValueError(
|
||||
"'%s' is named like an object reference list property but "
|
||||
"is not a ListProperty containing ObjectReferenceProperty." % prop_name,
|
||||
)
|
||||
else:
|
||||
# If using STIX2.1 (or newer...), check properties ending in "_ref/s" are ReferenceProperties
|
||||
for prop_name, prop in properties:
|
||||
if prop_name.endswith('_ref') and ('ReferenceProperty' not in get_class_hierarchy_names(prop)):
|
||||
raise ValueError(
|
||||
"'%s' is named like a reference property but "
|
||||
"is not a ReferenceProperty." % prop_name,
|
||||
)
|
||||
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
|
||||
'ReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
|
||||
raise ValueError(
|
||||
"'%s' is named like a reference list property but "
|
||||
"is not a ListProperty containing ReferenceProperty." % prop_name,
|
||||
)
|
||||
|
||||
_type = type
|
||||
_properties = OrderedDict(properties)
|
||||
if version != '2.0':
|
||||
_id_contributing_properties = id_contrib_props
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
_Observable.__init__(self, **kwargs)
|
||||
_cls_init(cls, self, kwargs)
|
||||
|
||||
_register_observable(_CustomObservable, version=version)
|
||||
return _CustomObservable
|
||||
|
||||
|
||||
def _custom_extension_builder(cls, observable, type, properties, version):
|
||||
|
||||
def _get_properties_dict(properties):
|
||||
try:
|
||||
prop_dict = OrderedDict(properties)
|
||||
return OrderedDict(properties)
|
||||
except TypeError as e:
|
||||
six.raise_from(
|
||||
ValueError(
|
||||
"Extension properties must be dict-like, e.g. a list "
|
||||
"properties must be dict-like, e.g. a list "
|
||||
"containing tuples. For example, "
|
||||
"[('property1', IntegerProperty())]",
|
||||
),
|
||||
e,
|
||||
)
|
||||
|
||||
class _CustomExtension(cls, _Extension):
|
||||
|
||||
def _custom_object_builder(cls, type, properties, version, base_class):
|
||||
prop_dict = _get_properties_dict(properties)
|
||||
|
||||
class _CustomObject(cls, base_class):
|
||||
|
||||
_type = type
|
||||
_properties = prop_dict
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
_Extension.__init__(self, **kwargs)
|
||||
base_class.__init__(self, **kwargs)
|
||||
_cls_init(cls, self, kwargs)
|
||||
|
||||
_register_object(_CustomObject, version=version)
|
||||
return _CustomObject
|
||||
|
||||
|
||||
def _custom_marking_builder(cls, type, properties, version, base_class):
|
||||
prop_dict = _get_properties_dict(properties)
|
||||
|
||||
class _CustomMarking(cls, base_class):
|
||||
|
||||
_type = type
|
||||
_properties = prop_dict
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
base_class.__init__(self, **kwargs)
|
||||
_cls_init(cls, self, kwargs)
|
||||
|
||||
_register_marking(_CustomMarking, version=version)
|
||||
return _CustomMarking
|
||||
|
||||
|
||||
def _custom_observable_builder(cls, type, properties, version, base_class, id_contrib_props=None):
|
||||
if id_contrib_props is None:
|
||||
id_contrib_props = []
|
||||
|
||||
prop_dict = _get_properties_dict(properties)
|
||||
|
||||
class _CustomObservable(cls, base_class):
|
||||
|
||||
_type = type
|
||||
_properties = prop_dict
|
||||
if version != '2.0':
|
||||
_id_contributing_properties = id_contrib_props
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
base_class.__init__(self, **kwargs)
|
||||
_cls_init(cls, self, kwargs)
|
||||
|
||||
_register_observable(_CustomObservable, version=version)
|
||||
return _CustomObservable
|
||||
|
||||
|
||||
def _custom_extension_builder(cls, observable, type, properties, version, base_class):
|
||||
prop_dict = _get_properties_dict(properties)
|
||||
|
||||
class _CustomExtension(cls, base_class):
|
||||
|
||||
_type = type
|
||||
_properties = prop_dict
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
base_class.__init__(self, **kwargs)
|
||||
_cls_init(cls, self, kwargs)
|
||||
|
||||
_register_observable_extension(observable, _CustomExtension, version=version)
|
||||
|
|
|
@ -10,11 +10,11 @@ import six
|
|||
|
||||
from stix2 import v20, v21
|
||||
from stix2.base import _STIXBase
|
||||
from stix2.core import parse
|
||||
from stix2.datastore import (
|
||||
DataSink, DataSource, DataSourceError, DataStoreMixin,
|
||||
)
|
||||
from stix2.datastore.filters import Filter, FilterSet, apply_common_filters
|
||||
from stix2.parsing import parse
|
||||
from stix2.utils import format_datetime, get_type_from_id
|
||||
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ import os
|
|||
|
||||
from stix2 import v20, v21
|
||||
from stix2.base import _STIXBase
|
||||
from stix2.core import parse
|
||||
from stix2.datastore import DataSink, DataSource, DataStoreMixin
|
||||
from stix2.datastore.filters import FilterSet, apply_common_filters
|
||||
from stix2.parsing import parse
|
||||
|
||||
|
||||
def _add(store, stix_data, allow_custom=True, version=None):
|
||||
|
|
|
@ -4,15 +4,15 @@ from requests.exceptions import HTTPError
|
|||
|
||||
from stix2 import v20, v21
|
||||
from stix2.base import _STIXBase
|
||||
from stix2.core import parse
|
||||
from stix2.datastore import (
|
||||
DataSink, DataSource, DataSourceError, DataStoreMixin,
|
||||
)
|
||||
from stix2.datastore.filters import Filter, FilterSet, apply_common_filters
|
||||
from stix2.parsing import parse
|
||||
from stix2.utils import deduplicate
|
||||
|
||||
try:
|
||||
from taxii2client import ValidationError
|
||||
from taxii2client.exceptions import ValidationError
|
||||
_taxii2_client = True
|
||||
except ImportError:
|
||||
_taxii2_client = False
|
||||
|
|
|
@ -4,8 +4,8 @@ import copy
|
|||
import logging
|
||||
import time
|
||||
|
||||
from .core import parse as _parse
|
||||
from .datastore import CompositeDataSource, DataStoreMixin
|
||||
from .parsing import parse as _parse
|
||||
from .utils import STIXdatetime, parse_into_datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -233,3 +233,16 @@ class STIXDeprecationWarning(DeprecationWarning):
|
|||
Represents usage of a deprecated component of a STIX specification.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class DuplicateRegistrationError(STIXError):
|
||||
"""A STIX object with the same type as an existing object is being registered"""
|
||||
|
||||
def __init__(self, obj_type, reg_obj_type):
|
||||
super(DuplicateRegistrationError, self).__init__()
|
||||
self.obj_type = obj_type
|
||||
self.reg_obj_type = reg_obj_type
|
||||
|
||||
def __str__(self):
|
||||
msg = "A(n) {0} with type '{1}' already exists and cannot be registered again"
|
||||
return msg.format(self.obj_type, self.reg_obj_type)
|
||||
|
|
|
@ -7,39 +7,13 @@ import re
|
|||
|
||||
import stix2
|
||||
|
||||
from .base import _Observable, _STIXBase
|
||||
from .exceptions import ParseError
|
||||
from .markings import _MarkingsMixin
|
||||
from .utils import SCO21_EXT_REGEX, TYPE_REGEX, _get_dict
|
||||
from .base import _DomainObject, _Observable
|
||||
from .exceptions import DuplicateRegistrationError, ParseError
|
||||
from .utils import PREFIX_21_REGEX, _get_dict, get_class_hierarchy_names
|
||||
|
||||
STIX2_OBJ_MAPS = {}
|
||||
|
||||
|
||||
class STIXDomainObject(_STIXBase, _MarkingsMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
interoperability = kwargs.get('interoperability', False)
|
||||
self.__interoperability = interoperability
|
||||
self._properties['id'].interoperability = interoperability
|
||||
self._properties['created_by_ref'].interoperability = interoperability
|
||||
if kwargs.get('object_marking_refs'):
|
||||
self._properties['object_marking_refs'].contained.interoperability = interoperability
|
||||
|
||||
super(STIXDomainObject, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
interoperability = kwargs.get('interoperability', False)
|
||||
self.__interoperability = interoperability
|
||||
self._properties['id'].interoperability = interoperability
|
||||
if kwargs.get('created_by_ref'):
|
||||
self._properties['created_by_ref'].interoperability = interoperability
|
||||
if kwargs.get('object_marking_refs'):
|
||||
self._properties['object_marking_refs'].contained.interoperability = interoperability
|
||||
|
||||
super(STIXRelationshipObject, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def parse(data, allow_custom=False, interoperability=False, version=None):
|
||||
"""Convert a string, dict or file-like object into a STIX object.
|
||||
|
||||
|
@ -218,7 +192,7 @@ def parse_observable(data, _valid_refs=None, allow_custom=False, version=None):
|
|||
return obj_class(allow_custom=allow_custom, **obj)
|
||||
|
||||
|
||||
def _register_object(new_type, version=None):
|
||||
def _register_object(new_type, version=stix2.DEFAULT_VERSION):
|
||||
"""Register a custom STIX Object type.
|
||||
|
||||
Args:
|
||||
|
@ -226,7 +200,26 @@ def _register_object(new_type, version=None):
|
|||
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
|
||||
None, use latest version.
|
||||
|
||||
Raises:
|
||||
ValueError: If the class being registered wasn't created with the
|
||||
@CustomObject decorator.
|
||||
DuplicateRegistrationError: If the class has already been registered.
|
||||
|
||||
"""
|
||||
|
||||
if not issubclass(new_type, _DomainObject):
|
||||
raise ValueError(
|
||||
"'%s' must be created with the @CustomObject decorator." %
|
||||
new_type.__name__,
|
||||
)
|
||||
|
||||
properties = new_type._properties
|
||||
|
||||
if version == "2.1":
|
||||
for prop_name, prop in properties.items():
|
||||
if not re.match(PREFIX_21_REGEX, prop_name):
|
||||
raise ValueError("Property name '%s' must begin with an alpha character" % prop_name)
|
||||
|
||||
if version:
|
||||
v = 'v' + version.replace('.', '')
|
||||
else:
|
||||
|
@ -234,10 +227,12 @@ def _register_object(new_type, version=None):
|
|||
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
||||
|
||||
OBJ_MAP = STIX2_OBJ_MAPS[v]['objects']
|
||||
if new_type._type in OBJ_MAP.keys():
|
||||
raise DuplicateRegistrationError("STIX Object", new_type._type)
|
||||
OBJ_MAP[new_type._type] = new_type
|
||||
|
||||
|
||||
def _register_marking(new_marking, version=None):
|
||||
def _register_marking(new_marking, version=stix2.DEFAULT_VERSION):
|
||||
"""Register a custom STIX Marking Definition type.
|
||||
|
||||
Args:
|
||||
|
@ -246,6 +241,17 @@ def _register_marking(new_marking, version=None):
|
|||
None, use latest version.
|
||||
|
||||
"""
|
||||
|
||||
mark_type = new_marking._type
|
||||
properties = new_marking._properties
|
||||
|
||||
stix2.properties._validate_type(mark_type, version)
|
||||
|
||||
if version == "2.1":
|
||||
for prop_name, prop_value in properties.items():
|
||||
if not re.match(PREFIX_21_REGEX, prop_name):
|
||||
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
|
||||
|
||||
if version:
|
||||
v = 'v' + version.replace('.', '')
|
||||
else:
|
||||
|
@ -253,10 +259,12 @@ def _register_marking(new_marking, version=None):
|
|||
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
||||
|
||||
OBJ_MAP_MARKING = STIX2_OBJ_MAPS[v]['markings']
|
||||
OBJ_MAP_MARKING[new_marking._type] = new_marking
|
||||
if mark_type in OBJ_MAP_MARKING.keys():
|
||||
raise DuplicateRegistrationError("STIX Marking", mark_type)
|
||||
OBJ_MAP_MARKING[mark_type] = new_marking
|
||||
|
||||
|
||||
def _register_observable(new_observable, version=None):
|
||||
def _register_observable(new_observable, version=stix2.DEFAULT_VERSION):
|
||||
"""Register a custom STIX Cyber Observable type.
|
||||
|
||||
Args:
|
||||
|
@ -265,6 +273,39 @@ def _register_observable(new_observable, version=None):
|
|||
None, use latest version.
|
||||
|
||||
"""
|
||||
properties = new_observable._properties
|
||||
|
||||
if version == "2.0":
|
||||
# If using STIX2.0, check properties ending in "_ref/s" are ObjectReferenceProperties
|
||||
for prop_name, prop in properties.items():
|
||||
if prop_name.endswith('_ref') and ('ObjectReferenceProperty' not in get_class_hierarchy_names(prop)):
|
||||
raise ValueError(
|
||||
"'%s' is named like an object reference property but "
|
||||
"is not an ObjectReferenceProperty." % prop_name,
|
||||
)
|
||||
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
|
||||
'ObjectReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
|
||||
raise ValueError(
|
||||
"'%s' is named like an object reference list property but "
|
||||
"is not a ListProperty containing ObjectReferenceProperty." % prop_name,
|
||||
)
|
||||
else:
|
||||
# If using STIX2.1 (or newer...), check properties ending in "_ref/s" are ReferenceProperties
|
||||
for prop_name, prop in properties.items():
|
||||
if not re.match(PREFIX_21_REGEX, prop_name):
|
||||
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
|
||||
elif prop_name.endswith('_ref') and ('ReferenceProperty' not in get_class_hierarchy_names(prop)):
|
||||
raise ValueError(
|
||||
"'%s' is named like a reference property but "
|
||||
"is not a ReferenceProperty." % prop_name,
|
||||
)
|
||||
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
|
||||
'ReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
|
||||
raise ValueError(
|
||||
"'%s' is named like a reference list property but "
|
||||
"is not a ListProperty containing ReferenceProperty." % prop_name,
|
||||
)
|
||||
|
||||
if version:
|
||||
v = 'v' + version.replace('.', '')
|
||||
else:
|
||||
|
@ -272,6 +313,8 @@ def _register_observable(new_observable, version=None):
|
|||
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
|
||||
|
||||
OBJ_MAP_OBSERVABLE = STIX2_OBJ_MAPS[v]['observables']
|
||||
if new_observable._type in OBJ_MAP_OBSERVABLE.keys():
|
||||
raise DuplicateRegistrationError("Cyber Observable", new_observable._type)
|
||||
OBJ_MAP_OBSERVABLE[new_observable._type] = new_observable
|
||||
|
||||
|
||||
|
@ -291,30 +334,12 @@ def _register_observable_extension(
|
|||
obs_class = observable if isinstance(observable, type) else \
|
||||
type(observable)
|
||||
ext_type = new_extension._type
|
||||
properties = new_extension._properties
|
||||
|
||||
if not issubclass(obs_class, _Observable):
|
||||
raise ValueError("'observable' must be a valid Observable class!")
|
||||
|
||||
if version == "2.0":
|
||||
if not re.match(TYPE_REGEX, ext_type):
|
||||
raise ValueError(
|
||||
"Invalid extension type name '%s': must only contain the "
|
||||
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." %
|
||||
ext_type,
|
||||
)
|
||||
else: # 2.1+
|
||||
if not re.match(SCO21_EXT_REGEX, ext_type):
|
||||
raise ValueError(
|
||||
"Invalid extension type name '%s': must only contain the "
|
||||
"characters a-z (lowercase ASCII), 0-9, hyphen (-), and end "
|
||||
"with '-ext'." % ext_type,
|
||||
)
|
||||
|
||||
if len(ext_type) < 3 or len(ext_type) > 250:
|
||||
raise ValueError(
|
||||
"Invalid extension type name '%s': must be between 3 and 250"
|
||||
" characters." % ext_type,
|
||||
)
|
||||
stix2.properties._validate_type(ext_type, version)
|
||||
|
||||
if not new_extension._properties:
|
||||
raise ValueError(
|
||||
|
@ -322,6 +347,17 @@ def _register_observable_extension(
|
|||
ext_type,
|
||||
)
|
||||
|
||||
if version == "2.1":
|
||||
if not ext_type.endswith('-ext'):
|
||||
raise ValueError(
|
||||
"Invalid extension type name '%s': must end with '-ext'." %
|
||||
ext_type,
|
||||
)
|
||||
|
||||
for prop_name, prop_value in properties.items():
|
||||
if not re.match(PREFIX_21_REGEX, prop_name):
|
||||
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
|
||||
|
||||
v = 'v' + version.replace('.', '')
|
||||
|
||||
try:
|
||||
|
@ -336,6 +372,8 @@ def _register_observable_extension(
|
|||
EXT_MAP = STIX2_OBJ_MAPS[v]['observable-extensions']
|
||||
|
||||
try:
|
||||
if ext_type in EXT_MAP[observable_type].keys():
|
||||
raise DuplicateRegistrationError("Observable Extension", ext_type)
|
||||
EXT_MAP[observable_type][ext_type] = new_extension
|
||||
except KeyError:
|
||||
if observable_type not in OBJ_MAP_OBSERVABLE:
|
|
@ -2,11 +2,19 @@ import importlib
|
|||
import inspect
|
||||
|
||||
from stix2patterns.exceptions import ParseException
|
||||
from stix2patterns.grammars.STIXPatternParser import (
|
||||
STIXPatternParser, TerminalNode,
|
||||
)
|
||||
from stix2patterns.grammars.STIXPatternVisitor import STIXPatternVisitor
|
||||
from stix2patterns.v20.pattern import Pattern
|
||||
from stix2patterns.grammars.STIXPatternParser import TerminalNode
|
||||
from stix2patterns.v20.grammars.STIXPatternParser import \
|
||||
STIXPatternParser as STIXPatternParser20
|
||||
from stix2patterns.v20.grammars.STIXPatternVisitor import \
|
||||
STIXPatternVisitor as STIXPatternVisitor20
|
||||
from stix2patterns.v20.pattern import Pattern as Pattern20
|
||||
from stix2patterns.v21.grammars.STIXPatternParser import \
|
||||
STIXPatternParser as STIXPatternParser21
|
||||
from stix2patterns.v21.grammars.STIXPatternVisitor import \
|
||||
STIXPatternVisitor as STIXPatternVisitor21
|
||||
from stix2patterns.v21.pattern import Pattern as Pattern21
|
||||
|
||||
import stix2
|
||||
|
||||
from .patterns import *
|
||||
from .patterns import _BooleanExpression
|
||||
|
@ -32,23 +40,12 @@ def remove_terminal_nodes(parse_tree_nodes):
|
|||
return values
|
||||
|
||||
|
||||
# This class defines a complete generic visitor for a parse tree produced by STIXPatternParser.
|
||||
|
||||
|
||||
class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
|
||||
|
||||
class STIXPatternVisitorForSTIX2():
|
||||
classes = {}
|
||||
|
||||
def __init__(self, module_suffix, module_name):
|
||||
if module_suffix and module_name:
|
||||
self.module_suffix = module_suffix
|
||||
if not STIXPatternVisitorForSTIX2.classes:
|
||||
module = importlib.import_module(module_name)
|
||||
for k, c in inspect.getmembers(module, inspect.isclass):
|
||||
STIXPatternVisitorForSTIX2.classes[k] = c
|
||||
else:
|
||||
self.module_suffix = None
|
||||
super(STIXPatternVisitor, self).__init__()
|
||||
|
||||
def get_class(self, class_name):
|
||||
if class_name in STIXPatternVisitorForSTIX2.classes:
|
||||
return STIXPatternVisitorForSTIX2.classes[class_name]
|
||||
|
@ -106,7 +103,10 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
|
|||
# Visit a parse tree produced by STIXPatternParser#observationExpressionCompound.
|
||||
def visitObservationExpressionCompound(self, ctx):
|
||||
children = self.visitChildren(ctx)
|
||||
return self.instantiate("ObservationExpression", children[1])
|
||||
if isinstance(children[0], TerminalNode) and children[0].symbol.type == self.parser_class.LPAREN:
|
||||
return self.instantiate("ParentheticalExpression", children[1])
|
||||
else:
|
||||
return self.instantiate("ObservationExpression", children[0])
|
||||
|
||||
# Visit a parse tree produced by STIXPatternParser#observationExpressionWithin.
|
||||
def visitObservationExpressionWithin(self, ctx):
|
||||
|
@ -147,7 +147,7 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
|
|||
def visitPropTestEqual(self, ctx):
|
||||
children = self.visitChildren(ctx)
|
||||
operator = children[1].symbol.type
|
||||
negated = operator != STIXPatternParser.EQ
|
||||
negated = operator != self.parser_class.EQ
|
||||
return self.instantiate(
|
||||
"EqualityComparisonExpression", children[0], children[3 if len(children) > 3 else 2],
|
||||
negated,
|
||||
|
@ -157,22 +157,22 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
|
|||
def visitPropTestOrder(self, ctx):
|
||||
children = self.visitChildren(ctx)
|
||||
operator = children[1].symbol.type
|
||||
if operator == STIXPatternParser.GT:
|
||||
if operator == self.parser_class.GT:
|
||||
return self.instantiate(
|
||||
"GreaterThanComparisonExpression", children[0],
|
||||
children[3 if len(children) > 3 else 2], False,
|
||||
)
|
||||
elif operator == STIXPatternParser.LT:
|
||||
elif operator == self.parser_class.LT:
|
||||
return self.instantiate(
|
||||
"LessThanComparisonExpression", children[0],
|
||||
children[3 if len(children) > 3 else 2], False,
|
||||
)
|
||||
elif operator == STIXPatternParser.GE:
|
||||
elif operator == self.parser_class.GE:
|
||||
return self.instantiate(
|
||||
"GreaterThanEqualComparisonExpression", children[0],
|
||||
children[3 if len(children) > 3 else 2], False,
|
||||
)
|
||||
elif operator == STIXPatternParser.LE:
|
||||
elif operator == self.parser_class.LE:
|
||||
return self.instantiate(
|
||||
"LessThanEqualComparisonExpression", children[0],
|
||||
children[3 if len(children) > 3 else 2], False,
|
||||
|
@ -294,22 +294,22 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
|
|||
return children[0]
|
||||
|
||||
def visitTerminal(self, node):
|
||||
if node.symbol.type == STIXPatternParser.IntPosLiteral or node.symbol.type == STIXPatternParser.IntNegLiteral:
|
||||
if node.symbol.type == self.parser_class.IntPosLiteral or node.symbol.type == self.parser_class.IntNegLiteral:
|
||||
return IntegerConstant(node.getText())
|
||||
elif node.symbol.type == STIXPatternParser.FloatPosLiteral or node.symbol.type == STIXPatternParser.FloatNegLiteral:
|
||||
elif node.symbol.type == self.parser_class.FloatPosLiteral or node.symbol.type == self.parser_class.FloatNegLiteral:
|
||||
return FloatConstant(node.getText())
|
||||
elif node.symbol.type == STIXPatternParser.HexLiteral:
|
||||
elif node.symbol.type == self.parser_class.HexLiteral:
|
||||
return HexConstant(node.getText(), from_parse_tree=True)
|
||||
elif node.symbol.type == STIXPatternParser.BinaryLiteral:
|
||||
elif node.symbol.type == self.parser_class.BinaryLiteral:
|
||||
return BinaryConstant(node.getText(), from_parse_tree=True)
|
||||
elif node.symbol.type == STIXPatternParser.StringLiteral:
|
||||
elif node.symbol.type == self.parser_class.StringLiteral:
|
||||
if node.getText()[0] == "'" and node.getText()[-1] == "'":
|
||||
return StringConstant(node.getText()[1:-1], from_parse_tree=True)
|
||||
else:
|
||||
raise ParseException("The pattern does not start and end with a single quote")
|
||||
elif node.symbol.type == STIXPatternParser.BoolLiteral:
|
||||
elif node.symbol.type == self.parser_class.BoolLiteral:
|
||||
return BooleanConstant(node.getText())
|
||||
elif node.symbol.type == STIXPatternParser.TimestampLiteral:
|
||||
elif node.symbol.type == self.parser_class.TimestampLiteral:
|
||||
return TimestampConstant(node.getText())
|
||||
else:
|
||||
return node
|
||||
|
@ -321,12 +321,51 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
|
|||
aggregate = [nextResult]
|
||||
return aggregate
|
||||
|
||||
# This class defines a complete generic visitor for a parse tree produced by STIXPatternParser.
|
||||
class STIXPatternVisitorForSTIX21(STIXPatternVisitorForSTIX2, STIXPatternVisitor21):
|
||||
classes = {}
|
||||
|
||||
def create_pattern_object(pattern, module_suffix="", module_name=""):
|
||||
def __init__(self, module_suffix, module_name):
|
||||
if module_suffix and module_name:
|
||||
self.module_suffix = module_suffix
|
||||
if not STIXPatternVisitorForSTIX2.classes:
|
||||
module = importlib.import_module(module_name)
|
||||
for k, c in inspect.getmembers(module, inspect.isclass):
|
||||
STIXPatternVisitorForSTIX2.classes[k] = c
|
||||
else:
|
||||
self.module_suffix = None
|
||||
self.parser_class = STIXPatternParser21
|
||||
super(STIXPatternVisitor21, self).__init__()
|
||||
|
||||
|
||||
class STIXPatternVisitorForSTIX20(STIXPatternVisitorForSTIX2, STIXPatternVisitor20):
|
||||
classes = {}
|
||||
|
||||
def __init__(self, module_suffix, module_name):
|
||||
if module_suffix and module_name:
|
||||
self.module_suffix = module_suffix
|
||||
if not STIXPatternVisitorForSTIX2.classes:
|
||||
module = importlib.import_module(module_name)
|
||||
for k, c in inspect.getmembers(module, inspect.isclass):
|
||||
STIXPatternVisitorForSTIX2.classes[k] = c
|
||||
else:
|
||||
self.module_suffix = None
|
||||
self.parser_class = STIXPatternParser20
|
||||
super(STIXPatternVisitor20, self).__init__()
|
||||
|
||||
|
||||
def create_pattern_object(pattern, module_suffix="", module_name="", version=stix2.DEFAULT_VERSION):
|
||||
"""
|
||||
Create a STIX pattern AST from a pattern string.
|
||||
"""
|
||||
|
||||
pattern_obj = Pattern(pattern)
|
||||
builder = STIXPatternVisitorForSTIX2(module_suffix, module_name)
|
||||
if version == "2.1":
|
||||
pattern_class = Pattern21
|
||||
visitor_class = STIXPatternVisitorForSTIX21
|
||||
else:
|
||||
pattern_class = Pattern20
|
||||
visitor_class = STIXPatternVisitorForSTIX20
|
||||
|
||||
pattern_obj = pattern_class(pattern)
|
||||
builder = visitor_class(module_suffix, module_name)
|
||||
return pattern_obj.visit(builder)
|
||||
|
|
|
@ -551,7 +551,7 @@ class ObservationExpression(_PatternExpression):
|
|||
self.operand = operand
|
||||
|
||||
def __str__(self):
|
||||
return "[%s]" % self.operand
|
||||
return "%s" % self.operand if isinstance(self.operand, (ObservationExpression, _CompoundObservationExpression)) else "[%s]" % self.operand
|
||||
|
||||
|
||||
class _CompoundObservationExpression(_PatternExpression):
|
||||
|
|
|
@ -12,12 +12,15 @@ from six import string_types, text_type
|
|||
import stix2
|
||||
|
||||
from .base import _STIXBase
|
||||
from .core import STIX2_OBJ_MAPS, parse, parse_observable
|
||||
from .exceptions import (
|
||||
CustomContentError, DictionaryKeyError, MissingPropertiesError,
|
||||
MutuallyExclusivePropertiesError,
|
||||
)
|
||||
from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime
|
||||
from .parsing import STIX2_OBJ_MAPS, parse, parse_observable
|
||||
from .utils import (
|
||||
TYPE_21_REGEX, TYPE_REGEX, _get_dict, get_class_hierarchy_names,
|
||||
parse_into_datetime,
|
||||
)
|
||||
|
||||
ID_REGEX_interoperability = re.compile(r"[0-9a-fA-F]{8}-"
|
||||
"[0-9a-fA-F]{4}-"
|
||||
|
@ -90,6 +93,36 @@ def _validate_id(id_, spec_version, required_prefix, interoperability):
|
|||
raise ValueError(ERROR_INVALID_ID.format(id_))
|
||||
|
||||
|
||||
def _validate_type(type_, spec_version):
|
||||
"""
|
||||
Check the STIX type name for correctness, raise an exception if there are
|
||||
errors.
|
||||
|
||||
:param type_: The STIX type name
|
||||
:param spec_version: The STIX specification version to use
|
||||
:raises ValueError: If there are any errors with the identifier
|
||||
"""
|
||||
if spec_version == "2.0":
|
||||
if not re.match(TYPE_REGEX, type_):
|
||||
raise ValueError(
|
||||
"Invalid type name '%s': must only contain the "
|
||||
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." %
|
||||
type_,
|
||||
)
|
||||
else: # 2.1+
|
||||
if not re.match(TYPE_21_REGEX, type_):
|
||||
raise ValueError(
|
||||
"Invalid type name '%s': must only contain the "
|
||||
"characters a-z (lowercase ASCII), 0-9, and hyphen (-) "
|
||||
"and must begin with an a-z character" % type_,
|
||||
)
|
||||
|
||||
if len(type_) < 3 or len(type_) > 250:
|
||||
raise ValueError(
|
||||
"Invalid type name '%s': must be between 3 and 250 characters." % type_,
|
||||
)
|
||||
|
||||
|
||||
class Property(object):
|
||||
"""Represent a property of STIX data type.
|
||||
|
||||
|
@ -241,7 +274,9 @@ class StringProperty(Property):
|
|||
|
||||
class TypeProperty(Property):
|
||||
|
||||
def __init__(self, type):
|
||||
def __init__(self, type, spec_version=stix2.DEFAULT_VERSION):
|
||||
_validate_type(type, spec_version)
|
||||
self.spec_version = spec_version
|
||||
super(TypeProperty, self).__init__(fixed=type)
|
||||
|
||||
|
||||
|
@ -333,12 +368,16 @@ class BooleanProperty(Property):
|
|||
|
||||
class TimestampProperty(Property):
|
||||
|
||||
def __init__(self, precision=None, **kwargs):
|
||||
def __init__(self, precision="any", precision_constraint="exact", **kwargs):
|
||||
self.precision = precision
|
||||
self.precision_constraint = precision_constraint
|
||||
|
||||
super(TimestampProperty, self).__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
return parse_into_datetime(value, self.precision)
|
||||
return parse_into_datetime(
|
||||
value, self.precision, self.precision_constraint,
|
||||
)
|
||||
|
||||
|
||||
class DictionaryProperty(Property):
|
||||
|
@ -650,7 +689,7 @@ class STIXObjectProperty(Property):
|
|||
def clean(self, value):
|
||||
# Any STIX Object (SDO, SRO, or Marking Definition) can be added to
|
||||
# a bundle with no further checks.
|
||||
if any(x in ('STIXDomainObject', 'STIXRelationshipObject', 'MarkingDefinition')
|
||||
if any(x in ('_DomainObject', '_RelationshipObject', 'MarkingDefinition')
|
||||
for x in get_class_hierarchy_names(value)):
|
||||
# A simple "is this a spec version 2.1+ object" test. For now,
|
||||
# limit 2.0 bundles to 2.0 objects. It's not possible yet to
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
|||
|
||||
import pytest
|
||||
|
||||
from stix2.core import _detect_spec_version
|
||||
from stix2.parsing import _detect_spec_version
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -114,6 +114,42 @@ def stix_objs1():
|
|||
return [ind1, ind2, ind3, ind4, ind5]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stix_objs1_manifests():
|
||||
# Tests against latest medallion (TAXII 2.1)
|
||||
ind1 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
ind2 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
ind3 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.936Z",
|
||||
}
|
||||
ind4 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
ind5 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
return [ind1, ind2, ind3, ind4, ind5]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stix_objs2():
|
||||
ind6 = {
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
import pytest
|
||||
|
||||
import stix2
|
||||
from stix2 import core, exceptions
|
||||
|
||||
from .constants import IDENTITY_ID
|
||||
|
||||
BUNDLE = {
|
||||
"type": "bundle",
|
||||
"spec_version": "2.0",
|
||||
"id": "bundle--00000000-0000-4000-8000-000000000007",
|
||||
"objects": [
|
||||
{
|
||||
"type": "indicator",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"valid_from": "2017-01-01T12:34:56Z",
|
||||
"labels": [
|
||||
"malicious-activity",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "malware",
|
||||
"id": "malware--00000000-0000-4000-8000-000000000003",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"name": "Cryptolocker",
|
||||
"labels": [
|
||||
"ransomware",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "relationship",
|
||||
"id": "relationship--00000000-0000-4000-8000-000000000005",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"target_ref": "malware--9c4638ec-f1de-4ddb-abf4-1b760417654e",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_dict_to_stix2_bundle_with_version():
|
||||
with pytest.raises(exceptions.ExtraPropertiesError) as excinfo:
|
||||
core.dict_to_stix2(BUNDLE, version='2.1')
|
||||
|
||||
assert str(excinfo.value) == "Unexpected properties for Bundle: (spec_version)."
|
||||
|
||||
|
||||
def test_parse_observable_with_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = core.parse_observable(observable, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="The default version is no longer 2.0", condition=stix2.DEFAULT_VERSION != "2.0")
|
||||
def test_parse_observable_with_no_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = core.parse_observable(observable)
|
||||
v = 'v20'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
def test_register_object_with_version():
|
||||
bundle = core.dict_to_stix2(BUNDLE, version='2.0')
|
||||
core._register_object(bundle.objects[0].__class__, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert bundle.objects[0].type in core.STIX2_OBJ_MAPS[v]['objects']
|
||||
# spec_version is not in STIX 2.0, and is required in 2.1, so this
|
||||
# suffices as a test for a STIX 2.0 object.
|
||||
assert "spec_version" not in bundle.objects[0]
|
||||
|
||||
|
||||
def test_register_marking_with_version():
|
||||
core._register_marking(stix2.v20.TLP_WHITE.__class__, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert stix2.v20.TLP_WHITE.definition._type in core.STIX2_OBJ_MAPS[v]['markings']
|
||||
assert v in str(stix2.v20.TLP_WHITE.__class__)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="The default version is no longer 2.0", condition=stix2.DEFAULT_VERSION != "2.0")
|
||||
def test_register_marking_with_no_version():
|
||||
# Uses default version (2.0 in this case)
|
||||
core._register_marking(stix2.v20.TLP_WHITE.__class__)
|
||||
v = 'v20'
|
||||
|
||||
assert stix2.v20.TLP_WHITE.definition._type in core.STIX2_OBJ_MAPS[v]['markings']
|
||||
assert v in str(stix2.v20.TLP_WHITE.__class__)
|
||||
|
||||
|
||||
def test_register_observable_with_version():
|
||||
observed_data = stix2.v20.ObservedData(
|
||||
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||
created_by_ref=IDENTITY_ID,
|
||||
created="2016-04-06T19:58:16.000Z",
|
||||
modified="2016-04-06T19:58:16.000Z",
|
||||
first_observed="2015-12-21T19:00:00Z",
|
||||
last_observed="2015-12-21T19:00:00Z",
|
||||
number_observed=50,
|
||||
objects={
|
||||
"0": {
|
||||
"name": "foo.exe",
|
||||
"type": "file",
|
||||
"extensions": {
|
||||
"ntfs-ext": {
|
||||
"alternate_data_streams": [
|
||||
{
|
||||
"name": "second.stream",
|
||||
"size": 25536,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"1": {
|
||||
"type": "directory",
|
||||
"path": "/usr/home",
|
||||
"contains_refs": ["0"],
|
||||
},
|
||||
},
|
||||
)
|
||||
core._register_observable(observed_data.objects['0'].__class__, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert observed_data.objects['0'].type in core.STIX2_OBJ_MAPS[v]['observables']
|
||||
assert v in str(observed_data.objects['0'].__class__)
|
||||
|
||||
|
||||
def test_register_observable_extension_with_version():
|
||||
observed_data = stix2.v20.ObservedData(
|
||||
id="observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf",
|
||||
created_by_ref=IDENTITY_ID,
|
||||
created="2016-04-06T19:58:16.000Z",
|
||||
modified="2016-04-06T19:58:16.000Z",
|
||||
first_observed="2015-12-21T19:00:00Z",
|
||||
last_observed="2015-12-21T19:00:00Z",
|
||||
number_observed=50,
|
||||
objects={
|
||||
"0": {
|
||||
"name": "foo.exe",
|
||||
"type": "file",
|
||||
"extensions": {
|
||||
"ntfs-ext": {
|
||||
"alternate_data_streams": [
|
||||
{
|
||||
"name": "second.stream",
|
||||
"size": 25536,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"1": {
|
||||
"type": "directory",
|
||||
"path": "/usr/home",
|
||||
"contains_refs": ["0"],
|
||||
},
|
||||
},
|
||||
)
|
||||
core._register_observable_extension(observed_data.objects['0'], observed_data.objects['0'].extensions['ntfs-ext'].__class__, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert observed_data.objects['0'].type in core.STIX2_OBJ_MAPS[v]['observables']
|
||||
assert v in str(observed_data.objects['0'].__class__)
|
||||
|
||||
assert observed_data.objects['0'].extensions['ntfs-ext']._type in core.STIX2_OBJ_MAPS[v]['observable-extensions']['file']
|
||||
assert v in str(observed_data.objects['0'].extensions['ntfs-ext'].__class__)
|
|
@ -1,9 +1,10 @@
|
|||
import pytest
|
||||
|
||||
import stix2
|
||||
from stix2 import parsing
|
||||
import stix2.v20
|
||||
|
||||
from ...exceptions import InvalidValueError
|
||||
from ...exceptions import DuplicateRegistrationError, InvalidValueError
|
||||
from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID
|
||||
|
||||
IDENTITY_CUSTOM_PROP = stix2.v20.Identity(
|
||||
|
@ -449,7 +450,7 @@ def test_custom_observable_raises_exception():
|
|||
|
||||
def test_custom_observable_object_no_init_1():
|
||||
@stix2.v20.CustomObservable(
|
||||
'x-new-observable', [
|
||||
'x-new-observable-1', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
|
@ -482,7 +483,7 @@ def test_custom_observable_object_invalid_type_name():
|
|||
)
|
||||
class NewObs(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid observable type name 'x':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v20.CustomObservable(
|
||||
|
@ -492,7 +493,7 @@ def test_custom_observable_object_invalid_type_name():
|
|||
)
|
||||
class NewObs2(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid observable type name 'x_new_obs':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x_new_obs':" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_observable_object_invalid_ref_property():
|
||||
|
@ -807,7 +808,7 @@ def test_custom_extension_invalid_type_name():
|
|||
)
|
||||
class FooExtension():
|
||||
pass # pragma: no cover
|
||||
assert "Invalid extension type name 'x':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v20.CustomExtension(
|
||||
|
@ -817,7 +818,7 @@ def test_custom_extension_invalid_type_name():
|
|||
)
|
||||
class BlaExtension():
|
||||
pass # pragma: no cover
|
||||
assert "Invalid extension type name 'x_new_ext':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x_new_ext':" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_extension_no_properties():
|
||||
|
@ -967,9 +968,8 @@ def test_register_custom_object():
|
|||
class CustomObject2(object):
|
||||
_type = 'awesome-object'
|
||||
|
||||
stix2.core._register_object(CustomObject2, version="2.0")
|
||||
# Note that we will always check against newest OBJ_MAP.
|
||||
assert (CustomObject2._type, CustomObject2) in stix2.v20.OBJ_MAP.items()
|
||||
with pytest.raises(ValueError):
|
||||
stix2.parsing._register_object(CustomObject2, version="2.0")
|
||||
|
||||
|
||||
def test_extension_property_location():
|
||||
|
@ -1011,3 +1011,123 @@ def test_custom_object_nested_dictionary(data):
|
|||
)
|
||||
|
||||
assert data == str(example)
|
||||
|
||||
|
||||
@stix2.v20.CustomObject(
|
||||
'x-new-type-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
('property2', stix2.properties.IntegerProperty()),
|
||||
],
|
||||
)
|
||||
class NewType2(object):
|
||||
pass
|
||||
|
||||
|
||||
def test_register_custom_object_with_version():
|
||||
custom_obj_1 = {
|
||||
"type": "x-new-type-2",
|
||||
"id": "x-new-type-2--00000000-0000-4000-8000-000000000007",
|
||||
}
|
||||
|
||||
cust_obj_1 = parsing.dict_to_stix2(custom_obj_1, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert cust_obj_1.type in parsing.STIX2_OBJ_MAPS[v]['objects']
|
||||
# spec_version is not in STIX 2.0, and is required in 2.1, so this
|
||||
# suffices as a test for a STIX 2.0 object.
|
||||
assert "spec_version" not in cust_obj_1
|
||||
|
||||
|
||||
def test_register_duplicate_object_with_version():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v20.CustomObject(
|
||||
'x-new-type-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
('property2', stix2.properties.IntegerProperty()),
|
||||
],
|
||||
)
|
||||
class NewType2(object):
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
||||
|
||||
@stix2.v20.CustomObservable(
|
||||
'x-new-observable-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
class NewObservable2(object):
|
||||
pass
|
||||
|
||||
|
||||
def test_register_observable_with_version():
|
||||
custom_obs = NewObservable2(property1="Test Observable")
|
||||
v = 'v20'
|
||||
|
||||
assert custom_obs.type in parsing.STIX2_OBJ_MAPS[v]['observables']
|
||||
|
||||
|
||||
def test_register_duplicate_observable_with_version():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v20.CustomObservable(
|
||||
'x-new-observable-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
class NewObservable2(object):
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_register_marking_with_version():
|
||||
@stix2.v20.CustomMarking(
|
||||
'x-new-obj-2', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj2():
|
||||
pass
|
||||
v = 'v20'
|
||||
|
||||
no = NewObj2(property1='something')
|
||||
assert no._type in parsing.STIX2_OBJ_MAPS[v]['markings']
|
||||
|
||||
|
||||
def test_register_observable_extension_with_version():
|
||||
@stix2.v20.CustomExtension(
|
||||
stix2.v20.UserAccount, 'some-extension-2', [
|
||||
('keys', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class SomeCustomExtension2:
|
||||
pass
|
||||
|
||||
v = 'v20'
|
||||
example = SomeCustomExtension2(keys='test123')
|
||||
|
||||
assert example._type in parsing.STIX2_OBJ_MAPS[v]['observable-extensions']['user-account']
|
||||
|
||||
|
||||
def test_register_duplicate_observable_extension():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v20.CustomExtension(
|
||||
stix2.v20.UserAccount, 'some-extension-2', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
('property2', stix2.properties.IntegerProperty()),
|
||||
],
|
||||
)
|
||||
class NewExtension2():
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_register_duplicate_marking():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v20.CustomMarking(
|
||||
'x-new-obj-2', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj2():
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
|
|
@ -635,7 +635,7 @@ def test_filesystem_object_with_custom_property_in_bundle(fs_store):
|
|||
|
||||
def test_filesystem_custom_object(fs_store):
|
||||
@stix2.v20.CustomObject(
|
||||
'x-new-obj', [
|
||||
'x-new-obj-2', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
|
@ -650,7 +650,7 @@ def test_filesystem_custom_object(fs_store):
|
|||
assert newobj_r["property1"] == 'something'
|
||||
|
||||
# remove dir
|
||||
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj"), True)
|
||||
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj-2"), True)
|
||||
|
||||
|
||||
def test_relationships(rel_fs_store):
|
||||
|
|
|
@ -329,7 +329,7 @@ def test_memory_store_object_with_custom_property_in_bundle(mem_store):
|
|||
|
||||
def test_memory_store_custom_object(mem_store):
|
||||
@CustomObject(
|
||||
'x-new-obj', [
|
||||
'x-new-obj-3', [
|
||||
('property1', properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -4,11 +4,13 @@ from medallion.filters.basic_filter import BasicFilter
|
|||
import pytest
|
||||
from requests.models import Response
|
||||
import six
|
||||
from taxii2client import Collection, _filter_kwargs_to_query_params
|
||||
from taxii2client.common import _filter_kwargs_to_query_params
|
||||
from taxii2client.v20 import Collection
|
||||
|
||||
import stix2
|
||||
from stix2.datastore import DataSourceError
|
||||
from stix2.datastore.filters import Filter
|
||||
from stix2.utils import get_timestamp
|
||||
|
||||
COLLECTION_URL = 'https://example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/'
|
||||
|
||||
|
@ -21,6 +23,7 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
url, collection_info=collection_info,
|
||||
)
|
||||
self.objects = []
|
||||
self.manifests = []
|
||||
|
||||
def add_objects(self, bundle):
|
||||
self._verify_can_write()
|
||||
|
@ -28,6 +31,14 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
bundle = json.loads(bundle, encoding='utf-8')
|
||||
for object in bundle.get("objects", []):
|
||||
self.objects.append(object)
|
||||
self.manifests.append(
|
||||
{
|
||||
"date_added": get_timestamp(),
|
||||
"id": object["id"],
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": object.get("modified", object.get("created", get_timestamp())),
|
||||
},
|
||||
)
|
||||
|
||||
def get_objects(self, **filter_kwargs):
|
||||
self._verify_can_read()
|
||||
|
@ -37,8 +48,9 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
objs = full_filter.process_filter(
|
||||
self.objects,
|
||||
("id", "type", "version"),
|
||||
[],
|
||||
)
|
||||
self.manifests,
|
||||
100,
|
||||
)[0]
|
||||
if objs:
|
||||
return stix2.v20.Bundle(objects=objs)
|
||||
else:
|
||||
|
@ -58,8 +70,9 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
filtered_objects = full_filter.process_filter(
|
||||
objects,
|
||||
("version",),
|
||||
[],
|
||||
)
|
||||
self.manifests,
|
||||
100,
|
||||
)[0]
|
||||
else:
|
||||
filtered_objects = []
|
||||
if filtered_objects:
|
||||
|
@ -71,7 +84,7 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def collection(stix_objs1):
|
||||
def collection(stix_objs1, stix_objs1_manifests):
|
||||
mock = MockTAXIICollectionEndpoint(
|
||||
COLLECTION_URL, {
|
||||
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
|
||||
|
@ -86,11 +99,12 @@ def collection(stix_objs1):
|
|||
)
|
||||
|
||||
mock.objects.extend(stix_objs1)
|
||||
mock.manifests.extend(stix_objs1_manifests)
|
||||
return mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def collection_no_rw_access(stix_objs1):
|
||||
def collection_no_rw_access(stix_objs1, stix_objs1_manifests):
|
||||
mock = MockTAXIICollectionEndpoint(
|
||||
COLLECTION_URL, {
|
||||
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
|
||||
|
@ -105,6 +119,7 @@ def collection_no_rw_access(stix_objs1):
|
|||
)
|
||||
|
||||
mock.objects.extend(stix_objs1)
|
||||
mock.manifests.extend(stix_objs1_manifests)
|
||||
return mock
|
||||
|
||||
|
||||
|
|
|
@ -249,14 +249,12 @@ def test_not_registered_marking_raises_exception():
|
|||
|
||||
|
||||
def test_marking_wrong_type_construction():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
with pytest.raises(ValueError):
|
||||
# Test passing wrong type for properties.
|
||||
@stix2.v20.CustomMarking('x-new-marking-type2', ("a", "b"))
|
||||
class NewObject3(object):
|
||||
pass
|
||||
|
||||
assert str(excinfo.value) == "Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]"
|
||||
|
||||
|
||||
def test_campaign_add_markings():
|
||||
campaign = stix2.v20.Campaign(
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import pytest
|
||||
|
||||
import stix2
|
||||
from stix2 import exceptions, parsing
|
||||
|
||||
BUNDLE = {
|
||||
"type": "bundle",
|
||||
"spec_version": "2.0",
|
||||
"id": "bundle--00000000-0000-4000-8000-000000000007",
|
||||
"objects": [
|
||||
{
|
||||
"type": "indicator",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"valid_from": "2017-01-01T12:34:56Z",
|
||||
"labels": [
|
||||
"malicious-activity",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "malware",
|
||||
"id": "malware--00000000-0000-4000-8000-000000000003",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"name": "Cryptolocker",
|
||||
"labels": [
|
||||
"ransomware",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "relationship",
|
||||
"id": "relationship--00000000-0000-4000-8000-000000000005",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"target_ref": "malware--9c4638ec-f1de-4ddb-abf4-1b760417654e",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_dict_to_stix2_bundle_with_version():
|
||||
with pytest.raises(exceptions.ExtraPropertiesError) as excinfo:
|
||||
parsing.dict_to_stix2(BUNDLE, version='2.1')
|
||||
|
||||
assert str(excinfo.value) == "Unexpected properties for Bundle: (spec_version)."
|
||||
|
||||
|
||||
def test_parse_observable_with_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = parsing.parse_observable(observable, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="The default version is no longer 2.0", condition=stix2.DEFAULT_VERSION != "2.0")
|
||||
def test_parse_observable_with_no_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = parsing.parse_observable(observable)
|
||||
v = 'v20'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
def test_register_marking_with_version():
|
||||
parsing._register_marking(stix2.v20.TLP_WHITE.__class__, version='2.0')
|
||||
v = 'v20'
|
||||
|
||||
assert stix2.v20.TLP_WHITE.definition._type in parsing.STIX2_OBJ_MAPS[v]['markings']
|
||||
assert v in str(stix2.v20.TLP_WHITE.__class__)
|
|
@ -494,13 +494,14 @@ def test_make_constant_already_a_constant():
|
|||
|
||||
|
||||
def test_parsing_comparison_expression():
|
||||
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']")
|
||||
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']", version="2.0")
|
||||
assert str(patt_obj) == "[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']"
|
||||
|
||||
|
||||
def test_parsing_qualified_expression():
|
||||
patt_obj = create_pattern_object(
|
||||
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS",
|
||||
version="2.0",
|
||||
)
|
||||
assert str(
|
||||
patt_obj,
|
||||
|
@ -508,5 +509,5 @@ def test_parsing_qualified_expression():
|
|||
|
||||
|
||||
def test_list_constant():
|
||||
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]")
|
||||
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]", version="2.0")
|
||||
assert str(patt_obj) == "[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]"
|
||||
|
|
|
@ -392,7 +392,7 @@ def test_dictionary_property_invalid(d):
|
|||
|
||||
def test_property_list_of_dictionary():
|
||||
@stix2.v20.CustomObject(
|
||||
'x-new-obj', [
|
||||
'x-new-obj-4', [
|
||||
('property1', ListProperty(DictionaryProperty(spec_version="2.0"), required=True)),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -135,6 +135,42 @@ def stix_objs1():
|
|||
return [ind1, ind2, ind3, ind4, ind5]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stix_objs1_manifests():
|
||||
# Tests against latest medallion (TAXII 2.1)
|
||||
ind1 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
ind2 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
ind3 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.936Z",
|
||||
}
|
||||
ind4 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
ind5 = {
|
||||
"date_added": "2017-01-27T13:49:53.935Z",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000002",
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": "2017-01-27T13:49:53.935Z",
|
||||
}
|
||||
return [ind1, ind2, ind3, ind4, ind5]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stix_objs2():
|
||||
ind6 = {
|
||||
|
|
|
@ -88,8 +88,8 @@ def test_attack_pattern_invalid_labels():
|
|||
def test_overly_precise_timestamps():
|
||||
ap = stix2.v21.AttackPattern(
|
||||
id=ATTACK_PATTERN_ID,
|
||||
created="2016-05-12T08:17:27.0000342Z",
|
||||
modified="2016-05-12T08:17:27.000287Z",
|
||||
created="2016-05-12T08:17:27.000000342Z",
|
||||
modified="2016-05-12T08:17:27.000000287Z",
|
||||
name="Spear Phishing",
|
||||
external_references=[{
|
||||
"source_name": "capec",
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
import pytest
|
||||
|
||||
import stix2
|
||||
from stix2 import core, exceptions
|
||||
|
||||
from .constants import IDENTITY_ID, OBSERVED_DATA_ID
|
||||
|
||||
BUNDLE = {
|
||||
"type": "bundle",
|
||||
"id": "bundle--00000000-0000-4000-8000-000000000007",
|
||||
"objects": [
|
||||
{
|
||||
"type": "indicator",
|
||||
"spec_version": "2.1",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"pattern_type": "stix",
|
||||
"valid_from": "2017-01-01T12:34:56Z",
|
||||
"indicator_types": [
|
||||
"malicious-activity",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "malware",
|
||||
"spec_version": "2.1",
|
||||
"id": "malware--00000000-0000-4000-8000-000000000003",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"name": "Cryptolocker",
|
||||
"malware_types": [
|
||||
"ransomware",
|
||||
],
|
||||
"is_family": False,
|
||||
},
|
||||
{
|
||||
"type": "relationship",
|
||||
"spec_version": "2.1",
|
||||
"id": "relationship--00000000-0000-4000-8000-000000000005",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"target_ref": "malware--9c4638ec-f1de-4ddb-abf4-1b760417654e",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_dict_to_stix2_bundle_with_version():
|
||||
with pytest.raises(exceptions.InvalidValueError) as excinfo:
|
||||
core.dict_to_stix2(BUNDLE, version='2.0')
|
||||
|
||||
msg = "Invalid value for Bundle 'objects': Spec version 2.0 bundles don't yet support containing objects of a different spec version."
|
||||
assert str(excinfo.value) == msg
|
||||
|
||||
|
||||
def test_parse_observable_with_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = core.parse_observable(observable, version='2.1')
|
||||
v = 'v21'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="The default version is not 2.1", condition=stix2.DEFAULT_VERSION != "2.1")
|
||||
def test_parse_observable_with_no_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = core.parse_observable(observable)
|
||||
v = 'v21'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
def test_register_object_with_version():
|
||||
bundle = core.dict_to_stix2(BUNDLE, version='2.1')
|
||||
core._register_object(bundle.objects[0].__class__)
|
||||
v = 'v21'
|
||||
|
||||
assert bundle.objects[0].type in core.STIX2_OBJ_MAPS[v]['objects']
|
||||
assert bundle.objects[0].spec_version == "2.1"
|
||||
|
||||
|
||||
def test_register_marking_with_version():
|
||||
core._register_marking(stix2.v21.TLP_WHITE.__class__, version='2.1')
|
||||
v = 'v21'
|
||||
|
||||
assert stix2.v21.TLP_WHITE.definition._type in core.STIX2_OBJ_MAPS[v]['markings']
|
||||
assert v in str(stix2.v21.TLP_WHITE.__class__)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="The default version is not 2.1", condition=stix2.DEFAULT_VERSION != "2.1")
|
||||
def test_register_marking_with_no_version():
|
||||
# Uses default version (2.0 in this case)
|
||||
core._register_marking(stix2.v21.TLP_WHITE.__class__)
|
||||
v = 'v21'
|
||||
|
||||
assert stix2.v21.TLP_WHITE.definition._type in core.STIX2_OBJ_MAPS[v]['markings']
|
||||
assert v in str(stix2.v21.TLP_WHITE.__class__)
|
||||
|
||||
|
||||
def test_register_observable_with_default_version():
|
||||
observed_data = stix2.v21.ObservedData(
|
||||
id=OBSERVED_DATA_ID,
|
||||
created_by_ref=IDENTITY_ID,
|
||||
created="2016-04-06T19:58:16.000Z",
|
||||
modified="2016-04-06T19:58:16.000Z",
|
||||
first_observed="2015-12-21T19:00:00Z",
|
||||
last_observed="2015-12-21T19:00:00Z",
|
||||
number_observed=50,
|
||||
objects={
|
||||
"0": {
|
||||
"name": "foo.exe",
|
||||
"type": "file",
|
||||
"extensions": {
|
||||
"ntfs-ext": {
|
||||
"alternate_data_streams": [
|
||||
{
|
||||
"name": "second.stream",
|
||||
"size": 25536,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"1": {
|
||||
"type": "directory",
|
||||
"path": "/usr/home",
|
||||
"contains_refs": ["file--420bc087-8b53-5ae9-8210-20d27d5e96c8"],
|
||||
},
|
||||
},
|
||||
)
|
||||
core._register_observable(observed_data.objects['0'].__class__)
|
||||
v = 'v21'
|
||||
|
||||
assert observed_data.objects['0'].type in core.STIX2_OBJ_MAPS[v]['observables']
|
||||
assert v in str(observed_data.objects['0'].__class__)
|
||||
|
||||
|
||||
def test_register_observable_extension_with_default_version():
|
||||
observed_data = stix2.v21.ObservedData(
|
||||
id=OBSERVED_DATA_ID,
|
||||
created_by_ref=IDENTITY_ID,
|
||||
created="2016-04-06T19:58:16.000Z",
|
||||
modified="2016-04-06T19:58:16.000Z",
|
||||
first_observed="2015-12-21T19:00:00Z",
|
||||
last_observed="2015-12-21T19:00:00Z",
|
||||
number_observed=50,
|
||||
objects={
|
||||
"0": {
|
||||
"name": "foo.exe",
|
||||
"type": "file",
|
||||
"extensions": {
|
||||
"ntfs-ext": {
|
||||
"alternate_data_streams": [
|
||||
{
|
||||
"name": "second.stream",
|
||||
"size": 25536,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"1": {
|
||||
"type": "directory",
|
||||
"path": "/usr/home",
|
||||
"contains_refs": ["file--420bc087-8b53-5ae9-8210-20d27d5e96c8"],
|
||||
},
|
||||
},
|
||||
)
|
||||
core._register_observable_extension(observed_data.objects['0'], observed_data.objects['0'].extensions['ntfs-ext'].__class__)
|
||||
v = 'v21'
|
||||
|
||||
assert observed_data.objects['0'].type in core.STIX2_OBJ_MAPS[v]['observables']
|
||||
assert v in str(observed_data.objects['0'].__class__)
|
||||
|
||||
assert observed_data.objects['0'].extensions['ntfs-ext']._type in core.STIX2_OBJ_MAPS[v]['observable-extensions']['file']
|
||||
assert v in str(observed_data.objects['0'].extensions['ntfs-ext'].__class__)
|
|
@ -6,9 +6,11 @@ import stix2
|
|||
import stix2.base
|
||||
import stix2.v21
|
||||
|
||||
from ...exceptions import InvalidValueError
|
||||
from ...exceptions import DuplicateRegistrationError, InvalidValueError
|
||||
from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID
|
||||
|
||||
# Custom Properties in SDOs
|
||||
|
||||
IDENTITY_CUSTOM_PROP = stix2.v21.Identity(
|
||||
name="John Smith",
|
||||
identity_class="individual",
|
||||
|
@ -18,6 +20,18 @@ IDENTITY_CUSTOM_PROP = stix2.v21.Identity(
|
|||
|
||||
|
||||
def test_identity_custom_property():
|
||||
identity = stix2.v21.Identity(
|
||||
id=IDENTITY_ID,
|
||||
created="2015-12-21T19:59:11Z",
|
||||
modified="2015-12-21T19:59:11Z",
|
||||
name="John Smith",
|
||||
identity_class="individual",
|
||||
custom_properties={
|
||||
"foo": "bar",
|
||||
},
|
||||
)
|
||||
assert identity.foo == "bar"
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
stix2.v21.Identity(
|
||||
id=IDENTITY_ID,
|
||||
|
@ -43,17 +57,47 @@ def test_identity_custom_property():
|
|||
)
|
||||
assert "Unexpected properties for Identity" in str(excinfo.value)
|
||||
|
||||
identity = stix2.v21.Identity(
|
||||
id=IDENTITY_ID,
|
||||
created="2015-12-21T19:59:11Z",
|
||||
modified="2015-12-21T19:59:11Z",
|
||||
name="John Smith",
|
||||
identity_class="individual",
|
||||
custom_properties={
|
||||
"foo": "bar",
|
||||
},
|
||||
)
|
||||
assert identity.foo == "bar"
|
||||
# leading numeric character is illegal in 2.1
|
||||
|
||||
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
||||
stix2.v21.Identity(
|
||||
id=IDENTITY_ID,
|
||||
created="2015-12-21T19:59:11Z",
|
||||
modified="2015-12-21T19:59:11Z",
|
||||
name="John Smith",
|
||||
identity_class="individual",
|
||||
custom_properties={
|
||||
"7foo": "bar",
|
||||
},
|
||||
)
|
||||
assert "must begin with an alpha character." in str(excinfo.value)
|
||||
|
||||
# leading "_" is illegal in 2.1
|
||||
|
||||
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
||||
stix2.v21.Identity(
|
||||
id=IDENTITY_ID,
|
||||
created="2015-12-21T19:59:11Z",
|
||||
modified="2015-12-21T19:59:11Z",
|
||||
name="John Smith",
|
||||
identity_class="individual",
|
||||
custom_properties={
|
||||
"_foo": "bar",
|
||||
},
|
||||
)
|
||||
assert "must begin with an alpha character." in str(excinfo.value)
|
||||
|
||||
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
||||
identity = stix2.v21.Identity(
|
||||
id=IDENTITY_ID,
|
||||
created="2015-12-21T19:59:11Z",
|
||||
modified="2015-12-21T19:59:11Z",
|
||||
name="John Smith",
|
||||
identity_class="individual",
|
||||
_x_foo="bar",
|
||||
allow_custom=True,
|
||||
)
|
||||
assert "must begin with an alpha character." in str(excinfo.value)
|
||||
|
||||
|
||||
def test_identity_custom_property_invalid():
|
||||
|
@ -165,6 +209,8 @@ def test_custom_properties_dict_in_bundled_object():
|
|||
assert bundle.objects[0].x_foo == "bar"
|
||||
assert '"x_foo": "bar"' in str(bundle)
|
||||
|
||||
# Custom properties in SCOs
|
||||
|
||||
|
||||
def test_custom_property_in_observed_data():
|
||||
artifact = stix2.v21.File(
|
||||
|
@ -184,6 +230,18 @@ def test_custom_property_in_observed_data():
|
|||
assert '"x_foo": "bar"' in str(observed_data)
|
||||
|
||||
|
||||
def test_invalid_custom_property_in_observed_data():
|
||||
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
|
||||
stix2.v21.File(
|
||||
custom_properties={"8foo": 1},
|
||||
allow_custom=True,
|
||||
name='test',
|
||||
x_foo='bar',
|
||||
)
|
||||
|
||||
assert "must begin with an alpha character." in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_property_object_in_observable_extension():
|
||||
ntfs = stix2.v21.NTFSExt(
|
||||
allow_custom=True,
|
||||
|
@ -245,6 +303,8 @@ def test_identity_custom_property_revoke():
|
|||
identity = IDENTITY_CUSTOM_PROP.revoke()
|
||||
assert identity.x_foo == "bar"
|
||||
|
||||
# Custom markings
|
||||
|
||||
|
||||
def test_identity_custom_property_edit_markings():
|
||||
marking_obj = stix2.v21.MarkingDefinition(
|
||||
|
@ -267,6 +327,19 @@ def test_identity_custom_property_edit_markings():
|
|||
identity2.clear_markings('x_foo')
|
||||
|
||||
|
||||
def test_invalid_custom_property_in_marking():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomMarking(
|
||||
'x-new-obj', [
|
||||
('9property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj():
|
||||
pass
|
||||
|
||||
assert "must begin with an alpha character." in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_marking_no_init_1():
|
||||
@stix2.v21.CustomMarking(
|
||||
'x-new-obj', [
|
||||
|
@ -293,6 +366,40 @@ def test_custom_marking_no_init_2():
|
|||
assert no2.property1 == 'something'
|
||||
|
||||
|
||||
def test_custom_marking_invalid_type_name():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomMarking(
|
||||
'x', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid type name 'x': " in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomMarking(
|
||||
'x_new_marking', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj2(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid type name 'x_new_marking':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomMarking(
|
||||
'7x-new-marking', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj3(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid type name '7x-new-marking':" in str(excinfo.value)
|
||||
|
||||
# Custom Objects
|
||||
|
||||
|
||||
@stix2.v21.CustomObject(
|
||||
'x-new-type', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
|
@ -374,6 +481,16 @@ def test_custom_object_invalid_type_name():
|
|||
pass # pragma: no cover
|
||||
assert "Invalid type name 'x_new_object':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomObject(
|
||||
'7x-new-object', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj3(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid type name '7x-new-object':" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_parse_custom_object_type():
|
||||
nt_string = """{
|
||||
|
@ -412,6 +529,8 @@ def test_parse_unregistered_custom_object_type_w_allow_custom():
|
|||
custom_obj = stix2.parse(nt_string, version="2.1", allow_custom=True)
|
||||
assert custom_obj["type"] == "x-foobar-observable"
|
||||
|
||||
# Custom SCOs
|
||||
|
||||
|
||||
@stix2.v21.CustomObservable(
|
||||
'x-new-observable', [
|
||||
|
@ -455,7 +574,7 @@ def test_custom_observable_raises_exception():
|
|||
|
||||
def test_custom_observable_object_no_init_1():
|
||||
@stix2.v21.CustomObservable(
|
||||
'x-new-observable', [
|
||||
'x-new-observable-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
|
@ -479,6 +598,18 @@ def test_custom_observable_object_no_init_2():
|
|||
assert no2.property1 == 'something'
|
||||
|
||||
|
||||
def test_invalid_custom_property_in_custom_observable_object():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomObservable(
|
||||
'x-new-sco', [
|
||||
('5property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
class NewObs(object):
|
||||
pass # pragma: no cover
|
||||
assert "must begin with an alpha character." in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_observable_object_invalid_type_name():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomObservable(
|
||||
|
@ -488,7 +619,7 @@ def test_custom_observable_object_invalid_type_name():
|
|||
)
|
||||
class NewObs(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid observable type name 'x':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomObservable(
|
||||
|
@ -498,7 +629,17 @@ def test_custom_observable_object_invalid_type_name():
|
|||
)
|
||||
class NewObs2(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid observable type name 'x_new_obs':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x_new_obs':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomObservable(
|
||||
'7x-new-obs', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
class NewObs3(object):
|
||||
pass # pragma: no cover
|
||||
assert "Invalid type name '7x-new-obs':" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_observable_object_invalid_ref_property():
|
||||
|
@ -736,6 +877,8 @@ def test_custom_observable_object_no_id_contrib_props():
|
|||
assert uuid_obj.variant == uuid.RFC_4122
|
||||
assert uuid_obj.version == 4
|
||||
|
||||
# Custom Extensions
|
||||
|
||||
|
||||
@stix2.v21.CustomExtension(
|
||||
stix2.v21.DomainName, 'x-new-ext', [
|
||||
|
@ -862,7 +1005,7 @@ def test_custom_extension_invalid_type_name():
|
|||
)
|
||||
class FooExtension():
|
||||
pass # pragma: no cover
|
||||
assert "Invalid extension type name 'x':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomExtension(
|
||||
|
@ -872,7 +1015,17 @@ def test_custom_extension_invalid_type_name():
|
|||
)
|
||||
class BlaExtension():
|
||||
pass # pragma: no cover
|
||||
assert "Invalid extension type name 'x_new_ext':" in str(excinfo.value)
|
||||
assert "Invalid type name 'x_new_ext':" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomExtension(
|
||||
stix2.v21.File, '7x-new-ext', {
|
||||
'property1': stix2.properties.StringProperty(required=True),
|
||||
},
|
||||
)
|
||||
class Bla2Extension():
|
||||
pass # pragma: no cover
|
||||
assert "Invalid type name '7x-new-ext':" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_custom_extension_no_properties():
|
||||
|
@ -922,6 +1075,19 @@ def test_custom_extension_no_init_2():
|
|||
assert ne2.property1 == "foobar"
|
||||
|
||||
|
||||
def test_invalid_custom_property_in_extension():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@stix2.v21.CustomExtension(
|
||||
stix2.v21.DomainName, 'x-new3-ext', [
|
||||
('6property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewExt():
|
||||
pass
|
||||
|
||||
assert "must begin with an alpha character." in str(excinfo.value)
|
||||
|
||||
|
||||
def test_parse_observable_with_custom_extension():
|
||||
input_str = """{
|
||||
"type": "domain-name",
|
||||
|
@ -1020,9 +1186,9 @@ def test_register_custom_object():
|
|||
class CustomObject2(object):
|
||||
_type = 'awesome-object'
|
||||
|
||||
stix2.core._register_object(CustomObject2, version="2.1")
|
||||
# Note that we will always check against newest OBJ_MAP.
|
||||
assert (CustomObject2._type, CustomObject2) in stix2.v21.OBJ_MAP.items()
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
stix2.parsing._register_object(CustomObject2, version="2.1")
|
||||
assert '@CustomObject decorator' in str(excinfo)
|
||||
|
||||
|
||||
def test_extension_property_location():
|
||||
|
@ -1065,3 +1231,110 @@ def test_custom_object_nested_dictionary(data):
|
|||
)
|
||||
|
||||
assert data == str(example)
|
||||
|
||||
|
||||
@stix2.v21.CustomObject(
|
||||
'x-new-type-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
('property2', stix2.properties.IntegerProperty()),
|
||||
],
|
||||
)
|
||||
class NewType3(object):
|
||||
pass
|
||||
|
||||
|
||||
def test_register_custom_object_with_version():
|
||||
custom_obj_1 = {
|
||||
"type": "x-new-type-2",
|
||||
"id": "x-new-type-2--00000000-0000-4000-8000-000000000007",
|
||||
"spec_version": "2.1",
|
||||
}
|
||||
|
||||
cust_obj_1 = stix2.parsing.dict_to_stix2(custom_obj_1, version='2.1')
|
||||
v = 'v21'
|
||||
|
||||
assert cust_obj_1.type in stix2.parsing.STIX2_OBJ_MAPS[v]['objects']
|
||||
assert cust_obj_1.spec_version == "2.1"
|
||||
|
||||
|
||||
def test_register_duplicate_object_with_version():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v21.CustomObject(
|
||||
'x-new-type-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
('property2', stix2.properties.IntegerProperty()),
|
||||
],
|
||||
)
|
||||
class NewType2(object):
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
||||
|
||||
@stix2.v21.CustomObservable(
|
||||
'x-new-observable-3', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
class NewObservable3(object):
|
||||
pass
|
||||
|
||||
|
||||
def test_register_observable():
|
||||
custom_obs = NewObservable3(property1="Test Observable")
|
||||
v = 'v21'
|
||||
|
||||
assert custom_obs.type in stix2.parsing.STIX2_OBJ_MAPS[v]['observables']
|
||||
|
||||
|
||||
def test_register_duplicate_observable():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v21.CustomObservable(
|
||||
'x-new-observable-2', [
|
||||
('property1', stix2.properties.StringProperty()),
|
||||
],
|
||||
)
|
||||
class NewObservable2(object):
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_register_observable_custom_extension():
|
||||
@stix2.v21.CustomExtension(
|
||||
stix2.v21.DomainName, 'x-new-2-ext', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
('property2', stix2.properties.IntegerProperty()),
|
||||
],
|
||||
)
|
||||
class NewExtension2():
|
||||
pass
|
||||
|
||||
example = NewExtension2(property1="Hi there")
|
||||
v = 'v21'
|
||||
|
||||
assert 'domain-name' in stix2.parsing.STIX2_OBJ_MAPS[v]['observables']
|
||||
assert example._type in stix2.parsing.STIX2_OBJ_MAPS[v]['observable-extensions']['domain-name']
|
||||
|
||||
|
||||
def test_register_duplicate_observable_extension():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v21.CustomExtension(
|
||||
stix2.v21.DomainName, 'x-new-2-ext', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
('property2', stix2.properties.IntegerProperty()),
|
||||
],
|
||||
)
|
||||
class NewExtension2():
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_register_duplicate_marking():
|
||||
with pytest.raises(DuplicateRegistrationError) as excinfo:
|
||||
@stix2.v21.CustomMarking(
|
||||
'x-new-obj', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
class NewObj2():
|
||||
pass
|
||||
assert "cannot be registered again" in str(excinfo.value)
|
||||
|
|
|
@ -656,7 +656,7 @@ def test_filesystem_object_with_custom_property_in_bundle(fs_store):
|
|||
|
||||
def test_filesystem_custom_object(fs_store):
|
||||
@stix2.v21.CustomObject(
|
||||
'x-new-obj', [
|
||||
'x-new-obj-2', [
|
||||
('property1', stix2.properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
|
@ -671,7 +671,7 @@ def test_filesystem_custom_object(fs_store):
|
|||
assert newobj_r["property1"] == 'something'
|
||||
|
||||
# remove dir
|
||||
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj"), True)
|
||||
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj-2"), True)
|
||||
|
||||
|
||||
def test_relationships(rel_fs_store):
|
||||
|
|
|
@ -344,7 +344,7 @@ def test_memory_store_object_with_custom_property_in_bundle(mem_store):
|
|||
|
||||
def test_memory_store_custom_object(mem_store):
|
||||
@CustomObject(
|
||||
'x-new-obj', [
|
||||
'x-new-obj-3', [
|
||||
('property1', properties.StringProperty(required=True)),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -4,11 +4,13 @@ from medallion.filters.basic_filter import BasicFilter
|
|||
import pytest
|
||||
from requests.models import Response
|
||||
import six
|
||||
from taxii2client import Collection, _filter_kwargs_to_query_params
|
||||
from taxii2client.common import _filter_kwargs_to_query_params
|
||||
from taxii2client.v21 import Collection
|
||||
|
||||
import stix2
|
||||
from stix2.datastore import DataSourceError
|
||||
from stix2.datastore.filters import Filter
|
||||
from stix2.utils import get_timestamp
|
||||
|
||||
COLLECTION_URL = 'https://example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/'
|
||||
|
||||
|
@ -21,6 +23,7 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
url, collection_info=collection_info,
|
||||
)
|
||||
self.objects = []
|
||||
self.manifests = []
|
||||
|
||||
def add_objects(self, bundle):
|
||||
self._verify_can_write()
|
||||
|
@ -28,6 +31,14 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
bundle = json.loads(bundle, encoding='utf-8')
|
||||
for object in bundle.get("objects", []):
|
||||
self.objects.append(object)
|
||||
self.manifests.append(
|
||||
{
|
||||
"date_added": get_timestamp(),
|
||||
"id": object["id"],
|
||||
"media_type": "application/stix+json;version=2.1",
|
||||
"version": object.get("modified", object.get("created", get_timestamp())),
|
||||
},
|
||||
)
|
||||
|
||||
def get_objects(self, **filter_kwargs):
|
||||
self._verify_can_read()
|
||||
|
@ -37,8 +48,9 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
objs = full_filter.process_filter(
|
||||
self.objects,
|
||||
("id", "type", "version"),
|
||||
[],
|
||||
)
|
||||
self.manifests,
|
||||
100,
|
||||
)[0]
|
||||
if objs:
|
||||
return stix2.v21.Bundle(objects=objs)
|
||||
else:
|
||||
|
@ -58,8 +70,9 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
filtered_objects = full_filter.process_filter(
|
||||
objects,
|
||||
("version",),
|
||||
[],
|
||||
)
|
||||
self.manifests,
|
||||
100,
|
||||
)[0]
|
||||
else:
|
||||
filtered_objects = []
|
||||
if filtered_objects:
|
||||
|
@ -71,7 +84,7 @@ class MockTAXIICollectionEndpoint(Collection):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def collection(stix_objs1):
|
||||
def collection(stix_objs1, stix_objs1_manifests):
|
||||
mock = MockTAXIICollectionEndpoint(
|
||||
COLLECTION_URL, {
|
||||
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
|
||||
|
@ -86,11 +99,12 @@ def collection(stix_objs1):
|
|||
)
|
||||
|
||||
mock.objects.extend(stix_objs1)
|
||||
mock.manifests.extend(stix_objs1_manifests)
|
||||
return mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def collection_no_rw_access(stix_objs1):
|
||||
def collection_no_rw_access(stix_objs1, stix_objs1_manifests):
|
||||
mock = MockTAXIICollectionEndpoint(
|
||||
COLLECTION_URL, {
|
||||
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
|
||||
|
@ -105,6 +119,7 @@ def collection_no_rw_access(stix_objs1):
|
|||
)
|
||||
|
||||
mock.objects.extend(stix_objs1)
|
||||
mock.manifests.extend(stix_objs1_manifests)
|
||||
return mock
|
||||
|
||||
|
||||
|
|
|
@ -277,14 +277,12 @@ def test_not_registered_marking_raises_exception():
|
|||
|
||||
|
||||
def test_marking_wrong_type_construction():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
with pytest.raises(ValueError):
|
||||
# Test passing wrong type for properties.
|
||||
@stix2.v21.CustomMarking('x-new-marking-type2', ("a", "b"))
|
||||
class NewObject3(object):
|
||||
pass
|
||||
|
||||
assert str(excinfo.value) == "Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]"
|
||||
|
||||
|
||||
def test_campaign_add_markings():
|
||||
campaign = stix2.v21.Campaign(
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import pytest
|
||||
|
||||
import stix2
|
||||
from stix2 import exceptions, parsing
|
||||
|
||||
BUNDLE = {
|
||||
"type": "bundle",
|
||||
"id": "bundle--00000000-0000-4000-8000-000000000007",
|
||||
"objects": [
|
||||
{
|
||||
"type": "indicator",
|
||||
"spec_version": "2.1",
|
||||
"id": "indicator--00000000-0000-4000-8000-000000000001",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
|
||||
"pattern_type": "stix",
|
||||
"valid_from": "2017-01-01T12:34:56Z",
|
||||
"indicator_types": [
|
||||
"malicious-activity",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "malware",
|
||||
"spec_version": "2.1",
|
||||
"id": "malware--00000000-0000-4000-8000-000000000003",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"name": "Cryptolocker",
|
||||
"malware_types": [
|
||||
"ransomware",
|
||||
],
|
||||
"is_family": False,
|
||||
},
|
||||
{
|
||||
"type": "relationship",
|
||||
"spec_version": "2.1",
|
||||
"id": "relationship--00000000-0000-4000-8000-000000000005",
|
||||
"created": "2017-01-01T12:34:56.000Z",
|
||||
"modified": "2017-01-01T12:34:56.000Z",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7",
|
||||
"target_ref": "malware--9c4638ec-f1de-4ddb-abf4-1b760417654e",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_dict_to_stix2_bundle_with_version():
|
||||
with pytest.raises(exceptions.InvalidValueError) as excinfo:
|
||||
parsing.dict_to_stix2(BUNDLE, version='2.0')
|
||||
|
||||
msg = "Invalid value for Bundle 'objects': Spec version 2.0 bundles don't yet support containing objects of a different spec version."
|
||||
assert str(excinfo.value) == msg
|
||||
|
||||
|
||||
def test_parse_observable_with_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = parsing.parse_observable(observable, version='2.1')
|
||||
v = 'v21'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="The default version is not 2.1", condition=stix2.DEFAULT_VERSION != "2.1")
|
||||
def test_parse_observable_with_no_version():
|
||||
observable = {"type": "file", "name": "foo.exe"}
|
||||
obs_obj = parsing.parse_observable(observable)
|
||||
v = 'v21'
|
||||
|
||||
assert v in str(obs_obj.__class__)
|
||||
|
||||
|
||||
def test_register_marking_with_version():
|
||||
parsing._register_marking(stix2.v21.TLP_WHITE.__class__, version='2.1')
|
||||
v = 'v21'
|
||||
|
||||
assert stix2.v21.TLP_WHITE.definition._type in parsing.STIX2_OBJ_MAPS[v]['markings']
|
||||
assert v in str(stix2.v21.TLP_WHITE.__class__)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="The default version is not 2.1", condition=stix2.DEFAULT_VERSION != "2.1")
|
||||
def test_register_marking_with_no_version():
|
||||
# Uses default version (2.0 in this case)
|
||||
parsing._register_marking(stix2.v21.TLP_WHITE.__class__)
|
||||
v = 'v21'
|
||||
|
||||
assert stix2.v21.TLP_WHITE.definition._type in parsing.STIX2_OBJ_MAPS[v]['markings']
|
||||
assert v in str(stix2.v21.TLP_WHITE.__class__)
|
|
@ -175,20 +175,34 @@ def test_greater_than():
|
|||
assert str(exp) == "[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.0]"
|
||||
|
||||
|
||||
def test_parsing_greater_than():
|
||||
patt_obj = create_pattern_object("[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.478901]", version="2.1")
|
||||
assert str(patt_obj) == "[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.478901]"
|
||||
|
||||
|
||||
def test_less_than():
|
||||
exp = stix2.LessThanComparisonExpression("file:size", 1024)
|
||||
assert str(exp) == "file:size < 1024"
|
||||
|
||||
|
||||
def test_parsing_less_than():
|
||||
patt_obj = create_pattern_object("[file:size < 1024]", version="2.1")
|
||||
assert str(patt_obj) == "[file:size < 1024]"
|
||||
|
||||
|
||||
def test_greater_than_or_equal():
|
||||
exp = stix2.GreaterThanEqualComparisonExpression(
|
||||
"file:size",
|
||||
1024,
|
||||
)
|
||||
|
||||
assert str(exp) == "file:size >= 1024"
|
||||
|
||||
|
||||
def test_parsing_greater_than_or_equal():
|
||||
patt_obj = create_pattern_object("[file:size >= 1024]", version="2.1")
|
||||
assert str(patt_obj) == "[file:size >= 1024]"
|
||||
|
||||
|
||||
def test_less_than_or_equal():
|
||||
exp = stix2.LessThanEqualComparisonExpression(
|
||||
"file:size",
|
||||
|
@ -197,6 +211,36 @@ def test_less_than_or_equal():
|
|||
assert str(exp) == "file:size <= 1024"
|
||||
|
||||
|
||||
def test_parsing_less_than_or_equal():
|
||||
patt_obj = create_pattern_object("[file:size <= 1024]", version="2.1")
|
||||
assert str(patt_obj) == "[file:size <= 1024]"
|
||||
|
||||
|
||||
def test_parsing_issubset():
|
||||
patt_obj = create_pattern_object("[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']", version="2.1")
|
||||
assert str(patt_obj) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
|
||||
|
||||
|
||||
def test_parsing_issuperset():
|
||||
patt_obj = create_pattern_object("[network-traffic:dst_ref.value ISSUPERSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']", version="2.1")
|
||||
assert str(patt_obj) == "[network-traffic:dst_ref.value ISSUPERSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
|
||||
|
||||
|
||||
def test_parsing_like():
|
||||
patt_obj = create_pattern_object("[directory:path LIKE 'C:\\\\Windows\\\\%\\\\foo']", version="2.1")
|
||||
assert str(patt_obj) == "[directory:path LIKE 'C:\\\\Windows\\\\%\\\\foo']"
|
||||
|
||||
|
||||
def test_parsing_match():
|
||||
patt_obj = create_pattern_object("[process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachine Root$'] FOLLOWEDBY [process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$'] WITHIN 300 SECONDS", version="2.1") # noqa
|
||||
assert str(patt_obj) == "[process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachine Root$'] FOLLOWEDBY [process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$'] WITHIN 300 SECONDS" # noqa
|
||||
|
||||
|
||||
def test_parsing_followed_by():
|
||||
patt_obj = create_pattern_object("([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [windows-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']) WITHIN 300 SECONDS", version="2.1") # noqa
|
||||
assert str(patt_obj) == "([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [windows-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']) WITHIN 300 SECONDS" # noqa
|
||||
|
||||
|
||||
def test_not():
|
||||
exp = stix2.LessThanComparisonExpression(
|
||||
"file:size",
|
||||
|
@ -257,6 +301,67 @@ def test_and_observable_expression():
|
|||
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1009' AND user-account:account_login = 'Mary']" # noqa
|
||||
|
||||
|
||||
def test_parsing_and_observable_expression():
|
||||
exp = create_pattern_object("[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']", version="2.1") # noqa
|
||||
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']" # noqa
|
||||
|
||||
|
||||
def test_or_observable_expression():
|
||||
exp1 = stix2.AndBooleanExpression([
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:account_type",
|
||||
"unix",
|
||||
),
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:user_id",
|
||||
stix2.StringConstant("1007"),
|
||||
),
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:account_login",
|
||||
"Peter",
|
||||
),
|
||||
])
|
||||
exp2 = stix2.AndBooleanExpression([
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:account_type",
|
||||
"unix",
|
||||
),
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:user_id",
|
||||
stix2.StringConstant("1008"),
|
||||
),
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:account_login",
|
||||
"Paul",
|
||||
),
|
||||
])
|
||||
exp3 = stix2.AndBooleanExpression([
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:account_type",
|
||||
"unix",
|
||||
),
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:user_id",
|
||||
stix2.StringConstant("1009"),
|
||||
),
|
||||
stix2.EqualityComparisonExpression(
|
||||
"user-account:account_login",
|
||||
"Mary",
|
||||
),
|
||||
])
|
||||
exp = stix2.OrObservationExpression([
|
||||
stix2.ObservationExpression(exp1),
|
||||
stix2.ObservationExpression(exp2),
|
||||
stix2.ObservationExpression(exp3),
|
||||
])
|
||||
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1009' AND user-account:account_login = 'Mary']" # noqa
|
||||
|
||||
|
||||
def test_parsing_or_observable_expression():
|
||||
exp = create_pattern_object("[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']", version="2.1") # noqa
|
||||
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']" # noqa
|
||||
|
||||
|
||||
def test_invalid_and_observable_expression():
|
||||
with pytest.raises(ValueError):
|
||||
stix2.AndBooleanExpression([
|
||||
|
@ -286,6 +391,11 @@ def test_hex():
|
|||
assert str(exp) == "[file:mime_type = 'image/bmp' AND file:magic_number_hex = h'ffd8']"
|
||||
|
||||
|
||||
def test_parsing_hex():
|
||||
patt_obj = create_pattern_object("[file:magic_number_hex = h'ffd8']", version="2.1")
|
||||
assert str(patt_obj) == "[file:magic_number_hex = h'ffd8']"
|
||||
|
||||
|
||||
def test_multiple_qualifiers():
|
||||
exp_and = stix2.AndBooleanExpression([
|
||||
stix2.EqualityComparisonExpression(
|
||||
|
@ -334,6 +444,11 @@ def test_binary():
|
|||
assert str(exp) == "artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q='"
|
||||
|
||||
|
||||
def test_parsing_binary():
|
||||
patt_obj = create_pattern_object("[artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q=']", version="2.1")
|
||||
assert str(patt_obj) == "[artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q=']"
|
||||
|
||||
|
||||
def test_list():
|
||||
exp = stix2.InComparisonExpression(
|
||||
"process:name",
|
||||
|
@ -495,29 +610,45 @@ def test_make_constant_already_a_constant():
|
|||
|
||||
|
||||
def test_parsing_comparison_expression():
|
||||
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']")
|
||||
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']", version="2.1")
|
||||
assert str(patt_obj) == "[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']"
|
||||
|
||||
|
||||
def test_parsing_qualified_expression():
|
||||
def test_parsing_repeat_and_within_qualified_expression():
|
||||
patt_obj = create_pattern_object(
|
||||
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS",
|
||||
version="2.1",
|
||||
)
|
||||
assert str(
|
||||
patt_obj,
|
||||
) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS"
|
||||
|
||||
|
||||
def test_parsing_start_stop_qualified_expression():
|
||||
patt_obj = create_pattern_object(
|
||||
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] START t'2016-06-01T00:00:00Z' STOP t'2017-03-12T08:30:00Z'", # noqa
|
||||
version="2.1",
|
||||
)
|
||||
assert str(
|
||||
patt_obj,
|
||||
) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] START t'2016-06-01T00:00:00Z' STOP t'2017-03-12T08:30:00Z'" # noqa
|
||||
|
||||
|
||||
def test_list_constant():
|
||||
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]")
|
||||
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]", version="2.1")
|
||||
assert str(patt_obj) == "[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]"
|
||||
|
||||
|
||||
def test_parsing_boolean():
|
||||
patt_obj = create_pattern_object("[network-traffic:is_active = true]", version="2.1")
|
||||
assert str(patt_obj) == "[network-traffic:is_active = true]"
|
||||
|
||||
|
||||
def test_parsing_multiple_slashes_quotes():
|
||||
patt_obj = create_pattern_object("[ file:name = 'weird_name\\'' ]")
|
||||
patt_obj = create_pattern_object("[ file:name = 'weird_name\\'' ]", version="2.1")
|
||||
assert str(patt_obj) == "[file:name = 'weird_name\\'']"
|
||||
|
||||
|
||||
def test_parse_error():
|
||||
with pytest.raises(ParseException):
|
||||
create_pattern_object("[ file: name = 'weirdname]")
|
||||
create_pattern_object("[ file: name = 'weirdname]", version="2.1")
|
||||
|
|
|
@ -404,7 +404,7 @@ def test_dictionary_property_invalid(d):
|
|||
|
||||
def test_property_list_of_dictionary():
|
||||
@stix2.v21.CustomObject(
|
||||
'x-new-obj', [
|
||||
'x-new-obj-4', [
|
||||
('property1', ListProperty(DictionaryProperty(spec_version='2.1'), required=True)),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
import datetime
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
import stix2
|
||||
from stix2.utils import (
|
||||
Precision, PrecisionConstraint, STIXdatetime, _to_enum, format_datetime,
|
||||
parse_into_datetime,
|
||||
)
|
||||
|
||||
_DT = datetime.datetime.utcnow()
|
||||
# intentionally omit microseconds from the following. We add it in as
|
||||
# needed for each test.
|
||||
_DT_STR = _DT.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value, enum_type, enum_default, enum_expected", [
|
||||
("second", Precision, None, Precision.SECOND),
|
||||
(
|
||||
"eXaCt", PrecisionConstraint, PrecisionConstraint.MIN,
|
||||
PrecisionConstraint.EXACT,
|
||||
),
|
||||
(None, Precision, Precision.MILLISECOND, Precision.MILLISECOND),
|
||||
(Precision.ANY, Precision, None, Precision.ANY),
|
||||
],
|
||||
)
|
||||
def test_to_enum(value, enum_type, enum_default, enum_expected):
|
||||
result = _to_enum(value, enum_type, enum_default)
|
||||
assert result == enum_expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value, err_type", [
|
||||
("foo", KeyError),
|
||||
(1, TypeError),
|
||||
(PrecisionConstraint.EXACT, TypeError),
|
||||
(None, TypeError),
|
||||
],
|
||||
)
|
||||
def test_to_enum_errors(value, err_type):
|
||||
with pytest.raises(err_type):
|
||||
_to_enum(value, Precision)
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
sys.version_info[:2] == (3, 6), strict=True,
|
||||
reason="https://bugs.python.org/issue32404",
|
||||
)
|
||||
def test_stix_datetime_now():
|
||||
dt = STIXdatetime.utcnow()
|
||||
assert dt.precision is Precision.ANY
|
||||
assert dt.precision_constraint is PrecisionConstraint.EXACT
|
||||
|
||||
|
||||
def test_stix_datetime():
|
||||
dt = datetime.datetime.utcnow()
|
||||
|
||||
sdt = STIXdatetime(dt, precision=Precision.SECOND)
|
||||
assert sdt.precision is Precision.SECOND
|
||||
assert sdt == dt
|
||||
|
||||
sdt = STIXdatetime(
|
||||
dt,
|
||||
precision_constraint=PrecisionConstraint.EXACT,
|
||||
)
|
||||
assert sdt.precision_constraint is PrecisionConstraint.EXACT
|
||||
assert sdt == dt
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"us, precision, precision_constraint, expected_truncated_us", [
|
||||
(123456, Precision.ANY, PrecisionConstraint.EXACT, 123456),
|
||||
(123456, Precision.SECOND, PrecisionConstraint.EXACT, 0),
|
||||
(123456, Precision.SECOND, PrecisionConstraint.MIN, 123456),
|
||||
(123456, Precision.MILLISECOND, PrecisionConstraint.EXACT, 123000),
|
||||
(123456, Precision.MILLISECOND, PrecisionConstraint.MIN, 123456),
|
||||
(1234, Precision.MILLISECOND, PrecisionConstraint.EXACT, 1000),
|
||||
(123, Precision.MILLISECOND, PrecisionConstraint.EXACT, 0),
|
||||
],
|
||||
)
|
||||
def test_parse_datetime(
|
||||
us, precision, precision_constraint, expected_truncated_us,
|
||||
):
|
||||
|
||||
# complete the datetime string with microseconds
|
||||
dt_us_str = "{}.{:06d}Z".format(_DT_STR, us)
|
||||
|
||||
sdt = parse_into_datetime(
|
||||
dt_us_str,
|
||||
precision=precision,
|
||||
precision_constraint=precision_constraint,
|
||||
)
|
||||
|
||||
assert sdt.precision is precision
|
||||
assert sdt.precision_constraint is precision_constraint
|
||||
assert sdt.microsecond == expected_truncated_us
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"us, precision, precision_constraint, expected_us_str", [
|
||||
(123456, Precision.ANY, PrecisionConstraint.EXACT, ".123456"),
|
||||
(123456, Precision.SECOND, PrecisionConstraint.EXACT, ""),
|
||||
(123456, Precision.SECOND, PrecisionConstraint.MIN, ".123456"),
|
||||
(123456, Precision.MILLISECOND, PrecisionConstraint.EXACT, ".123"),
|
||||
(123456, Precision.MILLISECOND, PrecisionConstraint.MIN, ".123456"),
|
||||
(0, Precision.SECOND, PrecisionConstraint.MIN, ""),
|
||||
(0, Precision.MILLISECOND, PrecisionConstraint.MIN, ".000"),
|
||||
(0, Precision.MILLISECOND, PrecisionConstraint.EXACT, ".000"),
|
||||
(1000, Precision.MILLISECOND, PrecisionConstraint.EXACT, ".001"),
|
||||
(10000, Precision.MILLISECOND, PrecisionConstraint.EXACT, ".010"),
|
||||
(100000, Precision.MILLISECOND, PrecisionConstraint.EXACT, ".100"),
|
||||
(1000, Precision.ANY, PrecisionConstraint.EXACT, ".001"),
|
||||
(10000, Precision.ANY, PrecisionConstraint.EXACT, ".01"),
|
||||
(100000, Precision.ANY, PrecisionConstraint.EXACT, ".1"),
|
||||
(1001, Precision.MILLISECOND, PrecisionConstraint.MIN, ".001001"),
|
||||
(10010, Precision.MILLISECOND, PrecisionConstraint.MIN, ".01001"),
|
||||
(100100, Precision.MILLISECOND, PrecisionConstraint.MIN, ".1001"),
|
||||
],
|
||||
)
|
||||
def test_format_datetime(us, precision, precision_constraint, expected_us_str):
|
||||
|
||||
dt = _DT.replace(microsecond=us)
|
||||
expected_dt_str = "{}{}Z".format(_DT_STR, expected_us_str)
|
||||
|
||||
sdt = STIXdatetime(
|
||||
dt,
|
||||
precision=precision,
|
||||
precision_constraint=precision_constraint,
|
||||
)
|
||||
s = format_datetime(sdt)
|
||||
assert s == expected_dt_str
|
||||
|
||||
|
||||
def test_sdo_extra_precision():
|
||||
# add extra precision for "modified", ensure it's not lost
|
||||
identity_dict = {
|
||||
"type": "identity",
|
||||
"id": "identity--4a457eeb-6639-4aa3-be81-5930a3000c39",
|
||||
"created": "2015-12-21T19:59:11.000Z",
|
||||
"modified": "2015-12-21T19:59:11.0001Z",
|
||||
"name": "John Smith",
|
||||
"identity_class": "individual",
|
||||
"spec_version": "2.1",
|
||||
}
|
||||
|
||||
identity_obj = stix2.parse(identity_dict)
|
||||
assert identity_obj.modified.microsecond == 100
|
||||
assert identity_obj.modified.precision is Precision.MILLISECOND
|
||||
assert identity_obj.modified.precision_constraint is PrecisionConstraint.MIN
|
||||
|
||||
identity_str = identity_obj.serialize(pretty=True)
|
||||
|
||||
# ensure precision is retained in JSON
|
||||
assert identity_str == """{
|
||||
"type": "identity",
|
||||
"spec_version": "2.1",
|
||||
"id": "identity--4a457eeb-6639-4aa3-be81-5930a3000c39",
|
||||
"created": "2015-12-21T19:59:11.000Z",
|
||||
"modified": "2015-12-21T19:59:11.0001Z",
|
||||
"name": "John Smith",
|
||||
"identity_class": "individual"
|
||||
}"""
|
|
@ -1,6 +1,9 @@
|
|||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
import stix2
|
||||
import stix2.utils
|
||||
|
||||
from .constants import CAMPAIGN_MORE_KWARGS
|
||||
|
||||
|
@ -236,8 +239,7 @@ def test_remove_custom_stix_property():
|
|||
mal_nc = stix2.utils.remove_custom_stix(mal)
|
||||
|
||||
assert "x_custom" not in mal_nc
|
||||
assert (stix2.utils.parse_into_datetime(mal["modified"], precision="millisecond") <
|
||||
stix2.utils.parse_into_datetime(mal_nc["modified"], precision="millisecond"))
|
||||
assert mal["modified"] < mal_nc["modified"]
|
||||
|
||||
|
||||
def test_remove_custom_stix_object():
|
||||
|
@ -264,3 +266,33 @@ def test_remove_custom_stix_no_custom():
|
|||
assert len(campaign_v1.keys()) == len(campaign_v2.keys())
|
||||
assert campaign_v1.id == campaign_v2.id
|
||||
assert campaign_v1.description == campaign_v2.description
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"old, candidate_new, expected_new, use_stix21", [
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.001Z", "1999-08-15T00:19:07.001Z", False),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.001Z", False),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.000Z", "1999-08-15T00:19:07.001Z", False),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.999Z", "1999-08-15T00:19:07.001Z", False),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.0001Z", "1999-08-15T00:19:07.001Z", False),
|
||||
("1999-08-15T00:19:07.999Z", "1999-08-15T00:19:07.9999Z", "1999-08-15T00:19:08.000Z", False),
|
||||
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.001Z", "1999-08-15T00:19:07.001Z", True),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000001Z", True),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.000Z", "1999-08-15T00:19:07.000001Z", True),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.999999Z", "1999-08-15T00:19:07.000001Z", True),
|
||||
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000001Z", "1999-08-15T00:19:07.000001Z", True),
|
||||
("1999-08-15T00:19:07.999Z", "1999-08-15T00:19:07.999999Z", "1999-08-15T00:19:07.999999Z", True),
|
||||
],
|
||||
)
|
||||
def test_fudge_modified(old, candidate_new, expected_new, use_stix21):
|
||||
old_dt = datetime.datetime.strptime(old, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
candidate_new_dt = datetime.datetime.strptime(
|
||||
candidate_new, "%Y-%m-%dT%H:%M:%S.%fZ",
|
||||
)
|
||||
expected_new_dt = datetime.datetime.strptime(
|
||||
expected_new, "%Y-%m-%dT%H:%M:%S.%fZ",
|
||||
)
|
||||
|
||||
fudged = stix2.utils._fudge_modified(old_dt, candidate_new_dt, use_stix21)
|
||||
assert fudged == expected_new_dt
|
||||
|
|
258
stix2/utils.py
258
stix2/utils.py
|
@ -6,12 +6,15 @@ except ImportError:
|
|||
from collections import Mapping
|
||||
import copy
|
||||
import datetime as dt
|
||||
import enum
|
||||
import json
|
||||
import re
|
||||
|
||||
from dateutil import parser
|
||||
import pytz
|
||||
import six
|
||||
|
||||
import stix2.base
|
||||
import stix2
|
||||
|
||||
from .exceptions import (
|
||||
InvalidValueError, RevokeError, UnmodifiablePropertyError,
|
||||
|
@ -25,13 +28,84 @@ NOW = object()
|
|||
# STIX object properties that cannot be modified
|
||||
STIX_UNMOD_PROPERTIES = ['created', 'created_by_ref', 'id', 'type']
|
||||
|
||||
TYPE_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$'
|
||||
SCO21_EXT_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-ext$'
|
||||
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]+)*\-?$')
|
||||
PREFIX_21_REGEX = re.compile(r'^[a-z].*')
|
||||
|
||||
|
||||
class Precision(enum.Enum):
|
||||
"""
|
||||
Timestamp format precisions.
|
||||
"""
|
||||
# auto() wasn't introduced until Python 3.6.
|
||||
ANY = 1
|
||||
SECOND = 2
|
||||
MILLISECOND = 3
|
||||
|
||||
|
||||
class PrecisionConstraint(enum.Enum):
|
||||
"""
|
||||
Timestamp precision constraints. These affect how the Precision
|
||||
values are applied when formatting a timestamp.
|
||||
|
||||
These constraints don't really make sense with the ANY precision, so they
|
||||
have no effect in that case.
|
||||
"""
|
||||
EXACT = 1 # format must have exactly the given precision
|
||||
MIN = 2 # format must have at least the given precision
|
||||
# no need for a MAX constraint yet
|
||||
|
||||
|
||||
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
|
||||
you use a string, it must name a valid enum value. This implementation is
|
||||
case-insensitive.
|
||||
|
||||
:param value: A value to be interpreted as an enum (string, Enum instance,
|
||||
or None). If an Enum instance, it must be an instance of enum_type.
|
||||
:param enum_type: The enum type which strings will be interpreted against
|
||||
:param enum_default: The default enum to use if value is None. Must be
|
||||
an instance of enum_type, or None. If None, you are disallowing a
|
||||
default and requiring that value be non-None.
|
||||
:return: An instance of enum_type
|
||||
:raises TypeError: If value was neither an instance of enum_type, None, nor
|
||||
a string
|
||||
:raises KeyError: If value was a string which couldn't be interpreted as an
|
||||
enum value from enum_type
|
||||
"""
|
||||
assert enum_default is None or isinstance(enum_default, enum_type)
|
||||
|
||||
if not isinstance(value, enum_type):
|
||||
if value is None and enum_default is not None:
|
||||
value = enum_default
|
||||
elif isinstance(value, six.string_types):
|
||||
value = enum_type[value.upper()]
|
||||
else:
|
||||
raise TypeError("Not a valid {}: {}".format(
|
||||
enum_type.__name__, value,
|
||||
))
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class STIXdatetime(dt.datetime):
|
||||
"""
|
||||
Bundle a datetime with some format-related metadata, so that JSON
|
||||
serialization has the info it needs to produce compliant timestamps.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
precision = kwargs.pop('precision', None)
|
||||
precision = _to_enum(
|
||||
kwargs.pop("precision", Precision.ANY),
|
||||
Precision,
|
||||
)
|
||||
precision_constraint = _to_enum(
|
||||
kwargs.pop("precision_constraint", PrecisionConstraint.EXACT),
|
||||
PrecisionConstraint,
|
||||
)
|
||||
|
||||
if isinstance(args[0], dt.datetime): # Allow passing in a datetime object
|
||||
dttm = args[0]
|
||||
args = (
|
||||
|
@ -41,6 +115,7 @@ class STIXdatetime(dt.datetime):
|
|||
# self will be an instance of STIXdatetime, not dt.datetime
|
||||
self = dt.datetime.__new__(cls, *args, **kwargs)
|
||||
self.precision = precision
|
||||
self.precision_constraint = precision_constraint
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -90,7 +165,7 @@ def format_datetime(dttm):
|
|||
2. Convert to UTC
|
||||
3. Format in ISO format
|
||||
4. Ensure correct precision
|
||||
a. Add subsecond value if non-zero and precision not defined
|
||||
a. Add subsecond value if warranted, according to precision settings
|
||||
5. Add "Z"
|
||||
|
||||
"""
|
||||
|
@ -101,20 +176,74 @@ def format_datetime(dttm):
|
|||
else:
|
||||
zoned = dttm.astimezone(pytz.utc)
|
||||
ts = zoned.strftime('%Y-%m-%dT%H:%M:%S')
|
||||
ms = zoned.strftime('%f')
|
||||
precision = getattr(dttm, 'precision', None)
|
||||
if precision == 'second':
|
||||
pass # Already precise to the second
|
||||
elif precision == 'millisecond':
|
||||
ts = ts + '.' + ms[:3]
|
||||
elif zoned.microsecond > 0:
|
||||
ts = ts + '.' + ms.rstrip('0')
|
||||
return ts + 'Z'
|
||||
precision = getattr(dttm, 'precision', Precision.ANY)
|
||||
precision_constraint = getattr(
|
||||
dttm, 'precision_constraint', PrecisionConstraint.EXACT,
|
||||
)
|
||||
|
||||
frac_seconds_str = ""
|
||||
if precision == Precision.ANY:
|
||||
# No need to truncate; ignore constraint
|
||||
if zoned.microsecond:
|
||||
frac_seconds_str = "{:06d}".format(zoned.microsecond).rstrip("0")
|
||||
|
||||
elif precision == Precision.SECOND:
|
||||
if precision_constraint == PrecisionConstraint.MIN:
|
||||
# second precision, or better. Winds up being the same as ANY:
|
||||
# just use all our digits
|
||||
if zoned.microsecond:
|
||||
frac_seconds_str = "{:06d}".format(zoned.microsecond)\
|
||||
.rstrip("0")
|
||||
# exact: ignore microseconds entirely
|
||||
|
||||
else:
|
||||
# precision == millisecond
|
||||
if precision_constraint == PrecisionConstraint.EXACT:
|
||||
# can't rstrip() here or we may lose precision
|
||||
frac_seconds_str = "{:06d}".format(zoned.microsecond)[:3]
|
||||
|
||||
else:
|
||||
# millisecond precision, or better. So we can rstrip() zeros, but
|
||||
# only to a length of at least 3 digits (ljust() adds zeros back,
|
||||
# if it stripped too far.)
|
||||
frac_seconds_str = "{:06d}"\
|
||||
.format(zoned.microsecond)\
|
||||
.rstrip("0")\
|
||||
.ljust(3, "0")
|
||||
|
||||
ts = "{}{}{}Z".format(
|
||||
ts,
|
||||
"." if frac_seconds_str else "",
|
||||
frac_seconds_str,
|
||||
)
|
||||
|
||||
return ts
|
||||
|
||||
|
||||
def parse_into_datetime(value, precision=None):
|
||||
"""Parse a value into a valid STIX timestamp object.
|
||||
def parse_into_datetime(
|
||||
value, precision=Precision.ANY,
|
||||
precision_constraint=PrecisionConstraint.EXACT,
|
||||
):
|
||||
"""
|
||||
Parse a value into a valid STIX timestamp object. Also, optionally adjust
|
||||
precision of fractional seconds. This allows alignment with JSON
|
||||
serialization requirements, and helps ensure we're not using extra
|
||||
precision which would be lost upon JSON serialization. The precision
|
||||
info will be embedded in the returned object, so that JSON serialization
|
||||
will format it correctly.
|
||||
|
||||
:param value: A datetime.datetime or datetime.date instance, or a string
|
||||
:param precision: A precision value: either an instance of the Precision
|
||||
enum, or a string naming one of the enum values (case-insensitive)
|
||||
:param precision_constraint: A precision constraint value: either an
|
||||
instance of the PrecisionConstraint enum, or a string naming one of
|
||||
the enum values (case-insensitive)
|
||||
: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)
|
||||
|
||||
if isinstance(value, dt.date):
|
||||
if hasattr(value, 'hour'):
|
||||
ts = value
|
||||
|
@ -138,20 +267,23 @@ def parse_into_datetime(value, precision=None):
|
|||
ts = pytz.utc.localize(parsed)
|
||||
|
||||
# Ensure correct precision
|
||||
if not precision:
|
||||
return STIXdatetime(ts, precision=precision)
|
||||
ms = ts.microsecond
|
||||
if precision == 'second':
|
||||
ts = ts.replace(microsecond=0)
|
||||
elif precision == 'millisecond':
|
||||
ms_len = len(str(ms))
|
||||
if ms_len > 3:
|
||||
# Truncate to millisecond precision
|
||||
factor = 10 ** (ms_len - 3)
|
||||
ts = ts.replace(microsecond=(ts.microsecond // factor) * factor)
|
||||
else:
|
||||
if precision == Precision.SECOND:
|
||||
if precision_constraint == PrecisionConstraint.EXACT:
|
||||
ts = ts.replace(microsecond=0)
|
||||
return STIXdatetime(ts, precision=precision)
|
||||
# else, no need to modify fractional seconds
|
||||
|
||||
elif precision == Precision.MILLISECOND:
|
||||
if precision_constraint == PrecisionConstraint.EXACT:
|
||||
us = (ts.microsecond // 1000) * 1000
|
||||
ts = ts.replace(microsecond=us)
|
||||
# else: at least millisecond precision: the constraint will affect JSON
|
||||
# formatting, but there's nothing we need to do here.
|
||||
|
||||
# else, precision == Precision.ANY: nothing for us to do.
|
||||
|
||||
return STIXdatetime(
|
||||
ts, precision=precision, precision_constraint=precision_constraint,
|
||||
)
|
||||
|
||||
|
||||
def _get_dict(data):
|
||||
|
@ -230,14 +362,12 @@ def find_property_index(obj, search_key, search_value):
|
|||
Returns:
|
||||
int: An index; -1 if the key and value aren't found
|
||||
"""
|
||||
from .base import _STIXBase
|
||||
|
||||
# Special-case keys which are numbers-as-strings, e.g. for cyber-observable
|
||||
# mappings. Use the int value of the key as the index.
|
||||
if search_key.isdigit():
|
||||
return int(search_key)
|
||||
|
||||
if isinstance(obj, _STIXBase):
|
||||
if isinstance(obj, stix2.base._STIXBase):
|
||||
if search_key in obj and obj[search_key] == search_value:
|
||||
idx = _find(obj.object_properties(), search_key)
|
||||
else:
|
||||
|
@ -256,6 +386,39 @@ def find_property_index(obj, search_key, search_value):
|
|||
return idx
|
||||
|
||||
|
||||
def _fudge_modified(old_modified, new_modified, use_stix21):
|
||||
"""
|
||||
Ensures a new modified timestamp is newer than the old. When they are
|
||||
too close together, new_modified must be pushed further ahead to ensure
|
||||
it is distinct and later, after JSON serialization (which may mean it's
|
||||
actually being pushed a little ways into the future). JSON serialization
|
||||
can remove precision, which can cause distinct timestamps to accidentally
|
||||
become equal, if we're not careful.
|
||||
|
||||
:param old_modified: A previous "modified" timestamp, as a datetime object
|
||||
:param new_modified: A candidate new "modified" timestamp, as a datetime
|
||||
object
|
||||
:param use_stix21: Whether to use STIX 2.1+ versioning timestamp precision
|
||||
rules (boolean). This is important so that we are aware of how
|
||||
timestamp precision will be truncated, so we know how close together
|
||||
the timestamps can be, and how far ahead to potentially push the new
|
||||
one.
|
||||
:return: A suitable new "modified" timestamp. This may be different from
|
||||
what was passed in, if it had to be pushed ahead.
|
||||
"""
|
||||
if use_stix21:
|
||||
# 2.1+: we can use full precision
|
||||
if new_modified <= old_modified:
|
||||
new_modified = old_modified + dt.timedelta(microseconds=1)
|
||||
else:
|
||||
# 2.0: we must use millisecond precision
|
||||
one_ms = dt.timedelta(milliseconds=1)
|
||||
if new_modified - old_modified < one_ms:
|
||||
new_modified = old_modified + one_ms
|
||||
|
||||
return new_modified
|
||||
|
||||
|
||||
def new_version(data, **kwargs):
|
||||
"""Create a new version of a STIX object, by modifying properties and
|
||||
updating the ``modified`` property.
|
||||
|
@ -283,12 +446,32 @@ def new_version(data, **kwargs):
|
|||
if unchangable_properties:
|
||||
raise UnmodifiablePropertyError(unchangable_properties)
|
||||
|
||||
# Different versioning precision rules in STIX 2.0 vs 2.1, so we need
|
||||
# to know which rules to apply.
|
||||
is_21 = "spec_version" in data
|
||||
precision_constraint = "min" if is_21 else "exact"
|
||||
|
||||
cls = type(data)
|
||||
if 'modified' not in kwargs:
|
||||
kwargs['modified'] = get_timestamp()
|
||||
old_modified = parse_into_datetime(
|
||||
data["modified"], precision="millisecond",
|
||||
precision_constraint=precision_constraint,
|
||||
)
|
||||
|
||||
new_modified = get_timestamp()
|
||||
new_modified = _fudge_modified(old_modified, new_modified, is_21)
|
||||
|
||||
kwargs['modified'] = new_modified
|
||||
|
||||
elif 'modified' in data:
|
||||
old_modified_property = parse_into_datetime(data.get('modified'), precision='millisecond')
|
||||
new_modified_property = parse_into_datetime(kwargs['modified'], precision='millisecond')
|
||||
old_modified_property = parse_into_datetime(
|
||||
data.get('modified'), precision='millisecond',
|
||||
precision_constraint=precision_constraint,
|
||||
)
|
||||
new_modified_property = parse_into_datetime(
|
||||
kwargs['modified'], precision='millisecond',
|
||||
precision_constraint=precision_constraint,
|
||||
)
|
||||
if new_modified_property <= old_modified_property:
|
||||
raise InvalidValueError(
|
||||
cls, 'modified',
|
||||
|
@ -378,11 +561,6 @@ def remove_custom_stix(stix_obj):
|
|||
|
||||
new_obj = new_version(stix_obj, **(dict(props)))
|
||||
|
||||
while parse_into_datetime(new_obj['modified']) == parse_into_datetime(stix_obj['modified']):
|
||||
# Prevents bug when fast computation allows multiple STIX object
|
||||
# versions to be created in single unit of time
|
||||
new_obj = new_version(stix_obj, **(dict(props)))
|
||||
|
||||
return new_obj
|
||||
|
||||
else:
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
# flake8: noqa
|
||||
|
||||
from .base import (
|
||||
_DomainObject, _Extension, _Observable, _RelationshipObject, _STIXBase20,
|
||||
)
|
||||
from .bundle import Bundle
|
||||
from .common import (
|
||||
TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking, ExternalReference,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
"""Base classes for STIX 2.0 type definitions."""
|
||||
|
||||
from ..base import (
|
||||
_DomainObject, _Extension, _Observable, _RelationshipObject, _STIXBase,
|
||||
)
|
||||
|
||||
|
||||
class _STIXBase20(_STIXBase):
|
||||
pass
|
||||
|
||||
|
||||
class _Observable(_Observable, _STIXBase20):
|
||||
pass
|
||||
|
||||
|
||||
class _Extension(_Extension, _STIXBase20):
|
||||
pass
|
||||
|
||||
|
||||
class _DomainObject(_DomainObject, _STIXBase20):
|
||||
pass
|
||||
|
||||
|
||||
class _RelationshipObject(_RelationshipObject, _STIXBase20):
|
||||
pass
|
|
@ -2,20 +2,20 @@
|
|||
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..base import _STIXBase
|
||||
from ..properties import (
|
||||
IDProperty, ListProperty, STIXObjectProperty, StringProperty, TypeProperty,
|
||||
)
|
||||
from .base import _STIXBase20
|
||||
|
||||
|
||||
class Bundle(_STIXBase):
|
||||
class Bundle(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709293>`__.
|
||||
"""
|
||||
|
||||
_type = 'bundle'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
# Not technically correct: STIX 2.0 spec doesn't say spec_version must
|
||||
# have this value, but it's all we support for now.
|
||||
|
|
|
@ -5,7 +5,6 @@ import copy
|
|||
|
||||
import six
|
||||
|
||||
from ..base import _STIXBase
|
||||
from ..custom import _custom_marking_builder
|
||||
from ..markings import _MarkingsMixin
|
||||
from ..markings.utils import check_tlp_marking
|
||||
|
@ -14,6 +13,7 @@ from ..properties import (
|
|||
SelectorProperty, StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW, _get_dict
|
||||
from .base import _STIXBase20
|
||||
|
||||
|
||||
def _should_set_millisecond(cr, marking_type):
|
||||
|
@ -31,7 +31,7 @@ def _should_set_millisecond(cr, marking_type):
|
|||
return False
|
||||
|
||||
|
||||
class ExternalReference(_STIXBase):
|
||||
class ExternalReference(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709261>`__.
|
||||
"""
|
||||
|
@ -49,7 +49,7 @@ class ExternalReference(_STIXBase):
|
|||
self._check_at_least_one_property(['description', 'external_id', 'url'])
|
||||
|
||||
|
||||
class KillChainPhase(_STIXBase):
|
||||
class KillChainPhase(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709267>`__.
|
||||
"""
|
||||
|
@ -60,7 +60,7 @@ class KillChainPhase(_STIXBase):
|
|||
])
|
||||
|
||||
|
||||
class GranularMarking(_STIXBase):
|
||||
class GranularMarking(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709290>`__.
|
||||
"""
|
||||
|
@ -71,7 +71,7 @@ class GranularMarking(_STIXBase):
|
|||
])
|
||||
|
||||
|
||||
class TLPMarking(_STIXBase):
|
||||
class TLPMarking(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709287>`__.
|
||||
"""
|
||||
|
@ -83,7 +83,7 @@ class TLPMarking(_STIXBase):
|
|||
])
|
||||
|
||||
|
||||
class StatementMarking(_STIXBase):
|
||||
class StatementMarking(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709286>`__.
|
||||
"""
|
||||
|
@ -113,14 +113,14 @@ class MarkingProperty(Property):
|
|||
raise ValueError("must be a Statement, TLP Marking or a registered marking.")
|
||||
|
||||
|
||||
class MarkingDefinition(_STIXBase, _MarkingsMixin):
|
||||
class MarkingDefinition(_STIXBase20, _MarkingsMixin):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709284>`__.
|
||||
"""
|
||||
|
||||
_type = 'marking-definition'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW)),
|
||||
|
@ -188,7 +188,7 @@ def CustomMarking(type='x-custom-marking', properties=None):
|
|||
|
||||
"""
|
||||
def wrapper(cls):
|
||||
return _custom_marking_builder(cls, type, properties, '2.0')
|
||||
return _custom_marking_builder(cls, type, properties, '2.0', _STIXBase20)
|
||||
return wrapper
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
"""STIX 2.0 Cyber Observable Objects.
|
||||
|
||||
Embedded observable object types, such as Email MIME Component, which is
|
||||
embedded in Email Message objects, inherit from ``_STIXBase`` instead of
|
||||
Observable and do not have a ``_type`` attribute.
|
||||
embedded in Email Message objects, inherit from ``_STIXBase20`` instead of
|
||||
_Observable and do not have a ``_type`` attribute.
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
import itertools
|
||||
|
||||
from ..base import _Extension, _Observable, _STIXBase
|
||||
from ..custom import _custom_extension_builder, _custom_observable_builder
|
||||
from ..exceptions import AtLeastOnePropertyError, DependentPropertiesError
|
||||
from ..properties import (
|
||||
|
@ -17,6 +16,7 @@ from ..properties import (
|
|||
HashesProperty, HexProperty, IntegerProperty, ListProperty,
|
||||
ObjectReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from .base import _Extension, _Observable, _STIXBase20
|
||||
|
||||
|
||||
class Artifact(_Observable):
|
||||
|
@ -26,7 +26,7 @@ class Artifact(_Observable):
|
|||
|
||||
_type = 'artifact'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('mime_type', StringProperty()),
|
||||
('payload_bin', BinaryProperty()),
|
||||
('url', StringProperty()),
|
||||
|
@ -47,7 +47,7 @@ class AutonomousSystem(_Observable):
|
|||
|
||||
_type = 'autonomous-system'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('number', IntegerProperty(required=True)),
|
||||
('name', StringProperty()),
|
||||
('rir', StringProperty()),
|
||||
|
@ -62,7 +62,7 @@ class Directory(_Observable):
|
|||
|
||||
_type = 'directory'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('path', StringProperty(required=True)),
|
||||
('path_enc', StringProperty()),
|
||||
# these are not the created/modified timestamps of the object itself
|
||||
|
@ -81,7 +81,7 @@ class DomainName(_Observable):
|
|||
|
||||
_type = 'domain-name'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('value', StringProperty(required=True)),
|
||||
('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name']))),
|
||||
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
|
||||
|
@ -95,7 +95,7 @@ class EmailAddress(_Observable):
|
|||
|
||||
_type = 'email-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('value', StringProperty(required=True)),
|
||||
('display_name', StringProperty()),
|
||||
('belongs_to_ref', ObjectReferenceProperty(valid_types='user-account')),
|
||||
|
@ -103,7 +103,7 @@ class EmailAddress(_Observable):
|
|||
])
|
||||
|
||||
|
||||
class EmailMIMEComponent(_STIXBase):
|
||||
class EmailMIMEComponent(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part4-cyber-observable-objects/stix-v2.0-cs01-part4-cyber-observable-objects.html#_Toc496716231>`__.
|
||||
""" # noqa
|
||||
|
@ -127,7 +127,7 @@ class EmailMessage(_Observable):
|
|||
|
||||
_type = 'email-message'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('is_multipart', BooleanProperty(required=True)),
|
||||
('date', TimestampProperty()),
|
||||
('content_type', StringProperty()),
|
||||
|
@ -166,7 +166,7 @@ class ArchiveExt(_Extension):
|
|||
])
|
||||
|
||||
|
||||
class AlternateDataStream(_STIXBase):
|
||||
class AlternateDataStream(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part4-cyber-observable-objects/stix-v2.0-cs01-part4-cyber-observable-objects.html#_Toc496716239>`__.
|
||||
""" # noqa
|
||||
|
@ -220,7 +220,7 @@ class RasterImageExt(_Extension):
|
|||
])
|
||||
|
||||
|
||||
class WindowsPEOptionalHeaderType(_STIXBase):
|
||||
class WindowsPEOptionalHeaderType(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part4-cyber-observable-objects/stix-v2.0-cs01-part4-cyber-observable-objects.html#_Toc496716248>`__.
|
||||
""" # noqa
|
||||
|
@ -264,7 +264,7 @@ class WindowsPEOptionalHeaderType(_STIXBase):
|
|||
self._check_at_least_one_property()
|
||||
|
||||
|
||||
class WindowsPESection(_STIXBase):
|
||||
class WindowsPESection(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part4-cyber-observable-objects/stix-v2.0-cs01-part4-cyber-observable-objects.html#_Toc496716250>`__.
|
||||
""" # noqa
|
||||
|
@ -306,7 +306,7 @@ class File(_Observable):
|
|||
|
||||
_type = 'file'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('hashes', HashesProperty()),
|
||||
('size', IntegerProperty()),
|
||||
('name', StringProperty()),
|
||||
|
@ -339,7 +339,7 @@ class IPv4Address(_Observable):
|
|||
|
||||
_type = 'ipv4-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('value', StringProperty(required=True)),
|
||||
('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))),
|
||||
('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))),
|
||||
|
@ -354,7 +354,7 @@ class IPv6Address(_Observable):
|
|||
|
||||
_type = 'ipv6-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('value', StringProperty(required=True)),
|
||||
('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))),
|
||||
('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))),
|
||||
|
@ -369,7 +369,7 @@ class MACAddress(_Observable):
|
|||
|
||||
_type = 'mac-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('value', StringProperty(required=True)),
|
||||
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
|
||||
])
|
||||
|
@ -382,7 +382,7 @@ class Mutex(_Observable):
|
|||
|
||||
_type = 'mutex'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('name', StringProperty(required=True)),
|
||||
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
|
||||
])
|
||||
|
@ -483,7 +483,7 @@ class NetworkTraffic(_Observable):
|
|||
|
||||
_type = 'network-traffic'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('start', TimestampProperty()),
|
||||
('end', TimestampProperty()),
|
||||
('is_active', BooleanProperty()),
|
||||
|
@ -575,7 +575,7 @@ class Process(_Observable):
|
|||
|
||||
_type = 'process'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('is_hidden', BooleanProperty()),
|
||||
('pid', IntegerProperty()),
|
||||
('name', StringProperty()),
|
||||
|
@ -615,7 +615,7 @@ class Software(_Observable):
|
|||
|
||||
_type = 'software'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('name', StringProperty(required=True)),
|
||||
('cpe', StringProperty()),
|
||||
('languages', ListProperty(StringProperty)),
|
||||
|
@ -632,7 +632,7 @@ class URL(_Observable):
|
|||
|
||||
_type = 'url'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('value', StringProperty(required=True)),
|
||||
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
|
||||
])
|
||||
|
@ -659,7 +659,7 @@ class UserAccount(_Observable):
|
|||
|
||||
_type = 'user-account'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('user_id', StringProperty(required=True)),
|
||||
('account_login', StringProperty()),
|
||||
('account_type', StringProperty()), # open vocab
|
||||
|
@ -677,7 +677,7 @@ class UserAccount(_Observable):
|
|||
])
|
||||
|
||||
|
||||
class WindowsRegistryValueType(_STIXBase):
|
||||
class WindowsRegistryValueType(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part4-cyber-observable-objects/stix-v2.0-cs01-part4-cyber-observable-objects.html#_Toc496716293>`__.
|
||||
""" # noqa
|
||||
|
@ -713,7 +713,7 @@ class WindowsRegistryKey(_Observable):
|
|||
|
||||
_type = 'windows-registry-key'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('key', StringProperty(required=True)),
|
||||
('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))),
|
||||
# this is not the modified timestamps of the object itself
|
||||
|
@ -724,7 +724,7 @@ class WindowsRegistryKey(_Observable):
|
|||
])
|
||||
|
||||
|
||||
class X509V3ExtenstionsType(_STIXBase):
|
||||
class X509V3ExtenstionsType(_STIXBase20):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part4-cyber-observable-objects/stix-v2.0-cs01-part4-cyber-observable-objects.html#_Toc496716298>`__.
|
||||
""" # noqa
|
||||
|
@ -757,7 +757,7 @@ class X509Certificate(_Observable):
|
|||
|
||||
_type = 'x509-certificate'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('is_self_signed', BooleanProperty()),
|
||||
('hashes', HashesProperty()),
|
||||
('version', StringProperty()),
|
||||
|
@ -791,11 +791,11 @@ def CustomObservable(type='x-custom-observable', properties=None):
|
|||
"""
|
||||
def wrapper(cls):
|
||||
_properties = list(itertools.chain.from_iterable([
|
||||
[('type', TypeProperty(type))],
|
||||
[('type', TypeProperty(type, spec_version='2.0'))],
|
||||
properties,
|
||||
[('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=type))],
|
||||
]))
|
||||
return _custom_observable_builder(cls, type, _properties, '2.0')
|
||||
return _custom_observable_builder(cls, type, _properties, '2.0', _Observable)
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -803,5 +803,5 @@ def CustomExtension(observable=None, type='x-custom-observable-ext', properties=
|
|||
"""Decorator for custom extensions to STIX Cyber Observables.
|
||||
"""
|
||||
def wrapper(cls):
|
||||
return _custom_extension_builder(cls, observable, type, properties, '2.0')
|
||||
return _custom_extension_builder(cls, observable, type, properties, '2.0', _Extension)
|
||||
return wrapper
|
||||
|
|
|
@ -5,7 +5,6 @@ import itertools
|
|||
|
||||
from stix2patterns.validator import run_validator
|
||||
|
||||
from ..core import STIXDomainObject
|
||||
from ..custom import _custom_object_builder
|
||||
from ..exceptions import InvalidValueError
|
||||
from ..properties import (
|
||||
|
@ -14,17 +13,18 @@ from ..properties import (
|
|||
TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW
|
||||
from .base import _DomainObject
|
||||
from .common import ExternalReference, GranularMarking, KillChainPhase
|
||||
|
||||
|
||||
class AttackPattern(STIXDomainObject):
|
||||
class AttackPattern(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714302>`__.
|
||||
"""
|
||||
|
||||
_type = 'attack-pattern'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -40,14 +40,14 @@ class AttackPattern(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Campaign(STIXDomainObject):
|
||||
class Campaign(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714305>`__.
|
||||
"""
|
||||
|
||||
_type = 'campaign'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -66,14 +66,14 @@ class Campaign(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class CourseOfAction(STIXDomainObject):
|
||||
class CourseOfAction(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714308>`__.
|
||||
"""
|
||||
|
||||
_type = 'course-of-action'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -88,14 +88,14 @@ class CourseOfAction(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Identity(STIXDomainObject):
|
||||
class Identity(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714311>`__.
|
||||
"""
|
||||
|
||||
_type = 'identity'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -113,14 +113,14 @@ class Identity(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Indicator(STIXDomainObject):
|
||||
class Indicator(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714314>`__.
|
||||
"""
|
||||
|
||||
_type = 'indicator'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -144,14 +144,14 @@ class Indicator(STIXDomainObject):
|
|||
raise InvalidValueError(self.__class__, 'pattern', str(errors[0]))
|
||||
|
||||
|
||||
class IntrusionSet(STIXDomainObject):
|
||||
class IntrusionSet(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714317>`__.
|
||||
"""
|
||||
|
||||
_type = 'intrusion-set'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -173,14 +173,14 @@ class IntrusionSet(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Malware(STIXDomainObject):
|
||||
class Malware(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714320>`__.
|
||||
"""
|
||||
|
||||
_type = 'malware'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -196,14 +196,14 @@ class Malware(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class ObservedData(STIXDomainObject):
|
||||
class ObservedData(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714323>`__.
|
||||
"""
|
||||
|
||||
_type = 'observed-data'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -226,14 +226,14 @@ class ObservedData(STIXDomainObject):
|
|||
super(ObservedData, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class Report(STIXDomainObject):
|
||||
class Report(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714326>`__.
|
||||
"""
|
||||
|
||||
_type = 'report'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -256,14 +256,14 @@ class Report(STIXDomainObject):
|
|||
super(Report, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class ThreatActor(STIXDomainObject):
|
||||
class ThreatActor(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714329>`__.
|
||||
"""
|
||||
|
||||
_type = 'threat-actor'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -286,14 +286,14 @@ class ThreatActor(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Tool(STIXDomainObject):
|
||||
class Tool(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714332>`__.
|
||||
"""
|
||||
|
||||
_type = 'tool'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -310,14 +310,14 @@ class Tool(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Vulnerability(STIXDomainObject):
|
||||
class Vulnerability(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714335>`__.
|
||||
"""
|
||||
|
||||
_type = 'vulnerability'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -364,7 +364,7 @@ def CustomObject(type='x-custom-type', properties=None):
|
|||
def wrapper(cls):
|
||||
_properties = list(itertools.chain.from_iterable([
|
||||
[
|
||||
('type', TypeProperty(type)),
|
||||
('type', TypeProperty(type, spec_version='2.0')),
|
||||
('id', IDProperty(type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -380,5 +380,5 @@ def CustomObject(type='x-custom-type', properties=None):
|
|||
],
|
||||
sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
|
||||
]))
|
||||
return _custom_object_builder(cls, type, _properties, '2.0')
|
||||
return _custom_object_builder(cls, type, _properties, '2.0', _DomainObject)
|
||||
return wrapper
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..core import STIXRelationshipObject
|
||||
from ..properties import (
|
||||
BooleanProperty, IDProperty, IntegerProperty, ListProperty,
|
||||
ReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW
|
||||
from .base import _RelationshipObject
|
||||
from .common import ExternalReference, GranularMarking
|
||||
|
||||
|
||||
class Relationship(STIXRelationshipObject):
|
||||
class Relationship(_RelationshipObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714340>`__.
|
||||
"""
|
||||
|
@ -20,7 +20,7 @@ class Relationship(STIXRelationshipObject):
|
|||
|
||||
_type = 'relationship'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
@ -55,14 +55,14 @@ class Relationship(STIXRelationshipObject):
|
|||
super(Relationship, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class Sighting(STIXRelationshipObject):
|
||||
class Sighting(_RelationshipObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714343>`__.
|
||||
"""
|
||||
|
||||
_type = 'sighting'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.0')),
|
||||
('id', IDProperty(_type, spec_version='2.0')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
# flake8: noqa
|
||||
|
||||
from .base import (
|
||||
_DomainObject, _Extension, _Observable, _RelationshipObject, _STIXBase21,
|
||||
)
|
||||
from .bundle import Bundle
|
||||
from .common import (
|
||||
TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking, ExternalReference,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
"""Base classes for STIX 2.1 type definitions."""
|
||||
|
||||
from ..base import (
|
||||
_DomainObject, _Extension, _Observable, _RelationshipObject, _STIXBase,
|
||||
)
|
||||
|
||||
|
||||
class _STIXBase21(_STIXBase):
|
||||
pass
|
||||
|
||||
|
||||
class _Observable(_Observable, _STIXBase21):
|
||||
pass
|
||||
|
||||
|
||||
class _Extension(_Extension, _STIXBase21):
|
||||
pass
|
||||
|
||||
|
||||
class _DomainObject(_DomainObject, _STIXBase21):
|
||||
pass
|
||||
|
||||
|
||||
class _RelationshipObject(_RelationshipObject, _STIXBase21):
|
||||
pass
|
|
@ -2,20 +2,20 @@
|
|||
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..base import _STIXBase
|
||||
from ..properties import (
|
||||
IDProperty, ListProperty, STIXObjectProperty, TypeProperty,
|
||||
)
|
||||
from .base import _STIXBase21
|
||||
|
||||
|
||||
class Bundle(_STIXBase):
|
||||
class Bundle(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_nuwp4rox8c7r>`__.
|
||||
"""
|
||||
|
||||
_type = 'bundle'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('objects', ListProperty(STIXObjectProperty(spec_version='2.1'))),
|
||||
])
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..base import _STIXBase
|
||||
from ..custom import _custom_marking_builder
|
||||
from ..exceptions import InvalidValueError
|
||||
from ..markings import _MarkingsMixin
|
||||
|
@ -13,9 +12,10 @@ from ..properties import (
|
|||
SelectorProperty, StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW, _get_dict
|
||||
from .base import _STIXBase21
|
||||
|
||||
|
||||
class ExternalReference(_STIXBase):
|
||||
class ExternalReference(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_bajcvqteiard>`__.
|
||||
"""
|
||||
|
@ -49,7 +49,7 @@ class ExternalReference(_STIXBase):
|
|||
)
|
||||
|
||||
|
||||
class KillChainPhase(_STIXBase):
|
||||
class KillChainPhase(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_i4tjv75ce50h>`__.
|
||||
"""
|
||||
|
@ -60,7 +60,7 @@ class KillChainPhase(_STIXBase):
|
|||
])
|
||||
|
||||
|
||||
class GranularMarking(_STIXBase):
|
||||
class GranularMarking(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_robezi5egfdr>`__.
|
||||
"""
|
||||
|
@ -76,19 +76,19 @@ class GranularMarking(_STIXBase):
|
|||
self._check_at_least_one_property(['lang', 'marking_ref'])
|
||||
|
||||
|
||||
class LanguageContent(_STIXBase):
|
||||
class LanguageContent(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_nfwr8z9ax2bi>`__.
|
||||
"""
|
||||
|
||||
_type = 'language-content'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('object_ref', ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1', required=True)),
|
||||
# TODO: 'object_modified' it MUST be an exact match for the modified time of the STIX Object (SRO or SDO) being referenced.
|
||||
('object_modified', TimestampProperty(precision='millisecond')),
|
||||
|
@ -103,7 +103,7 @@ class LanguageContent(_STIXBase):
|
|||
])
|
||||
|
||||
|
||||
class TLPMarking(_STIXBase):
|
||||
class TLPMarking(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_yd3ar14ekwrs>`__.
|
||||
"""
|
||||
|
@ -114,7 +114,7 @@ class TLPMarking(_STIXBase):
|
|||
])
|
||||
|
||||
|
||||
class StatementMarking(_STIXBase):
|
||||
class StatementMarking(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_3ru8r05saera>`__.
|
||||
"""
|
||||
|
@ -144,18 +144,18 @@ class MarkingProperty(Property):
|
|||
raise ValueError("must be a Statement, TLP Marking or a registered marking.")
|
||||
|
||||
|
||||
class MarkingDefinition(_STIXBase, _MarkingsMixin):
|
||||
class MarkingDefinition(_STIXBase21, _MarkingsMixin):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_hr5vgqxjk7ns>`__.
|
||||
"""
|
||||
|
||||
_type = 'marking-definition'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type)),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('external_references', ListProperty(ExternalReference)),
|
||||
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
|
||||
('granular_markings', ListProperty(GranularMarking)),
|
||||
|
@ -214,7 +214,7 @@ def CustomMarking(type='x-custom-marking', properties=None):
|
|||
|
||||
"""
|
||||
def wrapper(cls):
|
||||
return _custom_marking_builder(cls, type, properties, '2.1')
|
||||
return _custom_marking_builder(cls, type, properties, '2.1', _STIXBase21)
|
||||
return wrapper
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
"""STIX 2.1 Cyber Observable Objects.
|
||||
|
||||
Embedded observable object types, such as Email MIME Component, which is
|
||||
embedded in Email Message objects, inherit from ``_STIXBase`` instead of
|
||||
Observable and do not have a ``_type`` attribute.
|
||||
embedded in Email Message objects, inherit from ``_STIXBase21`` instead of
|
||||
_Observable and do not have a ``_type`` attribute.
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
import itertools
|
||||
|
||||
from ..base import _Extension, _Observable, _STIXBase
|
||||
from ..custom import _custom_extension_builder, _custom_observable_builder
|
||||
from ..exceptions import AtLeastOnePropertyError, DependentPropertiesError
|
||||
from ..properties import (
|
||||
|
@ -18,6 +17,7 @@ from ..properties import (
|
|||
ObjectReferenceProperty, ReferenceProperty, StringProperty,
|
||||
TimestampProperty, TypeProperty,
|
||||
)
|
||||
from .base import _Extension, _Observable, _STIXBase21
|
||||
from .common import GranularMarking
|
||||
|
||||
|
||||
|
@ -28,7 +28,7 @@ class Artifact(_Observable):
|
|||
|
||||
_type = 'artifact'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('mime_type', StringProperty()),
|
||||
('payload_bin', BinaryProperty()),
|
||||
|
@ -57,7 +57,7 @@ class AutonomousSystem(_Observable):
|
|||
|
||||
_type = 'autonomous-system'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('number', IntegerProperty(required=True)),
|
||||
('name', StringProperty()),
|
||||
|
@ -78,7 +78,7 @@ class Directory(_Observable):
|
|||
|
||||
_type = 'directory'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('path', StringProperty(required=True)),
|
||||
('path_enc', StringProperty()),
|
||||
|
@ -103,7 +103,7 @@ class DomainName(_Observable):
|
|||
|
||||
_type = 'domain-name'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('value', StringProperty(required=True)),
|
||||
('resolves_to_refs', ListProperty(ReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name'], spec_version='2.1'))),
|
||||
|
@ -123,7 +123,7 @@ class EmailAddress(_Observable):
|
|||
|
||||
_type = 'email-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('value', StringProperty(required=True)),
|
||||
('display_name', StringProperty()),
|
||||
|
@ -137,7 +137,7 @@ class EmailAddress(_Observable):
|
|||
_id_contributing_properties = ["value"]
|
||||
|
||||
|
||||
class EmailMIMEComponent(_STIXBase):
|
||||
class EmailMIMEComponent(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_kzv52qqc0xw1>`__.
|
||||
"""
|
||||
|
@ -161,7 +161,7 @@ class EmailMessage(_Observable):
|
|||
|
||||
_type = 'email-message'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('is_multipart', BooleanProperty(required=True)),
|
||||
('date', TimestampProperty()),
|
||||
|
@ -206,7 +206,7 @@ class ArchiveExt(_Extension):
|
|||
])
|
||||
|
||||
|
||||
class AlternateDataStream(_STIXBase):
|
||||
class AlternateDataStream(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_nbqgazg6fsma>`__.
|
||||
"""
|
||||
|
@ -259,7 +259,7 @@ class RasterImageExt(_Extension):
|
|||
])
|
||||
|
||||
|
||||
class WindowsPEOptionalHeaderType(_STIXBase):
|
||||
class WindowsPEOptionalHeaderType(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_wyp5qdc2wugy>`__.
|
||||
"""
|
||||
|
@ -303,7 +303,7 @@ class WindowsPEOptionalHeaderType(_STIXBase):
|
|||
self._check_at_least_one_property()
|
||||
|
||||
|
||||
class WindowsPESection(_STIXBase):
|
||||
class WindowsPESection(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_wiqw87xsov3t>`__.
|
||||
"""
|
||||
|
@ -345,7 +345,7 @@ class File(_Observable):
|
|||
|
||||
_type = 'file'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
('size', IntegerProperty(min=0)),
|
||||
|
@ -380,7 +380,7 @@ class IPv4Address(_Observable):
|
|||
|
||||
_type = 'ipv4-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('value', StringProperty(required=True)),
|
||||
('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))),
|
||||
|
@ -401,7 +401,7 @@ class IPv6Address(_Observable):
|
|||
|
||||
_type = 'ipv6-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('value', StringProperty(required=True)),
|
||||
('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))),
|
||||
|
@ -422,7 +422,7 @@ class MACAddress(_Observable):
|
|||
|
||||
_type = 'mac-addr'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('value', StringProperty(required=True)),
|
||||
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
|
||||
|
@ -441,7 +441,7 @@ class Mutex(_Observable):
|
|||
|
||||
_type = 'mutex'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('name', StringProperty(required=True)),
|
||||
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
|
||||
|
@ -562,7 +562,7 @@ class NetworkTraffic(_Observable):
|
|||
|
||||
_type = 'network-traffic'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('start', TimestampProperty()),
|
||||
('end', TimestampProperty()),
|
||||
|
@ -684,7 +684,7 @@ class Process(_Observable):
|
|||
|
||||
_type = 'process'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('is_hidden', BooleanProperty()),
|
||||
('pid', IntegerProperty()),
|
||||
|
@ -728,7 +728,7 @@ class Software(_Observable):
|
|||
|
||||
_type = 'software'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('name', StringProperty(required=True)),
|
||||
('cpe', StringProperty()),
|
||||
|
@ -752,7 +752,7 @@ class URL(_Observable):
|
|||
|
||||
_type = 'url'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('value', StringProperty(required=True)),
|
||||
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
|
||||
|
@ -785,7 +785,7 @@ class UserAccount(_Observable):
|
|||
|
||||
_type = 'user-account'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('user_id', StringProperty()),
|
||||
('credential', StringProperty()),
|
||||
|
@ -810,7 +810,7 @@ class UserAccount(_Observable):
|
|||
_id_contributing_properties = ["account_type", "user_id", "account_login"]
|
||||
|
||||
|
||||
class WindowsRegistryValueType(_STIXBase):
|
||||
class WindowsRegistryValueType(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_6jiqabgqp2hp>`__.
|
||||
"""
|
||||
|
@ -846,7 +846,7 @@ class WindowsRegistryKey(_Observable):
|
|||
|
||||
_type = 'windows-registry-key'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('key', StringProperty()),
|
||||
('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))),
|
||||
|
@ -863,7 +863,7 @@ class WindowsRegistryKey(_Observable):
|
|||
_id_contributing_properties = ["key", "values"]
|
||||
|
||||
|
||||
class X509V3ExtenstionsType(_STIXBase):
|
||||
class X509V3ExtenstionsType(_STIXBase21):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_c1kt4dheb6vz>`__.
|
||||
"""
|
||||
|
@ -896,7 +896,7 @@ class X509Certificate(_Observable):
|
|||
|
||||
_type = 'x509-certificate'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('is_self_signed', BooleanProperty()),
|
||||
('hashes', HashesProperty(spec_version='2.1')),
|
||||
|
@ -948,12 +948,12 @@ def CustomObservable(type='x-custom-observable', properties=None, id_contrib_pro
|
|||
"""
|
||||
def wrapper(cls):
|
||||
_properties = list(itertools.chain.from_iterable([
|
||||
[('type', TypeProperty(type))],
|
||||
[('type', TypeProperty(type, spec_version='2.1'))],
|
||||
[('id', IDProperty(type, spec_version='2.1'))],
|
||||
properties,
|
||||
[('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=type))],
|
||||
]))
|
||||
return _custom_observable_builder(cls, type, _properties, '2.1', id_contrib_props)
|
||||
return _custom_observable_builder(cls, type, _properties, '2.1', _Observable, id_contrib_props)
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -961,5 +961,5 @@ def CustomExtension(observable=None, type='x-custom-observable-ext', properties=
|
|||
"""Decorator for custom extensions to STIX Cyber Observables.
|
||||
"""
|
||||
def wrapper(cls):
|
||||
return _custom_extension_builder(cls, observable, type, properties, '2.1')
|
||||
return _custom_extension_builder(cls, observable, type, properties, '2.1', _Extension)
|
||||
return wrapper
|
||||
|
|
156
stix2/v21/sdo.py
156
stix2/v21/sdo.py
|
@ -7,7 +7,6 @@ import warnings
|
|||
from six.moves.urllib.parse import quote_plus
|
||||
from stix2patterns.validator import run_validator
|
||||
|
||||
from ..core import STIXDomainObject
|
||||
from ..custom import _custom_object_builder
|
||||
from ..exceptions import (
|
||||
InvalidValueError, PropertyPresenceError, STIXDeprecationWarning,
|
||||
|
@ -18,22 +17,23 @@ from ..properties import (
|
|||
StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW
|
||||
from .base import _DomainObject
|
||||
from .common import ExternalReference, GranularMarking, KillChainPhase
|
||||
|
||||
|
||||
class AttackPattern(STIXDomainObject):
|
||||
class AttackPattern(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_4ohsa4pay4h4>`__.
|
||||
"""
|
||||
|
||||
_type = 'attack-pattern'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
|
@ -48,19 +48,19 @@ class AttackPattern(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Campaign(STIXDomainObject):
|
||||
class Campaign(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_vvysvm8mt434>`__.
|
||||
"""
|
||||
|
||||
_type = 'campaign'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
|
@ -87,19 +87,19 @@ class Campaign(STIXDomainObject):
|
|||
raise ValueError(msg.format(self))
|
||||
|
||||
|
||||
class CourseOfAction(STIXDomainObject):
|
||||
class CourseOfAction(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_d5yf99f0a230>`__.
|
||||
"""
|
||||
|
||||
_type = 'course-of-action'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
|
@ -112,18 +112,18 @@ class CourseOfAction(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Grouping(STIXDomainObject):
|
||||
class Grouping(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_9e3uldaqqha2>`__.
|
||||
"""
|
||||
|
||||
_type = 'grouping'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -139,19 +139,19 @@ class Grouping(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Identity(STIXDomainObject):
|
||||
class Identity(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_ru8fmldl2p6w>`__.
|
||||
"""
|
||||
|
||||
_type = 'identity'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('roles', ListProperty(StringProperty)),
|
||||
|
@ -168,19 +168,19 @@ class Identity(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Indicator(STIXDomainObject):
|
||||
class Indicator(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_wfiae74706sw>`__.
|
||||
"""
|
||||
|
||||
_type = 'indicator'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('indicator_types', ListProperty(StringProperty)),
|
||||
|
@ -204,7 +204,7 @@ class Indicator(STIXDomainObject):
|
|||
if kwargs.get('pattern') and kwargs.get('pattern_type') == 'stix' and not kwargs.get('pattern_version'):
|
||||
kwargs['pattern_version'] = '2.1'
|
||||
|
||||
super(STIXDomainObject, self).__init__(*args, **kwargs)
|
||||
super(_DomainObject, self).__init__(*args, **kwargs)
|
||||
|
||||
def _check_object_constraints(self):
|
||||
super(Indicator, self)._check_object_constraints()
|
||||
|
@ -227,19 +227,19 @@ class Indicator(STIXDomainObject):
|
|||
raise InvalidValueError(self.__class__, 'pattern', str(errors[0]))
|
||||
|
||||
|
||||
class Infrastructure(STIXDomainObject):
|
||||
class Infrastructure(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_l2alfbbcmfep>`__.
|
||||
"""
|
||||
|
||||
_type = 'infrastructure'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
('confidence', IntegerProperty()),
|
||||
|
@ -267,19 +267,19 @@ class Infrastructure(STIXDomainObject):
|
|||
raise ValueError(msg.format(self))
|
||||
|
||||
|
||||
class IntrusionSet(STIXDomainObject):
|
||||
class IntrusionSet(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_ticprjb32bc4>`__.
|
||||
"""
|
||||
|
||||
_type = 'intrusion-set'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('aliases', ListProperty(StringProperty)),
|
||||
|
@ -309,19 +309,19 @@ class IntrusionSet(STIXDomainObject):
|
|||
raise ValueError(msg.format(self))
|
||||
|
||||
|
||||
class Location(STIXDomainObject):
|
||||
class Location(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_sqez6sri9vtz>`__.
|
||||
"""
|
||||
|
||||
_type = 'location'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('latitude', FloatProperty(min=-90.0, max=90.0)),
|
||||
|
@ -416,19 +416,19 @@ class Location(STIXDomainObject):
|
|||
return final_url
|
||||
|
||||
|
||||
class Malware(STIXDomainObject):
|
||||
class Malware(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_gc4ooz6oaz7y>`__.
|
||||
"""
|
||||
|
||||
_type = 'malware'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty()),
|
||||
('description', StringProperty()),
|
||||
('malware_types', ListProperty(StringProperty)),
|
||||
|
@ -468,18 +468,18 @@ class Malware(STIXDomainObject):
|
|||
)
|
||||
|
||||
|
||||
class MalwareAnalysis(STIXDomainObject):
|
||||
class MalwareAnalysis(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_dw67pa20zss5>`__.
|
||||
"""
|
||||
|
||||
_type = 'malware-analysis'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
('labels', ListProperty(StringProperty)),
|
||||
|
@ -512,19 +512,19 @@ class MalwareAnalysis(STIXDomainObject):
|
|||
self._check_at_least_one_property(["result", "analysis_sco_refs"])
|
||||
|
||||
|
||||
class Note(STIXDomainObject):
|
||||
class Note(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_hr77jvcbs9jk>`__.
|
||||
"""
|
||||
|
||||
_type = 'note'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('abstract', StringProperty()),
|
||||
('content', StringProperty(required=True)),
|
||||
('authors', ListProperty(StringProperty)),
|
||||
|
@ -539,19 +539,19 @@ class Note(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class ObservedData(STIXDomainObject):
|
||||
class ObservedData(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_h1590esrzg5f>`__.
|
||||
"""
|
||||
|
||||
_type = 'observed-data'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('first_observed', TimestampProperty(required=True)),
|
||||
('last_observed', TimestampProperty(required=True)),
|
||||
('number_observed', IntegerProperty(min=1, max=999999999, required=True)),
|
||||
|
@ -594,19 +594,19 @@ class ObservedData(STIXDomainObject):
|
|||
)
|
||||
|
||||
|
||||
class Opinion(STIXDomainObject):
|
||||
class Opinion(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_sr2hswmu5t1>`__.
|
||||
"""
|
||||
|
||||
_type = 'opinion'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('explanation', StringProperty()),
|
||||
('authors', ListProperty(StringProperty)),
|
||||
(
|
||||
|
@ -631,19 +631,19 @@ class Opinion(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Report(STIXDomainObject):
|
||||
class Report(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_ha4fpad0r9pf>`__.
|
||||
"""
|
||||
|
||||
_type = 'report'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('report_types', ListProperty(StringProperty)),
|
||||
|
@ -664,19 +664,19 @@ class Report(STIXDomainObject):
|
|||
super(Report, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class ThreatActor(STIXDomainObject):
|
||||
class ThreatActor(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_2wowmlcbkqst>`__.
|
||||
"""
|
||||
|
||||
_type = 'threat-actor'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('threat_actor_types', ListProperty(StringProperty)),
|
||||
|
@ -710,19 +710,19 @@ class ThreatActor(STIXDomainObject):
|
|||
raise ValueError(msg.format(self))
|
||||
|
||||
|
||||
class Tool(STIXDomainObject):
|
||||
class Tool(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_m21z3a1f3lou>`__.
|
||||
"""
|
||||
|
||||
_type = 'tool'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('tool_types', ListProperty(StringProperty)),
|
||||
|
@ -739,19 +739,19 @@ class Tool(STIXDomainObject):
|
|||
])
|
||||
|
||||
|
||||
class Vulnerability(STIXDomainObject):
|
||||
class Vulnerability(_DomainObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_d9f0iay06wtx>`__.
|
||||
"""
|
||||
|
||||
_type = 'vulnerability'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('name', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('revoked', BooleanProperty(default=lambda: False)),
|
||||
|
@ -796,12 +796,12 @@ def CustomObject(type='x-custom-type', properties=None):
|
|||
def wrapper(cls):
|
||||
_properties = list(itertools.chain.from_iterable([
|
||||
[
|
||||
('type', TypeProperty(type)),
|
||||
('type', TypeProperty(type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
],
|
||||
[x for x in properties if not x[0].startswith('x_')],
|
||||
[
|
||||
|
@ -815,6 +815,6 @@ def CustomObject(type='x-custom-type', properties=None):
|
|||
],
|
||||
sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
|
||||
]))
|
||||
return _custom_object_builder(cls, type, _properties, '2.1')
|
||||
return _custom_object_builder(cls, type, _properties, '2.1', _DomainObject)
|
||||
|
||||
return wrapper
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..core import STIXRelationshipObject
|
||||
from ..properties import (
|
||||
BooleanProperty, IDProperty, IntegerProperty, ListProperty,
|
||||
ReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
|
||||
)
|
||||
from ..utils import NOW
|
||||
from .base import _RelationshipObject
|
||||
from .common import ExternalReference, GranularMarking
|
||||
|
||||
|
||||
class Relationship(STIXRelationshipObject):
|
||||
class Relationship(_RelationshipObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_al0fb8fcd9e7>`__.
|
||||
"""
|
||||
|
@ -20,12 +20,12 @@ class Relationship(STIXRelationshipObject):
|
|||
|
||||
_type = 'relationship'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('relationship_type', StringProperty(required=True)),
|
||||
('description', StringProperty()),
|
||||
('source_ref', ReferenceProperty(invalid_types=_invalid_source_target_types, spec_version='2.1', required=True)),
|
||||
|
@ -70,19 +70,19 @@ class Relationship(STIXRelationshipObject):
|
|||
raise ValueError(msg.format(self))
|
||||
|
||||
|
||||
class Sighting(STIXRelationshipObject):
|
||||
class Sighting(_RelationshipObject):
|
||||
"""For more detailed information on this object's properties, see
|
||||
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_7p0n81ikux8f>`__.
|
||||
"""
|
||||
|
||||
_type = 'sighting'
|
||||
_properties = OrderedDict([
|
||||
('type', TypeProperty(_type)),
|
||||
('type', TypeProperty(_type, spec_version='2.1')),
|
||||
('spec_version', StringProperty(fixed='2.1')),
|
||||
('id', IDProperty(_type, spec_version='2.1')),
|
||||
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
|
||||
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
|
||||
('description', StringProperty()),
|
||||
('first_seen', TimestampProperty()),
|
||||
('last_seen', TimestampProperty()),
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "1.3.1"
|
||||
__version__ = "1.4.0"
|
||||
|
|
Loading…
Reference in New Issue