Merge 'master' and fix imports for consistency

pull/1/head
Chris Lenk 2021-01-15 12:34:10 -05:00
commit a0d535336e
37 changed files with 532 additions and 422 deletions

33
.github/workflows/python-ci-tests.yml vendored Normal file
View File

@ -0,0 +1,33 @@
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: cti-python-stix2 test harness
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
name: Python ${{ matrix.python-version }} Build
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install and update essential dependencies
run: |
pip install -U pip setuptools
pip install tox-gh-actions
pip install codecov
- name: Test with Tox
run: |
tox
- name: Upload coverage information to Codecov
uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true # optional (default = false)
verbose: true # optional (default = false)

View File

@ -1,17 +1,25 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.3.0 rev: v3.4.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: flake8
args:
- --max-line-length=160
- id: check-merge-conflict - id: check-merge-conflict
- repo: https://github.com/asottile/add-trailing-comma - repo: https://github.com/asottile/add-trailing-comma
rev: v0.6.4 rev: v2.0.2
hooks: hooks:
- id: add-trailing-comma - id: add-trailing-comma
- repo: https://github.com/FalconSocial/pre-commit-python-sorter - repo: https://github.com/PyCQA/flake8
sha: b57843b0b874df1d16eb0bef00b868792cb245c2 rev: 3.8.4
hooks: hooks:
- id: python-import-sorter - id: flake8
name: Check project styling
args:
- --max-line-length=160
- repo: https://github.com/PyCQA/isort
rev: 5.7.0
hooks:
- id: isort
name: Sort python imports (shows diff)
args: ["-c", "--diff"]
- id: isort
name: Sort python imports (fixes files)

View File

@ -1,19 +0,0 @@
os: linux
language: python
cache: pip
dist: bionic
python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
install:
- pip install -U pip setuptools
- pip install tox-travis
- pip install codecov
- pip install pre-commit
script:
- tox
- pre-commit run --all-files
after_success:
- codecov

View File

@ -163,8 +163,8 @@ questions about TC Open Repository participation to OASIS Staff at
repository-admin@oasis-open.org and any specific CLA-related questions repository-admin@oasis-open.org and any specific CLA-related questions
to repository-cla@oasis-open.org. to repository-cla@oasis-open.org.
.. |Build_Status| image:: https://travis-ci.org/oasis-open/cti-python-stix2.svg?branch=master .. |Build_Status| image:: https://github.com/oasis-open/cti-python-stix2/workflows/cti-python-stix2%20test%20harness/badge.svg
:target: https://travis-ci.org/oasis-open/cti-python-stix2 :target: https://github.com/oasis-open/cti-python-stix2/actions?query=workflow%3A%22cti-python-stix2+test+harness%22
:alt: Build Status :alt: Build Status
.. |Coverage| image:: https://codecov.io/gh/oasis-open/cti-python-stix2/branch/master/graph/badge.svg .. |Coverage| image:: https://codecov.io/gh/oasis-open/cti-python-stix2/branch/master/graph/badge.svg
:target: https://codecov.io/gh/oasis-open/cti-python-stix2 :target: https://codecov.io/gh/oasis-open/cti-python-stix2

View File

@ -40,10 +40,10 @@ setup(
'Topic :: Security', 'Topic :: Security',
'License :: OSI Approved :: BSD License', 'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
], ],
keywords='stix stix2 json cti cyber threat intelligence', keywords='stix stix2 json cti cyber threat intelligence',
packages=find_packages(exclude=['*.test', '*.test.*']), packages=find_packages(exclude=['*.test', '*.test.*']),

View File

@ -164,8 +164,10 @@ class _STIXBase(Mapping):
defaulted = [] defaulted = []
for name, prop in self._properties.items(): for name, prop in self._properties.items():
try: try:
if (not prop.required and not hasattr(prop, '_fixed_value') and if (
prop.default() == setting_kwargs[name]): not prop.required and not hasattr(prop, '_fixed_value') and
prop.default() == setting_kwargs[name]
):
defaulted.append(name) defaulted.append(name)
except (AttributeError, KeyError): except (AttributeError, KeyError):
continue continue
@ -194,8 +196,10 @@ class _STIXBase(Mapping):
unpickling = '_inner' not in self.__dict__ unpickling = '_inner' not in self.__dict__
if not unpickling and name in self: if not unpickling and name in self:
return self.__getitem__(name) return self.__getitem__(name)
raise AttributeError("'%s' object has no attribute '%s'" % raise AttributeError(
(self.__class__.__name__, name)) "'%s' object has no attribute '%s'" %
(self.__class__.__name__, name),
)
def __setattr__(self, name, value): def __setattr__(self, name, value):
if not name.startswith("_"): if not name.startswith("_"):

View File

@ -75,8 +75,10 @@ class _ObjectFamily(object):
def add(self, obj): def add(self, obj):
self.all_versions[obj["modified"]] = obj self.all_versions[obj["modified"]] = obj
if (self.latest_version is None or if (
obj["modified"] > self.latest_version["modified"]): self.latest_version is None or
obj["modified"] > self.latest_version["modified"]
):
self.latest_version = obj self.latest_version = obj
def __str__(self): def __str__(self):
@ -188,11 +190,13 @@ class MemorySink(DataSink):
def save_to_file(self, path, encoding="utf-8"): def save_to_file(self, path, encoding="utf-8"):
path = os.path.abspath(path) path = os.path.abspath(path)
all_objs = list(itertools.chain.from_iterable( all_objs = list(
itertools.chain.from_iterable(
value.all_versions.values() if isinstance(value, _ObjectFamily) value.all_versions.values() if isinstance(value, _ObjectFamily)
else [value] else [value]
for value in self._data.values() for value in self._data.values()
)) ),
)
if any("spec_version" in x for x in all_objs): if any("spec_version" in x for x in all_objs):
bundle = v21.Bundle(all_objs, allow_custom=self.allow_custom) bundle = v21.Bundle(all_objs, allow_custom=self.allow_custom)

View File

@ -9,18 +9,14 @@
| |
""" """
import stix2 from ... import pattern_visitor
from stix2.equivalence.pattern.compare.observation import ( from ...version import DEFAULT_VERSION
observation_expression_cmp, from .compare.observation import observation_expression_cmp
) from .transform import ChainTransformer, SettleTransformer
from stix2.equivalence.pattern.transform import ( from .transform.observation import (
ChainTransformer, SettleTransformer,
)
from stix2.equivalence.pattern.transform.observation import (
AbsorptionTransformer, CanonicalizeComparisonExpressionsTransformer, AbsorptionTransformer, CanonicalizeComparisonExpressionsTransformer,
DNFTransformer, FlattenTransformer, OrderDedupeTransformer, DNFTransformer, FlattenTransformer, OrderDedupeTransformer,
) )
import stix2.pattern_visitor
# Lazy-initialize # Lazy-initialize
_pattern_canonicalizer = None _pattern_canonicalizer = None
@ -61,7 +57,7 @@ def _get_pattern_canonicalizer():
return _pattern_canonicalizer return _pattern_canonicalizer
def equivalent_patterns(pattern1, pattern2, stix_version=stix2.DEFAULT_VERSION): def equivalent_patterns(pattern1, pattern2, stix_version=DEFAULT_VERSION):
""" """
Determine whether two STIX patterns are semantically equivalent. Determine whether two STIX patterns are semantically equivalent.
@ -74,10 +70,10 @@ def equivalent_patterns(pattern1, pattern2, stix_version=stix2.DEFAULT_VERSION):
Returns: Returns:
True if the patterns are semantically equivalent; False if not True if the patterns are semantically equivalent; False if not
""" """
patt_ast1 = stix2.pattern_visitor.create_pattern_object( patt_ast1 = pattern_visitor.create_pattern_object(
pattern1, version=stix_version, pattern1, version=stix_version,
) )
patt_ast2 = stix2.pattern_visitor.create_pattern_object( patt_ast2 = pattern_visitor.create_pattern_object(
pattern2, version=stix_version, pattern2, version=stix_version,
) )
@ -91,7 +87,7 @@ def equivalent_patterns(pattern1, pattern2, stix_version=stix2.DEFAULT_VERSION):
def find_equivalent_patterns( def find_equivalent_patterns(
search_pattern, patterns, stix_version=stix2.DEFAULT_VERSION, search_pattern, patterns, stix_version=DEFAULT_VERSION,
): ):
""" """
Find patterns from a sequence which are equivalent to a given pattern. Find patterns from a sequence which are equivalent to a given pattern.
@ -109,7 +105,7 @@ def find_equivalent_patterns(
Returns: Returns:
A generator iterator producing the semantically equivalent patterns A generator iterator producing the semantically equivalent patterns
""" """
search_pattern_ast = stix2.pattern_visitor.create_pattern_object( search_pattern_ast = pattern_visitor.create_pattern_object(
search_pattern, version=stix_version, search_pattern, version=stix_version,
) )
@ -119,7 +115,7 @@ def find_equivalent_patterns(
) )
for pattern in patterns: for pattern in patterns:
pattern_ast = stix2.pattern_visitor.create_pattern_object( pattern_ast = pattern_visitor.create_pattern_object(
pattern, version=stix_version, pattern, version=stix_version,
) )
canon_pattern_ast, _ = pattern_canonicalizer.transform(pattern_ast) canon_pattern_ast, _ = pattern_canonicalizer.transform(pattern_ast)

