From f8a72b0937dea29817b5c6fc60a137bbd73a83e0 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Wed, 17 Oct 2018 07:34:15 -0400 Subject: [PATCH] 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. --- setup.py | 7 ++-- stix2/custom.py | 46 +++------------------- stix2/patterns.py | 2 +- stix2/properties.py | 36 ++++++++--------- stix2/test/v20/test_pattern_expressions.py | 2 +- stix2/test/v20/test_workbench.py | 4 +- stix2/test/v21/test_pattern_expressions.py | 2 +- 7 files changed, 32 insertions(+), 67 deletions(-) diff --git a/setup.py b/setup.py index 87ff9cd..a674a7e 100644 --- a/setup.py +++ b/setup.py @@ -17,15 +17,16 @@ def get_version(): raise AttributeError("Package does not have a __version__") -with open('README.rst') as f: - long_description = f.read() +def get_long_description(): + with open('README.rst') as f: + return f.read() setup( name='stix2', version=get_version(), 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', author='OASIS Cyber Threat Intelligence Technical Committee', author_email='cti-users@lists.oasis-open.org', diff --git a/stix2/custom.py b/stix2/custom.py index 1c5d017..484cbb0 100644 --- a/stix2/custom.py +++ b/stix2/custom.py @@ -1,7 +1,7 @@ from collections import OrderedDict import re -from .base import _Extension, _Observable, _STIXBase +from .base import _cls_init, _Extension, _Observable, _STIXBase from .core import ( STIXDomainObject, _register_marking, _register_object, _register_observable, _register_observable_extension, @@ -30,16 +30,7 @@ def _custom_object_builder(cls, type, properties, version): def __init__(self, **kwargs): _STIXBase.__init__(self, **kwargs) - try: - 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 + _cls_init(cls, self, kwargs) _register_object(_CustomObject, version=version) return _CustomObject @@ -56,16 +47,7 @@ def _custom_marking_builder(cls, type, properties, version): def __init__(self, **kwargs): _STIXBase.__init__(self, **kwargs) - try: - 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 + _cls_init(cls, self, kwargs) _register_marking(_CustomMarking, version=version) return _CustomMarking @@ -104,16 +86,7 @@ def _custom_observable_builder(cls, type, properties, version): def __init__(self, **kwargs): _Observable.__init__(self, **kwargs) - try: - 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 + _cls_init(cls, self, kwargs) _register_observable(_CustomObservable, version=version) return _CustomObservable @@ -141,16 +114,7 @@ def _custom_extension_builder(cls, observable, type, properties, version): def __init__(self, **kwargs): _Extension.__init__(self, **kwargs) - try: - 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 + _cls_init(cls, self, kwargs) _register_observable_extension(observable, _CustomExtension, version=version) return _CustomExtension diff --git a/stix2/patterns.py b/stix2/patterns.py index 8bcaea3..6d5ce57 100644 --- a/stix2/patterns.py +++ b/stix2/patterns.py @@ -170,7 +170,7 @@ class HexConstant(_Constant): value (str): hexadecimal 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") self.value = value diff --git a/stix2/properties.py b/stix2/properties.py index fe03974..817107c 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -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 # must be 8, 9, a, or b (10xx bit pattern). 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]{4}-" "4[0-9a-fA-F]{3}-" @@ -302,7 +302,7 @@ class DictionaryProperty(Property): elif self.spec_version == '2.1': if len(k) > 250: 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 = ( "contains characters other than lowercase a-z, " "uppercase A-Z, numerals 0-9, hyphen (-), or " @@ -313,20 +313,20 @@ class DictionaryProperty(Property): HASHES_REGEX = { - "MD5": ("^[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"), - "RIPEMD160": ("^[a-fA-F0-9]{40}$", "RIPEMD-160"), - "SHA1": ("^[a-fA-F0-9]{40}$", "SHA-1"), - "SHA224": ("^[a-fA-F0-9]{56}$", "SHA-224"), - "SHA256": ("^[a-fA-F0-9]{64}$", "SHA-256"), - "SHA384": ("^[a-fA-F0-9]{96}$", "SHA-384"), - "SHA512": ("^[a-fA-F0-9]{128}$", "SHA-512"), - "SHA3224": ("^[a-fA-F0-9]{56}$", "SHA3-224"), - "SHA3256": ("^[a-fA-F0-9]{64}$", "SHA3-256"), - "SHA3384": ("^[a-fA-F0-9]{96}$", "SHA3-384"), - "SHA3512": ("^[a-fA-F0-9]{128}$", "SHA3-512"), - "SSDEEP": ("^[a-zA-Z0-9/+:.]{1,128}$", "ssdeep"), - "WHIRLPOOL": ("^[a-fA-F0-9]{128}$", "WHIRLPOOL"), + "MD5": (r"^[a-fA-F0-9]{32}$", "MD5"), + "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": (r"^[a-fA-F0-9]{40}$", "RIPEMD-160"), + "SHA1": (r"^[a-fA-F0-9]{40}$", "SHA-1"), + "SHA224": (r"^[a-fA-F0-9]{56}$", "SHA-224"), + "SHA256": (r"^[a-fA-F0-9]{64}$", "SHA-256"), + "SHA384": (r"^[a-fA-F0-9]{96}$", "SHA-384"), + "SHA512": (r"^[a-fA-F0-9]{128}$", "SHA-512"), + "SHA3224": (r"^[a-fA-F0-9]{56}$", "SHA3-224"), + "SHA3256": (r"^[a-fA-F0-9]{64}$", "SHA3-256"), + "SHA3384": (r"^[a-fA-F0-9]{96}$", "SHA3-384"), + "SHA3512": (r"^[a-fA-F0-9]{128}$", "SHA3-512"), + "SSDEEP": (r"^[a-zA-Z0-9/+:.]{1,128}$", "ssdeep"), + "WHIRLPOOL": (r"^[a-fA-F0-9]{128}$", "WHIRLPOOL"), } @@ -359,7 +359,7 @@ class BinaryProperty(Property): class HexProperty(Property): 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") return value @@ -385,7 +385,7 @@ class ReferenceProperty(Property): 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): diff --git a/stix2/test/v20/test_pattern_expressions.py b/stix2/test/v20/test_pattern_expressions.py index fa635fe..518dd6b 100644 --- a/stix2/test/v20/test_pattern_expressions.py +++ b/stix2/test/v20/test_pattern_expressions.py @@ -374,7 +374,7 @@ def test_invalid_integer_constant(): def test_invalid_timestamp_constant(): with pytest.raises(ValueError) as excinfo: 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(): diff --git a/stix2/test/v20/test_workbench.py b/stix2/test/v20/test_workbench.py index 8123e41..ebadb1c 100644 --- a/stix2/test/v20/test_workbench.py +++ b/stix2/test/v20/test_workbench.py @@ -299,7 +299,7 @@ def test_workbench_custom_property_object_in_observable_extension(): allow_custom=True, first_observed="2015-12-21T19:00:00Z", last_observed="2015-12-21T19:00:00Z", - number_observed=0, + number_observed=1, objects={"0": artifact}, ) @@ -323,7 +323,7 @@ def test_workbench_custom_property_dict_in_observable_extension(): allow_custom=True, first_observed="2015-12-21T19:00:00Z", last_observed="2015-12-21T19:00:00Z", - number_observed=0, + number_observed=1, objects={"0": artifact}, ) diff --git a/stix2/test/v21/test_pattern_expressions.py b/stix2/test/v21/test_pattern_expressions.py index fa635fe..518dd6b 100644 --- a/stix2/test/v21/test_pattern_expressions.py +++ b/stix2/test/v21/test_pattern_expressions.py @@ -374,7 +374,7 @@ def test_invalid_integer_constant(): def test_invalid_timestamp_constant(): with pytest.raises(ValueError) as excinfo: 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():