Custom builder code updated for 3.7 support.
Updated properties to support more constrains. Make all regexes literal strings. Update tests to align to new constrains. Workbench problem. _check_object_constraints() uses instance class to perform proper class resolution calls.stix2.1
parent
b2ef77b322
commit
f8a72b0937
7
setup.py
7
setup.py
|
@ -17,15 +17,16 @@ def get_version():
|
||||||
raise AttributeError("Package does not have a __version__")
|
raise AttributeError("Package does not have a __version__")
|
||||||
|
|
||||||
|
|
||||||
with open('README.rst') as f:
|
def get_long_description():
|
||||||
long_description = f.read()
|
with open('README.rst') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='stix2',
|
name='stix2',
|
||||||
version=get_version(),
|
version=get_version(),
|
||||||
description='Produce and consume STIX 2 JSON content',
|
description='Produce and consume STIX 2 JSON content',
|
||||||
long_description=long_description,
|
long_description=get_long_description(),
|
||||||
url='https://github.com/oasis-open/cti-python-stix2',
|
url='https://github.com/oasis-open/cti-python-stix2',
|
||||||
author='OASIS Cyber Threat Intelligence Technical Committee',
|
author='OASIS Cyber Threat Intelligence Technical Committee',
|
||||||
author_email='cti-users@lists.oasis-open.org',
|
author_email='cti-users@lists.oasis-open.org',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .base import _Extension, _Observable, _STIXBase
|
from .base import _cls_init, _Extension, _Observable, _STIXBase
|
||||||
from .core import (
|
from .core import (
|
||||||
STIXDomainObject, _register_marking, _register_object,
|
STIXDomainObject, _register_marking, _register_object,
|
||||||
_register_observable, _register_observable_extension,
|
_register_observable, _register_observable_extension,
|
||||||
|
@ -30,16 +30,7 @@ def _custom_object_builder(cls, type, properties, version):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
_STIXBase.__init__(self, **kwargs)
|
_STIXBase.__init__(self, **kwargs)
|
||||||
try:
|
_cls_init(cls, self, kwargs)
|
||||||
cls.__init__(self, **kwargs)
|
|
||||||
except (AttributeError, TypeError) as e:
|
|
||||||
# Don't accidentally catch errors raised in a custom __init__()
|
|
||||||
if (
|
|
||||||
"has no attribute '__init__'" in str(e) or
|
|
||||||
str(e) == "object.__init__() takes no parameters"
|
|
||||||
):
|
|
||||||
return
|
|
||||||
raise e
|
|
||||||
|
|
||||||
_register_object(_CustomObject, version=version)
|
_register_object(_CustomObject, version=version)
|
||||||
return _CustomObject
|
return _CustomObject
|
||||||
|
@ -56,16 +47,7 @@ def _custom_marking_builder(cls, type, properties, version):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
_STIXBase.__init__(self, **kwargs)
|
_STIXBase.__init__(self, **kwargs)
|
||||||
try:
|
_cls_init(cls, self, kwargs)
|
||||||
cls.__init__(self, **kwargs)
|
|
||||||
except (AttributeError, TypeError) as e:
|
|
||||||
# Don't accidentally catch errors raised in a custom __init__()
|
|
||||||
if (
|
|
||||||
"has no attribute '__init__'" in str(e) or
|
|
||||||
str(e) == "object.__init__() takes no parameters"
|
|
||||||
):
|
|
||||||
return
|
|
||||||
raise e
|
|
||||||
|
|
||||||
_register_marking(_CustomMarking, version=version)
|
_register_marking(_CustomMarking, version=version)
|
||||||
return _CustomMarking
|
return _CustomMarking
|
||||||
|
@ -104,16 +86,7 @@ def _custom_observable_builder(cls, type, properties, version):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
_Observable.__init__(self, **kwargs)
|
_Observable.__init__(self, **kwargs)
|
||||||
try:
|
_cls_init(cls, self, kwargs)
|
||||||
cls.__init__(self, **kwargs)
|
|
||||||
except (AttributeError, TypeError) as e:
|
|
||||||
# Don't accidentally catch errors raised in a custom __init__()
|
|
||||||
if (
|
|
||||||
"has no attribute '__init__'" in str(e) or
|
|
||||||
str(e) == "object.__init__() takes no parameters"
|
|
||||||
):
|
|
||||||
return
|
|
||||||
raise e
|
|
||||||
|
|
||||||
_register_observable(_CustomObservable, version=version)
|
_register_observable(_CustomObservable, version=version)
|
||||||
return _CustomObservable
|
return _CustomObservable
|
||||||
|
@ -141,16 +114,7 @@ def _custom_extension_builder(cls, observable, type, properties, version):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
_Extension.__init__(self, **kwargs)
|
_Extension.__init__(self, **kwargs)
|
||||||
try:
|
_cls_init(cls, self, kwargs)
|
||||||
cls.__init__(self, **kwargs)
|
|
||||||
except (AttributeError, TypeError) as e:
|
|
||||||
# Don't accidentally catch errors raised in a custom __init__()
|
|
||||||
if (
|
|
||||||
"has no attribute '__init__'" in str(e) or
|
|
||||||
str(e) == "object.__init__() takes no parameters"
|
|
||||||
):
|
|
||||||
return
|
|
||||||
raise e
|
|
||||||
|
|
||||||
_register_observable_extension(observable, _CustomExtension, version=version)
|
_register_observable_extension(observable, _CustomExtension, version=version)
|
||||||
return _CustomExtension
|
return _CustomExtension
|
||||||
|
|
|
@ -170,7 +170,7 @@ class HexConstant(_Constant):
|
||||||
value (str): hexadecimal value
|
value (str): hexadecimal value
|
||||||
"""
|
"""
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
if not re.match('^([a-fA-F0-9]{2})+$', value):
|
if not re.match(r'^([a-fA-F0-9]{2})+$', value):
|
||||||
raise ValueError("must contain an even number of hexadecimal characters")
|
raise ValueError("must contain an even number of hexadecimal characters")
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime
|
||||||
# component must be a 4, and the first hex digit of the fourth component
|
# component must be a 4, and the first hex digit of the fourth component
|
||||||
# must be 8, 9, a, or b (10xx bit pattern).
|
# must be 8, 9, a, or b (10xx bit pattern).
|
||||||
ID_REGEX = re.compile(
|
ID_REGEX = re.compile(
|
||||||
"^[a-z0-9][a-z0-9-]+[a-z0-9]--" # object type
|
r"^[a-z0-9][a-z0-9-]+[a-z0-9]--" # object type
|
||||||
"[0-9a-fA-F]{8}-"
|
"[0-9a-fA-F]{8}-"
|
||||||
"[0-9a-fA-F]{4}-"
|
"[0-9a-fA-F]{4}-"
|
||||||
"4[0-9a-fA-F]{3}-"
|
"4[0-9a-fA-F]{3}-"
|
||||||
|
@ -302,7 +302,7 @@ class DictionaryProperty(Property):
|
||||||
elif self.spec_version == '2.1':
|
elif self.spec_version == '2.1':
|
||||||
if len(k) > 250:
|
if len(k) > 250:
|
||||||
raise DictionaryKeyError(k, "longer than 250 characters")
|
raise DictionaryKeyError(k, "longer than 250 characters")
|
||||||
if not re.match("^[a-zA-Z0-9_-]+$", k):
|
if not re.match(r"^[a-zA-Z0-9_-]+$", k):
|
||||||
msg = (
|
msg = (
|
||||||
"contains characters other than lowercase a-z, "
|
"contains characters other than lowercase a-z, "
|
||||||
"uppercase A-Z, numerals 0-9, hyphen (-), or "
|
"uppercase A-Z, numerals 0-9, hyphen (-), or "
|
||||||
|
@ -313,20 +313,20 @@ class DictionaryProperty(Property):
|
||||||
|
|
||||||
|
|
||||||
HASHES_REGEX = {
|
HASHES_REGEX = {
|
||||||
"MD5": ("^[a-fA-F0-9]{32}$", "MD5"),
|
"MD5": (r"^[a-fA-F0-9]{32}$", "MD5"),
|
||||||
"MD6": ("^[a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{56}|[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}$", "MD6"),
|
"MD6": (r"^[a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{56}|[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}$", "MD6"),
|
||||||
"RIPEMD160": ("^[a-fA-F0-9]{40}$", "RIPEMD-160"),
|
"RIPEMD160": (r"^[a-fA-F0-9]{40}$", "RIPEMD-160"),
|
||||||
"SHA1": ("^[a-fA-F0-9]{40}$", "SHA-1"),
|
"SHA1": (r"^[a-fA-F0-9]{40}$", "SHA-1"),
|
||||||
"SHA224": ("^[a-fA-F0-9]{56}$", "SHA-224"),
|
"SHA224": (r"^[a-fA-F0-9]{56}$", "SHA-224"),
|
||||||
"SHA256": ("^[a-fA-F0-9]{64}$", "SHA-256"),
|
"SHA256": (r"^[a-fA-F0-9]{64}$", "SHA-256"),
|
||||||
"SHA384": ("^[a-fA-F0-9]{96}$", "SHA-384"),
|
"SHA384": (r"^[a-fA-F0-9]{96}$", "SHA-384"),
|
||||||
"SHA512": ("^[a-fA-F0-9]{128}$", "SHA-512"),
|
"SHA512": (r"^[a-fA-F0-9]{128}$", "SHA-512"),
|
||||||
"SHA3224": ("^[a-fA-F0-9]{56}$", "SHA3-224"),
|
"SHA3224": (r"^[a-fA-F0-9]{56}$", "SHA3-224"),
|
||||||
"SHA3256": ("^[a-fA-F0-9]{64}$", "SHA3-256"),
|
"SHA3256": (r"^[a-fA-F0-9]{64}$", "SHA3-256"),
|
||||||
"SHA3384": ("^[a-fA-F0-9]{96}$", "SHA3-384"),
|
"SHA3384": (r"^[a-fA-F0-9]{96}$", "SHA3-384"),
|
||||||
"SHA3512": ("^[a-fA-F0-9]{128}$", "SHA3-512"),
|
"SHA3512": (r"^[a-fA-F0-9]{128}$", "SHA3-512"),
|
||||||
"SSDEEP": ("^[a-zA-Z0-9/+:.]{1,128}$", "ssdeep"),
|
"SSDEEP": (r"^[a-zA-Z0-9/+:.]{1,128}$", "ssdeep"),
|
||||||
"WHIRLPOOL": ("^[a-fA-F0-9]{128}$", "WHIRLPOOL"),
|
"WHIRLPOOL": (r"^[a-fA-F0-9]{128}$", "WHIRLPOOL"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ class BinaryProperty(Property):
|
||||||
class HexProperty(Property):
|
class HexProperty(Property):
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
if not re.match("^([a-fA-F0-9]{2})+$", value):
|
if not re.match(r"^([a-fA-F0-9]{2})+$", value):
|
||||||
raise ValueError("must contain an even number of hexadecimal characters")
|
raise ValueError("must contain an even number of hexadecimal characters")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -385,7 +385,7 @@ class ReferenceProperty(Property):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
SELECTOR_REGEX = re.compile("^[a-z0-9_-]{3,250}(\\.(\\[\\d+\\]|[a-z0-9_-]{1,250}))*$")
|
SELECTOR_REGEX = re.compile(r"^[a-z0-9_-]{3,250}(\.(\[\d+\]|[a-z0-9_-]{1,250}))*$")
|
||||||
|
|
||||||
|
|
||||||
class SelectorProperty(Property):
|
class SelectorProperty(Property):
|
||||||
|
|
|
@ -374,7 +374,7 @@ def test_invalid_integer_constant():
|
||||||
def test_invalid_timestamp_constant():
|
def test_invalid_timestamp_constant():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.TimestampConstant('foo')
|
stix2.TimestampConstant('foo')
|
||||||
assert 'must be a datetime object or timestamp string' in str(excinfo)
|
assert 'Must be a datetime object or timestamp string' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_float_constant():
|
def test_invalid_float_constant():
|
||||||
|
|
|
@ -299,7 +299,7 @@ def test_workbench_custom_property_object_in_observable_extension():
|
||||||
allow_custom=True,
|
allow_custom=True,
|
||||||
first_observed="2015-12-21T19:00:00Z",
|
first_observed="2015-12-21T19:00:00Z",
|
||||||
last_observed="2015-12-21T19:00:00Z",
|
last_observed="2015-12-21T19:00:00Z",
|
||||||
number_observed=0,
|
number_observed=1,
|
||||||
objects={"0": artifact},
|
objects={"0": artifact},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ def test_workbench_custom_property_dict_in_observable_extension():
|
||||||
allow_custom=True,
|
allow_custom=True,
|
||||||
first_observed="2015-12-21T19:00:00Z",
|
first_observed="2015-12-21T19:00:00Z",
|
||||||
last_observed="2015-12-21T19:00:00Z",
|
last_observed="2015-12-21T19:00:00Z",
|
||||||
number_observed=0,
|
number_observed=1,
|
||||||
objects={"0": artifact},
|
objects={"0": artifact},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -374,7 +374,7 @@ def test_invalid_integer_constant():
|
||||||
def test_invalid_timestamp_constant():
|
def test_invalid_timestamp_constant():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.TimestampConstant('foo')
|
stix2.TimestampConstant('foo')
|
||||||
assert 'must be a datetime object or timestamp string' in str(excinfo)
|
assert 'Must be a datetime object or timestamp string' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_float_constant():
|
def test_invalid_float_constant():
|
||||||
|
|
Loading…
Reference in New Issue