View File

@ -144,7 +144,7 @@ class ComparisonExpressionTransformer(Transformer):
class OrderDedupeTransformer( class OrderDedupeTransformer(
ComparisonExpressionTransformer ComparisonExpressionTransformer,
): ):
""" """
Canonically order the children of all nodes in the AST. Because the Canonically order the children of all nodes in the AST. Because the
@ -247,7 +247,7 @@ class FlattenTransformer(ComparisonExpressionTransformer):
class AbsorptionTransformer( class AbsorptionTransformer(
ComparisonExpressionTransformer ComparisonExpressionTransformer,
): ):
""" """
Applies boolean "absorption" rules for AST simplification. E.g.: Applies boolean "absorption" rules for AST simplification. E.g.:

View File

@ -152,9 +152,11 @@ class ObservationExpressionTransformer(Transformer):
changed = True changed = True
else: else:
raise TypeError("Not an observation expression: {}: {}".format( raise TypeError(
"Not an observation expression: {}: {}".format(
type(ast).__name__, str(ast), type(ast).__name__, str(ast),
)) ),
)
return result, changed return result, changed
@ -229,7 +231,7 @@ class FlattenTransformer(ObservationExpressionTransformer):
class OrderDedupeTransformer( class OrderDedupeTransformer(
ObservationExpressionTransformer ObservationExpressionTransformer,
): ):
""" """
Canonically order AND/OR expressions, and dedupe ORs. E.g.: Canonically order AND/OR expressions, and dedupe ORs. E.g.:
@ -272,7 +274,7 @@ class OrderDedupeTransformer(
class AbsorptionTransformer( class AbsorptionTransformer(
ObservationExpressionTransformer ObservationExpressionTransformer,
): ):
""" """
Applies boolean "absorption" rules for observation expressions, for AST Applies boolean "absorption" rules for observation expressions, for AST
@ -479,7 +481,7 @@ class DNFTransformer(ObservationExpressionTransformer):
class CanonicalizeComparisonExpressionsTransformer( class CanonicalizeComparisonExpressionsTransformer(
ObservationExpressionTransformer ObservationExpressionTransformer,
): ):
""" """
Canonicalize all comparison expressions. Canonicalize all comparison expressions.

View File

@ -182,7 +182,9 @@ def parse_observable(data, _valid_refs=None, allow_custom=False, version=None):
# flag allows for unknown custom objects too, but will not # flag allows for unknown custom objects too, but will not
# be parsed into STIX observable object, just returned as is # be parsed into STIX observable object, just returned as is
return obj return obj
raise ParseError("Can't parse unknown observable type '%s'! For custom observables, " raise ParseError(
"use the CustomObservable decorator." % obj['type']) "Can't parse unknown observable type '%s'! For custom observables, "
"use the CustomObservable decorator." % obj['type'],
)
return obj_class(allow_custom=allow_custom, **obj) return obj_class(allow_custom=allow_custom, **obj)

View File

@ -17,10 +17,9 @@ from stix2patterns.v21.grammars.STIXPatternVisitor import \
STIXPatternVisitor as STIXPatternVisitor21 STIXPatternVisitor as STIXPatternVisitor21
from stix2patterns.v21.pattern import Pattern as Pattern21 from stix2patterns.v21.pattern import Pattern as Pattern21
import stix2
from .patterns import * from .patterns import *
from .patterns import _BooleanExpression from .patterns import _BooleanExpression
from .version import DEFAULT_VERSION
# flake8: noqa F405 # flake8: noqa F405
@ -261,11 +260,13 @@ class STIXPatternVisitorForSTIX2():
property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.getText())) property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.getText()))
i += 2 i += 2
elif isinstance(next, IntegerConstant): elif isinstance(next, IntegerConstant):
property_path.append(self.instantiate( property_path.append(
self.instantiate(
"ListObjectPathComponent", "ListObjectPathComponent",
current.property_name if isinstance(current, BasicObjectPathComponent) else text_type(current), current.property_name if isinstance(current, BasicObjectPathComponent) else text_type(current),
next.value, next.value,
)) ),
)
i += 2 i += 2
else: else:
property_path.append(current) property_path.append(current)
@ -389,7 +390,7 @@ class STIXPatternVisitorForSTIX20(STIXPatternVisitorForSTIX2, STIXPatternVisitor
super(STIXPatternVisitor20, self).__init__() super(STIXPatternVisitor20, self).__init__()
def create_pattern_object(pattern, module_suffix="", module_name="", version=stix2.DEFAULT_VERSION): def create_pattern_object(pattern, module_suffix="", module_name="", version=DEFAULT_VERSION):
""" """
Create a STIX pattern AST from a pattern string. Create a STIX pattern AST from a pattern string.
""" """

View File

@ -9,8 +9,6 @@ import uuid
from six import string_types, text_type from six import string_types, text_type
import stix2
from .base import _STIXBase from .base import _STIXBase
from .exceptions import ( from .exceptions import (
CustomContentError, DictionaryKeyError, MissingPropertiesError, CustomContentError, DictionaryKeyError, MissingPropertiesError,
@ -19,6 +17,7 @@ from .exceptions import (
from .parsing import parse, parse_observable from .parsing import parse, parse_observable
from .registry import STIX2_OBJ_MAPS from .registry import STIX2_OBJ_MAPS
from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime
from .version import DEFAULT_VERSION
try: try:
from collections.abc import Mapping from collections.abc import Mapping
@ -248,9 +247,11 @@ class ListProperty(Property):
valid = self.contained(**item) valid = self.contained(**item)
else: else:
raise ValueError("Can't create a {} out of {}".format( raise ValueError(
"Can't create a {} out of {}".format(
self.contained._type, str(item), self.contained._type, str(item),
)) ),
)
result.append(valid) result.append(valid)
@ -274,7 +275,7 @@ class StringProperty(Property):
class TypeProperty(Property): class TypeProperty(Property):
def __init__(self, type, spec_version=stix2.DEFAULT_VERSION): def __init__(self, type, spec_version=DEFAULT_VERSION):
_validate_type(type, spec_version) _validate_type(type, spec_version)
self.spec_version = spec_version self.spec_version = spec_version
super(TypeProperty, self).__init__(fixed=type) super(TypeProperty, self).__init__(fixed=type)
@ -282,7 +283,7 @@ class TypeProperty(Property):
class IDProperty(Property): class IDProperty(Property):
def __init__(self, type, spec_version=stix2.DEFAULT_VERSION): def __init__(self, type, spec_version=DEFAULT_VERSION):
self.required_prefix = type + "--" self.required_prefix = type + "--"
self.spec_version = spec_version self.spec_version = spec_version
super(IDProperty, self).__init__() super(IDProperty, self).__init__()
@ -381,7 +382,7 @@ class TimestampProperty(Property):
class DictionaryProperty(Property): class DictionaryProperty(Property):
def __init__(self, spec_version=stix2.DEFAULT_VERSION, **kwargs): def __init__(self, spec_version=DEFAULT_VERSION, **kwargs):
self.spec_version = spec_version self.spec_version = spec_version
super(DictionaryProperty, self).__init__(**kwargs) super(DictionaryProperty, self).__init__(**kwargs)
@ -470,7 +471,7 @@ class HexProperty(Property):
class ReferenceProperty(Property): class ReferenceProperty(Property):
def __init__(self, valid_types=None, invalid_types=None, spec_version=stix2.DEFAULT_VERSION, **kwargs): def __init__(self, valid_types=None, invalid_types=None, spec_version=DEFAULT_VERSION, **kwargs):
""" """
references sometimes must be to a specific object type references sometimes must be to a specific object type
""" """
@ -604,7 +605,7 @@ class ObservableProperty(Property):
"""Property for holding Cyber Observable Objects. """Property for holding Cyber Observable Objects.
""" """
def __init__(self, spec_version=stix2.DEFAULT_VERSION, allow_custom=False, *args, **kwargs): def __init__(self, spec_version=DEFAULT_VERSION, allow_custom=False, *args, **kwargs):
self.allow_custom = allow_custom self.allow_custom = allow_custom
self.spec_version = spec_version self.spec_version = spec_version
super(ObservableProperty, self).__init__(*args, **kwargs) super(ObservableProperty, self).__init__(*args, **kwargs)
@ -639,7 +640,7 @@ class ExtensionsProperty(DictionaryProperty):
"""Property for representing extensions on Observable objects. """Property for representing extensions on Observable objects.
""" """
def __init__(self, spec_version=stix2.DEFAULT_VERSION, allow_custom=False, enclosing_type=None, required=False): def __init__(self, spec_version=DEFAULT_VERSION, allow_custom=False, enclosing_type=None, required=False):
self.allow_custom = allow_custom self.allow_custom = allow_custom
self.enclosing_type = enclosing_type self.enclosing_type = enclosing_type
super(ExtensionsProperty, self).__init__(spec_version=spec_version, required=required) super(ExtensionsProperty, self).__init__(spec_version=spec_version, required=required)
@ -681,7 +682,7 @@ class ExtensionsProperty(DictionaryProperty):
class STIXObjectProperty(Property): class STIXObjectProperty(Property):
def __init__(self, spec_version=stix2.DEFAULT_VERSION, allow_custom=False, *args, **kwargs): def __init__(self, spec_version=DEFAULT_VERSION, allow_custom=False, *args, **kwargs):
self.allow_custom = allow_custom self.allow_custom = allow_custom
self.spec_version = spec_version self.spec_version = spec_version
super(STIXObjectProperty, self).__init__(*args, **kwargs) super(STIXObjectProperty, self).__init__(*args, **kwargs)
@ -689,8 +690,10 @@ class STIXObjectProperty(Property):
def clean(self, value): def clean(self, value):
# Any STIX Object (SDO, SRO, or Marking Definition) can be added to # Any STIX Object (SDO, SRO, or Marking Definition) can be added to
# a bundle with no further checks. # a bundle with no further checks.
if any(x in ('_DomainObject', '_RelationshipObject', 'MarkingDefinition') if any(
for x in get_class_hierarchy_names(value)): 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, # A simple "is this a spec version 2.1+ object" test. For now,
# limit 2.0 bundles to 2.0 objects. It's not possible yet to # limit 2.0 bundles to 2.0 objects. It's not possible yet to
# have validation co-constraints among properties, e.g. have # have validation co-constraints among properties, e.g. have

View File

@ -1,14 +1,14 @@
import re import re
import stix2 from . import registry
import stix2.registry
from .base import _DomainObject, _Observable from .base import _DomainObject, _Observable
from .exceptions import DuplicateRegistrationError from .exceptions import DuplicateRegistrationError
from .properties import _validate_type
from .utils import PREFIX_21_REGEX, get_class_hierarchy_names from .utils import PREFIX_21_REGEX, get_class_hierarchy_names
from .version import DEFAULT_VERSION
def _register_object(new_type, version=stix2.DEFAULT_VERSION): def _register_object(new_type, version=DEFAULT_VERSION):
"""Register a custom STIX Object type. """Register a custom STIX Object type.
Args: Args:
@ -40,15 +40,15 @@ def _register_object(new_type, version=stix2.DEFAULT_VERSION):
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
# Use default version (latest) if no version was provided. # Use default version (latest) if no version was provided.
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') v = 'v' + DEFAULT_VERSION.replace('.', '')
OBJ_MAP = stix2.registry.STIX2_OBJ_MAPS[v]['objects'] OBJ_MAP = registry.STIX2_OBJ_MAPS[v]['objects']
if new_type._type in OBJ_MAP.keys(): if new_type._type in OBJ_MAP.keys():
raise DuplicateRegistrationError("STIX Object", new_type._type) raise DuplicateRegistrationError("STIX Object", new_type._type)
OBJ_MAP[new_type._type] = new_type OBJ_MAP[new_type._type] = new_type
def _register_marking(new_marking, version=stix2.DEFAULT_VERSION): def _register_marking(new_marking, version=DEFAULT_VERSION):
"""Register a custom STIX Marking Definition type. """Register a custom STIX Marking Definition type.
Args: Args:
@ -61,7 +61,7 @@ def _register_marking(new_marking, version=stix2.DEFAULT_VERSION):
mark_type = new_marking._type mark_type = new_marking._type
properties = new_marking._properties properties = new_marking._properties
stix2.properties._validate_type(mark_type, version) _validate_type(mark_type, version)
if version == "2.1": if version == "2.1":
for prop_name, prop_value in properties.items(): for prop_name, prop_value in properties.items():
@ -72,15 +72,15 @@ def _register_marking(new_marking, version=stix2.DEFAULT_VERSION):
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
# Use default version (latest) if no version was provided. # Use default version (latest) if no version was provided.
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') v = 'v' + DEFAULT_VERSION.replace('.', '')
OBJ_MAP_MARKING = stix2.registry.STIX2_OBJ_MAPS[v]['markings'] OBJ_MAP_MARKING = registry.STIX2_OBJ_MAPS[v]['markings']
if mark_type in OBJ_MAP_MARKING.keys(): if mark_type in OBJ_MAP_MARKING.keys():
raise DuplicateRegistrationError("STIX Marking", mark_type) raise DuplicateRegistrationError("STIX Marking", mark_type)
OBJ_MAP_MARKING[mark_type] = new_marking OBJ_MAP_MARKING[mark_type] = new_marking
def _register_observable(new_observable, version=stix2.DEFAULT_VERSION): def _register_observable(new_observable, version=DEFAULT_VERSION):
"""Register a custom STIX Cyber Observable type. """Register a custom STIX Cyber Observable type.
Args: Args:
@ -99,8 +99,12 @@ def _register_observable(new_observable, version=stix2.DEFAULT_VERSION):
"'%s' is named like an object reference property but " "'%s' is named like an object reference property but "
"is not an ObjectReferenceProperty." % prop_name, "is not an ObjectReferenceProperty." % prop_name,
) )
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or elif (
'ObjectReferenceProperty' not in get_class_hierarchy_names(prop.contained))): 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( raise ValueError(
"'%s' is named like an object reference list property but " "'%s' is named like an object reference list property but "
"is not a ListProperty containing ObjectReferenceProperty." % prop_name, "is not a ListProperty containing ObjectReferenceProperty." % prop_name,
@ -115,8 +119,12 @@ def _register_observable(new_observable, version=stix2.DEFAULT_VERSION):
"'%s' is named like a reference property but " "'%s' is named like a reference property but "
"is not a ReferenceProperty." % prop_name, "is not a ReferenceProperty." % prop_name,
) )
elif (prop_name.endswith('_refs') and ('ListProperty' not in get_class_hierarchy_names(prop) or elif (
'ReferenceProperty' not in get_class_hierarchy_names(prop.contained))): 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( raise ValueError(
"'%s' is named like a reference list property but " "'%s' is named like a reference list property but "
"is not a ListProperty containing ReferenceProperty." % prop_name, "is not a ListProperty containing ReferenceProperty." % prop_name,
@ -126,16 +134,16 @@ def _register_observable(new_observable, version=stix2.DEFAULT_VERSION):
v = 'v' + version.replace('.', '') v = 'v' + version.replace('.', '')
else: else:
# Use default version (latest) if no version was provided. # Use default version (latest) if no version was provided.
v = 'v' + stix2.DEFAULT_VERSION.replace('.', '') v = 'v' + DEFAULT_VERSION.replace('.', '')
OBJ_MAP_OBSERVABLE = stix2.registry.STIX2_OBJ_MAPS[v]['observables'] OBJ_MAP_OBSERVABLE = registry.STIX2_OBJ_MAPS[v]['observables']
if new_observable._type in OBJ_MAP_OBSERVABLE.keys(): if new_observable._type in OBJ_MAP_OBSERVABLE.keys():
raise DuplicateRegistrationError("Cyber Observable", new_observable._type) raise DuplicateRegistrationError("Cyber Observable", new_observable._type)
OBJ_MAP_OBSERVABLE[new_observable._type] = new_observable OBJ_MAP_OBSERVABLE[new_observable._type] = new_observable
def _register_observable_extension( def _register_observable_extension(
observable, new_extension, version=stix2.DEFAULT_VERSION, observable, new_extension, version=DEFAULT_VERSION,
): ):
"""Register a custom extension to a STIX Cyber Observable type. """Register a custom extension to a STIX Cyber Observable type.
@ -155,7 +163,7 @@ def _register_observable_extension(
if not issubclass(obs_class, _Observable): if not issubclass(obs_class, _Observable):
raise ValueError("'observable' must be a valid Observable class!") raise ValueError("'observable' must be a valid Observable class!")
stix2.properties._validate_type(ext_type, version) _validate_type(ext_type, version)
if not new_extension._properties: if not new_extension._properties:
raise ValueError( raise ValueError(
@ -184,8 +192,8 @@ def _register_observable_extension(
"created with the @CustomObservable decorator.", "created with the @CustomObservable decorator.",
) )
OBJ_MAP_OBSERVABLE = stix2.registry.STIX2_OBJ_MAPS[v]['observables'] OBJ_MAP_OBSERVABLE = registry.STIX2_OBJ_MAPS[v]['observables']
EXT_MAP = stix2.registry.STIX2_OBJ_MAPS[v]['observable-extensions'] EXT_MAP = registry.STIX2_OBJ_MAPS[v]['observable-extensions']
try: try:
if ext_type in EXT_MAP[observable_type].keys(): if ext_type in EXT_MAP[observable_type].keys():

View File

@ -175,12 +175,14 @@ def test_memory_source_get_nonexistant_object(mem_source):
def test_memory_store_all_versions(mem_store): def test_memory_store_all_versions(mem_store):
# Add bundle of items to sink # Add bundle of items to sink
mem_store.add(dict( mem_store.add(
dict(
id="bundle--%s" % make_id(), id="bundle--%s" % make_id(),
objects=STIX_OBJS2, objects=STIX_OBJS2,
spec_version="2.0", spec_version="2.0",
type="bundle", type="bundle",
)) ),
)
resp = mem_store.all_versions("indicator--00000000-0000-4000-8000-000000000001") resp = mem_store.all_versions("indicator--00000000-0000-4000-8000-000000000001")
assert len(resp) == 3 assert len(resp) == 3

View File

@ -39,15 +39,19 @@ def ds2():
cam = stix2.v20.Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS) cam = stix2.v20.Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS)
idy = stix2.v20.Identity(id=IDENTITY_ID, **IDENTITY_KWARGS) idy = stix2.v20.Identity(id=IDENTITY_ID, **IDENTITY_KWARGS)
ind = stix2.v20.Indicator(id=INDICATOR_ID, created_by_ref=idy.id, **INDICATOR_KWARGS) ind = stix2.v20.Indicator(id=INDICATOR_ID, created_by_ref=idy.id, **INDICATOR_KWARGS)
indv2 = ind.new_version(external_references=[{ indv2 = ind.new_version(
external_references=[{
"source_name": "unknown", "source_name": "unknown",
"url": "https://examplewebsite.com/", "url": "https://examplewebsite.com/",
}]) }],
)
mal = stix2.v20.Malware(id=MALWARE_ID, created_by_ref=idy.id, **MALWARE_KWARGS) mal = stix2.v20.Malware(id=MALWARE_ID, created_by_ref=idy.id, **MALWARE_KWARGS)
malv2 = mal.new_version(external_references=[{ malv2 = mal.new_version(
external_references=[{
"source_name": "unknown", "source_name": "unknown",
"url": "https://examplewebsite2.com/", "url": "https://examplewebsite2.com/",
}]) }],
)
rel1 = stix2.v20.Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0]) rel1 = stix2.v20.Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0])
rel2 = stix2.v20.Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1]) rel2 = stix2.v20.Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1])
rel3 = stix2.v20.Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2]) rel3 = stix2.v20.Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2])

