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
cache: pip
dist: xenial

View File

@ -1,6 +1,12 @@
CHANGELOG
=========
1.4.0 - 2020-04-03
* #347, #355, #356, #357, #358, #360, #362, #369, #370, #379, #374, #384 Updates STIX 2.1 support to CS01
* #376 Fixes bug where registering object of same name would overwrite it; will
now raise an error
1.3.1 - 2020-03-06
* #322 Adds encoding option FileSystemSource and MemorySource

View File

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

View File

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

View File

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

View File

@ -2,11 +2,13 @@
import copy
import datetime as dt
import re
import uuid
import simplejson as json
import six
import stix2
from stix2.canonicalization.Canonicalize import canonicalize
from .exceptions import (
@ -14,8 +16,11 @@ from .exceptions import (
ImmutableError, InvalidObjRefError, InvalidValueError,
MissingPropertiesError, MutuallyExclusivePropertiesError,
)
from .markings import _MarkingsMixin
from .markings.utils import validate
from .utils import NOW, find_property_index, format_datetime, get_timestamp
from .utils import (
NOW, PREFIX_21_REGEX, find_property_index, format_datetime, get_timestamp,
)
from .utils import new_version as _new_version
from .utils import revoke as _revoke
@ -158,12 +163,23 @@ class _STIXBase(Mapping):
custom_props = kwargs.pop('custom_properties', {})
if custom_props and not isinstance(custom_props, dict):
raise ValueError("'custom_properties' must be a dictionary")
if not self._allow_custom:
extra_kwargs = list(set(kwargs) - set(self._properties))
if extra_kwargs:
raise ExtraPropertiesError(cls, extra_kwargs)
if custom_props:
extra_kwargs = list(set(kwargs) - set(self._properties))
if extra_kwargs and not self._allow_custom:
raise ExtraPropertiesError(cls, extra_kwargs)
# because allow_custom is true, any extra kwargs are custom
if custom_props or extra_kwargs:
self._allow_custom = True
if isinstance(self, stix2.v21._STIXBase21):
all_custom_prop_names = extra_kwargs
all_custom_prop_names.extend(list(custom_props.keys()))
for prop_name in all_custom_prop_names:
if not re.match(PREFIX_21_REGEX, prop_name):
raise InvalidValueError(
self.__class__, prop_name,
reason="Property name '%s' must begin with an alpha character." % prop_name,
)
# Remove any keyword arguments whose value is None or [] (i.e. empty list)
setting_kwargs = {}
@ -307,6 +323,14 @@ class _STIXBase(Mapping):
return json.dumps(self, cls=STIXJSONEncoder, **kwargs)
class _DomainObject(_STIXBase, _MarkingsMixin):
pass
class _RelationshipObject(_STIXBase, _MarkingsMixin):
pass
class _Observable(_STIXBase):
def __init__(self, **kwargs):

View File

@ -1,140 +1,91 @@
from collections import OrderedDict
import re
import six
from .base import _cls_init, _Extension, _Observable, _STIXBase
from .core import (
STIXDomainObject, _register_marking, _register_object,
_register_observable, _register_observable_extension,
from .base import _cls_init
from .parsing import (
_register_marking, _register_object, _register_observable,
_register_observable_extension,
)
from .utils import TYPE_REGEX, get_class_hierarchy_names
def _custom_object_builder(cls, type, properties, version):
class _CustomObject(cls, STIXDomainObject):
if not re.match(TYPE_REGEX, type):
raise ValueError(
"Invalid type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % type,
)
elif len(type) < 3 or len(type) > 250:
raise ValueError(
"Invalid type name '%s': must be between 3 and 250 characters." % type,
)
if not properties or not isinstance(properties, list):
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
_type = type
_properties = OrderedDict(properties)
def __init__(self, **kwargs):
_STIXBase.__init__(self, **kwargs)
_cls_init(cls, self, kwargs)
_register_object(_CustomObject, version=version)
return _CustomObject
def _custom_marking_builder(cls, type, properties, version):
class _CustomMarking(cls, _STIXBase):
if not properties or not isinstance(properties, list):
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
_type = type
_properties = OrderedDict(properties)
def __init__(self, **kwargs):
_STIXBase.__init__(self, **kwargs)
_cls_init(cls, self, kwargs)
_register_marking(_CustomMarking, version=version)
return _CustomMarking
def _custom_observable_builder(cls, type, properties, version, id_contrib_props=None):
if id_contrib_props is None:
id_contrib_props = []
class _CustomObservable(cls, _Observable):
if not re.match(TYPE_REGEX, type):
raise ValueError(
"Invalid observable type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." % type,
)
elif len(type) < 3 or len(type) > 250:
raise ValueError("Invalid observable type name '%s': must be between 3 and 250 characters." % type)
if not properties or not isinstance(properties, list):
raise ValueError("Must supply a list, containing tuples. For example, [('property1', IntegerProperty())]")
if version == "2.0":
# If using STIX2.0, check properties ending in "_ref/s" are ObjectReferenceProperties
for prop_name, prop in properties:
if prop_name.endswith('_ref') and ('ObjectReferenceProperty' not in get_class_hierarchy_names(prop)):
raise ValueError(
"'%s' is named like an object reference property but "
"is not an ObjectReferenceProperty." % prop_name,
)
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
'ObjectReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
raise ValueError(
"'%s' is named like an object reference list property but "
"is not a ListProperty containing ObjectReferenceProperty." % prop_name,
)
else:
# If using STIX2.1 (or newer...), check properties ending in "_ref/s" are ReferenceProperties
for prop_name, prop in properties:
if prop_name.endswith('_ref') and ('ReferenceProperty' not in get_class_hierarchy_names(prop)):
raise ValueError(
"'%s' is named like a reference property but "
"is not a ReferenceProperty." % prop_name,
)
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
'ReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
raise ValueError(
"'%s' is named like a reference list property but "
"is not a ListProperty containing ReferenceProperty." % prop_name,
)
_type = type
_properties = OrderedDict(properties)
if version != '2.0':
_id_contributing_properties = id_contrib_props
def __init__(self, **kwargs):
_Observable.__init__(self, **kwargs)
_cls_init(cls, self, kwargs)
_register_observable(_CustomObservable, version=version)
return _CustomObservable
def _custom_extension_builder(cls, observable, type, properties, version):
def _get_properties_dict(properties):
try:
prop_dict = OrderedDict(properties)
return OrderedDict(properties)
except TypeError as e:
six.raise_from(
ValueError(
"Extension properties must be dict-like, e.g. a list "
"properties must be dict-like, e.g. a list "
"containing tuples. For example, "
"[('property1', IntegerProperty())]",
),
e,
)
class _CustomExtension(cls, _Extension):
def _custom_object_builder(cls, type, properties, version, base_class):
prop_dict = _get_properties_dict(properties)
class _CustomObject(cls, base_class):
_type = type
_properties = prop_dict
def __init__(self, **kwargs):
_Extension.__init__(self, **kwargs)
base_class.__init__(self, **kwargs)
_cls_init(cls, self, kwargs)
_register_object(_CustomObject, version=version)
return _CustomObject
def _custom_marking_builder(cls, type, properties, version, base_class):
prop_dict = _get_properties_dict(properties)
class _CustomMarking(cls, base_class):
_type = type
_properties = prop_dict
def __init__(self, **kwargs):
base_class.__init__(self, **kwargs)
_cls_init(cls, self, kwargs)
_register_marking(_CustomMarking, version=version)
return _CustomMarking
def _custom_observable_builder(cls, type, properties, version, base_class, id_contrib_props=None):
if id_contrib_props is None:
id_contrib_props = []
prop_dict = _get_properties_dict(properties)
class _CustomObservable(cls, base_class):
_type = type
_properties = prop_dict
if version != '2.0':
_id_contributing_properties = id_contrib_props
def __init__(self, **kwargs):
base_class.__init__(self, **kwargs)
_cls_init(cls, self, kwargs)
_register_observable(_CustomObservable, version=version)
return _CustomObservable
def _custom_extension_builder(cls, observable, type, properties, version, base_class):
prop_dict = _get_properties_dict(properties)
class _CustomExtension(cls, base_class):
_type = type
_properties = prop_dict
def __init__(self, **kwargs):
base_class.__init__(self, **kwargs)
_cls_init(cls, self, kwargs)
_register_observable_extension(observable, _CustomExtension, version=version)

View File

@ -10,11 +10,11 @@ import six
from stix2 import v20, v21
from stix2.base import _STIXBase
from stix2.core import parse
from stix2.datastore import (
DataSink, DataSource, DataSourceError, DataStoreMixin,
)
from stix2.datastore.filters import Filter, FilterSet, apply_common_filters
from stix2.parsing import parse
from stix2.utils import format_datetime, get_type_from_id

View File

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

View File

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

View File

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

View File

@ -233,3 +233,16 @@ class STIXDeprecationWarning(DeprecationWarning):
Represents usage of a deprecated component of a STIX specification.
"""
pass
class DuplicateRegistrationError(STIXError):
"""A STIX object with the same type as an existing object is being registered"""
def __init__(self, obj_type, reg_obj_type):
super(DuplicateRegistrationError, self).__init__()
self.obj_type = obj_type
self.reg_obj_type = reg_obj_type
def __str__(self):
msg = "A(n) {0} with type '{1}' already exists and cannot be registered again"
return msg.format(self.obj_type, self.reg_obj_type)

View File

@ -7,39 +7,13 @@ import re
import stix2
from .base import _Observable, _STIXBase
from .exceptions import ParseError
from .markings import _MarkingsMixin
from .utils import SCO21_EXT_REGEX, TYPE_REGEX, _get_dict
from .base import _DomainObject, _Observable
from .exceptions import DuplicateRegistrationError, ParseError
from .utils import PREFIX_21_REGEX, _get_dict, get_class_hierarchy_names
STIX2_OBJ_MAPS = {}
class STIXDomainObject(_STIXBase, _MarkingsMixin):
def __init__(self, *args, **kwargs):
interoperability = kwargs.get('interoperability', False)
self.__interoperability = interoperability
self._properties['id'].interoperability = interoperability
self._properties['created_by_ref'].interoperability = interoperability
if kwargs.get('object_marking_refs'):
self._properties['object_marking_refs'].contained.interoperability = interoperability
super(STIXDomainObject, self).__init__(*args, **kwargs)
class STIXRelationshipObject(_STIXBase, _MarkingsMixin):
def __init__(self, *args, **kwargs):
interoperability = kwargs.get('interoperability', False)
self.__interoperability = interoperability
self._properties['id'].interoperability = interoperability
if kwargs.get('created_by_ref'):
self._properties['created_by_ref'].interoperability = interoperability
if kwargs.get('object_marking_refs'):
self._properties['object_marking_refs'].contained.interoperability = interoperability
super(STIXRelationshipObject, self).__init__(*args, **kwargs)
def parse(data, allow_custom=False, interoperability=False, version=None):
"""Convert a string, dict or file-like object into a STIX object.
@ -218,7 +192,7 @@ def parse_observable(data, _valid_refs=None, allow_custom=False, version=None):
return obj_class(allow_custom=allow_custom, **obj)
def _register_object(new_type, version=None):
def _register_object(new_type, version=stix2.DEFAULT_VERSION):
"""Register a custom STIX Object type.
Args:
@ -226,7 +200,26 @@ def _register_object(new_type, version=None):
version (str): Which STIX2 version to use. (e.g. "2.0", "2.1"). If
None, use latest version.
Raises:
ValueError: If the class being registered wasn't created with the
@CustomObject decorator.
DuplicateRegistrationError: If the class has already been registered.
"""
if not issubclass(new_type, _DomainObject):
raise ValueError(
"'%s' must be created with the @CustomObject decorator." %
new_type.__name__,
)
properties = new_type._properties
if version == "2.1":
for prop_name, prop in properties.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name '%s' must begin with an alpha character" % prop_name)
if version:
v = 'v' + version.replace('.', '')
else:
@ -234,10 +227,12 @@ def _register_object(new_type, version=None):
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP = STIX2_OBJ_MAPS[v]['objects']
if new_type._type in OBJ_MAP.keys():
raise DuplicateRegistrationError("STIX Object", new_type._type)
OBJ_MAP[new_type._type] = new_type
def _register_marking(new_marking, version=None):
def _register_marking(new_marking, version=stix2.DEFAULT_VERSION):
"""Register a custom STIX Marking Definition type.
Args:
@ -246,6 +241,17 @@ def _register_marking(new_marking, version=None):
None, use latest version.
"""
mark_type = new_marking._type
properties = new_marking._properties
stix2.properties._validate_type(mark_type, version)
if version == "2.1":
for prop_name, prop_value in properties.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
if version:
v = 'v' + version.replace('.', '')
else:
@ -253,10 +259,12 @@ def _register_marking(new_marking, version=None):
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP_MARKING = STIX2_OBJ_MAPS[v]['markings']
OBJ_MAP_MARKING[new_marking._type] = new_marking
if mark_type in OBJ_MAP_MARKING.keys():
raise DuplicateRegistrationError("STIX Marking", mark_type)
OBJ_MAP_MARKING[mark_type] = new_marking
def _register_observable(new_observable, version=None):
def _register_observable(new_observable, version=stix2.DEFAULT_VERSION):
"""Register a custom STIX Cyber Observable type.
Args:
@ -265,6 +273,39 @@ def _register_observable(new_observable, version=None):
None, use latest version.
"""
properties = new_observable._properties
if version == "2.0":
# If using STIX2.0, check properties ending in "_ref/s" are ObjectReferenceProperties
for prop_name, prop in properties.items():
if prop_name.endswith('_ref') and ('ObjectReferenceProperty' not in get_class_hierarchy_names(prop)):
raise ValueError(
"'%s' is named like an object reference property but "
"is not an ObjectReferenceProperty." % prop_name,
)
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
'ObjectReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
raise ValueError(
"'%s' is named like an object reference list property but "
"is not a ListProperty containing ObjectReferenceProperty." % prop_name,
)
else:
# If using STIX2.1 (or newer...), check properties ending in "_ref/s" are ReferenceProperties
for prop_name, prop in properties.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
elif prop_name.endswith('_ref') and ('ReferenceProperty' not in get_class_hierarchy_names(prop)):
raise ValueError(
"'%s' is named like a reference property but "
"is not a ReferenceProperty." % prop_name,
)
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or
'ReferenceProperty' not in get_class_hierarchy_names(prop.contained))):
raise ValueError(
"'%s' is named like a reference list property but "
"is not a ListProperty containing ReferenceProperty." % prop_name,
)
if version:
v = 'v' + version.replace('.', '')
else:
@ -272,6 +313,8 @@ def _register_observable(new_observable, version=None):
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '')
OBJ_MAP_OBSERVABLE = STIX2_OBJ_MAPS[v]['observables']
if new_observable._type in OBJ_MAP_OBSERVABLE.keys():
raise DuplicateRegistrationError("Cyber Observable", new_observable._type)
OBJ_MAP_OBSERVABLE[new_observable._type] = new_observable
@ -291,30 +334,12 @@ def _register_observable_extension(
obs_class = observable if isinstance(observable, type) else \
type(observable)
ext_type = new_extension._type
properties = new_extension._properties
if not issubclass(obs_class, _Observable):
raise ValueError("'observable' must be a valid Observable class!")
if version == "2.0":
if not re.match(TYPE_REGEX, ext_type):
raise ValueError(
"Invalid extension type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." %
ext_type,
)
else: # 2.1+
if not re.match(SCO21_EXT_REGEX, ext_type):
raise ValueError(
"Invalid extension type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, hyphen (-), and end "
"with '-ext'." % ext_type,
)
if len(ext_type) < 3 or len(ext_type) > 250:
raise ValueError(
"Invalid extension type name '%s': must be between 3 and 250"
" characters." % ext_type,
)
stix2.properties._validate_type(ext_type, version)
if not new_extension._properties:
raise ValueError(
@ -322,6 +347,17 @@ def _register_observable_extension(
ext_type,
)
if version == "2.1":
if not ext_type.endswith('-ext'):
raise ValueError(
"Invalid extension type name '%s': must end with '-ext'." %
ext_type,
)
for prop_name, prop_value in properties.items():
if not re.match(PREFIX_21_REGEX, prop_name):
raise ValueError("Property name '%s' must begin with an alpha character." % prop_name)
v = 'v' + version.replace('.', '')
try:
@ -336,6 +372,8 @@ def _register_observable_extension(
EXT_MAP = STIX2_OBJ_MAPS[v]['observable-extensions']
try:
if ext_type in EXT_MAP[observable_type].keys():
raise DuplicateRegistrationError("Observable Extension", ext_type)
EXT_MAP[observable_type][ext_type] = new_extension
except KeyError:
if observable_type not in OBJ_MAP_OBSERVABLE:

View File

@ -2,11 +2,19 @@ import importlib
import inspect
from stix2patterns.exceptions import ParseException
from stix2patterns.grammars.STIXPatternParser import (
STIXPatternParser, TerminalNode,
)
from stix2patterns.grammars.STIXPatternVisitor import STIXPatternVisitor
from stix2patterns.v20.pattern import Pattern
from stix2patterns.grammars.STIXPatternParser import TerminalNode
from stix2patterns.v20.grammars.STIXPatternParser import \
STIXPatternParser as STIXPatternParser20
from stix2patterns.v20.grammars.STIXPatternVisitor import \
STIXPatternVisitor as STIXPatternVisitor20
from stix2patterns.v20.pattern import Pattern as Pattern20
from stix2patterns.v21.grammars.STIXPatternParser import \
STIXPatternParser as STIXPatternParser21
from stix2patterns.v21.grammars.STIXPatternVisitor import \
STIXPatternVisitor as STIXPatternVisitor21
from stix2patterns.v21.pattern import Pattern as Pattern21
import stix2
from .patterns import *
from .patterns import _BooleanExpression
@ -32,23 +40,12 @@ def remove_terminal_nodes(parse_tree_nodes):
return values
# This class defines a complete generic visitor for a parse tree produced by STIXPatternParser.
class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
class STIXPatternVisitorForSTIX2():
classes = {}
def __init__(self, module_suffix, module_name):
if module_suffix and module_name:
self.module_suffix = module_suffix
if not STIXPatternVisitorForSTIX2.classes:
module = importlib.import_module(module_name)
for k, c in inspect.getmembers(module, inspect.isclass):
STIXPatternVisitorForSTIX2.classes[k] = c
else:
self.module_suffix = None
super(STIXPatternVisitor, self).__init__()
def get_class(self, class_name):
if class_name in STIXPatternVisitorForSTIX2.classes:
return STIXPatternVisitorForSTIX2.classes[class_name]
@ -106,7 +103,10 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
# Visit a parse tree produced by STIXPatternParser#observationExpressionCompound.
def visitObservationExpressionCompound(self, ctx):
children = self.visitChildren(ctx)
return self.instantiate("ObservationExpression", children[1])
if isinstance(children[0], TerminalNode) and children[0].symbol.type == self.parser_class.LPAREN:
return self.instantiate("ParentheticalExpression", children[1])
else:
return self.instantiate("ObservationExpression", children[0])
# Visit a parse tree produced by STIXPatternParser#observationExpressionWithin.
def visitObservationExpressionWithin(self, ctx):
@ -147,7 +147,7 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
def visitPropTestEqual(self, ctx):
children = self.visitChildren(ctx)
operator = children[1].symbol.type
negated = operator != STIXPatternParser.EQ
negated = operator != self.parser_class.EQ
return self.instantiate(
"EqualityComparisonExpression", children[0], children[3 if len(children) > 3 else 2],
negated,
@ -157,22 +157,22 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
def visitPropTestOrder(self, ctx):
children = self.visitChildren(ctx)
operator = children[1].symbol.type
if operator == STIXPatternParser.GT:
if operator == self.parser_class.GT:
return self.instantiate(
"GreaterThanComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False,
)
elif operator == STIXPatternParser.LT:
elif operator == self.parser_class.LT:
return self.instantiate(
"LessThanComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False,
)
elif operator == STIXPatternParser.GE:
elif operator == self.parser_class.GE:
return self.instantiate(
"GreaterThanEqualComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False,
)
elif operator == STIXPatternParser.LE:
elif operator == self.parser_class.LE:
return self.instantiate(
"LessThanEqualComparisonExpression", children[0],
children[3 if len(children) > 3 else 2], False,
@ -294,22 +294,22 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
return children[0]
def visitTerminal(self, node):
if node.symbol.type == STIXPatternParser.IntPosLiteral or node.symbol.type == STIXPatternParser.IntNegLiteral:
if node.symbol.type == self.parser_class.IntPosLiteral or node.symbol.type == self.parser_class.IntNegLiteral:
return IntegerConstant(node.getText())
elif node.symbol.type == STIXPatternParser.FloatPosLiteral or node.symbol.type == STIXPatternParser.FloatNegLiteral:
elif node.symbol.type == self.parser_class.FloatPosLiteral or node.symbol.type == self.parser_class.FloatNegLiteral:
return FloatConstant(node.getText())
elif node.symbol.type == STIXPatternParser.HexLiteral:
elif node.symbol.type == self.parser_class.HexLiteral:
return HexConstant(node.getText(), from_parse_tree=True)
elif node.symbol.type == STIXPatternParser.BinaryLiteral:
elif node.symbol.type == self.parser_class.BinaryLiteral:
return BinaryConstant(node.getText(), from_parse_tree=True)
elif node.symbol.type == STIXPatternParser.StringLiteral:
elif node.symbol.type == self.parser_class.StringLiteral:
if node.getText()[0] == "'" and node.getText()[-1] == "'":
return StringConstant(node.getText()[1:-1], from_parse_tree=True)
else:
raise ParseException("The pattern does not start and end with a single quote")
elif node.symbol.type == STIXPatternParser.BoolLiteral:
elif node.symbol.type == self.parser_class.BoolLiteral:
return BooleanConstant(node.getText())
elif node.symbol.type == STIXPatternParser.TimestampLiteral:
elif node.symbol.type == self.parser_class.TimestampLiteral:
return TimestampConstant(node.getText())
else:
return node
@ -321,12 +321,51 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
aggregate = [nextResult]
return aggregate
# This class defines a complete generic visitor for a parse tree produced by STIXPatternParser.
class STIXPatternVisitorForSTIX21(STIXPatternVisitorForSTIX2, STIXPatternVisitor21):
classes = {}
def create_pattern_object(pattern, module_suffix="", module_name=""):
def __init__(self, module_suffix, module_name):
if module_suffix and module_name:
self.module_suffix = module_suffix
if not STIXPatternVisitorForSTIX2.classes:
module = importlib.import_module(module_name)
for k, c in inspect.getmembers(module, inspect.isclass):
STIXPatternVisitorForSTIX2.classes[k] = c
else:
self.module_suffix = None
self.parser_class = STIXPatternParser21
super(STIXPatternVisitor21, self).__init__()
class STIXPatternVisitorForSTIX20(STIXPatternVisitorForSTIX2, STIXPatternVisitor20):
classes = {}
def __init__(self, module_suffix, module_name):
if module_suffix and module_name:
self.module_suffix = module_suffix
if not STIXPatternVisitorForSTIX2.classes:
module = importlib.import_module(module_name)
for k, c in inspect.getmembers(module, inspect.isclass):
STIXPatternVisitorForSTIX2.classes[k] = c
else:
self.module_suffix = None
self.parser_class = STIXPatternParser20
super(STIXPatternVisitor20, self).__init__()
def create_pattern_object(pattern, module_suffix="", module_name="", version=stix2.DEFAULT_VERSION):
"""
Create a STIX pattern AST from a pattern string.
"""
pattern_obj = Pattern(pattern)
builder = STIXPatternVisitorForSTIX2(module_suffix, module_name)
if version == "2.1":
pattern_class = Pattern21
visitor_class = STIXPatternVisitorForSTIX21
else:
pattern_class = Pattern20
visitor_class = STIXPatternVisitorForSTIX20
pattern_obj = pattern_class(pattern)
builder = visitor_class(module_suffix, module_name)
return pattern_obj.visit(builder)

View File

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

View File

@ -12,12 +12,15 @@ from six import string_types, text_type
import stix2
from .base import _STIXBase
from .core import STIX2_OBJ_MAPS, parse, parse_observable
from .exceptions import (
CustomContentError, DictionaryKeyError, MissingPropertiesError,
MutuallyExclusivePropertiesError,
)
from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime
from .parsing import STIX2_OBJ_MAPS, parse, parse_observable
from .utils import (
TYPE_21_REGEX, TYPE_REGEX, _get_dict, get_class_hierarchy_names,
parse_into_datetime,
)
ID_REGEX_interoperability = re.compile(r"[0-9a-fA-F]{8}-"
"[0-9a-fA-F]{4}-"
@ -90,6 +93,36 @@ def _validate_id(id_, spec_version, required_prefix, interoperability):
raise ValueError(ERROR_INVALID_ID.format(id_))
def _validate_type(type_, spec_version):
"""
Check the STIX type name for correctness, raise an exception if there are
errors.
:param type_: The STIX type name
:param spec_version: The STIX specification version to use
:raises ValueError: If there are any errors with the identifier
"""
if spec_version == "2.0":
if not re.match(TYPE_REGEX, type_):
raise ValueError(
"Invalid type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-)." %
type_,
)
else: # 2.1+
if not re.match(TYPE_21_REGEX, type_):
raise ValueError(
"Invalid type name '%s': must only contain the "
"characters a-z (lowercase ASCII), 0-9, and hyphen (-) "
"and must begin with an a-z character" % type_,
)
if len(type_) < 3 or len(type_) > 250:
raise ValueError(
"Invalid type name '%s': must be between 3 and 250 characters." % type_,
)
class Property(object):
"""Represent a property of STIX data type.
@ -241,7 +274,9 @@ class StringProperty(Property):
class TypeProperty(Property):
def __init__(self, type):
def __init__(self, type, spec_version=stix2.DEFAULT_VERSION):
_validate_type(type, spec_version)
self.spec_version = spec_version
super(TypeProperty, self).__init__(fixed=type)
@ -333,12 +368,16 @@ class BooleanProperty(Property):
class TimestampProperty(Property):
def __init__(self, precision=None, **kwargs):
def __init__(self, precision="any", precision_constraint="exact", **kwargs):
self.precision = precision
self.precision_constraint = precision_constraint
super(TimestampProperty, self).__init__(**kwargs)
def clean(self, value):
return parse_into_datetime(value, self.precision)
return parse_into_datetime(
value, self.precision, self.precision_constraint,
)
class DictionaryProperty(Property):
@ -650,7 +689,7 @@ class STIXObjectProperty(Property):
def clean(self, value):
# Any STIX Object (SDO, SRO, or Marking Definition) can be added to
# a bundle with no further checks.
if any(x in ('STIXDomainObject', 'STIXRelationshipObject', 'MarkingDefinition')
if any(x in ('_DomainObject', '_RelationshipObject', 'MarkingDefinition')
for x in get_class_hierarchy_names(value)):
# A simple "is this a spec version 2.1+ object" test. For now,
# limit 2.0 bundles to 2.0 objects. It's not possible yet to

View File

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

View File

@ -114,6 +114,42 @@ def stix_objs1():
return [ind1, ind2, ind3, ind4, ind5]
@pytest.fixture
def stix_objs1_manifests():
# Tests against latest medallion (TAXII 2.1)
ind1 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000001",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
ind2 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000001",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
ind3 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000001",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.936Z",
}
ind4 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000002",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
ind5 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000002",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
return [ind1, ind2, ind3, ind4, ind5]
@pytest.fixture
def stix_objs2():
ind6 = {

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 stix2
from stix2 import parsing
import stix2.v20
from ...exceptions import InvalidValueError
from ...exceptions import DuplicateRegistrationError, InvalidValueError
from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID
IDENTITY_CUSTOM_PROP = stix2.v20.Identity(
@ -449,7 +450,7 @@ def test_custom_observable_raises_exception():
def test_custom_observable_object_no_init_1():
@stix2.v20.CustomObservable(
'x-new-observable', [
'x-new-observable-1', [
('property1', stix2.properties.StringProperty()),
],
)
@ -482,7 +483,7 @@ def test_custom_observable_object_invalid_type_name():
)
class NewObs(object):
pass # pragma: no cover
assert "Invalid observable type name 'x':" in str(excinfo.value)
assert "Invalid type name 'x':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v20.CustomObservable(
@ -492,7 +493,7 @@ def test_custom_observable_object_invalid_type_name():
)
class NewObs2(object):
pass # pragma: no cover
assert "Invalid observable type name 'x_new_obs':" in str(excinfo.value)
assert "Invalid type name 'x_new_obs':" in str(excinfo.value)
def test_custom_observable_object_invalid_ref_property():
@ -807,7 +808,7 @@ def test_custom_extension_invalid_type_name():
)
class FooExtension():
pass # pragma: no cover
assert "Invalid extension type name 'x':" in str(excinfo.value)
assert "Invalid type name 'x':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v20.CustomExtension(
@ -817,7 +818,7 @@ def test_custom_extension_invalid_type_name():
)
class BlaExtension():
pass # pragma: no cover
assert "Invalid extension type name 'x_new_ext':" in str(excinfo.value)
assert "Invalid type name 'x_new_ext':" in str(excinfo.value)
def test_custom_extension_no_properties():
@ -967,9 +968,8 @@ def test_register_custom_object():
class CustomObject2(object):
_type = 'awesome-object'
stix2.core._register_object(CustomObject2, version="2.0")
# Note that we will always check against newest OBJ_MAP.
assert (CustomObject2._type, CustomObject2) in stix2.v20.OBJ_MAP.items()
with pytest.raises(ValueError):
stix2.parsing._register_object(CustomObject2, version="2.0")
def test_extension_property_location():
@ -1011,3 +1011,123 @@ def test_custom_object_nested_dictionary(data):
)
assert data == str(example)
@stix2.v20.CustomObject(
'x-new-type-2', [
('property1', stix2.properties.StringProperty()),
('property2', stix2.properties.IntegerProperty()),
],
)
class NewType2(object):
pass
def test_register_custom_object_with_version():
custom_obj_1 = {
"type": "x-new-type-2",
"id": "x-new-type-2--00000000-0000-4000-8000-000000000007",
}
cust_obj_1 = parsing.dict_to_stix2(custom_obj_1, version='2.0')
v = 'v20'
assert cust_obj_1.type in parsing.STIX2_OBJ_MAPS[v]['objects']
# spec_version is not in STIX 2.0, and is required in 2.1, so this
# suffices as a test for a STIX 2.0 object.
assert "spec_version" not in cust_obj_1
def test_register_duplicate_object_with_version():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v20.CustomObject(
'x-new-type-2', [
('property1', stix2.properties.StringProperty()),
('property2', stix2.properties.IntegerProperty()),
],
)
class NewType2(object):
pass
assert "cannot be registered again" in str(excinfo.value)
@stix2.v20.CustomObservable(
'x-new-observable-2', [
('property1', stix2.properties.StringProperty()),
],
)
class NewObservable2(object):
pass
def test_register_observable_with_version():
custom_obs = NewObservable2(property1="Test Observable")
v = 'v20'
assert custom_obs.type in parsing.STIX2_OBJ_MAPS[v]['observables']
def test_register_duplicate_observable_with_version():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v20.CustomObservable(
'x-new-observable-2', [
('property1', stix2.properties.StringProperty()),
],
)
class NewObservable2(object):
pass
assert "cannot be registered again" in str(excinfo.value)
def test_register_marking_with_version():
@stix2.v20.CustomMarking(
'x-new-obj-2', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj2():
pass
v = 'v20'
no = NewObj2(property1='something')
assert no._type in parsing.STIX2_OBJ_MAPS[v]['markings']
def test_register_observable_extension_with_version():
@stix2.v20.CustomExtension(
stix2.v20.UserAccount, 'some-extension-2', [
('keys', stix2.properties.StringProperty(required=True)),
],
)
class SomeCustomExtension2:
pass
v = 'v20'
example = SomeCustomExtension2(keys='test123')
assert example._type in parsing.STIX2_OBJ_MAPS[v]['observable-extensions']['user-account']
def test_register_duplicate_observable_extension():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v20.CustomExtension(
stix2.v20.UserAccount, 'some-extension-2', [
('property1', stix2.properties.StringProperty(required=True)),
('property2', stix2.properties.IntegerProperty()),
],
)
class NewExtension2():
pass
assert "cannot be registered again" in str(excinfo.value)
def test_register_duplicate_marking():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v20.CustomMarking(
'x-new-obj-2', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj2():
pass
assert "cannot be registered again" in str(excinfo.value)

View File

@ -635,7 +635,7 @@ def test_filesystem_object_with_custom_property_in_bundle(fs_store):
def test_filesystem_custom_object(fs_store):
@stix2.v20.CustomObject(
'x-new-obj', [
'x-new-obj-2', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
@ -650,7 +650,7 @@ def test_filesystem_custom_object(fs_store):
assert newobj_r["property1"] == 'something'
# remove dir
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj"), True)
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj-2"), True)
def test_relationships(rel_fs_store):

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):
@CustomObject(
'x-new-obj', [
'x-new-obj-3', [
('property1', properties.StringProperty(required=True)),
],
)

View File

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

View File

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

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():
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']")
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']", version="2.0")
assert str(patt_obj) == "[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']"
def test_parsing_qualified_expression():
patt_obj = create_pattern_object(
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS",
version="2.0",
)
assert str(
patt_obj,
@ -508,5 +509,5 @@ def test_parsing_qualified_expression():
def test_list_constant():
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]")
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]", version="2.0")
assert str(patt_obj) == "[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]"

View File

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

View File

@ -135,6 +135,42 @@ def stix_objs1():
return [ind1, ind2, ind3, ind4, ind5]
@pytest.fixture
def stix_objs1_manifests():
# Tests against latest medallion (TAXII 2.1)
ind1 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000001",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
ind2 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000001",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
ind3 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000001",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.936Z",
}
ind4 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000002",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
ind5 = {
"date_added": "2017-01-27T13:49:53.935Z",
"id": "indicator--00000000-0000-4000-8000-000000000002",
"media_type": "application/stix+json;version=2.1",
"version": "2017-01-27T13:49:53.935Z",
}
return [ind1, ind2, ind3, ind4, ind5]
@pytest.fixture
def stix_objs2():
ind6 = {

View File

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

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.v21
from ...exceptions import InvalidValueError
from ...exceptions import DuplicateRegistrationError, InvalidValueError
from .constants import FAKE_TIME, IDENTITY_ID, MARKING_DEFINITION_ID
# Custom Properties in SDOs
IDENTITY_CUSTOM_PROP = stix2.v21.Identity(
name="John Smith",
identity_class="individual",
@ -18,6 +20,18 @@ IDENTITY_CUSTOM_PROP = stix2.v21.Identity(
def test_identity_custom_property():
identity = stix2.v21.Identity(
id=IDENTITY_ID,
created="2015-12-21T19:59:11Z",
modified="2015-12-21T19:59:11Z",
name="John Smith",
identity_class="individual",
custom_properties={
"foo": "bar",
},
)
assert identity.foo == "bar"
with pytest.raises(ValueError) as excinfo:
stix2.v21.Identity(
id=IDENTITY_ID,
@ -43,17 +57,47 @@ def test_identity_custom_property():
)
assert "Unexpected properties for Identity" in str(excinfo.value)
identity = stix2.v21.Identity(
id=IDENTITY_ID,
created="2015-12-21T19:59:11Z",
modified="2015-12-21T19:59:11Z",
name="John Smith",
identity_class="individual",
custom_properties={
"foo": "bar",
},
)
assert identity.foo == "bar"
# leading numeric character is illegal in 2.1
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
stix2.v21.Identity(
id=IDENTITY_ID,
created="2015-12-21T19:59:11Z",
modified="2015-12-21T19:59:11Z",
name="John Smith",
identity_class="individual",
custom_properties={
"7foo": "bar",
},
)
assert "must begin with an alpha character." in str(excinfo.value)
# leading "_" is illegal in 2.1
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
stix2.v21.Identity(
id=IDENTITY_ID,
created="2015-12-21T19:59:11Z",
modified="2015-12-21T19:59:11Z",
name="John Smith",
identity_class="individual",
custom_properties={
"_foo": "bar",
},
)
assert "must begin with an alpha character." in str(excinfo.value)
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
identity = stix2.v21.Identity(
id=IDENTITY_ID,
created="2015-12-21T19:59:11Z",
modified="2015-12-21T19:59:11Z",
name="John Smith",
identity_class="individual",
_x_foo="bar",
allow_custom=True,
)
assert "must begin with an alpha character." in str(excinfo.value)
def test_identity_custom_property_invalid():
@ -165,6 +209,8 @@ def test_custom_properties_dict_in_bundled_object():
assert bundle.objects[0].x_foo == "bar"
assert '"x_foo": "bar"' in str(bundle)
# Custom properties in SCOs
def test_custom_property_in_observed_data():
artifact = stix2.v21.File(
@ -184,6 +230,18 @@ def test_custom_property_in_observed_data():
assert '"x_foo": "bar"' in str(observed_data)
def test_invalid_custom_property_in_observed_data():
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
stix2.v21.File(
custom_properties={"8foo": 1},
allow_custom=True,
name='test',
x_foo='bar',
)
assert "must begin with an alpha character." in str(excinfo.value)
def test_custom_property_object_in_observable_extension():
ntfs = stix2.v21.NTFSExt(
allow_custom=True,
@ -245,6 +303,8 @@ def test_identity_custom_property_revoke():
identity = IDENTITY_CUSTOM_PROP.revoke()
assert identity.x_foo == "bar"
# Custom markings
def test_identity_custom_property_edit_markings():
marking_obj = stix2.v21.MarkingDefinition(
@ -267,6 +327,19 @@ def test_identity_custom_property_edit_markings():
identity2.clear_markings('x_foo')
def test_invalid_custom_property_in_marking():
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomMarking(
'x-new-obj', [
('9property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj():
pass
assert "must begin with an alpha character." in str(excinfo.value)
def test_custom_marking_no_init_1():
@stix2.v21.CustomMarking(
'x-new-obj', [
@ -293,6 +366,40 @@ def test_custom_marking_no_init_2():
assert no2.property1 == 'something'
def test_custom_marking_invalid_type_name():
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomMarking(
'x', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj(object):
pass # pragma: no cover
assert "Invalid type name 'x': " in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomMarking(
'x_new_marking', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj2(object):
pass # pragma: no cover
assert "Invalid type name 'x_new_marking':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomMarking(
'7x-new-marking', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj3(object):
pass # pragma: no cover
assert "Invalid type name '7x-new-marking':" in str(excinfo.value)
# Custom Objects
@stix2.v21.CustomObject(
'x-new-type', [
('property1', stix2.properties.StringProperty(required=True)),
@ -374,6 +481,16 @@ def test_custom_object_invalid_type_name():
pass # pragma: no cover
assert "Invalid type name 'x_new_object':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObject(
'7x-new-object', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj3(object):
pass # pragma: no cover
assert "Invalid type name '7x-new-object':" in str(excinfo.value)
def test_parse_custom_object_type():
nt_string = """{
@ -412,6 +529,8 @@ def test_parse_unregistered_custom_object_type_w_allow_custom():
custom_obj = stix2.parse(nt_string, version="2.1", allow_custom=True)
assert custom_obj["type"] == "x-foobar-observable"
# Custom SCOs
@stix2.v21.CustomObservable(
'x-new-observable', [
@ -455,7 +574,7 @@ def test_custom_observable_raises_exception():
def test_custom_observable_object_no_init_1():
@stix2.v21.CustomObservable(
'x-new-observable', [
'x-new-observable-2', [
('property1', stix2.properties.StringProperty()),
],
)
@ -479,6 +598,18 @@ def test_custom_observable_object_no_init_2():
assert no2.property1 == 'something'
def test_invalid_custom_property_in_custom_observable_object():
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable(
'x-new-sco', [
('5property1', stix2.properties.StringProperty()),
],
)
class NewObs(object):
pass # pragma: no cover
assert "must begin with an alpha character." in str(excinfo.value)
def test_custom_observable_object_invalid_type_name():
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable(
@ -488,7 +619,7 @@ def test_custom_observable_object_invalid_type_name():
)
class NewObs(object):
pass # pragma: no cover
assert "Invalid observable type name 'x':" in str(excinfo.value)
assert "Invalid type name 'x':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable(
@ -498,7 +629,17 @@ def test_custom_observable_object_invalid_type_name():
)
class NewObs2(object):
pass # pragma: no cover
assert "Invalid observable type name 'x_new_obs':" in str(excinfo.value)
assert "Invalid type name 'x_new_obs':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomObservable(
'7x-new-obs', [
('property1', stix2.properties.StringProperty()),
],
)
class NewObs3(object):
pass # pragma: no cover
assert "Invalid type name '7x-new-obs':" in str(excinfo.value)
def test_custom_observable_object_invalid_ref_property():
@ -736,6 +877,8 @@ def test_custom_observable_object_no_id_contrib_props():
assert uuid_obj.variant == uuid.RFC_4122
assert uuid_obj.version == 4
# Custom Extensions
@stix2.v21.CustomExtension(
stix2.v21.DomainName, 'x-new-ext', [
@ -862,7 +1005,7 @@ def test_custom_extension_invalid_type_name():
)
class FooExtension():
pass # pragma: no cover
assert "Invalid extension type name 'x':" in str(excinfo.value)
assert "Invalid type name 'x':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomExtension(
@ -872,7 +1015,17 @@ def test_custom_extension_invalid_type_name():
)
class BlaExtension():
pass # pragma: no cover
assert "Invalid extension type name 'x_new_ext':" in str(excinfo.value)
assert "Invalid type name 'x_new_ext':" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomExtension(
stix2.v21.File, '7x-new-ext', {
'property1': stix2.properties.StringProperty(required=True),
},
)
class Bla2Extension():
pass # pragma: no cover
assert "Invalid type name '7x-new-ext':" in str(excinfo.value)
def test_custom_extension_no_properties():
@ -922,6 +1075,19 @@ def test_custom_extension_no_init_2():
assert ne2.property1 == "foobar"
def test_invalid_custom_property_in_extension():
with pytest.raises(ValueError) as excinfo:
@stix2.v21.CustomExtension(
stix2.v21.DomainName, 'x-new3-ext', [
('6property1', stix2.properties.StringProperty(required=True)),
],
)
class NewExt():
pass
assert "must begin with an alpha character." in str(excinfo.value)
def test_parse_observable_with_custom_extension():
input_str = """{
"type": "domain-name",
@ -1020,9 +1186,9 @@ def test_register_custom_object():
class CustomObject2(object):
_type = 'awesome-object'
stix2.core._register_object(CustomObject2, version="2.1")
# Note that we will always check against newest OBJ_MAP.
assert (CustomObject2._type, CustomObject2) in stix2.v21.OBJ_MAP.items()
with pytest.raises(ValueError) as excinfo:
stix2.parsing._register_object(CustomObject2, version="2.1")
assert '@CustomObject decorator' in str(excinfo)
def test_extension_property_location():
@ -1065,3 +1231,110 @@ def test_custom_object_nested_dictionary(data):
)
assert data == str(example)
@stix2.v21.CustomObject(
'x-new-type-2', [
('property1', stix2.properties.StringProperty()),
('property2', stix2.properties.IntegerProperty()),
],
)
class NewType3(object):
pass
def test_register_custom_object_with_version():
custom_obj_1 = {
"type": "x-new-type-2",
"id": "x-new-type-2--00000000-0000-4000-8000-000000000007",
"spec_version": "2.1",
}
cust_obj_1 = stix2.parsing.dict_to_stix2(custom_obj_1, version='2.1')
v = 'v21'
assert cust_obj_1.type in stix2.parsing.STIX2_OBJ_MAPS[v]['objects']
assert cust_obj_1.spec_version == "2.1"
def test_register_duplicate_object_with_version():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v21.CustomObject(
'x-new-type-2', [
('property1', stix2.properties.StringProperty()),
('property2', stix2.properties.IntegerProperty()),
],
)
class NewType2(object):
pass
assert "cannot be registered again" in str(excinfo.value)
@stix2.v21.CustomObservable(
'x-new-observable-3', [
('property1', stix2.properties.StringProperty()),
],
)
class NewObservable3(object):
pass
def test_register_observable():
custom_obs = NewObservable3(property1="Test Observable")
v = 'v21'
assert custom_obs.type in stix2.parsing.STIX2_OBJ_MAPS[v]['observables']
def test_register_duplicate_observable():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v21.CustomObservable(
'x-new-observable-2', [
('property1', stix2.properties.StringProperty()),
],
)
class NewObservable2(object):
pass
assert "cannot be registered again" in str(excinfo.value)
def test_register_observable_custom_extension():
@stix2.v21.CustomExtension(
stix2.v21.DomainName, 'x-new-2-ext', [
('property1', stix2.properties.StringProperty(required=True)),
('property2', stix2.properties.IntegerProperty()),
],
)
class NewExtension2():
pass
example = NewExtension2(property1="Hi there")
v = 'v21'
assert 'domain-name' in stix2.parsing.STIX2_OBJ_MAPS[v]['observables']
assert example._type in stix2.parsing.STIX2_OBJ_MAPS[v]['observable-extensions']['domain-name']
def test_register_duplicate_observable_extension():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v21.CustomExtension(
stix2.v21.DomainName, 'x-new-2-ext', [
('property1', stix2.properties.StringProperty(required=True)),
('property2', stix2.properties.IntegerProperty()),
],
)
class NewExtension2():
pass
assert "cannot be registered again" in str(excinfo.value)
def test_register_duplicate_marking():
with pytest.raises(DuplicateRegistrationError) as excinfo:
@stix2.v21.CustomMarking(
'x-new-obj', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
class NewObj2():
pass
assert "cannot be registered again" in str(excinfo.value)

View File

@ -656,7 +656,7 @@ def test_filesystem_object_with_custom_property_in_bundle(fs_store):
def test_filesystem_custom_object(fs_store):
@stix2.v21.CustomObject(
'x-new-obj', [
'x-new-obj-2', [
('property1', stix2.properties.StringProperty(required=True)),
],
)
@ -671,7 +671,7 @@ def test_filesystem_custom_object(fs_store):
assert newobj_r["property1"] == 'something'
# remove dir
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj"), True)
shutil.rmtree(os.path.join(FS_PATH, "x-new-obj-2"), True)
def test_relationships(rel_fs_store):

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):
@CustomObject(
'x-new-obj', [
'x-new-obj-3', [
('property1', properties.StringProperty(required=True)),
],
)

View File

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

View File

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

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]"
def test_parsing_greater_than():
patt_obj = create_pattern_object("[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.478901]", version="2.1")
assert str(patt_obj) == "[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.478901]"
def test_less_than():
exp = stix2.LessThanComparisonExpression("file:size", 1024)
assert str(exp) == "file:size < 1024"
def test_parsing_less_than():
patt_obj = create_pattern_object("[file:size < 1024]", version="2.1")
assert str(patt_obj) == "[file:size < 1024]"
def test_greater_than_or_equal():
exp = stix2.GreaterThanEqualComparisonExpression(
"file:size",
1024,
)
assert str(exp) == "file:size >= 1024"
def test_parsing_greater_than_or_equal():
patt_obj = create_pattern_object("[file:size >= 1024]", version="2.1")
assert str(patt_obj) == "[file:size >= 1024]"
def test_less_than_or_equal():
exp = stix2.LessThanEqualComparisonExpression(
"file:size",
@ -197,6 +211,36 @@ def test_less_than_or_equal():
assert str(exp) == "file:size <= 1024"
def test_parsing_less_than_or_equal():
patt_obj = create_pattern_object("[file:size <= 1024]", version="2.1")
assert str(patt_obj) == "[file:size <= 1024]"
def test_parsing_issubset():
patt_obj = create_pattern_object("[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']", version="2.1")
assert str(patt_obj) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
def test_parsing_issuperset():
patt_obj = create_pattern_object("[network-traffic:dst_ref.value ISSUPERSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']", version="2.1")
assert str(patt_obj) == "[network-traffic:dst_ref.value ISSUPERSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
def test_parsing_like():
patt_obj = create_pattern_object("[directory:path LIKE 'C:\\\\Windows\\\\%\\\\foo']", version="2.1")
assert str(patt_obj) == "[directory:path LIKE 'C:\\\\Windows\\\\%\\\\foo']"
def test_parsing_match():
patt_obj = create_pattern_object("[process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachine Root$'] FOLLOWEDBY [process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$'] WITHIN 300 SECONDS", version="2.1") # noqa
assert str(patt_obj) == "[process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachine Root$'] FOLLOWEDBY [process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$'] WITHIN 300 SECONDS" # noqa
def test_parsing_followed_by():
patt_obj = create_pattern_object("([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [windows-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']) WITHIN 300 SECONDS", version="2.1") # noqa
assert str(patt_obj) == "([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [windows-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']) WITHIN 300 SECONDS" # noqa
def test_not():
exp = stix2.LessThanComparisonExpression(
"file:size",
@ -257,6 +301,67 @@ def test_and_observable_expression():
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1009' AND user-account:account_login = 'Mary']" # noqa
def test_parsing_and_observable_expression():
exp = create_pattern_object("[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']", version="2.1") # noqa
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']" # noqa
def test_or_observable_expression():
exp1 = stix2.AndBooleanExpression([
stix2.EqualityComparisonExpression(
"user-account:account_type",
"unix",
),
stix2.EqualityComparisonExpression(
"user-account:user_id",
stix2.StringConstant("1007"),
),
stix2.EqualityComparisonExpression(
"user-account:account_login",
"Peter",
),
])
exp2 = stix2.AndBooleanExpression([
stix2.EqualityComparisonExpression(
"user-account:account_type",
"unix",
),
stix2.EqualityComparisonExpression(
"user-account:user_id",
stix2.StringConstant("1008"),
),
stix2.EqualityComparisonExpression(
"user-account:account_login",
"Paul",
),
])
exp3 = stix2.AndBooleanExpression([
stix2.EqualityComparisonExpression(
"user-account:account_type",
"unix",
),
stix2.EqualityComparisonExpression(
"user-account:user_id",
stix2.StringConstant("1009"),
),
stix2.EqualityComparisonExpression(
"user-account:account_login",
"Mary",
),
])
exp = stix2.OrObservationExpression([
stix2.ObservationExpression(exp1),
stix2.ObservationExpression(exp2),
stix2.ObservationExpression(exp3),
])
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1009' AND user-account:account_login = 'Mary']" # noqa
def test_parsing_or_observable_expression():
exp = create_pattern_object("[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']", version="2.1") # noqa
assert str(exp) == "[user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] OR [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul']" # noqa
def test_invalid_and_observable_expression():
with pytest.raises(ValueError):
stix2.AndBooleanExpression([
@ -286,6 +391,11 @@ def test_hex():
assert str(exp) == "[file:mime_type = 'image/bmp' AND file:magic_number_hex = h'ffd8']"
def test_parsing_hex():
patt_obj = create_pattern_object("[file:magic_number_hex = h'ffd8']", version="2.1")
assert str(patt_obj) == "[file:magic_number_hex = h'ffd8']"
def test_multiple_qualifiers():
exp_and = stix2.AndBooleanExpression([
stix2.EqualityComparisonExpression(
@ -334,6 +444,11 @@ def test_binary():
assert str(exp) == "artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q='"
def test_parsing_binary():
patt_obj = create_pattern_object("[artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q=']", version="2.1")
assert str(patt_obj) == "[artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q=']"
def test_list():
exp = stix2.InComparisonExpression(
"process:name",
@ -495,29 +610,45 @@ def test_make_constant_already_a_constant():
def test_parsing_comparison_expression():
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']")
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']", version="2.1")
assert str(patt_obj) == "[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']"
def test_parsing_qualified_expression():
def test_parsing_repeat_and_within_qualified_expression():
patt_obj = create_pattern_object(
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS",
version="2.1",
)
assert str(
patt_obj,
) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS"
def test_parsing_start_stop_qualified_expression():
patt_obj = create_pattern_object(
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] START t'2016-06-01T00:00:00Z' STOP t'2017-03-12T08:30:00Z'", # noqa
version="2.1",
)
assert str(
patt_obj,
) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] START t'2016-06-01T00:00:00Z' STOP t'2017-03-12T08:30:00Z'" # noqa
def test_list_constant():
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]")
patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]", version="2.1")
assert str(patt_obj) == "[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]"
def test_parsing_boolean():
patt_obj = create_pattern_object("[network-traffic:is_active = true]", version="2.1")
assert str(patt_obj) == "[network-traffic:is_active = true]"
def test_parsing_multiple_slashes_quotes():
patt_obj = create_pattern_object("[ file:name = 'weird_name\\'' ]")
patt_obj = create_pattern_object("[ file:name = 'weird_name\\'' ]", version="2.1")
assert str(patt_obj) == "[file:name = 'weird_name\\'']"
def test_parse_error():
with pytest.raises(ParseException):
create_pattern_object("[ file: name = 'weirdname]")
create_pattern_object("[ file: name = 'weirdname]", version="2.1")

View File

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

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 stix2
import stix2.utils
from .constants import CAMPAIGN_MORE_KWARGS
@ -236,8 +239,7 @@ def test_remove_custom_stix_property():
mal_nc = stix2.utils.remove_custom_stix(mal)
assert "x_custom" not in mal_nc
assert (stix2.utils.parse_into_datetime(mal["modified"], precision="millisecond") <
stix2.utils.parse_into_datetime(mal_nc["modified"], precision="millisecond"))
assert mal["modified"] < mal_nc["modified"]
def test_remove_custom_stix_object():
@ -264,3 +266,33 @@ def test_remove_custom_stix_no_custom():
assert len(campaign_v1.keys()) == len(campaign_v2.keys())
assert campaign_v1.id == campaign_v2.id
assert campaign_v1.description == campaign_v2.description
@pytest.mark.parametrize(
"old, candidate_new, expected_new, use_stix21", [
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.001Z", "1999-08-15T00:19:07.001Z", False),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.001Z", False),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.000Z", "1999-08-15T00:19:07.001Z", False),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.999Z", "1999-08-15T00:19:07.001Z", False),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.0001Z", "1999-08-15T00:19:07.001Z", False),
("1999-08-15T00:19:07.999Z", "1999-08-15T00:19:07.9999Z", "1999-08-15T00:19:08.000Z", False),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.001Z", "1999-08-15T00:19:07.001Z", True),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000001Z", True),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.000Z", "1999-08-15T00:19:07.000001Z", True),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:06.999999Z", "1999-08-15T00:19:07.000001Z", True),
("1999-08-15T00:19:07.000Z", "1999-08-15T00:19:07.000001Z", "1999-08-15T00:19:07.000001Z", True),
("1999-08-15T00:19:07.999Z", "1999-08-15T00:19:07.999999Z", "1999-08-15T00:19:07.999999Z", True),
],
)
def test_fudge_modified(old, candidate_new, expected_new, use_stix21):
old_dt = datetime.datetime.strptime(old, "%Y-%m-%dT%H:%M:%S.%fZ")
candidate_new_dt = datetime.datetime.strptime(
candidate_new, "%Y-%m-%dT%H:%M:%S.%fZ",
)
expected_new_dt = datetime.datetime.strptime(
expected_new, "%Y-%m-%dT%H:%M:%S.%fZ",
)
fudged = stix2.utils._fudge_modified(old_dt, candidate_new_dt, use_stix21)
assert fudged == expected_new_dt

View File

@ -6,12 +6,15 @@ except ImportError:
from collections import Mapping
import copy
import datetime as dt
import enum
import json
import re
from dateutil import parser
import pytz
import six
import stix2.base
import stix2
from .exceptions import (
InvalidValueError, RevokeError, UnmodifiablePropertyError,
@ -25,13 +28,84 @@ NOW = object()
# STIX object properties that cannot be modified
STIX_UNMOD_PROPERTIES = ['created', 'created_by_ref', 'id', 'type']
TYPE_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$'
SCO21_EXT_REGEX = r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-ext$'
TYPE_REGEX = re.compile(r'^\-?[a-z0-9]+(-[a-z0-9]+)*\-?$')
TYPE_21_REGEX = re.compile(r'^([a-z][a-z0-9]*)+(-[a-z0-9]+)*\-?$')
PREFIX_21_REGEX = re.compile(r'^[a-z].*')
class Precision(enum.Enum):
"""
Timestamp format precisions.
"""
# auto() wasn't introduced until Python 3.6.
ANY = 1
SECOND = 2
MILLISECOND = 3
class PrecisionConstraint(enum.Enum):
"""
Timestamp precision constraints. These affect how the Precision
values are applied when formatting a timestamp.
These constraints don't really make sense with the ANY precision, so they
have no effect in that case.
"""
EXACT = 1 # format must have exactly the given precision
MIN = 2 # format must have at least the given precision
# no need for a MAX constraint yet
def _to_enum(value, enum_type, enum_default=None):
"""
Detect and convert strings to enums and None to a default enum. This
allows use of strings and None in APIs, while enforcing the enum type: if
you use a string, it must name a valid enum value. This implementation is
case-insensitive.
:param value: A value to be interpreted as an enum (string, Enum instance,
or None). If an Enum instance, it must be an instance of enum_type.
:param enum_type: The enum type which strings will be interpreted against
:param enum_default: The default enum to use if value is None. Must be
an instance of enum_type, or None. If None, you are disallowing a
default and requiring that value be non-None.
:return: An instance of enum_type
:raises TypeError: If value was neither an instance of enum_type, None, nor
a string
:raises KeyError: If value was a string which couldn't be interpreted as an
enum value from enum_type
"""
assert enum_default is None or isinstance(enum_default, enum_type)
if not isinstance(value, enum_type):
if value is None and enum_default is not None:
value = enum_default
elif isinstance(value, six.string_types):
value = enum_type[value.upper()]
else:
raise TypeError("Not a valid {}: {}".format(
enum_type.__name__, value,
))
return value
class STIXdatetime(dt.datetime):
"""
Bundle a datetime with some format-related metadata, so that JSON
serialization has the info it needs to produce compliant timestamps.
"""
def __new__(cls, *args, **kwargs):
precision = kwargs.pop('precision', None)
precision = _to_enum(
kwargs.pop("precision", Precision.ANY),
Precision,
)
precision_constraint = _to_enum(
kwargs.pop("precision_constraint", PrecisionConstraint.EXACT),
PrecisionConstraint,
)
if isinstance(args[0], dt.datetime): # Allow passing in a datetime object
dttm = args[0]
args = (
@ -41,6 +115,7 @@ class STIXdatetime(dt.datetime):
# self will be an instance of STIXdatetime, not dt.datetime
self = dt.datetime.__new__(cls, *args, **kwargs)
self.precision = precision
self.precision_constraint = precision_constraint
return self
def __repr__(self):
@ -90,7 +165,7 @@ def format_datetime(dttm):
2. Convert to UTC
3. Format in ISO format
4. Ensure correct precision
a. Add subsecond value if non-zero and precision not defined
a. Add subsecond value if warranted, according to precision settings
5. Add "Z"
"""
@ -101,20 +176,74 @@ def format_datetime(dttm):
else:
zoned = dttm.astimezone(pytz.utc)
ts = zoned.strftime('%Y-%m-%dT%H:%M:%S')
ms = zoned.strftime('%f')
precision = getattr(dttm, 'precision', None)
if precision == 'second':
pass # Already precise to the second
elif precision == 'millisecond':
ts = ts + '.' + ms[:3]
elif zoned.microsecond > 0:
ts = ts + '.' + ms.rstrip('0')
return ts + 'Z'
precision = getattr(dttm, 'precision', Precision.ANY)
precision_constraint = getattr(
dttm, 'precision_constraint', PrecisionConstraint.EXACT,
)
frac_seconds_str = ""
if precision == Precision.ANY:
# No need to truncate; ignore constraint
if zoned.microsecond:
frac_seconds_str = "{:06d}".format(zoned.microsecond).rstrip("0")
elif precision == Precision.SECOND:
if precision_constraint == PrecisionConstraint.MIN:
# second precision, or better. Winds up being the same as ANY:
# just use all our digits
if zoned.microsecond:
frac_seconds_str = "{:06d}".format(zoned.microsecond)\
.rstrip("0")
# exact: ignore microseconds entirely
else:
# precision == millisecond
if precision_constraint == PrecisionConstraint.EXACT:
# can't rstrip() here or we may lose precision
frac_seconds_str = "{:06d}".format(zoned.microsecond)[:3]
else:
# millisecond precision, or better. So we can rstrip() zeros, but
# only to a length of at least 3 digits (ljust() adds zeros back,
# if it stripped too far.)
frac_seconds_str = "{:06d}"\
.format(zoned.microsecond)\
.rstrip("0")\
.ljust(3, "0")
ts = "{}{}{}Z".format(
ts,
"." if frac_seconds_str else "",
frac_seconds_str,
)
return ts
def parse_into_datetime(value, precision=None):
"""Parse a value into a valid STIX timestamp object.
def parse_into_datetime(
value, precision=Precision.ANY,
precision_constraint=PrecisionConstraint.EXACT,
):
"""
Parse a value into a valid STIX timestamp object. Also, optionally adjust
precision of fractional seconds. This allows alignment with JSON
serialization requirements, and helps ensure we're not using extra
precision which would be lost upon JSON serialization. The precision
info will be embedded in the returned object, so that JSON serialization
will format it correctly.
:param value: A datetime.datetime or datetime.date instance, or a string
:param precision: A precision value: either an instance of the Precision
enum, or a string naming one of the enum values (case-insensitive)
:param precision_constraint: A precision constraint value: either an
instance of the PrecisionConstraint enum, or a string naming one of
the enum values (case-insensitive)
:return: A STIXdatetime instance, which is a datetime but also carries the
precision info necessary to properly JSON-serialize it.
"""
precision = _to_enum(precision, Precision)
precision_constraint = _to_enum(precision_constraint, PrecisionConstraint)
if isinstance(value, dt.date):
if hasattr(value, 'hour'):
ts = value
@ -138,20 +267,23 @@ def parse_into_datetime(value, precision=None):
ts = pytz.utc.localize(parsed)
# Ensure correct precision
if not precision:
return STIXdatetime(ts, precision=precision)
ms = ts.microsecond
if precision == 'second':
ts = ts.replace(microsecond=0)
elif precision == 'millisecond':
ms_len = len(str(ms))
if ms_len > 3:
# Truncate to millisecond precision
factor = 10 ** (ms_len - 3)
ts = ts.replace(microsecond=(ts.microsecond // factor) * factor)
else:
if precision == Precision.SECOND:
if precision_constraint == PrecisionConstraint.EXACT:
ts = ts.replace(microsecond=0)
return STIXdatetime(ts, precision=precision)
# else, no need to modify fractional seconds
elif precision == Precision.MILLISECOND:
if precision_constraint == PrecisionConstraint.EXACT:
us = (ts.microsecond // 1000) * 1000
ts = ts.replace(microsecond=us)
# else: at least millisecond precision: the constraint will affect JSON
# formatting, but there's nothing we need to do here.
# else, precision == Precision.ANY: nothing for us to do.
return STIXdatetime(
ts, precision=precision, precision_constraint=precision_constraint,
)
def _get_dict(data):
@ -230,14 +362,12 @@ def find_property_index(obj, search_key, search_value):
Returns:
int: An index; -1 if the key and value aren't found
"""
from .base import _STIXBase
# Special-case keys which are numbers-as-strings, e.g. for cyber-observable
# mappings. Use the int value of the key as the index.
if search_key.isdigit():
return int(search_key)
if isinstance(obj, _STIXBase):
if isinstance(obj, stix2.base._STIXBase):
if search_key in obj and obj[search_key] == search_value:
idx = _find(obj.object_properties(), search_key)
else:
@ -256,6 +386,39 @@ def find_property_index(obj, search_key, search_value):
return idx
def _fudge_modified(old_modified, new_modified, use_stix21):
"""
Ensures a new modified timestamp is newer than the old. When they are
too close together, new_modified must be pushed further ahead to ensure
it is distinct and later, after JSON serialization (which may mean it's
actually being pushed a little ways into the future). JSON serialization
can remove precision, which can cause distinct timestamps to accidentally
become equal, if we're not careful.
:param old_modified: A previous "modified" timestamp, as a datetime object
:param new_modified: A candidate new "modified" timestamp, as a datetime
object
:param use_stix21: Whether to use STIX 2.1+ versioning timestamp precision
rules (boolean). This is important so that we are aware of how
timestamp precision will be truncated, so we know how close together
the timestamps can be, and how far ahead to potentially push the new
one.
:return: A suitable new "modified" timestamp. This may be different from
what was passed in, if it had to be pushed ahead.
"""
if use_stix21:
# 2.1+: we can use full precision
if new_modified <= old_modified:
new_modified = old_modified + dt.timedelta(microseconds=1)
else:
# 2.0: we must use millisecond precision
one_ms = dt.timedelta(milliseconds=1)
if new_modified - old_modified < one_ms:
new_modified = old_modified + one_ms
return new_modified
def new_version(data, **kwargs):
"""Create a new version of a STIX object, by modifying properties and
updating the ``modified`` property.
@ -283,12 +446,32 @@ def new_version(data, **kwargs):
if unchangable_properties:
raise UnmodifiablePropertyError(unchangable_properties)
# Different versioning precision rules in STIX 2.0 vs 2.1, so we need
# to know which rules to apply.
is_21 = "spec_version" in data
precision_constraint = "min" if is_21 else "exact"
cls = type(data)
if 'modified' not in kwargs:
kwargs['modified'] = get_timestamp()
old_modified = parse_into_datetime(
data["modified"], precision="millisecond",
precision_constraint=precision_constraint,
)
new_modified = get_timestamp()
new_modified = _fudge_modified(old_modified, new_modified, is_21)
kwargs['modified'] = new_modified
elif 'modified' in data:
old_modified_property = parse_into_datetime(data.get('modified'), precision='millisecond')
new_modified_property = parse_into_datetime(kwargs['modified'], precision='millisecond')
old_modified_property = parse_into_datetime(
data.get('modified'), precision='millisecond',
precision_constraint=precision_constraint,
)
new_modified_property = parse_into_datetime(
kwargs['modified'], precision='millisecond',
precision_constraint=precision_constraint,
)
if new_modified_property <= old_modified_property:
raise InvalidValueError(
cls, 'modified',
@ -378,11 +561,6 @@ def remove_custom_stix(stix_obj):
new_obj = new_version(stix_obj, **(dict(props)))
while parse_into_datetime(new_obj['modified']) == parse_into_datetime(stix_obj['modified']):
# Prevents bug when fast computation allows multiple STIX object
# versions to be created in single unit of time
new_obj = new_version(stix_obj, **(dict(props)))
return new_obj
else:

View File

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

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 ..base import _STIXBase
from ..properties import (
IDProperty, ListProperty, STIXObjectProperty, StringProperty, TypeProperty,
)
from .base import _STIXBase20
class Bundle(_STIXBase):
class Bundle(_STIXBase20):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709293>`__.
"""
_type = 'bundle'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
# Not technically correct: STIX 2.0 spec doesn't say spec_version must
# have this value, but it's all we support for now.

View File

@ -5,7 +5,6 @@ import copy
import six
from ..base import _STIXBase
from ..custom import _custom_marking_builder
from ..markings import _MarkingsMixin
from ..markings.utils import check_tlp_marking
@ -14,6 +13,7 @@ from ..properties import (
SelectorProperty, StringProperty, TimestampProperty, TypeProperty,
)
from ..utils import NOW, _get_dict
from .base import _STIXBase20
def _should_set_millisecond(cr, marking_type):
@ -31,7 +31,7 @@ def _should_set_millisecond(cr, marking_type):
return False
class ExternalReference(_STIXBase):
class ExternalReference(_STIXBase20):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709261>`__.
"""
@ -49,7 +49,7 @@ class ExternalReference(_STIXBase):
self._check_at_least_one_property(['description', 'external_id', 'url'])
class KillChainPhase(_STIXBase):
class KillChainPhase(_STIXBase20):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709267>`__.
"""
@ -60,7 +60,7 @@ class KillChainPhase(_STIXBase):
])
class GranularMarking(_STIXBase):
class GranularMarking(_STIXBase20):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709290>`__.
"""
@ -71,7 +71,7 @@ class GranularMarking(_STIXBase):
])
class TLPMarking(_STIXBase):
class TLPMarking(_STIXBase20):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709287>`__.
"""
@ -83,7 +83,7 @@ class TLPMarking(_STIXBase):
])
class StatementMarking(_STIXBase):
class StatementMarking(_STIXBase20):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709286>`__.
"""
@ -113,14 +113,14 @@ class MarkingProperty(Property):
raise ValueError("must be a Statement, TLP Marking or a registered marking.")
class MarkingDefinition(_STIXBase, _MarkingsMixin):
class MarkingDefinition(_STIXBase20, _MarkingsMixin):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part1-stix-core/stix-v2.0-cs01-part1-stix-core.html#_Toc496709284>`__.
"""
_type = 'marking-definition'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW)),
@ -188,7 +188,7 @@ def CustomMarking(type='x-custom-marking', properties=None):
"""
def wrapper(cls):
return _custom_marking_builder(cls, type, properties, '2.0')
return _custom_marking_builder(cls, type, properties, '2.0', _STIXBase20)
return wrapper

View File

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

View File

@ -5,7 +5,6 @@ import itertools
from stix2patterns.validator import run_validator
from ..core import STIXDomainObject
from ..custom import _custom_object_builder
from ..exceptions import InvalidValueError
from ..properties import (
@ -14,17 +13,18 @@ from ..properties import (
TimestampProperty, TypeProperty,
)
from ..utils import NOW
from .base import _DomainObject
from .common import ExternalReference, GranularMarking, KillChainPhase
class AttackPattern(STIXDomainObject):
class AttackPattern(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714302>`__.
"""
_type = 'attack-pattern'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -40,14 +40,14 @@ class AttackPattern(STIXDomainObject):
])
class Campaign(STIXDomainObject):
class Campaign(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714305>`__.
"""
_type = 'campaign'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -66,14 +66,14 @@ class Campaign(STIXDomainObject):
])
class CourseOfAction(STIXDomainObject):
class CourseOfAction(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714308>`__.
"""
_type = 'course-of-action'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -88,14 +88,14 @@ class CourseOfAction(STIXDomainObject):
])
class Identity(STIXDomainObject):
class Identity(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714311>`__.
"""
_type = 'identity'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -113,14 +113,14 @@ class Identity(STIXDomainObject):
])
class Indicator(STIXDomainObject):
class Indicator(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714314>`__.
"""
_type = 'indicator'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -144,14 +144,14 @@ class Indicator(STIXDomainObject):
raise InvalidValueError(self.__class__, 'pattern', str(errors[0]))
class IntrusionSet(STIXDomainObject):
class IntrusionSet(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714317>`__.
"""
_type = 'intrusion-set'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -173,14 +173,14 @@ class IntrusionSet(STIXDomainObject):
])
class Malware(STIXDomainObject):
class Malware(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714320>`__.
"""
_type = 'malware'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -196,14 +196,14 @@ class Malware(STIXDomainObject):
])
class ObservedData(STIXDomainObject):
class ObservedData(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714323>`__.
"""
_type = 'observed-data'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -226,14 +226,14 @@ class ObservedData(STIXDomainObject):
super(ObservedData, self).__init__(*args, **kwargs)
class Report(STIXDomainObject):
class Report(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714326>`__.
"""
_type = 'report'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -256,14 +256,14 @@ class Report(STIXDomainObject):
super(Report, self).__init__(*args, **kwargs)
class ThreatActor(STIXDomainObject):
class ThreatActor(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714329>`__.
"""
_type = 'threat-actor'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -286,14 +286,14 @@ class ThreatActor(STIXDomainObject):
])
class Tool(STIXDomainObject):
class Tool(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714332>`__.
"""
_type = 'tool'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -310,14 +310,14 @@ class Tool(STIXDomainObject):
])
class Vulnerability(STIXDomainObject):
class Vulnerability(_DomainObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714335>`__.
"""
_type = 'vulnerability'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -364,7 +364,7 @@ def CustomObject(type='x-custom-type', properties=None):
def wrapper(cls):
_properties = list(itertools.chain.from_iterable([
[
('type', TypeProperty(type)),
('type', TypeProperty(type, spec_version='2.0')),
('id', IDProperty(type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -380,5 +380,5 @@ def CustomObject(type='x-custom-type', properties=None):
],
sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
]))
return _custom_object_builder(cls, type, _properties, '2.0')
return _custom_object_builder(cls, type, _properties, '2.0', _DomainObject)
return wrapper

View File

@ -2,16 +2,16 @@
from collections import OrderedDict
from ..core import STIXRelationshipObject
from ..properties import (
BooleanProperty, IDProperty, IntegerProperty, ListProperty,
ReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
)
from ..utils import NOW
from .base import _RelationshipObject
from .common import ExternalReference, GranularMarking
class Relationship(STIXRelationshipObject):
class Relationship(_RelationshipObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714340>`__.
"""
@ -20,7 +20,7 @@ class Relationship(STIXRelationshipObject):
_type = 'relationship'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
@ -55,14 +55,14 @@ class Relationship(STIXRelationshipObject):
super(Relationship, self).__init__(**kwargs)
class Sighting(STIXRelationshipObject):
class Sighting(_RelationshipObject):
"""For more detailed information on this object's properties, see
`the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714343>`__.
"""
_type = 'sighting'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.0')),
('id', IDProperty(_type, spec_version='2.0')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),

View File

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

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

View File

@ -2,7 +2,6 @@
from collections import OrderedDict
from ..base import _STIXBase
from ..custom import _custom_marking_builder
from ..exceptions import InvalidValueError
from ..markings import _MarkingsMixin
@ -13,9 +12,10 @@ from ..properties import (
SelectorProperty, StringProperty, TimestampProperty, TypeProperty,
)
from ..utils import NOW, _get_dict
from .base import _STIXBase21
class ExternalReference(_STIXBase):
class ExternalReference(_STIXBase21):
"""For more detailed information on this object's properties, see
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_bajcvqteiard>`__.
"""
@ -49,7 +49,7 @@ class ExternalReference(_STIXBase):
)
class KillChainPhase(_STIXBase):
class KillChainPhase(_STIXBase21):
"""For more detailed information on this object's properties, see
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_i4tjv75ce50h>`__.
"""
@ -60,7 +60,7 @@ class KillChainPhase(_STIXBase):
])
class GranularMarking(_STIXBase):
class GranularMarking(_STIXBase21):
"""For more detailed information on this object's properties, see
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_robezi5egfdr>`__.
"""
@ -76,19 +76,19 @@ class GranularMarking(_STIXBase):
self._check_at_least_one_property(['lang', 'marking_ref'])
class LanguageContent(_STIXBase):
class LanguageContent(_STIXBase21):
"""For more detailed information on this object's properties, see
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_nfwr8z9ax2bi>`__.
"""
_type = 'language-content'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type, spec_version='2.1')),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('object_ref', ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1', required=True)),
# TODO: 'object_modified' it MUST be an exact match for the modified time of the STIX Object (SRO or SDO) being referenced.
('object_modified', TimestampProperty(precision='millisecond')),
@ -103,7 +103,7 @@ class LanguageContent(_STIXBase):
])
class TLPMarking(_STIXBase):
class TLPMarking(_STIXBase21):
"""For more detailed information on this object's properties, see
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_yd3ar14ekwrs>`__.
"""
@ -114,7 +114,7 @@ class TLPMarking(_STIXBase):
])
class StatementMarking(_STIXBase):
class StatementMarking(_STIXBase21):
"""For more detailed information on this object's properties, see
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_3ru8r05saera>`__.
"""
@ -144,18 +144,18 @@ class MarkingProperty(Property):
raise ValueError("must be a Statement, TLP Marking or a registered marking.")
class MarkingDefinition(_STIXBase, _MarkingsMixin):
class MarkingDefinition(_STIXBase21, _MarkingsMixin):
"""For more detailed information on this object's properties, see
`the STIX 2.1 specification <https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_hr5vgqxjk7ns>`__.
"""
_type = 'marking-definition'
_properties = OrderedDict([
('type', TypeProperty(_type)),
('type', TypeProperty(_type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')),
('id', IDProperty(_type)),
('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
('created', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')),
('external_references', ListProperty(ExternalReference)),
('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
('granular_markings', ListProperty(GranularMarking)),
@ -214,7 +214,7 @@ def CustomMarking(type='x-custom-marking', properties=None):
"""
def wrapper(cls):
return _custom_marking_builder(cls, type, properties, '2.1')
return _custom_marking_builder(cls, type, properties, '2.1', _STIXBase21)
return wrapper

View File

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

View File

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

View File

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

View File

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