Merge branch 'master' of github.com:oasis-open/cti-python-stix2

master
chrisr3d 2020-04-15 16:26:20 +02:00
commit 8e95dbfce2
No known key found for this signature in database
GPG Key ID: 6BBED1B63A6D639F
56 changed files with 1869 additions and 897 deletions

View File

@ -1,4 +1,4 @@
sudo: false os: linux
language: python language: python
cache: pip cache: pip
dist: xenial dist: xenial

View File

@ -1,6 +1,12 @@
CHANGELOG 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 1.3.1 - 2020-03-06
* #322 Adds encoding option FileSystemSource and MemorySource * #322 Adds encoding option FileSystemSource and MemorySource

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 1.3.1 current_version = 1.4.0
commit = True commit = True
tag = True tag = True

View File

@ -51,6 +51,7 @@ setup(
keywords='stix stix2 json cti cyber threat intelligence', keywords='stix stix2 json cti cyber threat intelligence',
packages=find_packages(exclude=['*.test', '*.test.*']), packages=find_packages(exclude=['*.test', '*.test.*']),
install_requires=[ install_requires=[
'enum34 ; python_version<"3.4"',
'python-dateutil', 'python-dateutil',
'pytz', 'pytz',
'requests', 'requests',

View File

@ -23,7 +23,6 @@
DEFAULT_VERSION = '2.0' # Default version will always be the latest STIX 2.X version DEFAULT_VERSION = '2.0' # Default version will always be the latest STIX 2.X version
from .confidence import scales from .confidence import scales
from .core import _collect_stix2_mappings, parse, parse_observable
from .datastore import CompositeDataSource from .datastore import CompositeDataSource
from .datastore.filesystem import ( from .datastore.filesystem import (
FileSystemSink, FileSystemSource, FileSystemStore, FileSystemSink, FileSystemSource, FileSystemStore,
@ -38,6 +37,7 @@ from .markings import (
add_markings, clear_markings, get_markings, is_marked, remove_markings, add_markings, clear_markings, get_markings, is_marked, remove_markings,
set_markings, set_markings,
) )
from .parsing import _collect_stix2_mappings, parse, parse_observable
from .patterns import ( from .patterns import (
AndBooleanExpression, AndObservationExpression, BasicObjectPathComponent, AndBooleanExpression, AndObservationExpression, BasicObjectPathComponent,
BinaryConstant, BooleanConstant, EqualityComparisonExpression, BinaryConstant, BooleanConstant, EqualityComparisonExpression,

View File

@ -2,11 +2,13 @@
import copy import copy
import datetime as dt import datetime as dt
import re
import uuid import uuid
import simplejson as json import simplejson as json
import six import six
import stix2
from stix2.canonicalization.Canonicalize import canonicalize from stix2.canonicalization.Canonicalize import canonicalize
from .exceptions import ( from .exceptions import (
@ -14,8 +16,11 @@ from .exceptions import (
ImmutableError, InvalidObjRefError, InvalidValueError, ImmutableError, InvalidObjRefError, InvalidValueError,
MissingPropertiesError, MutuallyExclusivePropertiesError, MissingPropertiesError, MutuallyExclusivePropertiesError,
) )
from .markings import _MarkingsMixin
from .markings.utils import validate 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 new_version as _new_version
from .utils import revoke as _revoke from .utils import revoke as _revoke
@ -158,12 +163,23 @@ class _STIXBase(Mapping):
custom_props = kwargs.pop('custom_properties', {}) custom_props = kwargs.pop('custom_properties', {})
if custom_props and not isinstance(custom_props, dict): if custom_props and not isinstance(custom_props, dict):
raise ValueError("'custom_properties' must be a dictionary") raise ValueError("'custom_properties' must be a dictionary")
if not self._allow_custom:
extra_kwargs = list(set(kwargs) - set(self._properties)) extra_kwargs = list(set(kwargs) - set(self._properties))
if extra_kwargs: if extra_kwargs and not self._allow_custom:
raise ExtraPropertiesError(cls, extra_kwargs) raise ExtraPropertiesError(cls, extra_kwargs)
if custom_props:
# because allow_custom is true, any extra kwargs are custom
if custom_props or extra_kwargs:
self._allow_custom = True 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) # Remove any keyword arguments whose value is None or [] (i.e. empty list)
setting_kwargs = {} setting_kwargs = {}
@ -307,6 +323,14 @@ class _STIXBase(Mapping):
return json.dumps(self, cls=STIXJSONEncoder, **kwargs) return json.dumps(self, cls=STIXJSONEncoder, **kwargs)
class _DomainObject(_STIXBase, _MarkingsMixin):
pass
class _RelationshipObject(_STIXBase, _MarkingsMixin):
pass
class _Observable(_STIXBase): class _Observable(_STIXBase):
def __init__(self, **kwargs): def __init__(self, **kwargs):

View File

@ -1,140 +1,91 @@
from collections import OrderedDict from collections import OrderedDict
import re
import six import six
from .base import _cls_init, _Extension, _Observable, _STIXBase from .base import _cls_init
from .core import ( from .parsing import (
STIXDomainObject, _register_marking, _register_object, _register_marking, _register_object, _register_observable,
_register_observable, _register_observable_extension, _register_observable_extension,
) )
from .utils import TYPE_REGEX, get_class_hierarchy_names
def _custom_object_builder(cls, type, properties, version): def _get_properties_dict(properties):
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):
try: try:
prop_dict = OrderedDict(properties) return OrderedDict(properties)
except TypeError as e: except TypeError as e:
six.raise_from( six.raise_from(
ValueError( ValueError(
"Extension properties must be dict-like, e.g. a list " "properties must be dict-like, e.g. a list "
"containing tuples. For example, " "containing tuples. For example, "
"[('property1', IntegerProperty())]", "[('property1', IntegerProperty())]",
), ),
e, 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 _type = type
_properties = prop_dict _properties = prop_dict
def __init__(self, **kwargs): 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) _cls_init(cls, self, kwargs)
_register_observable_extension(observable, _CustomExtension, version=version) _register_observable_extension(observable, _CustomExtension, version=version)

View File

@ -10,11 +10,11 @@ import six
from stix2 import v20, v21 from stix2 import v20, v21
from stix2.base import _STIXBase from stix2.base import _STIXBase
from stix2.core import parse
from stix2.datastore import ( from stix2.datastore import (
DataSink, DataSource, DataSourceError, DataStoreMixin, DataSink, DataSource, DataSourceError, DataStoreMixin,
) )
from stix2.datastore.filters import Filter, FilterSet, apply_common_filters 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 from stix2.utils import format_datetime, get_type_from_id

View File

@ -7,9 +7,9 @@ import os
from stix2 import v20, v21 from stix2 import v20, v21
from stix2.base import _STIXBase from stix2.base import _STIXBase
from stix2.core import parse
from stix2.datastore import DataSink, DataSource, DataStoreMixin from stix2.datastore import DataSink, DataSource, DataStoreMixin
from stix2.datastore.filters import FilterSet, apply_common_filters from stix2.datastore.filters import FilterSet, apply_common_filters
from stix2.parsing import parse
def _add(store, stix_data, allow_custom=True, version=None): def _add(store, stix_data, allow_custom=True, version=None):

View File

@ -4,15 +4,15 @@ from requests.exceptions import HTTPError
from stix2 import v20, v21 from stix2 import v20, v21
from stix2.base import _STIXBase from stix2.base import _STIXBase
from stix2.core import parse
from stix2.datastore import ( from stix2.datastore import (
DataSink, DataSource, DataSourceError, DataStoreMixin, DataSink, DataSource, DataSourceError, DataStoreMixin,
) )
from stix2.datastore.filters import Filter, FilterSet, apply_common_filters from stix2.datastore.filters import Filter, FilterSet, apply_common_filters
from stix2.parsing import parse
from stix2.utils import deduplicate from stix2.utils import deduplicate
try: try:
from taxii2client import ValidationError from taxii2client.exceptions import ValidationError
_taxii2_client = True _taxii2_client = True
except ImportError: except ImportError:
_taxii2_client = False _taxii2_client = False

View File

@ -4,8 +4,8 @@ import copy
import logging import logging
import time import time
from .core import parse as _parse
from .datastore import CompositeDataSource, DataStoreMixin from .datastore import CompositeDataSource, DataStoreMixin
from .parsing import parse as _parse
from .utils import STIXdatetime, parse_into_datetime from .utils import STIXdatetime, parse_into_datetime
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -233,3 +233,16 @@ class STIXDeprecationWarning(DeprecationWarning):
Represents usage of a deprecated component of a STIX specification. Represents usage of a deprecated component of a STIX specification.
""" """
pass 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)

View File

@ -7,39 +7,13 @@ import re
import stix2 import stix2
from .base import _Observable, _STIXBase from .base import _DomainObject, _Observable
from .exceptions import ParseError from .exceptions import DuplicateRegistrationError, ParseError
from .markings import _MarkingsMixin from .utils import PREFIX_21_REGEX, _get_dict, get_class_hierarchy_names
from .utils import SCO21_EXT_REGEX, TYPE_REGEX, _get_dict
STIX2_OBJ_MAPS = {} 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): def parse(data, allow_custom=False, interoperability=False, version=None):
"""Convert a string, dict or file-like object into a STIX object. """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) 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. """Register a custom STIX Object type.
Args: 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 version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version. 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: if version:
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
@ -234,10 +227,12 @@ def _register_object(new_type, version=None):
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP = STIX2_OBJ_MAPS[v]['objects'] 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 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. """Register a custom STIX Marking Definition type.
Args: Args:
@ -246,6 +241,17 @@ def _register_marking(new_marking, version=None):
None, use latest version. 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: if version:
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
@ -253,10 +259,12 @@ def _register_marking(new_marking, version=None):
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP_MARKING = STIX2_OBJ_MAPS[v]['markings'] 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. """Register a custom STIX Cyber Observable type.
Args: Args:
@ -265,6 +273,39 @@ def _register_observable(new_observable, version=None):
None, use latest version. 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: if version:
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
@ -272,6 +313,8 @@ def _register_observable(new_observable, version=None):
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP_OBSERVABLE = STIX2_OBJ_MAPS[v]['observables'] 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 OBJ_MAP_OBSERVABLE[new_observable._type] = new_observable
@ -291,30 +334,12 @@ def _register_observable_extension(
obs_class = observable if isinstance(observable, type) else \ obs_class = observable if isinstance(observable, type) else \
type(observable) type(observable)
ext_type = new_extension._type ext_type = new_extension._type
properties = new_extension._properties
if not issubclass(obs_class, _Observable): if not issubclass(obs_class, _Observable):
raise ValueError("'observable' must be a valid Observable class!") raise ValueError("'observable' must be a valid Observable class!")
if version == "2.0": stix2.properties._validate_type(ext_type, version)
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,
)
if not new_extension._properties: if not new_extension._properties:
raise ValueError( raise ValueError(
@ -322,6 +347,17 @@ def _register_observable_extension(
ext_type, 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('.', '') v = 'v' + version.replace('.', '')
try: try:
@ -336,6 +372,8 @@ def _register_observable_extension(
EXT_MAP = STIX2_OBJ_MAPS[v]['observable-extensions'] EXT_MAP = STIX2_OBJ_MAPS[v]['observable-extensions']
try: try:
if ext_type in EXT_MAP[observable_type].keys():
raise DuplicateRegistrationError("Observable Extension", ext_type)
EXT_MAP[observable_type][ext_type] = new_extension EXT_MAP[observable_type][ext_type] = new_extension
except KeyError: except KeyError:
if observable_type not in OBJ_MAP_OBSERVABLE: if observable_type not in OBJ_MAP_OBSERVABLE:

View File

@ -2,11 +2,19 @@ import importlib
import inspect import inspect
from stix2patterns.exceptions import ParseException from stix2patterns.exceptions import ParseException
from stix2patterns.grammars.STIXPatternParser import ( from stix2patterns.grammars.STIXPatternParser import TerminalNode
STIXPatternParser, TerminalNode, from stix2patterns.v20.grammars.STIXPatternParser import \
) STIXPatternParser as STIXPatternParser20
from stix2patterns.grammars.STIXPatternVisitor import STIXPatternVisitor from stix2patterns.v20.grammars.STIXPatternVisitor import \
from stix2patterns.v20.pattern import Pattern 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 *
from .patterns import _BooleanExpression from .patterns import _BooleanExpression
@ -32,23 +40,12 @@ def remove_terminal_nodes(parse_tree_nodes):
return values return values
# This class defines a complete generic visitor for a parse tree produced by STIXPatternParser.
class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
class STIXPatternVisitorForSTIX2():
classes = {} 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): def get_class(self, class_name):
if class_name in STIXPatternVisitorForSTIX2.classes: if class_name in STIXPatternVisitorForSTIX2.classes:
return STIXPatternVisitorForSTIX2.classes[class_name] return STIXPatternVisitorForSTIX2.classes[class_name]
@ -106,7 +103,10 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
# Visit a parse tree produced by STIXPatternParser#observationExpressionCompound. # Visit a parse tree produced by STIXPatternParser#observationExpressionCompound.
def visitObservationExpressionCompound(self, ctx): def visitObservationExpressionCompound(self, ctx):
children = self.visitChildren(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. # Visit a parse tree produced by STIXPatternParser#observationExpressionWithin.
def visitObservationExpressionWithin(self, ctx): def visitObservationExpressionWithin(self, ctx):
@ -147,7 +147,7 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
def visitPropTestEqual(self, ctx): def visitPropTestEqual(self, ctx):
children = self.visitChildren(ctx) children = self.visitChildren(ctx)
operator = children[1].symbol.type operator = children[1].symbol.type
negated = operator != STIXPatternParser.EQ negated = operator != self.parser_class.EQ
return self.instantiate( return self.instantiate(
"EqualityComparisonExpression", children[0], children[3 if len(children) > 3 else 2], "EqualityComparisonExpression", children[0], children[3 if len(children) > 3 else 2],
negated, negated,
@ -157,22 +157,22 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
def visitPropTestOrder(self, ctx): def visitPropTestOrder(self, ctx):
children = self.visitChildren(ctx) children = self.visitChildren(ctx)
operator = children[1].symbol.type operator = children[1].symbol.type
if operator == STIXPatternParser.GT: if operator == self.parser_class.GT:
return self.instantiate( return self.instantiate(
"GreaterThanComparisonExpression", children[0], "GreaterThanComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False, children[3 if len(children) > 3 else 2], False,
) )
elif operator == STIXPatternParser.LT: elif operator == self.parser_class.LT:
return self.instantiate( return self.instantiate(
"LessThanComparisonExpression", children[0], "LessThanComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False, children[3 if len(children) > 3 else 2], False,
) )
elif operator == STIXPatternParser.GE: elif operator == self.parser_class.GE:
return self.instantiate( return self.instantiate(
"GreaterThanEqualComparisonExpression", children[0], "GreaterThanEqualComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False, children[3 if len(children) > 3 else 2], False,
) )
elif operator == STIXPatternParser.LE: elif operator == self.parser_class.LE:
return self.instantiate( return self.instantiate(
"LessThanEqualComparisonExpression", children[0], "LessThanEqualComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False, children[3 if len(children) > 3 else 2], False,
@ -294,22 +294,22 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
return children[0] return children[0]
def visitTerminal(self, node): 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()) 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()) 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) 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) 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] == "'": if node.getText()[0] == "'" and node.getText()[-1] == "'":
return StringConstant(node.getText()[1:-1], from_parse_tree=True) return StringConstant(node.getText()[1:-1], from_parse_tree=True)
else: else:
raise ParseException("The pattern does not start and end with a single quote") 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()) return BooleanConstant(node.getText())
elif node.symbol.type == STIXPatternParser.TimestampLiteral: elif node.symbol.type == self.parser_class.TimestampLiteral:
return TimestampConstant(node.getText()) return TimestampConstant(node.getText())
else: else:
return node return node
@ -321,12 +321,51 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
aggregate = [nextResult] aggregate = [nextResult]
return aggregate 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. Create a STIX pattern AST from a pattern string.
""" """
pattern_obj = Pattern(pattern) if version == "2.1":
builder = STIXPatternVisitorForSTIX2(module_suffix, module_name) 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) return pattern_obj.visit(builder)

View File

@ -551,7 +551,7 @@ class ObservationExpression(_PatternExpression):
self.operand = operand self.operand = operand
def __str__(self): def __str__(self):
return "[%s]" % self.operand return "%s" % self.operand if isinstance(self.operand, (ObservationExpression, _CompoundObservationExpression)) else "[%s]" % self.operand
class _CompoundObservationExpression(_PatternExpression): class _CompoundObservationExpression(_PatternExpression):

View File

@ -12,12 +12,15 @@ from six import string_types, text_type
import stix2 import stix2
from .base import _STIXBase from .base import _STIXBase
from .core import STIX2_OBJ_MAPS, parse, parse_observable
from .exceptions import ( from .exceptions import (
CustomContentError, DictionaryKeyError, MissingPropertiesError, CustomContentError, DictionaryKeyError, MissingPropertiesError,
MutuallyExclusivePropertiesError, 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}-" ID_REGEX_interoperability = re.compile(r"[0-9a-fA-F]{8}-"
"[0-9a-fA-F]{4}-" "[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_)) 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): class Property(object):
"""Represent a property of STIX data type. """Represent a property of STIX data type.
@ -241,7 +274,9 @@ class StringProperty(Property):
class TypeProperty(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) super(TypeProperty, self).__init__(fixed=type)
@ -333,12 +368,16 @@ class BooleanProperty(Property):
class TimestampProperty(Property): class TimestampProperty(Property):
def __init__(self, precision=None, **kwargs): def __init__(self, precision="any", precision_constraint="exact", **kwargs):
self.precision = precision self.precision = precision
self.precision_constraint = precision_constraint
super(TimestampProperty, self).__init__(**kwargs) super(TimestampProperty, self).__init__(**kwargs)
def clean(self, value): def clean(self, value):
return parse_into_datetime(value, self.precision) return parse_into_datetime(
value, self.precision, self.precision_constraint,
)
class DictionaryProperty(Property): class DictionaryProperty(Property):
@ -650,7 +689,7 @@ class STIXObjectProperty(Property):
def clean(self, value): def clean(self, value):
# Any STIX Object (SDO, SRO, or Marking Definition) can be added to # Any STIX Object (SDO, SRO, or Marking Definition) can be added to
# a bundle with no further checks. # 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)): for x in get_class_hierarchy_names(value)):
# A simple "is this a spec version 2.1+ object" test. For now, # 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 # limit 2.0 bundles to 2.0 objects. It's not possible yet to

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import pytest import pytest
from stix2.core import _detect_spec_version from stix2.parsing import _detect_spec_version
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -114,6 +114,42 @@ def stix_objs1():
return [ind1, ind2, ind3, ind4, ind5] 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 @pytest.fixture
def stix_objs2(): def stix_objs2():
ind6 = { ind6 = {

View File

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

View File

@ -1,9 +1,10 @@
import pytest import pytest
import stix2 import stix2
from stix2 import parsing
import stix2.v20 import stix2.v20
from ...exceptions import InvalidValueError from ...exceptions import DuplicateRegistrationError, InvalidValueError
from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID
IDENTITY_CUSTOM_PROP = stix2.v20.Identity( IDENTITY_CUSTOM_PROP = stix2.v20.Identity(
@ -449,7 +450,7 @@ def test_custom_observable_raises_exception():
def test_custom_observable_object_no_init_1(): def test_custom_observable_object_no_init_1():
@stix2.v20.CustomObservable( @stix2.v20.CustomObservable(
'x-new-observable', [ 'x-new-observable-1', [
('property1', stix2.properties.StringProperty()), ('property1', stix2.properties.StringProperty()),
], ],
) )
@ -482,7 +483,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs(object): class NewObs(object):
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v20.CustomObservable( @stix2.v20.CustomObservable(
@ -492,7 +493,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs2(object): class NewObs2(object):
pass # pragma: no cover 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(): def test_custom_observable_object_invalid_ref_property():
@ -807,7 +808,7 @@ def test_custom_extension_invalid_type_name():
) )
class FooExtension(): class FooExtension():
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v20.CustomExtension( @stix2.v20.CustomExtension(
@ -817,7 +818,7 @@ def test_custom_extension_invalid_type_name():
) )
class BlaExtension(): class BlaExtension():
pass # pragma: no cover 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(): def test_custom_extension_no_properties():
@ -967,9 +968,8 @@ def test_register_custom_object():
class CustomObject2(object): class CustomObject2(object):
_type = 'awesome-object' _type = 'awesome-object'
stix2.core._register_object(CustomObject2, version="2.0") with pytest.raises(ValueError):
# Note that we will always check against newest OBJ_MAP. stix2.parsing._register_object(CustomObject2, version="2.0")
assert (CustomObject2._type, CustomObject2) in stix2.v20.OBJ_MAP.items()
def test_extension_property_location(): def test_extension_property_location():
@ -1011,3 +1011,123 @@ def test_custom_object_nested_dictionary(data):
) )
assert data == str(example) 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)

View File

@ -635,7 +635,7 @@ def test_filesystem_object_with_custom_property_in_bundle(fs_store):
def test_filesystem_custom_object(fs_store): def test_filesystem_custom_object(fs_store):
@stix2.v20.CustomObject( @stix2.v20.CustomObject(
'x-new-obj', [ 'x-new-obj-2', [
('property1', stix2.properties.StringProperty(required=True)), ('property1', stix2.properties.StringProperty(required=True)),
], ],
) )
@ -650,7 +650,7 @@ def test_filesystem_custom_object(fs_store):
assert newobj_r["property1"] == 'something' assert newobj_r["property1"] == 'something'
# remove dir # 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): def test_relationships(rel_fs_store):

View File

@ -329,7 +329,7 @@ def test_memory_store_object_with_custom_property_in_bundle(mem_store):
def test_memory_store_custom_object(mem_store): def test_memory_store_custom_object(mem_store):
@CustomObject( @CustomObject(
'x-new-obj', [ 'x-new-obj-3', [
('property1', properties.StringProperty(required=True)), ('property1', properties.StringProperty(required=True)),
], ],
) )

View File

@ -4,11 +4,13 @@ from medallion.filters.basic_filter import BasicFilter
import pytest import pytest
from requests.models import Response from requests.models import Response
import six 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 import stix2
from stix2.datastore import DataSourceError from stix2.datastore import DataSourceError
from stix2.datastore.filters import Filter from stix2.datastore.filters import Filter
from stix2.utils import get_timestamp
COLLECTION_URL = 'https://example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/' COLLECTION_URL = 'https://example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/'
@ -21,6 +23,7 @@ class MockTAXIICollectionEndpoint(Collection):
url, collection_info=collection_info, url, collection_info=collection_info,
) )
self.objects = [] self.objects = []
self.manifests = []
def add_objects(self, bundle): def add_objects(self, bundle):
self._verify_can_write() self._verify_can_write()
@ -28,6 +31,14 @@ class MockTAXIICollectionEndpoint(Collection):
bundle = json.loads(bundle, encoding='utf-8') bundle = json.loads(bundle, encoding='utf-8')
for object in bundle.get("objects", []): for object in bundle.get("objects", []):
self.objects.append(object) 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): def get_objects(self, **filter_kwargs):
self._verify_can_read() self._verify_can_read()
@ -37,8 +48,9 @@ class MockTAXIICollectionEndpoint(Collection):
objs = full_filter.process_filter( objs = full_filter.process_filter(
self.objects, self.objects,
("id", "type", "version"), ("id", "type", "version"),
[], self.manifests,
) 100,
)[0]
if objs: if objs:
return stix2.v20.Bundle(objects=objs) return stix2.v20.Bundle(objects=objs)
else: else:
@ -58,8 +70,9 @@ class MockTAXIICollectionEndpoint(Collection):
filtered_objects = full_filter.process_filter( filtered_objects = full_filter.process_filter(
objects, objects,
("version",), ("version",),
[], self.manifests,
) 100,
)[0]
else: else:
filtered_objects = [] filtered_objects = []
if filtered_objects: if filtered_objects:
@ -71,7 +84,7 @@ class MockTAXIICollectionEndpoint(Collection):
@pytest.fixture @pytest.fixture
def collection(stix_objs1): def collection(stix_objs1, stix_objs1_manifests):
mock = MockTAXIICollectionEndpoint( mock = MockTAXIICollectionEndpoint(
COLLECTION_URL, { COLLECTION_URL, {
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116", "id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
@ -86,11 +99,12 @@ def collection(stix_objs1):
) )
mock.objects.extend(stix_objs1) mock.objects.extend(stix_objs1)
mock.manifests.extend(stix_objs1_manifests)
return mock return mock
@pytest.fixture @pytest.fixture
def collection_no_rw_access(stix_objs1): def collection_no_rw_access(stix_objs1, stix_objs1_manifests):
mock = MockTAXIICollectionEndpoint( mock = MockTAXIICollectionEndpoint(
COLLECTION_URL, { COLLECTION_URL, {
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116", "id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
@ -105,6 +119,7 @@ def collection_no_rw_access(stix_objs1):
) )
mock.objects.extend(stix_objs1) mock.objects.extend(stix_objs1)
mock.manifests.extend(stix_objs1_manifests)
return mock return mock

View File

@ -249,14 +249,12 @@ def test_not_registered_marking_raises_exception():
def test_marking_wrong_type_construction(): def test_marking_wrong_type_construction():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError):
# Test passing wrong type for properties. # Test passing wrong type for properties.
@stix2.v20.CustomMarking('x-new-marking-type2', ("a", "b")) @stix2.v20.CustomMarking('x-new-marking-type2', ("a", "b"))
class NewObject3(object): class NewObject3(object):
pass pass
assert str(excinfo.value) == "Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]"
def test_campaign_add_markings(): def test_campaign_add_markings():
campaign = stix2.v20.Campaign( campaign = stix2.v20.Campaign(

View File

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

View File

@ -494,13 +494,14 @@ def test_make_constant_already_a_constant():
def test_parsing_comparison_expression(): 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']" assert str(patt_obj) == "[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']"
def test_parsing_qualified_expression(): def test_parsing_qualified_expression():
patt_obj = create_pattern_object( 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", "[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( assert str(
patt_obj, patt_obj,
@ -508,5 +509,5 @@ def test_parsing_qualified_expression():
def test_list_constant(): 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')]" assert str(patt_obj) == "[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]"

View File

@ -392,7 +392,7 @@ def test_dictionary_property_invalid(d):
def test_property_list_of_dictionary(): def test_property_list_of_dictionary():
@stix2.v20.CustomObject( @stix2.v20.CustomObject(
'x-new-obj', [ 'x-new-obj-4', [
('property1', ListProperty(DictionaryProperty(spec_version="2.0"), required=True)), ('property1', ListProperty(DictionaryProperty(spec_version="2.0"), required=True)),
], ],
) )

View File

@ -135,6 +135,42 @@ def stix_objs1():
return [ind1, ind2, ind3, ind4, ind5] 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 @pytest.fixture
def stix_objs2(): def stix_objs2():
ind6 = { ind6 = {

View File

@ -88,8 +88,8 @@ def test_attack_pattern_invalid_labels():
def test_overly_precise_timestamps(): def test_overly_precise_timestamps():
ap = stix2.v21.AttackPattern( ap = stix2.v21.AttackPattern(
id=ATTACK_PATTERN_ID, id=ATTACK_PATTERN_ID,
created="2016-05-12T08:17:27.0000342Z", created="2016-05-12T08:17:27.000000342Z",
modified="2016-05-12T08:17:27.000287Z", modified="2016-05-12T08:17:27.000000287Z",
name="Spear Phishing", name="Spear Phishing",
external_references=[{ external_references=[{
"source_name": "capec", "source_name": "capec",

View File

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

View File

@ -6,9 +6,11 @@ import stix2
import stix2.base import stix2.base
import stix2.v21 import stix2.v21
from ...exceptions import InvalidValueError from ...exceptions import DuplicateRegistrationError, InvalidValueError
from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID
# Custom Properties in SDOs
IDENTITY_CUSTOM_PROP = stix2.v21.Identity( IDENTITY_CUSTOM_PROP = stix2.v21.Identity(
name="John Smith", name="John Smith",
identity_class="individual", identity_class="individual",
@ -18,6 +20,18 @@ IDENTITY_CUSTOM_PROP = stix2.v21.Identity(
def test_identity_custom_property(): 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: with pytest.raises(ValueError) as excinfo:
stix2.v21.Identity( stix2.v21.Identity(
id=IDENTITY_ID, id=IDENTITY_ID,
@ -43,17 +57,47 @@ def test_identity_custom_property():
) )
assert "Unexpected properties for Identity" in str(excinfo.value) assert "Unexpected properties for Identity" in str(excinfo.value)
identity = stix2.v21.Identity( # leading numeric character is illegal in 2.1
id=IDENTITY_ID,
created="2015-12-21T19:59:11Z", with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
modified="2015-12-21T19:59:11Z", stix2.v21.Identity(
name="John Smith", id=IDENTITY_ID,
identity_class="individual", created="2015-12-21T19:59:11Z",
custom_properties={ modified="2015-12-21T19:59:11Z",
"foo": "bar", name="John Smith",
}, identity_class="individual",
) custom_properties={
assert identity.foo == "bar" "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(): 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 bundle.objects[0].x_foo == "bar"
assert '"x_foo": "bar"' in str(bundle) assert '"x_foo": "bar"' in str(bundle)
# Custom properties in SCOs
def test_custom_property_in_observed_data(): def test_custom_property_in_observed_data():
artifact = stix2.v21.File( artifact = stix2.v21.File(
@ -184,6 +230,18 @@ def test_custom_property_in_observed_data():
assert '"x_foo": "bar"' in str(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(): def test_custom_property_object_in_observable_extension():
ntfs = stix2.v21.NTFSExt( ntfs = stix2.v21.NTFSExt(
allow_custom=True, allow_custom=True,
@ -245,6 +303,8 @@ def test_identity_custom_property_revoke():
identity = IDENTITY_CUSTOM_PROP.revoke() identity = IDENTITY_CUSTOM_PROP.revoke()
assert identity.x_foo == "bar" assert identity.x_foo == "bar"
# Custom markings
def test_identity_custom_property_edit_markings(): def test_identity_custom_property_edit_markings():
marking_obj = stix2.v21.MarkingDefinition( marking_obj = stix2.v21.MarkingDefinition(
@ -267,6 +327,19 @@ def test_identity_custom_property_edit_markings():
identity2.clear_markings('x_foo') 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(): def test_custom_marking_no_init_1():
@stix2.v21.CustomMarking( @stix2.v21.CustomMarking(
'x-new-obj', [ 'x-new-obj', [
@ -293,6 +366,40 @@ def test_custom_marking_no_init_2():
assert no2.property1 == 'something' 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( @stix2.v21.CustomObject(
'x-new-type', [ 'x-new-type', [
('property1', stix2.properties.StringProperty(required=True)), ('property1', stix2.properties.StringProperty(required=True)),
@ -374,6 +481,16 @@ def test_custom_object_invalid_type_name():
pass # pragma: no cover pass # pragma: no cover
assert "Invalid type name 'x_new_object':" in str(excinfo.value) 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(): def test_parse_custom_object_type():
nt_string = """{ 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) custom_obj = stix2.parse(nt_string, version="2.1", allow_custom=True)
assert custom_obj["type"] == "x-foobar-observable" assert custom_obj["type"] == "x-foobar-observable"
# Custom SCOs
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
'x-new-observable', [ 'x-new-observable', [
@ -455,7 +574,7 @@ def test_custom_observable_raises_exception():
def test_custom_observable_object_no_init_1(): def test_custom_observable_object_no_init_1():
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
'x-new-observable', [ 'x-new-observable-2', [
('property1', stix2.properties.StringProperty()), ('property1', stix2.properties.StringProperty()),
], ],
) )
@ -479,6 +598,18 @@ def test_custom_observable_object_no_init_2():
assert no2.property1 == 'something' 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(): def test_custom_observable_object_invalid_type_name():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
@ -488,7 +619,7 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs(object): class NewObs(object):
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable( @stix2.v21.CustomObservable(
@ -498,7 +629,17 @@ def test_custom_observable_object_invalid_type_name():
) )
class NewObs2(object): class NewObs2(object):
pass # pragma: no cover 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(): 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.variant == uuid.RFC_4122
assert uuid_obj.version == 4 assert uuid_obj.version == 4
# Custom Extensions
@stix2.v21.CustomExtension( @stix2.v21.CustomExtension(
stix2.v21.DomainName, 'x-new-ext', [ stix2.v21.DomainName, 'x-new-ext', [
@ -862,7 +1005,7 @@ def test_custom_extension_invalid_type_name():
) )
class FooExtension(): class FooExtension():
pass # pragma: no cover 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: with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomExtension( @stix2.v21.CustomExtension(
@ -872,7 +1015,17 @@ def test_custom_extension_invalid_type_name():
) )
class BlaExtension(): class BlaExtension():
pass # pragma: no cover 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(): def test_custom_extension_no_properties():
@ -922,6 +1075,19 @@ def test_custom_extension_no_init_2():
assert ne2.property1 == "foobar" 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(): def test_parse_observable_with_custom_extension():
input_str = """{ input_str = """{
"type": "domain-name", "type": "domain-name",
@ -1020,9 +1186,9 @@ def test_register_custom_object():
class CustomObject2(object): class CustomObject2(object):
_type = 'awesome-object' _type = 'awesome-object'
stix2.core._register_object(CustomObject2, version="2.1") with pytest.raises(ValueError) as excinfo:
# Note that we will always check against newest OBJ_MAP. stix2.parsing._register_object(CustomObject2, version="2.1")
assert (CustomObject2._type, CustomObject2) in stix2.v21.OBJ_MAP.items() assert '@CustomObject decorator' in str(excinfo)
def test_extension_property_location(): def test_extension_property_location():
@ -1065,3 +1231,110 @@ def test_custom_object_nested_dictionary(data):
) )
assert data == str(example) 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)

View File

@ -656,7 +656,7 @@ def test_filesystem_object_with_custom_property_in_bundle(fs_store):
def test_filesystem_custom_object(fs_store): def test_filesystem_custom_object(fs_store):
@stix2.v21.CustomObject( @stix2.v21.CustomObject(
'x-new-obj', [ 'x-new-obj-2', [
('property1', stix2.properties.StringProperty(required=True)), ('property1', stix2.properties.StringProperty(required=True)),
], ],
) )
@ -671,7 +671,7 @@ def test_filesystem_custom_object(fs_store):
assert newobj_r["property1"] == 'something' assert newobj_r["property1"] == 'something'
# remove dir # 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): def test_relationships(rel_fs_store):

View File

@ -344,7 +344,7 @@ def test_memory_store_object_with_custom_property_in_bundle(mem_store):
def test_memory_store_custom_object(mem_store): def test_memory_store_custom_object(mem_store):
@CustomObject( @CustomObject(
'x-new-obj', [ 'x-new-obj-3', [
('property1', properties.StringProperty(required=True)), ('property1', properties.StringProperty(required=True)),
], ],
) )

View File

@ -4,11 +4,13 @@ from medallion.filters.basic_filter import BasicFilter
import pytest import pytest
from requests.models import Response from requests.models import Response
import six 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 import stix2
from stix2.datastore import DataSourceError from stix2.datastore import DataSourceError
from stix2.datastore.filters import Filter from stix2.datastore.filters import Filter
from stix2.utils import get_timestamp
COLLECTION_URL = 'https://example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/' COLLECTION_URL = 'https://example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/'
@ -21,6 +23,7 @@ class MockTAXIICollectionEndpoint(Collection):
url, collection_info=collection_info, url, collection_info=collection_info,
) )
self.objects = [] self.objects = []
self.manifests = []
def add_objects(self, bundle): def add_objects(self, bundle):
self._verify_can_write() self._verify_can_write()
@ -28,6 +31,14 @@ class MockTAXIICollectionEndpoint(Collection):
bundle = json.loads(bundle, encoding='utf-8') bundle = json.loads(bundle, encoding='utf-8')
for object in bundle.get("objects", []): for object in bundle.get("objects", []):
self.objects.append(object) 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): def get_objects(self, **filter_kwargs):
self._verify_can_read() self._verify_can_read()
@ -37,8 +48,9 @@ class MockTAXIICollectionEndpoint(Collection):
objs = full_filter.process_filter( objs = full_filter.process_filter(
self.objects, self.objects,
("id", "type", "version"), ("id", "type", "version"),
[], self.manifests,
) 100,
)[0]
if objs: if objs:
return stix2.v21.Bundle(objects=objs) return stix2.v21.Bundle(objects=objs)
else: else:
@ -58,8 +70,9 @@ class MockTAXIICollectionEndpoint(Collection):
filtered_objects = full_filter.process_filter( filtered_objects = full_filter.process_filter(
objects, objects,
("version",), ("version",),
[], self.manifests,
) 100,
)[0]
else: else:
filtered_objects = [] filtered_objects = []
if filtered_objects: if filtered_objects:
@ -71,7 +84,7 @@ class MockTAXIICollectionEndpoint(Collection):
@pytest.fixture @pytest.fixture
def collection(stix_objs1): def collection(stix_objs1, stix_objs1_manifests):
mock = MockTAXIICollectionEndpoint( mock = MockTAXIICollectionEndpoint(
COLLECTION_URL, { COLLECTION_URL, {
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116", "id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
@ -86,11 +99,12 @@ def collection(stix_objs1):
) )
mock.objects.extend(stix_objs1) mock.objects.extend(stix_objs1)
mock.manifests.extend(stix_objs1_manifests)
return mock return mock
@pytest.fixture @pytest.fixture
def collection_no_rw_access(stix_objs1): def collection_no_rw_access(stix_objs1, stix_objs1_manifests):
mock = MockTAXIICollectionEndpoint( mock = MockTAXIICollectionEndpoint(
COLLECTION_URL, { COLLECTION_URL, {
"id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116", "id": "91a7b528-80eb-42ed-a74d-c6fbd5a26116",
@ -105,6 +119,7 @@ def collection_no_rw_access(stix_objs1):
) )
mock.objects.extend(stix_objs1) mock.objects.extend(stix_objs1)
mock.manifests.extend(stix_objs1_manifests)
return mock return mock

View File

@ -277,14 +277,12 @@ def test_not_registered_marking_raises_exception():
def test_marking_wrong_type_construction(): def test_marking_wrong_type_construction():
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError):
# Test passing wrong type for properties. # Test passing wrong type for properties.
@stix2.v21.CustomMarking('x-new-marking-type2', ("a", "b")) @stix2.v21.CustomMarking('x-new-marking-type2', ("a", "b"))
class NewObject3(object): class NewObject3(object):
pass pass
assert str(excinfo.value) == "Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]"
def test_campaign_add_markings(): def test_campaign_add_markings():
campaign = stix2.v21.Campaign( campaign = stix2.v21.Campaign(

View File

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

View File

@ -175,20 +175,34 @@ def test_greater_than():
assert str(exp) == "[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.0]" 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(): def test_less_than():
exp = stix2.LessThanComparisonExpression("file:size", 1024) exp = stix2.LessThanComparisonExpression("file:size", 1024)
assert str(exp) == "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(): def test_greater_than_or_equal():
exp = stix2.GreaterThanEqualComparisonExpression( exp = stix2.GreaterThanEqualComparisonExpression(
"file:size", "file:size",
1024, 1024,
) )
assert str(exp) == "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(): def test_less_than_or_equal():
exp = stix2.LessThanEqualComparisonExpression( exp = stix2.LessThanEqualComparisonExpression(
"file:size", "file:size",
@ -197,6 +211,36 @@ def test_less_than_or_equal():
assert str(exp) == "file:size <= 1024" 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(): def test_not():
exp = stix2.LessThanComparisonExpression( exp = stix2.LessThanComparisonExpression(
"file:size", "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 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(): def test_invalid_and_observable_expression():
with pytest.raises(ValueError): with pytest.raises(ValueError):
stix2.AndBooleanExpression([ stix2.AndBooleanExpression([
@ -286,6 +391,11 @@ def test_hex():
assert str(exp) == "[file:mime_type = 'image/bmp' AND file:magic_number_hex = h'ffd8']" 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(): def test_multiple_qualifiers():
exp_and = stix2.AndBooleanExpression([ exp_and = stix2.AndBooleanExpression([
stix2.EqualityComparisonExpression( stix2.EqualityComparisonExpression(
@ -334,6 +444,11 @@ def test_binary():
assert str(exp) == "artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q='" 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(): def test_list():
exp = stix2.InComparisonExpression( exp = stix2.InComparisonExpression(
"process:name", "process:name",
@ -495,29 +610,45 @@ def test_make_constant_already_a_constant():
def test_parsing_comparison_expression(): 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']" 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( 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", "[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( assert str(
patt_obj, patt_obj,
) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS" ) == "[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(): 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')]" 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(): 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\\'']" assert str(patt_obj) == "[file:name = 'weird_name\\'']"
def test_parse_error(): def test_parse_error():
with pytest.raises(ParseException): with pytest.raises(ParseException):
create_pattern_object("[ file: name = 'weirdname]") create_pattern_object("[ file: name = 'weirdname]", version="2.1")

View File

@ -404,7 +404,7 @@ def test_dictionary_property_invalid(d):
def test_property_list_of_dictionary(): def test_property_list_of_dictionary():
@stix2.v21.CustomObject( @stix2.v21.CustomObject(
'x-new-obj', [ 'x-new-obj-4', [
('property1', ListProperty(DictionaryProperty(spec_version='2.1'), required=True)), ('property1', ListProperty(DictionaryProperty(spec_version='2.1'), required=True)),
], ],
) )

View File

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

View File

@ -1,6 +1,9 @@
import datetime
import pytest import pytest
import stix2 import stix2
import stix2.utils
from .constants import CAMPAIGN_MORE_KWARGS from .constants import CAMPAIGN_MORE_KWARGS
@ -236,8 +239,7 @@ def test_remove_custom_stix_property():
mal_nc = stix2.utils.remove_custom_stix(mal) mal_nc = stix2.utils.remove_custom_stix(mal)
assert "x_custom" not in mal_nc assert "x_custom" not in mal_nc
assert (stix2.utils.parse_into_datetime(mal["modified"], precision="millisecond") < assert mal["modified"] < mal_nc["modified"]
stix2.utils.parse_into_datetime(mal_nc["modified"], precision="millisecond"))
def test_remove_custom_stix_object(): 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 len(campaign_v1.keys()) == len(campaign_v2.keys())
assert campaign_v1.id == campaign_v2.id assert campaign_v1.id == campaign_v2.id
assert campaign_v1.description == campaign_v2.description 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

View File

@ -6,12 +6,15 @@ except ImportError:
from collections import Mapping from collections import Mapping
import copy import copy
import datetime as dt import datetime as dt
import enum
import json import json
import re
from dateutil import parser from dateutil import parser
import pytz import pytz
import six
import stix2.base import stix2
from .exceptions import ( from .exceptions import (
InvalidValueError, RevokeError, UnmodifiablePropertyError, InvalidValueError, RevokeError, UnmodifiablePropertyError,
@ -25,13 +28,84 @@ NOW = object()
# STIX object properties that cannot be modified # STIX object properties that cannot be modified
STIX_UNMOD_PROPERTIES = ['created', 'created_by_ref', 'id', 'type'] STIX_UNMOD_PROPERTIES = ['created', 'created_by_ref', 'id', 'type']
TYPE_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$' TYPE_REGEX = re.compile(r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$')
SCO21_EXT_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-ext$' 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): 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): 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 if isinstance(args[0], dt.datetime): # Allow passing in a datetime object
dttm = args[0] dttm = args[0]
args = ( args = (
@ -41,6 +115,7 @@ class STIXdatetime(dt.datetime):
# self will be an instance of STIXdatetime, not dt.datetime # self will be an instance of STIXdatetime, not dt.datetime
self = dt.datetime.__new__(cls, *args, **kwargs) self = dt.datetime.__new__(cls, *args, **kwargs)
self.precision = precision self.precision = precision
self.precision_constraint = precision_constraint
return self return self
def __repr__(self): def __repr__(self):
@ -90,7 +165,7 @@ def format_datetime(dttm):
2. Convert to UTC 2. Convert to UTC
3. Format in ISO format 3. Format in ISO format
4. Ensure correct precision 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" 5. Add "Z"
""" """
@ -101,20 +176,74 @@ def format_datetime(dttm):
else: else:
zoned = dttm.astimezone(pytz.utc) zoned = dttm.astimezone(pytz.utc)
ts = zoned.strftime('%Y-%m-%dT%H:%M:%S') ts = zoned.strftime('%Y-%m-%dT%H:%M:%S')
ms = zoned.strftime('%f') precision = getattr(dttm, 'precision', Precision.ANY)
precision = getattr(dttm, 'precision', None) precision_constraint = getattr(
if precision == 'second': dttm, 'precision_constraint', PrecisionConstraint.EXACT,
pass # Already precise to the second )
elif precision == 'millisecond':
ts = ts + '.' + ms[:3] frac_seconds_str = ""
elif zoned.microsecond > 0: if precision == Precision.ANY:
ts = ts + '.' + ms.rstrip('0') # No need to truncate; ignore constraint
return ts + 'Z' 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): def parse_into_datetime(
"""Parse a value into a valid STIX timestamp object. 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 isinstance(value, dt.date):
if hasattr(value, 'hour'): if hasattr(value, 'hour'):
ts = value ts = value
@ -138,20 +267,23 @@ def parse_into_datetime(value, precision=None):
ts = pytz.utc.localize(parsed) ts = pytz.utc.localize(parsed)
# Ensure correct precision # Ensure correct precision
if not precision: if precision == Precision.SECOND:
return STIXdatetime(ts, precision=precision) if precision_constraint == PrecisionConstraint.EXACT:
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:
ts = ts.replace(microsecond=0) 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): def _get_dict(data):
@ -230,14 +362,12 @@ def find_property_index(obj, search_key, search_value):
Returns: Returns:
int: An index; -1 if the key and value aren't found 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 # Special-case keys which are numbers-as-strings, e.g. for cyber-observable
# mappings. Use the int value of the key as the index. # mappings. Use the int value of the key as the index.
if search_key.isdigit(): if search_key.isdigit():
return int(search_key) 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: if search_key in obj and obj[search_key] == search_value:
idx = _find(obj.object_properties(), search_key) idx = _find(obj.object_properties(), search_key)
else: else:
@ -256,6 +386,39 @@ def find_property_index(obj, search_key, search_value):
return idx 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): def new_version(data, **kwargs):
"""Create a new version of a STIX object, by modifying properties and """Create a new version of a STIX object, by modifying properties and
updating the ``modified`` property. updating the ``modified`` property.
@ -283,12 +446,32 @@ def new_version(data, **kwargs):
if unchangable_properties: if unchangable_properties:
raise UnmodifiablePropertyError(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) cls = type(data)
if 'modified' not in kwargs: 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: elif 'modified' in data:
old_modified_property = parse_into_datetime(data.get('modified'), precision='millisecond') old_modified_property = parse_into_datetime(
new_modified_property = parse_into_datetime(kwargs['modified'], precision='millisecond') 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: if new_modified_property <= old_modified_property:
raise InvalidValueError( raise InvalidValueError(
cls, 'modified', cls, 'modified',
@ -378,11 +561,6 @@ def remove_custom_stix(stix_obj):
new_obj = new_version(stix_obj, **(dict(props))) 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 return new_obj
else: else:

View File

@ -14,6 +14,9 @@
# flake8: noqa # flake8: noqa
from .base import (
_DomainObject, _Extension, _Observable, _RelationshipObject, _STIXBase20,
)
from .bundle import Bundle from .bundle import Bundle
from .common import ( from .common import (
TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking, ExternalReference, TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking, ExternalReference,

25
stix2/v20/base.py Normal file
View File

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

View File

@ -2,20 +2,20 @@
from collections import OrderedDict from collections import OrderedDict
from ..base import _STIXBase
from ..properties import ( from ..properties import (
IDProperty, ListProperty, STIXObjectProperty, StringProperty, TypeProperty, 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 """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>`__. `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' _type = 'bundle'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_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 # Not technically correct: STIX 2.0 spec doesn't say spec_version must
# have this value, but it's all we support for now. # have this value, but it's all we support for now.

View File

@ -5,7 +5,6 @@ import copy
import six import six
from ..base import _STIXBase
from ..custom import _custom_marking_builder from ..custom import _custom_marking_builder
from ..markings import _MarkingsMixin from ..markings import _MarkingsMixin
from ..markings.utils import check_tlp_marking from ..markings.utils import check_tlp_marking
@ -14,6 +13,7 @@ from ..properties import (
SelectorProperty, StringProperty, TimestampProperty, TypeProperty, SelectorProperty, StringProperty, TimestampProperty, TypeProperty,
) )
from ..utils import NOW, _get_dict from ..utils import NOW, _get_dict
from .base import _STIXBase20
def _should_set_millisecond(cr, marking_type): def _should_set_millisecond(cr, marking_type):
@ -31,7 +31,7 @@ def _should_set_millisecond(cr, marking_type):
return False return False
class ExternalReference(_STIXBase): class ExternalReference(_STIXBase20):
"""For more detailed information on this object's properties, see """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>`__. `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']) 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 """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>`__. `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 """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>`__. `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 """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>`__. `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 """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>`__. `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.") 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 """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>`__. `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' _type = 'marking-definition'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW)), ('created', TimestampProperty(default=lambda: NOW)),
@ -188,7 +188,7 @@ def CustomMarking(type='x-custom-marking', properties=None):
""" """
def wrapper(cls): def wrapper(cls):
return _custom_marking_builder(cls, type, properties, '2.0') return _custom_marking_builder(cls, type, properties, '2.0', _STIXBase20)
return wrapper return wrapper

View File

@ -1,14 +1,13 @@
"""STIX 2.0 Cyber Observable Objects. """STIX 2.0 Cyber Observable Objects.
Embedded observable object types, such as Email MIME Component, which is Embedded observable object types, such as Email MIME Component, which is
embedded in Email Message objects, inherit from ``_STIXBase`` instead of embedded in Email Message objects, inherit from ``_STIXBase20`` instead of
Observable and do not have a ``_type`` attribute. _Observable and do not have a ``_type`` attribute.
""" """
from collections import OrderedDict from collections import OrderedDict
import itertools import itertools
from ..base import _Extension, _Observable, _STIXBase
from ..custom import _custom_extension_builder, _custom_observable_builder from ..custom import _custom_extension_builder, _custom_observable_builder
from ..exceptions import AtLeastOnePropertyError, DependentPropertiesError from ..exceptions import AtLeastOnePropertyError, DependentPropertiesError
from ..properties import ( from ..properties import (
@ -17,6 +16,7 @@ from ..properties import (
HashesProperty, HexProperty, IntegerProperty, ListProperty, HashesProperty, HexProperty, IntegerProperty, ListProperty,
ObjectReferenceProperty, StringProperty, TimestampProperty, TypeProperty, ObjectReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
) )
from .base import _Extension, _Observable, _STIXBase20
class Artifact(_Observable): class Artifact(_Observable):
@ -26,7 +26,7 @@ class Artifact(_Observable):
_type = 'artifact' _type = 'artifact'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('mime_type', StringProperty()), ('mime_type', StringProperty()),
('payload_bin', BinaryProperty()), ('payload_bin', BinaryProperty()),
('url', StringProperty()), ('url', StringProperty()),
@ -47,7 +47,7 @@ class AutonomousSystem(_Observable):
_type = 'autonomous-system' _type = 'autonomous-system'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('number', IntegerProperty(required=True)), ('number', IntegerProperty(required=True)),
('name', StringProperty()), ('name', StringProperty()),
('rir', StringProperty()), ('rir', StringProperty()),
@ -62,7 +62,7 @@ class Directory(_Observable):
_type = 'directory' _type = 'directory'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('path', StringProperty(required=True)), ('path', StringProperty(required=True)),
('path_enc', StringProperty()), ('path_enc', StringProperty()),
# these are not the created/modified timestamps of the object itself # these are not the created/modified timestamps of the object itself
@ -81,7 +81,7 @@ class DomainName(_Observable):
_type = 'domain-name' _type = 'domain-name'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name']))), ('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name']))),
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
@ -95,7 +95,7 @@ class EmailAddress(_Observable):
_type = 'email-addr' _type = 'email-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('display_name', StringProperty()), ('display_name', StringProperty()),
('belongs_to_ref', ObjectReferenceProperty(valid_types='user-account')), ('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 """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>`__. `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 """ # noqa
@ -127,7 +127,7 @@ class EmailMessage(_Observable):
_type = 'email-message' _type = 'email-message'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('is_multipart', BooleanProperty(required=True)), ('is_multipart', BooleanProperty(required=True)),
('date', TimestampProperty()), ('date', TimestampProperty()),
('content_type', StringProperty()), ('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 """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>`__. `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 """ # noqa
@ -220,7 +220,7 @@ class RasterImageExt(_Extension):
]) ])
class WindowsPEOptionalHeaderType(_STIXBase): class WindowsPEOptionalHeaderType(_STIXBase20):
"""For more detailed information on this object's properties, see """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>`__. `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 """ # noqa
@ -264,7 +264,7 @@ class WindowsPEOptionalHeaderType(_STIXBase):
self._check_at_least_one_property() self._check_at_least_one_property()
class WindowsPESection(_STIXBase): class WindowsPESection(_STIXBase20):
"""For more detailed information on this object's properties, see """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>`__. `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 """ # noqa
@ -306,7 +306,7 @@ class File(_Observable):
_type = 'file' _type = 'file'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('hashes', HashesProperty()), ('hashes', HashesProperty()),
('size', IntegerProperty()), ('size', IntegerProperty()),
('name', StringProperty()), ('name', StringProperty()),
@ -339,7 +339,7 @@ class IPv4Address(_Observable):
_type = 'ipv4-addr' _type = 'ipv4-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))), ('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))),
('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))), ('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))),
@ -354,7 +354,7 @@ class IPv6Address(_Observable):
_type = 'ipv6-addr' _type = 'ipv6-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))), ('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))),
('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))), ('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))),
@ -369,7 +369,7 @@ class MACAddress(_Observable):
_type = 'mac-addr' _type = 'mac-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
]) ])
@ -382,7 +382,7 @@ class Mutex(_Observable):
_type = 'mutex' _type = 'mutex'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
]) ])
@ -483,7 +483,7 @@ class NetworkTraffic(_Observable):
_type = 'network-traffic' _type = 'network-traffic'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('start', TimestampProperty()), ('start', TimestampProperty()),
('end', TimestampProperty()), ('end', TimestampProperty()),
('is_active', BooleanProperty()), ('is_active', BooleanProperty()),
@ -575,7 +575,7 @@ class Process(_Observable):
_type = 'process' _type = 'process'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('is_hidden', BooleanProperty()), ('is_hidden', BooleanProperty()),
('pid', IntegerProperty()), ('pid', IntegerProperty()),
('name', StringProperty()), ('name', StringProperty()),
@ -615,7 +615,7 @@ class Software(_Observable):
_type = 'software' _type = 'software'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('cpe', StringProperty()), ('cpe', StringProperty()),
('languages', ListProperty(StringProperty)), ('languages', ListProperty(StringProperty)),
@ -632,7 +632,7 @@ class URL(_Observable):
_type = 'url' _type = 'url'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)),
]) ])
@ -659,7 +659,7 @@ class UserAccount(_Observable):
_type = 'user-account' _type = 'user-account'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('user_id', StringProperty(required=True)), ('user_id', StringProperty(required=True)),
('account_login', StringProperty()), ('account_login', StringProperty()),
('account_type', StringProperty()), # open vocab ('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 """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>`__. `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 """ # noqa
@ -713,7 +713,7 @@ class WindowsRegistryKey(_Observable):
_type = 'windows-registry-key' _type = 'windows-registry-key'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('key', StringProperty(required=True)), ('key', StringProperty(required=True)),
('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))), ('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))),
# this is not the modified timestamps of the object itself # 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 """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>`__. `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 """ # noqa
@ -757,7 +757,7 @@ class X509Certificate(_Observable):
_type = 'x509-certificate' _type = 'x509-certificate'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('is_self_signed', BooleanProperty()), ('is_self_signed', BooleanProperty()),
('hashes', HashesProperty()), ('hashes', HashesProperty()),
('version', StringProperty()), ('version', StringProperty()),
@ -791,11 +791,11 @@ def CustomObservable(type='x-custom-observable', properties=None):
""" """
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(itertools.chain.from_iterable([
[('type', TypeProperty(type))], [('type', TypeProperty(type, spec_version='2.0'))],
properties, properties,
[('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=type))], [('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 return wrapper
@ -803,5 +803,5 @@ def CustomExtension(observable=None, type='x-custom-observable-ext', properties=
"""Decorator for custom extensions to STIX Cyber Observables. """Decorator for custom extensions to STIX Cyber Observables.
""" """
def wrapper(cls): 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 return wrapper

View File

@ -5,7 +5,6 @@ import itertools
from stix2patterns.validator import run_validator from stix2patterns.validator import run_validator
from ..core import STIXDomainObject
from ..custom import _custom_object_builder from ..custom import _custom_object_builder
from ..exceptions import InvalidValueError from ..exceptions import InvalidValueError
from ..properties import ( from ..properties import (
@ -14,17 +13,18 @@ from ..properties import (
TimestampProperty, TypeProperty, TimestampProperty, TypeProperty,
) )
from ..utils import NOW from ..utils import NOW
from .base import _DomainObject
from .common import ExternalReference, GranularMarking, KillChainPhase from .common import ExternalReference, GranularMarking, KillChainPhase
class AttackPattern(STIXDomainObject): class AttackPattern(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'attack-pattern'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'campaign'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'course-of-action'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'identity'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'indicator'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -144,14 +144,14 @@ class Indicator(STIXDomainObject):
raise InvalidValueError(self.__class__, 'pattern', str(errors[0])) raise InvalidValueError(self.__class__, 'pattern', str(errors[0]))
class IntrusionSet(STIXDomainObject): class IntrusionSet(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'intrusion-set'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'malware'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'observed-data'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -226,14 +226,14 @@ class ObservedData(STIXDomainObject):
super(ObservedData, self).__init__(*args, **kwargs) super(ObservedData, self).__init__(*args, **kwargs)
class Report(STIXDomainObject): class Report(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'report'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -256,14 +256,14 @@ class Report(STIXDomainObject):
super(Report, self).__init__(*args, **kwargs) super(Report, self).__init__(*args, **kwargs)
class ThreatActor(STIXDomainObject): class ThreatActor(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'threat-actor'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'tool'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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 """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>`__. `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' _type = 'vulnerability'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -364,7 +364,7 @@ def CustomObject(type='x-custom-type', properties=None):
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(itertools.chain.from_iterable([
[ [
('type', TypeProperty(type)), ('type', TypeProperty(type, spec_version='2.0')),
('id', IDProperty(type, spec_version='2.0')), ('id', IDProperty(type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('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]), 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 return wrapper

View File

@ -2,16 +2,16 @@
from collections import OrderedDict from collections import OrderedDict
from ..core import STIXRelationshipObject
from ..properties import ( from ..properties import (
BooleanProperty, IDProperty, IntegerProperty, ListProperty, BooleanProperty, IDProperty, IntegerProperty, ListProperty,
ReferenceProperty, StringProperty, TimestampProperty, TypeProperty, ReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
) )
from ..utils import NOW from ..utils import NOW
from .base import _RelationshipObject
from .common import ExternalReference, GranularMarking from .common import ExternalReference, GranularMarking
class Relationship(STIXRelationshipObject): class Relationship(_RelationshipObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'relationship'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -55,14 +55,14 @@ class Relationship(STIXRelationshipObject):
super(Relationship, self).__init__(**kwargs) super(Relationship, self).__init__(**kwargs)
class Sighting(STIXRelationshipObject): class Sighting(_RelationshipObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'sighting'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')), ('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),

View File

@ -14,6 +14,9 @@
# flake8: noqa # flake8: noqa
from .base import (
_DomainObject, _Extension, _Observable, _RelationshipObject, _STIXBase21,
)
from .bundle import Bundle from .bundle import Bundle
from .common import ( from .common import (
TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking, ExternalReference, TLP_AMBER, TLP_GREEN, TLP_RED, TLP_WHITE, CustomMarking, ExternalReference,

25
stix2/v21/base.py Normal file
View File

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

View File

@ -2,20 +2,20 @@
from collections import OrderedDict from collections import OrderedDict
from ..base import _STIXBase
from ..properties import ( from ..properties import (
IDProperty, ListProperty, STIXObjectProperty, TypeProperty, IDProperty, ListProperty, STIXObjectProperty, TypeProperty,
) )
from .base import _STIXBase21
class Bundle(_STIXBase): class Bundle(_STIXBase21):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_nuwp4rox8c7r>`__.
""" """
_type = 'bundle' _type = 'bundle'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('objects', ListProperty(STIXObjectProperty(spec_version='2.1'))), ('objects', ListProperty(STIXObjectProperty(spec_version='2.1'))),
]) ])

View File

@ -2,7 +2,6 @@
from collections import OrderedDict from collections import OrderedDict
from ..base import _STIXBase
from ..custom import _custom_marking_builder from ..custom import _custom_marking_builder
from ..exceptions import InvalidValueError from ..exceptions import InvalidValueError
from ..markings import _MarkingsMixin from ..markings import _MarkingsMixin
@ -13,9 +12,10 @@ from ..properties import (
SelectorProperty, StringProperty, TimestampProperty, TypeProperty, SelectorProperty, StringProperty, TimestampProperty, TypeProperty,
) )
from ..utils import NOW, _get_dict 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 """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>`__. `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 """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>`__. `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 """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>`__. `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']) 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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_nfwr8z9ax2bi>`__.
""" """
_type = 'language-content' _type = 'language-content'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('object_ref', ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1', required=True)), ('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. # 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')), ('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 """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>`__. `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 """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>`__. `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.") 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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_hr5vgqxjk7ns>`__.
""" """
_type = 'marking-definition' _type = 'marking-definition'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type)), ('id', IDProperty(_type)),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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)), ('external_references', ListProperty(ExternalReference)),
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
('granular_markings', ListProperty(GranularMarking)), ('granular_markings', ListProperty(GranularMarking)),
@ -214,7 +214,7 @@ def CustomMarking(type='x-custom-marking', properties=None):
""" """
def wrapper(cls): def wrapper(cls):
return _custom_marking_builder(cls, type, properties, '2.1') return _custom_marking_builder(cls, type, properties, '2.1', _STIXBase21)
return wrapper return wrapper

View File

@ -1,14 +1,13 @@
"""STIX 2.1 Cyber Observable Objects. """STIX 2.1 Cyber Observable Objects.
Embedded observable object types, such as Email MIME Component, which is Embedded observable object types, such as Email MIME Component, which is
embedded in Email Message objects, inherit from ``_STIXBase`` instead of embedded in Email Message objects, inherit from ``_STIXBase21`` instead of
Observable and do not have a ``_type`` attribute. _Observable and do not have a ``_type`` attribute.
""" """
from collections import OrderedDict from collections import OrderedDict
import itertools import itertools
from ..base import _Extension, _Observable, _STIXBase
from ..custom import _custom_extension_builder, _custom_observable_builder from ..custom import _custom_extension_builder, _custom_observable_builder
from ..exceptions import AtLeastOnePropertyError, DependentPropertiesError from ..exceptions import AtLeastOnePropertyError, DependentPropertiesError
from ..properties import ( from ..properties import (
@ -18,6 +17,7 @@ from ..properties import (
ObjectReferenceProperty, ReferenceProperty, StringProperty, ObjectReferenceProperty, ReferenceProperty, StringProperty,
TimestampProperty, TypeProperty, TimestampProperty, TypeProperty,
) )
from .base import _Extension, _Observable, _STIXBase21
from .common import GranularMarking from .common import GranularMarking
@ -28,7 +28,7 @@ class Artifact(_Observable):
_type = 'artifact' _type = 'artifact'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('mime_type', StringProperty()), ('mime_type', StringProperty()),
('payload_bin', BinaryProperty()), ('payload_bin', BinaryProperty()),
@ -57,7 +57,7 @@ class AutonomousSystem(_Observable):
_type = 'autonomous-system' _type = 'autonomous-system'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('number', IntegerProperty(required=True)), ('number', IntegerProperty(required=True)),
('name', StringProperty()), ('name', StringProperty()),
@ -78,7 +78,7 @@ class Directory(_Observable):
_type = 'directory' _type = 'directory'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('path', StringProperty(required=True)), ('path', StringProperty(required=True)),
('path_enc', StringProperty()), ('path_enc', StringProperty()),
@ -103,7 +103,7 @@ class DomainName(_Observable):
_type = 'domain-name' _type = 'domain-name'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('resolves_to_refs', ListProperty(ReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name'], spec_version='2.1'))), ('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' _type = 'email-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('display_name', StringProperty()), ('display_name', StringProperty()),
@ -137,7 +137,7 @@ class EmailAddress(_Observable):
_id_contributing_properties = ["value"] _id_contributing_properties = ["value"]
class EmailMIMEComponent(_STIXBase): class EmailMIMEComponent(_STIXBase21):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'email-message'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('is_multipart', BooleanProperty(required=True)), ('is_multipart', BooleanProperty(required=True)),
('date', TimestampProperty()), ('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 """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>`__. `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 """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>`__. `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() self._check_at_least_one_property()
class WindowsPESection(_STIXBase): class WindowsPESection(_STIXBase21):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'file'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('hashes', HashesProperty(spec_version='2.1')), ('hashes', HashesProperty(spec_version='2.1')),
('size', IntegerProperty(min=0)), ('size', IntegerProperty(min=0)),
@ -380,7 +380,7 @@ class IPv4Address(_Observable):
_type = 'ipv4-addr' _type = 'ipv4-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))), ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))),
@ -401,7 +401,7 @@ class IPv6Address(_Observable):
_type = 'ipv6-addr' _type = 'ipv6-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))), ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))),
@ -422,7 +422,7 @@ class MACAddress(_Observable):
_type = 'mac-addr' _type = 'mac-addr'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
@ -441,7 +441,7 @@ class Mutex(_Observable):
_type = 'mutex' _type = 'mutex'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
@ -562,7 +562,7 @@ class NetworkTraffic(_Observable):
_type = 'network-traffic' _type = 'network-traffic'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('start', TimestampProperty()), ('start', TimestampProperty()),
('end', TimestampProperty()), ('end', TimestampProperty()),
@ -684,7 +684,7 @@ class Process(_Observable):
_type = 'process' _type = 'process'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('is_hidden', BooleanProperty()), ('is_hidden', BooleanProperty()),
('pid', IntegerProperty()), ('pid', IntegerProperty()),
@ -728,7 +728,7 @@ class Software(_Observable):
_type = 'software' _type = 'software'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('cpe', StringProperty()), ('cpe', StringProperty()),
@ -752,7 +752,7 @@ class URL(_Observable):
_type = 'url' _type = 'url'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('value', StringProperty(required=True)), ('value', StringProperty(required=True)),
('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)), ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
@ -785,7 +785,7 @@ class UserAccount(_Observable):
_type = 'user-account' _type = 'user-account'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('user_id', StringProperty()), ('user_id', StringProperty()),
('credential', StringProperty()), ('credential', StringProperty()),
@ -810,7 +810,7 @@ class UserAccount(_Observable):
_id_contributing_properties = ["account_type", "user_id", "account_login"] _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 """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>`__. `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' _type = 'windows-registry-key'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('key', StringProperty()), ('key', StringProperty()),
('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))), ('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))),
@ -863,7 +863,7 @@ class WindowsRegistryKey(_Observable):
_id_contributing_properties = ["key", "values"] _id_contributing_properties = ["key", "values"]
class X509V3ExtenstionsType(_STIXBase): class X509V3ExtenstionsType(_STIXBase21):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'x509-certificate'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('is_self_signed', BooleanProperty()), ('is_self_signed', BooleanProperty()),
('hashes', HashesProperty(spec_version='2.1')), ('hashes', HashesProperty(spec_version='2.1')),
@ -948,12 +948,12 @@ def CustomObservable(type='x-custom-observable', properties=None, id_contrib_pro
""" """
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(itertools.chain.from_iterable([
[('type', TypeProperty(type))], [('type', TypeProperty(type, spec_version='2.1'))],
[('id', IDProperty(type, spec_version='2.1'))], [('id', IDProperty(type, spec_version='2.1'))],
properties, properties,
[('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=type))], [('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 return wrapper
@ -961,5 +961,5 @@ def CustomExtension(observable=None, type='x-custom-observable-ext', properties=
"""Decorator for custom extensions to STIX Cyber Observables. """Decorator for custom extensions to STIX Cyber Observables.
""" """
def wrapper(cls): 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 return wrapper

View File

@ -7,7 +7,6 @@ import warnings
from six.moves.urllib.parse import quote_plus from six.moves.urllib.parse import quote_plus
from stix2patterns.validator import run_validator from stix2patterns.validator import run_validator
from ..core import STIXDomainObject
from ..custom import _custom_object_builder from ..custom import _custom_object_builder
from ..exceptions import ( from ..exceptions import (
InvalidValueError, PropertyPresenceError, STIXDeprecationWarning, InvalidValueError, PropertyPresenceError, STIXDeprecationWarning,
@ -18,22 +17,23 @@ from ..properties import (
StringProperty, TimestampProperty, TypeProperty, StringProperty, TimestampProperty, TypeProperty,
) )
from ..utils import NOW from ..utils import NOW
from .base import _DomainObject
from .common import ExternalReference, GranularMarking, KillChainPhase from .common import ExternalReference, GranularMarking, KillChainPhase
class AttackPattern(STIXDomainObject): class AttackPattern(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_4ohsa4pay4h4>`__.
""" """
_type = 'attack-pattern' _type = 'attack-pattern'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('aliases', ListProperty(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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_vvysvm8mt434>`__.
""" """
_type = 'campaign' _type = 'campaign'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('aliases', ListProperty(StringProperty)), ('aliases', ListProperty(StringProperty)),
@ -87,19 +87,19 @@ class Campaign(STIXDomainObject):
raise ValueError(msg.format(self)) raise ValueError(msg.format(self))
class CourseOfAction(STIXDomainObject): class CourseOfAction(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'course-of-action'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('revoked', BooleanProperty(default=lambda: False)), ('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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_9e3uldaqqha2>`__.
""" """
_type = 'grouping' _type = 'grouping'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty)), ('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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_ru8fmldl2p6w>`__.
""" """
_type = 'identity' _type = 'identity'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('roles', ListProperty(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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_wfiae74706sw>`__.
""" """
_type = 'indicator' _type = 'indicator'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty()), ('name', StringProperty()),
('description', StringProperty()), ('description', StringProperty()),
('indicator_types', ListProperty(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'): if kwargs.get('pattern') and kwargs.get('pattern_type') == 'stix' and not kwargs.get('pattern_version'):
kwargs['pattern_version'] = '2.1' kwargs['pattern_version'] = '2.1'
super(STIXDomainObject, self).__init__(*args, **kwargs) super(_DomainObject, self).__init__(*args, **kwargs)
def _check_object_constraints(self): def _check_object_constraints(self):
super(Indicator, self)._check_object_constraints() super(Indicator, self)._check_object_constraints()
@ -227,19 +227,19 @@ class Indicator(STIXDomainObject):
raise InvalidValueError(self.__class__, 'pattern', str(errors[0])) raise InvalidValueError(self.__class__, 'pattern', str(errors[0]))
class Infrastructure(STIXDomainObject): class Infrastructure(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_l2alfbbcmfep>`__.
""" """
_type = 'infrastructure' _type = 'infrastructure'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty)), ('labels', ListProperty(StringProperty)),
('confidence', IntegerProperty()), ('confidence', IntegerProperty()),
@ -267,19 +267,19 @@ class Infrastructure(STIXDomainObject):
raise ValueError(msg.format(self)) raise ValueError(msg.format(self))
class IntrusionSet(STIXDomainObject): class IntrusionSet(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_ticprjb32bc4>`__.
""" """
_type = 'intrusion-set' _type = 'intrusion-set'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('aliases', ListProperty(StringProperty)), ('aliases', ListProperty(StringProperty)),
@ -309,19 +309,19 @@ class IntrusionSet(STIXDomainObject):
raise ValueError(msg.format(self)) raise ValueError(msg.format(self))
class Location(STIXDomainObject): class Location(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_sqez6sri9vtz>`__.
""" """
_type = 'location' _type = 'location'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty()), ('name', StringProperty()),
('description', StringProperty()), ('description', StringProperty()),
('latitude', FloatProperty(min=-90.0, max=90.0)), ('latitude', FloatProperty(min=-90.0, max=90.0)),
@ -416,19 +416,19 @@ class Location(STIXDomainObject):
return final_url return final_url
class Malware(STIXDomainObject): class Malware(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_gc4ooz6oaz7y>`__.
""" """
_type = 'malware' _type = 'malware'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty()), ('name', StringProperty()),
('description', StringProperty()), ('description', StringProperty()),
('malware_types', ListProperty(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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_dw67pa20zss5>`__.
""" """
_type = 'malware-analysis' _type = 'malware-analysis'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
('labels', ListProperty(StringProperty)), ('labels', ListProperty(StringProperty)),
@ -512,19 +512,19 @@ class MalwareAnalysis(STIXDomainObject):
self._check_at_least_one_property(["result", "analysis_sco_refs"]) 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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_hr77jvcbs9jk>`__.
""" """
_type = 'note' _type = 'note'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('abstract', StringProperty()), ('abstract', StringProperty()),
('content', StringProperty(required=True)), ('content', StringProperty(required=True)),
('authors', ListProperty(StringProperty)), ('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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_h1590esrzg5f>`__.
""" """
_type = 'observed-data' _type = 'observed-data'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('first_observed', TimestampProperty(required=True)), ('first_observed', TimestampProperty(required=True)),
('last_observed', TimestampProperty(required=True)), ('last_observed', TimestampProperty(required=True)),
('number_observed', IntegerProperty(min=1, max=999999999, 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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_sr2hswmu5t1>`__.
""" """
_type = 'opinion' _type = 'opinion'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('explanation', StringProperty()), ('explanation', StringProperty()),
('authors', ListProperty(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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_ha4fpad0r9pf>`__.
""" """
_type = 'report' _type = 'report'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('report_types', ListProperty(StringProperty)), ('report_types', ListProperty(StringProperty)),
@ -664,19 +664,19 @@ class Report(STIXDomainObject):
super(Report, self).__init__(*args, **kwargs) super(Report, self).__init__(*args, **kwargs)
class ThreatActor(STIXDomainObject): class ThreatActor(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_2wowmlcbkqst>`__.
""" """
_type = 'threat-actor' _type = 'threat-actor'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('threat_actor_types', ListProperty(StringProperty)), ('threat_actor_types', ListProperty(StringProperty)),
@ -710,19 +710,19 @@ class ThreatActor(STIXDomainObject):
raise ValueError(msg.format(self)) raise ValueError(msg.format(self))
class Tool(STIXDomainObject): class Tool(_DomainObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_m21z3a1f3lou>`__.
""" """
_type = 'tool' _type = 'tool'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('tool_types', ListProperty(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 """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_d9f0iay06wtx>`__.
""" """
_type = 'vulnerability' _type = 'vulnerability'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('revoked', BooleanProperty(default=lambda: False)), ('revoked', BooleanProperty(default=lambda: False)),
@ -796,12 +796,12 @@ def CustomObject(type='x-custom-type', properties=None):
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(itertools.chain.from_iterable([
[ [
('type', TypeProperty(type)), ('type', TypeProperty(type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(type, spec_version='2.1')), ('id', IDProperty(type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
], ],
[x for x in properties if not x[0].startswith('x_')], [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]), 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 return wrapper

View File

@ -2,16 +2,16 @@
from collections import OrderedDict from collections import OrderedDict
from ..core import STIXRelationshipObject
from ..properties import ( from ..properties import (
BooleanProperty, IDProperty, IntegerProperty, ListProperty, BooleanProperty, IDProperty, IntegerProperty, ListProperty,
ReferenceProperty, StringProperty, TimestampProperty, TypeProperty, ReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
) )
from ..utils import NOW from ..utils import NOW
from .base import _RelationshipObject
from .common import ExternalReference, GranularMarking from .common import ExternalReference, GranularMarking
class Relationship(STIXRelationshipObject): class Relationship(_RelationshipObject):
"""For more detailed information on this object's properties, see """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>`__. `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' _type = 'relationship'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('relationship_type', StringProperty(required=True)), ('relationship_type', StringProperty(required=True)),
('description', StringProperty()), ('description', StringProperty()),
('source_ref', ReferenceProperty(invalid_types=_invalid_source_target_types, spec_version='2.1', required=True)), ('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)) raise ValueError(msg.format(self))
class Sighting(STIXRelationshipObject): class Sighting(_RelationshipObject):
"""For more detailed information on this object's properties, see """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>`__. `the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_7p0n81ikux8f>`__.
""" """
_type = 'sighting' _type = 'sighting'
_properties = OrderedDict([ _properties = OrderedDict([
('type', TypeProperty(_type)), ('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')), ('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')), ('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')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('description', StringProperty()), ('description', StringProperty()),
('first_seen', TimestampProperty()), ('first_seen', TimestampProperty()),
('last_seen', TimestampProperty()), ('last_seen', TimestampProperty()),

View File

@ -1 +1 @@
__version__ = "1.3.1" __version__ = "1.4.0"