View File

@ -20,7 +20,8 @@ EXPECTED_INDICATOR = """{
] ]
}""" }"""
EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join(""" EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join(
"""
type='indicator', type='indicator',
id='indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7', id='indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7',
created='2017-01-01T00:00:01.000Z', created='2017-01-01T00:00:01.000Z',
@ -28,7 +29,8 @@ EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join("""
pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']",
valid_from='1970-01-01T00:00:01Z', valid_from='1970-01-01T00:00:01Z',
labels=['malicious-activity'] labels=['malicious-activity']
""".split()) + ")" """.split(),
) + ")"
def test_indicator_with_all_required_properties(): def test_indicator_with_all_required_properties():

View File

@ -1180,21 +1180,24 @@ def test_process_example_extensions_empty():
def test_process_example_with_WindowsProcessExt_Object(): def test_process_example_with_WindowsProcessExt_Object():
p = stix2.v20.Process(extensions={ p = stix2.v20.Process(
extensions={
"windows-process-ext": stix2.v20.WindowsProcessExt( "windows-process-ext": stix2.v20.WindowsProcessExt(
aslr_enabled=True, aslr_enabled=True,
dep_enabled=True, dep_enabled=True,
priority="HIGH_PRIORITY_CLASS", priority="HIGH_PRIORITY_CLASS",
owner_sid="S-1-5-21-186985262-1144665072-74031268-1309", owner_sid="S-1-5-21-186985262-1144665072-74031268-1309",
), # noqa ), # noqa
}) },
)
assert p.extensions["windows-process-ext"].dep_enabled assert p.extensions["windows-process-ext"].dep_enabled
assert p.extensions["windows-process-ext"].owner_sid == "S-1-5-21-186985262-1144665072-74031268-1309" assert p.extensions["windows-process-ext"].owner_sid == "S-1-5-21-186985262-1144665072-74031268-1309"
def test_process_example_with_WindowsServiceExt(): def test_process_example_with_WindowsServiceExt():
p = stix2.v20.Process(extensions={ p = stix2.v20.Process(
extensions={
"windows-service-ext": { "windows-service-ext": {
"service_name": "sirvizio", "service_name": "sirvizio",
"display_name": "Sirvizio", "display_name": "Sirvizio",
@ -1202,14 +1205,16 @@ def test_process_example_with_WindowsServiceExt():
"service_type": "SERVICE_WIN32_OWN_PROCESS", "service_type": "SERVICE_WIN32_OWN_PROCESS",
"service_status": "SERVICE_RUNNING", "service_status": "SERVICE_RUNNING",
}, },
}) },
)
assert p.extensions["windows-service-ext"].service_name == "sirvizio" assert p.extensions["windows-service-ext"].service_name == "sirvizio"
assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS" assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS"
def test_process_example_with_WindowsProcessServiceExt(): def test_process_example_with_WindowsProcessServiceExt():
p = stix2.v20.Process(extensions={ p = stix2.v20.Process(
extensions={
"windows-service-ext": { "windows-service-ext": {
"service_name": "sirvizio", "service_name": "sirvizio",
"display_name": "Sirvizio", "display_name": "Sirvizio",
@ -1223,7 +1228,8 @@ def test_process_example_with_WindowsProcessServiceExt():
"priority": "HIGH_PRIORITY_CLASS", "priority": "HIGH_PRIORITY_CLASS",
"owner_sid": "S-1-5-21-186985262-1144665072-74031268-1309", "owner_sid": "S-1-5-21-186985262-1144665072-74031268-1309",
}, },
}) },
)
assert p.extensions["windows-service-ext"].service_name == "sirvizio" assert p.extensions["windows-service-ext"].service_name == "sirvizio"
assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS" assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS"

View File

@ -2,8 +2,7 @@ from collections import OrderedDict
import pytest import pytest
import stix2 from stix2 import DEFAULT_VERSION, exceptions, parsing, registration, registry
from stix2 import exceptions, parsing, registration, registry
BUNDLE = { BUNDLE = {
"type": "bundle", "type": "bundle",
@ -59,7 +58,7 @@ def test_parse_observable_with_version():
assert v in str(obs_obj.__class__) 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") @pytest.mark.xfail(reason="The default version is no longer 2.0", condition=DEFAULT_VERSION != "2.0")
def test_parse_observable_with_no_version(): def test_parse_observable_with_no_version():
observable = {"type": "file", "name": "foo.exe"} observable = {"type": "file", "name": "foo.exe"}
obs_obj = parsing.parse_observable(observable) obs_obj = parsing.parse_observable(observable)

View File

@ -306,10 +306,12 @@ def test_multiple_qualifiers():
def test_set_op(): def test_set_op():
exp = stix2.ObservationExpression(stix2.IsSubsetComparisonExpression( exp = stix2.ObservationExpression(
stix2.IsSubsetComparisonExpression(
"network-traffic:dst_ref.value", "network-traffic:dst_ref.value",
"2001:0db8:dead:beef:0000:0000:0000:0000/64", "2001:0db8:dead:beef:0000:0000:0000:0000/64",
)) ),
)
assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']" assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"

View File

@ -71,7 +71,7 @@ def test_parse_datetime_invalid(ts):
{"a": 1}, {"a": 1},
'{"a": 1}', '{"a": 1}',
StringIO(u'{"a": 1}'), StringIO(u'{"a": 1}'),
[("a", 1,)], [("a", 1)],
], ],
) )
def test_get_dict(data): def test_get_dict(data):

View File

@ -46,10 +46,12 @@ def test_making_new_version_with_embedded_object():
**CAMPAIGN_MORE_KWARGS **CAMPAIGN_MORE_KWARGS
) )
campaign_v2 = campaign_v1.new_version(external_references=[{ campaign_v2 = campaign_v1.new_version(
external_references=[{
"source_name": "capec", "source_name": "capec",
"external_id": "CAPEC-164", "external_id": "CAPEC-164",
}]) }],
)
assert campaign_v1.id == campaign_v2.id assert campaign_v1.id == campaign_v2.id
assert campaign_v1.created_by_ref == campaign_v2.created_by_ref assert campaign_v1.created_by_ref == campaign_v2.created_by_ref
@ -237,8 +239,10 @@ def test_remove_custom_stix_property():
mal_nc = stix2.versioning.remove_custom_stix(mal) mal_nc = stix2.versioning.remove_custom_stix(mal)
assert "x_custom" not in mal_nc assert "x_custom" not in mal_nc
assert (stix2.utils.parse_into_datetime(mal["modified"], precision="millisecond") < assert (
stix2.utils.parse_into_datetime(mal_nc["modified"], precision="millisecond")) stix2.utils.parse_into_datetime(mal["modified"], precision="millisecond") <
stix2.utils.parse_into_datetime(mal_nc["modified"], precision="millisecond")
)
def test_remove_custom_stix_object(): def test_remove_custom_stix_object():

View File

@ -191,11 +191,13 @@ def test_memory_source_get_nonexistant_object(mem_source):
def test_memory_store_all_versions(mem_store): def test_memory_store_all_versions(mem_store):
# Add bundle of items to sink # Add bundle of items to sink
mem_store.add(dict( mem_store.add(
dict(
id="bundle--%s" % make_id(), id="bundle--%s" % make_id(),
objects=STIX_OBJS2, objects=STIX_OBJS2,
type="bundle", type="bundle",
)) ),
)
resp = mem_store.all_versions("indicator--00000000-0000-4000-8000-000000000001") resp = mem_store.all_versions("indicator--00000000-0000-4000-8000-000000000001")
assert len(resp) == 3 assert len(resp) == 3

View File

@ -20,7 +20,8 @@ EXPECTED_INDICATOR = """{
"valid_from": "1970-01-01T00:00:01Z" "valid_from": "1970-01-01T00:00:01Z"
}""" }"""
EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join(""" EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join(
"""
type='indicator', type='indicator',
spec_version='2.1', spec_version='2.1',
id='indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7', id='indicator--a740531e-63ff-4e49-a9e1-a0a3eed0e3e7',
@ -30,7 +31,8 @@ EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join("""
pattern_type='stix', pattern_type='stix',
pattern_version='2.1', pattern_version='2.1',
valid_from='1970-01-01T00:00:01Z' valid_from='1970-01-01T00:00:01Z'
""".split()) + ")" """.split(),
) + ")"
def test_indicator_with_all_required_properties(): def test_indicator_with_all_required_properties():

View File

@ -19,14 +19,16 @@ EXPECTED_LOCATION_1 = """{
"longitude": 2.3522 "longitude": 2.3522
}""" }"""
EXPECTED_LOCATION_1_REPR = "Location(" + " ".join(""" EXPECTED_LOCATION_1_REPR = "Location(" + " ".join(
"""
type='location', type='location',
spec_version='2.1', spec_version='2.1',
id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64', id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64',
created='2016-04-06T20:03:00.000Z', created='2016-04-06T20:03:00.000Z',
modified='2016-04-06T20:03:00.000Z', modified='2016-04-06T20:03:00.000Z',
latitude=48.8566, latitude=48.8566,
longitude=2.3522""".split()) + ")" longitude=2.3522""".split(),
) + ")"
EXPECTED_LOCATION_2 = """{ EXPECTED_LOCATION_2 = """{
"type": "location", "type": "location",
@ -38,13 +40,15 @@ EXPECTED_LOCATION_2 = """{
} }
""" """
EXPECTED_LOCATION_2_REPR = "Location(" + " ".join(""" EXPECTED_LOCATION_2_REPR = "Location(" + " ".join(
"""
type='location', type='location',
spec_version='2.1', spec_version='2.1',
id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64', id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64',
created='2016-04-06T20:03:00.000Z', created='2016-04-06T20:03:00.000Z',
modified='2016-04-06T20:03:00.000Z', modified='2016-04-06T20:03:00.000Z',
region='north-america'""".split()) + ")" region='north-america'""".split(),
) + ")"
def test_location_with_some_required_properties(): def test_location_with_some_required_properties():

View File

@ -496,12 +496,14 @@ def test_parse_email_message_not_multipart(data):
def test_parse_file_archive(data): def test_parse_file_archive(data):
odata_str = OBJECTS_REGEX.sub('"objects": { %s }' % data, EXPECTED) odata_str = OBJECTS_REGEX.sub('"objects": { %s }' % data, EXPECTED)
odata = stix2.parse(odata_str, version="2.1") odata = stix2.parse(odata_str, version="2.1")
assert all(x in odata.objects["3"].extensions['archive-ext'].contains_refs assert all(
x in odata.objects["3"].extensions['archive-ext'].contains_refs
for x in [ for x in [
"file--ecd47d73-15e4-5250-afda-ef8897b22340", "file--ecd47d73-15e4-5250-afda-ef8897b22340",
"file--65f2873d-38c2-56b4-bfa5-e3ef21e8a3c3", "file--65f2873d-38c2-56b4-bfa5-e3ef21e8a3c3",
"file--ef2d6dca-ec7d-5ab7-8dd9-ec9c0dee0eac", "file--ef2d6dca-ec7d-5ab7-8dd9-ec9c0dee0eac",
]) ]
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -904,14 +906,14 @@ def test_file_with_archive_ext_object():
f_obj = stix2.v21.File( f_obj = stix2.v21.File(
name="foo", extensions={ name="foo", extensions={
"archive-ext": { "archive-ext": {
"contains_refs": [ad, ], "contains_refs": [ad],
}, },
}, },
) )
f_ref = stix2.v21.File( f_ref = stix2.v21.File(
name="foo", extensions={ name="foo", extensions={
"archive-ext": { "archive-ext": {
"contains_refs": [ad.id, ], "contains_refs": [ad.id],
}, },
}, },
) )
@ -1229,9 +1231,11 @@ def test_process_example_empty_error():
def test_process_example_empty_with_extensions(): def test_process_example_empty_with_extensions():
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo: with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
stix2.v21.Process(extensions={ stix2.v21.Process(
extensions={
"windows-process-ext": {}, "windows-process-ext": {},
}) },
)
assert excinfo.value.cls == stix2.v21.Process assert excinfo.value.cls == stix2.v21.Process
@ -1276,21 +1280,24 @@ def test_process_example_extensions_empty():
def test_process_example_with_WindowsProcessExt_Object(): def test_process_example_with_WindowsProcessExt_Object():
p = stix2.v21.Process(extensions={ p = stix2.v21.Process(
extensions={
"windows-process-ext": stix2.v21.WindowsProcessExt( "windows-process-ext": stix2.v21.WindowsProcessExt(
aslr_enabled=True, aslr_enabled=True,
dep_enabled=True, dep_enabled=True,
priority="HIGH_PRIORITY_CLASS", priority="HIGH_PRIORITY_CLASS",
owner_sid="S-1-5-21-186985262-1144665072-74031268-1309", owner_sid="S-1-5-21-186985262-1144665072-74031268-1309",
), # noqa ), # noqa
}) },
)
assert p.extensions["windows-process-ext"].dep_enabled assert p.extensions["windows-process-ext"].dep_enabled
assert p.extensions["windows-process-ext"].owner_sid == "S-1-5-21-186985262-1144665072-74031268-1309" assert p.extensions["windows-process-ext"].owner_sid == "S-1-5-21-186985262-1144665072-74031268-1309"
def test_process_example_with_WindowsServiceExt(): def test_process_example_with_WindowsServiceExt():
p = stix2.v21.Process(extensions={ p = stix2.v21.Process(
extensions={
"windows-service-ext": { "windows-service-ext": {
"service_name": "sirvizio", "service_name": "sirvizio",
"display_name": "Sirvizio", "display_name": "Sirvizio",
@ -1298,14 +1305,16 @@ def test_process_example_with_WindowsServiceExt():
"service_type": "SERVICE_WIN32_OWN_PROCESS", "service_type": "SERVICE_WIN32_OWN_PROCESS",
"service_status": "SERVICE_RUNNING", "service_status": "SERVICE_RUNNING",
}, },
}) },
)
assert p.extensions["windows-service-ext"].service_name == "sirvizio" assert p.extensions["windows-service-ext"].service_name == "sirvizio"
assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS" assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS"
def test_process_example_with_WindowsProcessServiceExt(): def test_process_example_with_WindowsProcessServiceExt():
p = stix2.v21.Process(extensions={ p = stix2.v21.Process(
extensions={
"windows-service-ext": { "windows-service-ext": {
"service_name": "sirvizio", "service_name": "sirvizio",
"display_name": "Sirvizio", "display_name": "Sirvizio",
@ -1319,7 +1328,8 @@ def test_process_example_with_WindowsProcessServiceExt():
"priority": "HIGH_PRIORITY_CLASS", "priority": "HIGH_PRIORITY_CLASS",
"owner_sid": "S-1-5-21-186985262-1144665072-74031268-1309", "owner_sid": "S-1-5-21-186985262-1144665072-74031268-1309",
}, },
}) },
)
assert p.extensions["windows-service-ext"].service_name == "sirvizio" assert p.extensions["windows-service-ext"].service_name == "sirvizio"
assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS" assert p.extensions["windows-service-ext"].service_type == "SERVICE_WIN32_OWN_PROCESS"

View File

@ -2,8 +2,7 @@ from collections import OrderedDict
import pytest import pytest
import stix2 from stix2 import DEFAULT_VERSION, exceptions, parsing, registration, registry
from stix2 import exceptions, parsing, registration, registry
BUNDLE = { BUNDLE = {
"type": "bundle", "type": "bundle",
@ -64,7 +63,7 @@ def test_parse_observable_with_version():
assert v in str(obs_obj.__class__) assert v in str(obs_obj.__class__)
@pytest.mark.xfail(reason="The default version is not 2.1", condition=stix2.DEFAULT_VERSION != "2.1") @pytest.mark.xfail(reason="The default version is not 2.1", condition=DEFAULT_VERSION != "2.1")
def test_parse_observable_with_no_version(): def test_parse_observable_with_no_version():
observable = {"type": "file", "name": "foo.exe", "spec_version": "2.1"} observable = {"type": "file", "name": "foo.exe", "spec_version": "2.1"}
obs_obj = parsing.parse_observable(observable) obs_obj = parsing.parse_observable(observable)
@ -85,7 +84,7 @@ def test_register_marking_with_version():
assert v in str(registry.STIX2_OBJ_MAPS[v]['markings'][NewMarking1._type]) assert v in str(registry.STIX2_OBJ_MAPS[v]['markings'][NewMarking1._type])
@pytest.mark.xfail(reason="The default version is not 2.1", condition=stix2.DEFAULT_VERSION != "2.1") @pytest.mark.xfail(reason="The default version is not 2.1", condition=DEFAULT_VERSION != "2.1")
def test_register_marking_with_no_version(): def test_register_marking_with_no_version():
# Uses default version (2.1 in this case) # Uses default version (2.1 in this case)
class NewMarking2: class NewMarking2:

View File

@ -444,10 +444,12 @@ def test_multiple_qualifiers():
def test_set_op(): def test_set_op():
exp = stix2.ObservationExpression(stix2.IsSubsetComparisonExpression( exp = stix2.ObservationExpression(
stix2.IsSubsetComparisonExpression(
"network-traffic:dst_ref.value", "network-traffic:dst_ref.value",
"2001:0db8:dead:beef:0000:0000:0000:0000/64", "2001:0db8:dead:beef:0000:0000:0000:0000/64",
)) ),
)
assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']" assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
@ -712,12 +714,12 @@ def test_parsing_boolean():
def test_parsing_mixed_boolean_expression_1(): def test_parsing_mixed_boolean_expression_1():
patt_obj = create_pattern_object("[a:b = 1 AND a:b = 2 OR a:b = 3]",) patt_obj = create_pattern_object("[a:b = 1 AND a:b = 2 OR a:b = 3]")
assert str(patt_obj) == "[a:b = 1 AND a:b = 2 OR a:b = 3]" assert str(patt_obj) == "[a:b = 1 AND a:b = 2 OR a:b = 3]"
def test_parsing_mixed_boolean_expression_2(): def test_parsing_mixed_boolean_expression_2():
patt_obj = create_pattern_object("[a:b = 1 OR a:b = 2 AND a:b = 3]",) patt_obj = create_pattern_object("[a:b = 1 OR a:b = 2 AND a:b = 3]")
assert str(patt_obj) == "[a:b = 1 OR a:b = 2 AND a:b = 3]" assert str(patt_obj) == "[a:b = 1 OR a:b = 2 AND a:b = 3]"

View File

@ -71,7 +71,7 @@ def test_parse_datetime_invalid(ts):
{"a": 1}, {"a": 1},
'{"a": 1}', '{"a": 1}',
StringIO(u'{"a": 1}'), StringIO(u'{"a": 1}'),
[("a", 1,)], [("a", 1)],
], ],
) )
def test_get_dict(data): def test_get_dict(data):

View File

@ -50,10 +50,12 @@ def test_making_new_version_with_embedded_object():
**CAMPAIGN_MORE_KWARGS **CAMPAIGN_MORE_KWARGS
) )
campaign_v2 = campaign_v1.new_version(external_references=[{ campaign_v2 = campaign_v1.new_version(
external_references=[{
"source_name": "capec", "source_name": "capec",
"external_id": "CAPEC-164", "external_id": "CAPEC-164",
}]) }],
)
assert campaign_v1.id == campaign_v2.id assert campaign_v1.id == campaign_v2.id
assert campaign_v1.spec_version == campaign_v2.spec_version assert campaign_v1.spec_version == campaign_v2.spec_version

View File

@ -71,9 +71,11 @@ def _to_enum(value, enum_type, enum_default=None):
elif isinstance(value, six.string_types): elif isinstance(value, six.string_types):
value = enum_type[value.upper()] value = enum_type[value.upper()]
else: else:
raise TypeError("Not a valid {}: {}".format( raise TypeError(
"Not a valid {}: {}".format(
enum_type.__name__, value, enum_type.__name__, value,
)) ),
)
return value return value

View File

@ -440,24 +440,28 @@ class SocketExt(_Extension):
('is_blocking', BooleanProperty()), ('is_blocking', BooleanProperty()),
('is_listening', BooleanProperty()), ('is_listening', BooleanProperty()),
( (
'protocol_family', EnumProperty(allowed=[ 'protocol_family', EnumProperty(
allowed=[
"PF_INET", "PF_INET",
"PF_IPX", "PF_IPX",
"PF_APPLETALK", "PF_APPLETALK",
"PF_INET6", "PF_INET6",
"PF_AX25", "PF_AX25",
"PF_NETROM", "PF_NETROM",
]), ],
),
), ),
('options', DictionaryProperty(spec_version="2.0")), ('options', DictionaryProperty(spec_version="2.0")),
( (
'socket_type', EnumProperty(allowed=[ 'socket_type', EnumProperty(
allowed=[
"SOCK_STREAM", "SOCK_STREAM",
"SOCK_DGRAM", "SOCK_DGRAM",
"SOCK_RAW", "SOCK_RAW",
"SOCK_RDM", "SOCK_RDM",
"SOCK_SEQPACKET", "SOCK_SEQPACKET",
]), ],
),
), ),
('socket_descriptor', IntegerProperty()), ('socket_descriptor', IntegerProperty()),
('socket_handle', IntegerProperty()), ('socket_handle', IntegerProperty()),
@ -537,25 +541,30 @@ class WindowsServiceExt(_Extension):
('display_name', StringProperty()), ('display_name', StringProperty()),
('group_name', StringProperty()), ('group_name', StringProperty()),
( (
'start_type', EnumProperty(allowed=[ 'start_type', EnumProperty(
allowed=[
"SERVICE_AUTO_START", "SERVICE_AUTO_START",
"SERVICE_BOOT_START", "SERVICE_BOOT_START",
"SERVICE_DEMAND_START", "SERVICE_DEMAND_START",
"SERVICE_DISABLED", "SERVICE_DISABLED",
"SERVICE_SYSTEM_ALERT", "SERVICE_SYSTEM_ALERT",
]), ],
),
), ),
('service_dll_refs', ListProperty(ObjectReferenceProperty(valid_types='file'))), ('service_dll_refs', ListProperty(ObjectReferenceProperty(valid_types='file'))),
( (
'service_type', EnumProperty(allowed=[ 'service_type', EnumProperty(
allowed=[
"SERVICE_KERNEL_DRIVER", "SERVICE_KERNEL_DRIVER",
"SERVICE_FILE_SYSTEM_DRIVER", "SERVICE_FILE_SYSTEM_DRIVER",
"SERVICE_WIN32_OWN_PROCESS", "SERVICE_WIN32_OWN_PROCESS",
"SERVICE_WIN32_SHARE_PROCESS", "SERVICE_WIN32_SHARE_PROCESS",
]), ],
),
), ),
( (
'service_status', EnumProperty(allowed=[ 'service_status', EnumProperty(
allowed=[
"SERVICE_CONTINUE_PENDING", "SERVICE_CONTINUE_PENDING",
"SERVICE_PAUSE_PENDING", "SERVICE_PAUSE_PENDING",
"SERVICE_PAUSED", "SERVICE_PAUSED",
@ -563,7 +572,8 @@ class WindowsServiceExt(_Extension):
"SERVICE_START_PENDING", "SERVICE_START_PENDING",
"SERVICE_STOP_PENDING", "SERVICE_STOP_PENDING",
"SERVICE_STOPPED", "SERVICE_STOPPED",
]), ],
),
), ),
]) ])
@ -687,7 +697,8 @@ class WindowsRegistryValueType(_STIXBase20):
('name', StringProperty(required=True)), ('name', StringProperty(required=True)),
('data', StringProperty()), ('data', StringProperty()),
( (
'data_type', EnumProperty(allowed=[ 'data_type', EnumProperty(
allowed=[
"REG_NONE", "REG_NONE",
"REG_SZ", "REG_SZ",
"REG_EXPAND_SZ", "REG_EXPAND_SZ",
@ -701,7 +712,8 @@ class WindowsRegistryValueType(_STIXBase20):
"REG_RESOURCE_REQUIREMENTS_LIST", "REG_RESOURCE_REQUIREMENTS_LIST",
"REG_QWORD", "REG_QWORD",
"REG_INVALID_TYPE", "REG_INVALID_TYPE",
]), ],
),
), ),
]) ])
@ -790,11 +802,13 @@ def CustomObservable(type='x-custom-observable', properties=None):
""" """
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(
itertools.chain.from_iterable([
[('type', TypeProperty(type, spec_version='2.0'))], [('type', TypeProperty(type, spec_version='2.0'))],
properties, properties,
[('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=type))], [('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=type))],
])) ]),
)
return _custom_observable_builder(cls, type, _properties, '2.0', _Observable) return _custom_observable_builder(cls, type, _properties, '2.0', _Observable)
return wrapper return wrapper

View File

@ -356,7 +356,8 @@ def CustomObject(type='x-custom-type', properties=None):
""" """
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(
itertools.chain.from_iterable([
[ [
('type', TypeProperty(type, spec_version='2.0')), ('type', TypeProperty(type, spec_version='2.0')),
('id', IDProperty(type, spec_version='2.0')), ('id', IDProperty(type, spec_version='2.0')),
@ -373,6 +374,7 @@ def CustomObject(type='x-custom-type', properties=None):
('granular_markings', ListProperty(GranularMarking)), ('granular_markings', ListProperty(GranularMarking)),
], ],
sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]), sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
])) ]),
)
return _custom_object_builder(cls, type, _properties, '2.0', _DomainObject) return _custom_object_builder(cls, type, _properties, '2.0', _DomainObject)
return wrapper return wrapper

View File

@ -505,13 +505,15 @@ class SocketExt(_Extension):
('is_listening', BooleanProperty()), ('is_listening', BooleanProperty()),
('options', DictionaryProperty(spec_version='2.1')), ('options', DictionaryProperty(spec_version='2.1')),
( (
'socket_type', EnumProperty(allowed=[ 'socket_type', EnumProperty(
allowed=[
"SOCK_STREAM", "SOCK_STREAM",
"SOCK_DGRAM", "SOCK_DGRAM",
"SOCK_RAW", "SOCK_RAW",
"SOCK_RDM", "SOCK_RDM",
"SOCK_SEQPACKET", "SOCK_SEQPACKET",
]), ],
),
), ),
('socket_descriptor', IntegerProperty(min=0)), ('socket_descriptor', IntegerProperty(min=0)),
('socket_handle', IntegerProperty()), ('socket_handle', IntegerProperty()),
@ -612,12 +614,14 @@ class WindowsProcessExt(_Extension):
('window_title', StringProperty()), ('window_title', StringProperty()),
('startup_info', DictionaryProperty(spec_version='2.1')), ('startup_info', DictionaryProperty(spec_version='2.1')),
( (
'integrity_level', EnumProperty(allowed=[ 'integrity_level', EnumProperty(
allowed=[
"low", "low",
"medium", "medium",
"high", "high",
"system", "system",
]), ],
),
), ),
]) ])
@ -634,25 +638,30 @@ class WindowsServiceExt(_Extension):
('display_name', StringProperty()), ('display_name', StringProperty()),
('group_name', StringProperty()), ('group_name', StringProperty()),
( (
'start_type', EnumProperty(allowed=[ 'start_type', EnumProperty(
allowed=[
"SERVICE_AUTO_START", "SERVICE_AUTO_START",
"SERVICE_BOOT_START", "SERVICE_BOOT_START",
"SERVICE_DEMAND_START", "SERVICE_DEMAND_START",
"SERVICE_DISABLED", "SERVICE_DISABLED",
"SERVICE_SYSTEM_ALERT", "SERVICE_SYSTEM_ALERT",
]), ],
),
), ),
('service_dll_refs', ListProperty(ReferenceProperty(valid_types='file', spec_version="2.1"))), ('service_dll_refs', ListProperty(ReferenceProperty(valid_types='file', spec_version="2.1"))),
( (
'service_type', EnumProperty(allowed=[ 'service_type', EnumProperty(
allowed=[
"SERVICE_KERNEL_DRIVER", "SERVICE_KERNEL_DRIVER",
"SERVICE_FILE_SYSTEM_DRIVER", "SERVICE_FILE_SYSTEM_DRIVER",
"SERVICE_WIN32_OWN_PROCESS", "SERVICE_WIN32_OWN_PROCESS",
"SERVICE_WIN32_SHARE_PROCESS", "SERVICE_WIN32_SHARE_PROCESS",
]), ],
),
), ),
( (
'service_status', EnumProperty(allowed=[ 'service_status', EnumProperty(
allowed=[
"SERVICE_CONTINUE_PENDING", "SERVICE_CONTINUE_PENDING",
"SERVICE_PAUSE_PENDING", "SERVICE_PAUSE_PENDING",
"SERVICE_PAUSED", "SERVICE_PAUSED",
@ -660,7 +669,8 @@ class WindowsServiceExt(_Extension):
"SERVICE_START_PENDING", "SERVICE_START_PENDING",
"SERVICE_STOP_PENDING", "SERVICE_STOP_PENDING",
"SERVICE_STOPPED", "SERVICE_STOPPED",
]), ],
),
), ),
]) ])
@ -808,7 +818,8 @@ class WindowsRegistryValueType(_STIXBase21):
('name', StringProperty()), ('name', StringProperty()),
('data', StringProperty()), ('data', StringProperty()),
( (
'data_type', EnumProperty(allowed=[ 'data_type', EnumProperty(
allowed=[
"REG_NONE", "REG_NONE",
"REG_SZ", "REG_SZ",
"REG_EXPAND_SZ", "REG_EXPAND_SZ",
@ -822,7 +833,8 @@ class WindowsRegistryValueType(_STIXBase21):
"REG_RESOURCE_REQUIREMENTS_LIST", "REG_RESOURCE_REQUIREMENTS_LIST",
"REG_QWORD", "REG_QWORD",
"REG_INVALID_TYPE", "REG_INVALID_TYPE",
]), ],
),
), ),
]) ])
@ -935,13 +947,15 @@ def CustomObservable(type='x-custom-observable', properties=None, id_contrib_pro
""" """
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(
itertools.chain.from_iterable([
[('type', TypeProperty(type, spec_version='2.1'))], [('type', TypeProperty(type, spec_version='2.1'))],
[('spec_version', StringProperty(fixed='2.1'))], [('spec_version', StringProperty(fixed='2.1'))],
[('id', IDProperty(type, spec_version='2.1'))], [('id', IDProperty(type, spec_version='2.1'))],
properties, properties,
[('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=type))], [('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=type))],
])) ]),
)
return _custom_observable_builder(cls, type, _properties, '2.1', _Observable, id_contrib_props) return _custom_observable_builder(cls, type, _properties, '2.1', _Observable, id_contrib_props)
return wrapper return wrapper

View File

@ -789,7 +789,8 @@ def CustomObject(type='x-custom-type', properties=None):
""" """
def wrapper(cls): def wrapper(cls):
_properties = list(itertools.chain.from_iterable([ _properties = list(
itertools.chain.from_iterable([
[ [
('type', TypeProperty(type, spec_version='2.1')), ('type', TypeProperty(type, spec_version='2.1')),
('spec_version', StringProperty(fixed='2.1')), ('spec_version', StringProperty(fixed='2.1')),
@ -809,7 +810,8 @@ def CustomObject(type='x-custom-type', properties=None):
('granular_markings', ListProperty(GranularMarking)), ('granular_markings', ListProperty(GranularMarking)),
], ],
sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]), sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
])) ]),
)
return _custom_object_builder(cls, type, _properties, '2.1', _DomainObject) return _custom_object_builder(cls, type, _properties, '2.1', _DomainObject)
return wrapper return wrapper

View File

@ -21,7 +21,7 @@
""" """
import functools import functools
import stix2
from . import AttackPattern as _AttackPattern from . import AttackPattern as _AttackPattern
from . import Campaign as _Campaign from . import Campaign as _Campaign
from . import CourseOfAction as _CourseOfAction from . import CourseOfAction as _CourseOfAction
@ -34,34 +34,37 @@ from . import Location as _Location
from . import Malware as _Malware from . import Malware as _Malware
from . import MalwareAnalysis as _MalwareAnalysis from . import MalwareAnalysis as _MalwareAnalysis
from . import Note as _Note from . import Note as _Note
from . import OBJ_MAP
from . import ObservedData as _ObservedData from . import ObservedData as _ObservedData
from . import Opinion as _Opinion from . import Opinion as _Opinion
from . import Report as _Report from . import Report as _Report
from . import ThreatActor as _ThreatActor from . import ThreatActor as _ThreatActor
from . import Tool as _Tool from . import Tool as _Tool
from . import Vulnerability as _Vulnerability from . import Vulnerability as _Vulnerability
from . import ( # noqa: F401 from .version import DEFAULT_VERSION
from . import ( # noqa: F401 isort:skip
AlternateDataStream, ArchiveExt, Artifact, AutonomousSystem, AlternateDataStream, ArchiveExt, Artifact, AutonomousSystem,
Bundle, CustomExtension, CustomMarking, CustomObservable, Bundle, CustomExtension, CustomMarking, CustomObservable,
Directory, DomainName, EmailAddress, EmailMessage, Directory, DomainName, EmailAddress, EmailMessage,
EmailMIMEComponent, Environment, ExternalReference, File, EmailMIMEComponent, Environment, ExternalReference, File,
FileSystemSource, Filter, GranularMarking, HTTPRequestExt, FileSystemSource, Filter, GranularMarking, HTTPRequestExt,
ICMPExt, IPv4Address, IPv6Address, KillChainPhase, LanguageContent, MACAddress, ICMPExt, IPv4Address, IPv6Address, KillChainPhase, LanguageContent,
MarkingDefinition, MemoryStore, Mutex, NetworkTraffic, NTFSExt, MACAddress, MarkingDefinition, MemoryStore, Mutex, NetworkTraffic,
parse_observable, PDFExt, Process, RasterImageExt, Relationship, NTFSExt, parse_observable, PDFExt, Process, RasterImageExt, Relationship,
Sighting, SocketExt, Software, StatementMarking, Sighting, SocketExt, Software, StatementMarking,
TAXIICollectionSource, TCPExt, TLP_AMBER, TLP_GREEN, TLP_RED, TAXIICollectionSource, TCPExt, TLP_AMBER, TLP_GREEN, TLP_RED,
TLP_WHITE, TLPMarking, UNIXAccountExt, URL, UserAccount, TLP_WHITE, TLPMarking, UNIXAccountExt, URL, UserAccount,
WindowsPEBinaryExt, WindowsPEOptionalHeaderType, WindowsPEBinaryExt, WindowsPEOptionalHeaderType,
WindowsPESection, WindowsProcessExt, WindowsRegistryKey, WindowsPESection, WindowsProcessExt, WindowsRegistryKey,
WindowsRegistryValueType, WindowsServiceExt, X509Certificate, WindowsRegistryValueType, WindowsServiceExt, X509Certificate,
X509V3ExtensionsType X509V3ExtensionsType,
) )
from .datastore.filters import FilterSet from .datastore.filters import FilterSet # isort:skip
# Enable some adaptation to the current default supported STIX version. # Enable some adaptation to the current default supported STIX version.
_STIX_VID = "v" + stix2.DEFAULT_VERSION.replace(".", "") _STIX_VID = "v" + DEFAULT_VERSION.replace(".", "")
# Use an implicit MemoryStore # Use an implicit MemoryStore
@ -161,7 +164,7 @@ def _setup_workbench():
# Add our new "class" to this module's globals and to the library-wide # Add our new "class" to this module's globals and to the library-wide
# mapping. This allows parse() to use the wrapped classes. # mapping. This allows parse() to use the wrapped classes.
globals()[obj_type.__name__] = factory_func globals()[obj_type.__name__] = factory_func
stix2.OBJ_MAP[obj_type._type] = factory_func OBJ_MAP[obj_type._type] = factory_func
_setup_workbench() _setup_workbench()

33
tox.ini
View File

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py35,py36,py37,py38,style,isort-check,packaging envlist = py36,py37,py38,py39,packaging,pre-commit-check
[testenv] [testenv]
deps = deps =
@ -15,33 +15,24 @@ deps =
commands = commands =
python -m pytest --cov=stix2 stix2/test/ --cov-report term-missing -W ignore::stix2.exceptions.STIXDeprecationWarning python -m pytest --cov=stix2 stix2/test/ --cov-report term-missing -W ignore::stix2.exceptions.STIXDeprecationWarning
passenv = CI TRAVIS TRAVIS_* passenv = GITHUB_*
[testenv:style]
deps =
flake8
commands =
flake8
[flake8]
max-line-length = 160
[testenv:isort-check]
deps = isort
commands =
isort stix2 examples --df
isort stix2 examples -c
[testenv:packaging] [testenv:packaging]
deps = deps =
twine twine
commands = commands =
python setup.py bdist_wheel --universal python setup.py sdist bdist_wheel --universal
twine check dist/* twine check dist/*
[travis] [testenv:pre-commit-check]
deps =
pre-commit
commands =
pre-commit run --all-files
[gh-actions]
python = python =
3.5: py35
3.6: py36 3.6: py36
3.7: py37 3.7: py37
3.8: py38, style, packaging 3.8: py38
3.9: py39, packaging, pre-commit-check