From 46f1778d04f615f55988cead10916ef982c8897b Mon Sep 17 00:00:00 2001 From: Zach Rush Date: Fri, 22 Nov 2019 13:24:09 -0500 Subject: [PATCH] Fixed all issues brought up in issue #305 by Chris Lenk --- stix2/properties.py | 5 ++++ stix2/test/v21/test_bundle.py | 2 ++ stix2/test/v21/test_indicator.py | 2 ++ stix2/test/v21/test_observed_data.py | 11 +++++++++ stix2/v21/observables.py | 35 +++++++++++++++++++++++++++- stix2/v21/sdo.py | 5 ++++ 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/stix2/properties.py b/stix2/properties.py index c956a08..f6211c8 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -363,6 +363,10 @@ class DictionaryProperty(Property): "underscore (_)" ) raise DictionaryKeyError(k, msg) + + if len(dictified) < 1: + raise ValueError("must not be empty.") + return dictified @@ -381,6 +385,7 @@ HASHES_REGEX = { "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"), + "TLSH": (r"^[a-fA-F0-9]{70}$", "TLSH"), } diff --git a/stix2/test/v21/test_bundle.py b/stix2/test/v21/test_bundle.py index 54ef318..4e30c84 100644 --- a/stix2/test/v21/test_bundle.py +++ b/stix2/test/v21/test_bundle.py @@ -22,6 +22,7 @@ EXPECTED_BUNDLE = """{ ], "pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", "pattern_type": "stix", + "pattern_version": "2.1", "valid_from": "2017-01-01T12:34:56Z" }, { @@ -61,6 +62,7 @@ EXPECTED_BUNDLE_DICT = { "modified": "2017-01-01T12:34:56.000Z", "pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", "pattern_type": "stix", + "pattern_version": "2.1", "valid_from": "2017-01-01T12:34:56Z", "indicator_types": [ "malicious-activity", diff --git a/stix2/test/v21/test_indicator.py b/stix2/test/v21/test_indicator.py index ea46d6d..0562dfd 100644 --- a/stix2/test/v21/test_indicator.py +++ b/stix2/test/v21/test_indicator.py @@ -19,6 +19,7 @@ EXPECTED_INDICATOR = """{ ], "pattern": "[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", "pattern_type": "stix", + "pattern_version": "2.1", "valid_from": "1970-01-01T00:00:01Z" }""" @@ -31,6 +32,7 @@ EXPECTED_INDICATOR_REPR = "Indicator(" + " ".join(""" indicator_types=['malicious-activity'], pattern="[file:hashes.MD5 = 'd41d8cd98f00b204e9800998ecf8427e']", pattern_type='stix', + pattern_version='2.1', valid_from='1970-01-01T00:00:01Z' """.split()) + ")" diff --git a/stix2/test/v21/test_observed_data.py b/stix2/test/v21/test_observed_data.py index 0074bf7..43d1ff7 100644 --- a/stix2/test/v21/test_observed_data.py +++ b/stix2/test/v21/test_observed_data.py @@ -1366,6 +1366,17 @@ def test_x509_certificate_example(): assert x509.subject == "C=US, ST=Maryland, L=Pasadena, O=Brent Baccala, OU=FreeSoft, CN=www.freesoft.org/emailAddress=baccala@freesoft.org" # noqa +def test_x509_certificate_error(): + + with pytest.raises(stix2.exceptions.PropertyPresenceError) as excinfo: + stix2.v21.X509Certificate( + defanged=True, + ) + + assert excinfo.value.cls == stix2.v21.X509Certificate + assert "X.509 Certificate objects must contain at least one object specific property" in str(excinfo.value) + + def test_new_version_with_related_objects(): data = stix2.v21.ObservedData( first_observed="2016-03-12T12:00:00Z", diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index 0d27bb4..12f0165 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -12,7 +12,8 @@ import warnings from ..base import _Extension, _Observable, _STIXBase from ..custom import _custom_extension_builder, _custom_observable_builder from ..exceptions import ( - AtLeastOnePropertyError, DependentPropertiesError, STIXDeprecationWarning, + AtLeastOnePropertyError, DependentPropertiesError, PropertyPresenceError, + STIXDeprecationWarning, ) from ..properties import ( BinaryProperty, BooleanProperty, CallableValues, DictionaryProperty, @@ -592,6 +593,18 @@ class SocketExt(_Extension): ('socket_handle', IntegerProperty()), ]) + def _check_object_constraints(self): + super(SocketExt, self)._check_object_constraints() + + options = self.get('options') + + if options is not None: + for key, val in options.items(): + if key[:3] != "SO_": + raise ValueError("Incorrect options key") + if not isinstance(val, int): + raise ValueError("Options value must be an integer") + class TCPExt(_Extension): # TODO: Add link @@ -986,6 +999,26 @@ class X509Certificate(_Observable): ]) _id_contributing_properties = ["hashes", "serial_number"] + def _check_object_constraints(self): + super(X509Certificate, self)._check_object_constraints() + + if ( + self.get('is_self_signed') is None and + self.get('hashes') is None and + self.get('version') is None and + self.get('serial_number') is None and + self.get('signature_algorithm') is None and + self.get('issuer') is None and + self.get('validity_not_before') is None and + self.get('validity_not_after') is None and + self.get('subject') is None and + self.get('subject_public_key_algorithm') is None and + self.get('subject_public_key_modulus') is None and + self.get('subject_public_key_exponent') is None and + self.get('x509_v3_extensions') is None + ): + raise PropertyPresenceError("X.509 Certificate objects must contain at least one object specific property", X509Certificate) + def CustomObservable(type='x-custom-observable', properties=None): """Custom STIX Cyber Observable Object type decorator. diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 7cd7891..e43ed84 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -220,11 +220,16 @@ class Indicator(STIXDomainObject): valid_from = self.get('valid_from') valid_until = self.get('valid_until') + pattern_type = self.get('pattern_type') + pattern_version = self.get('pattern_version') if valid_from and valid_until and valid_until <= valid_from: msg = "{0.id} 'valid_until' must be greater than 'valid_from'" raise ValueError(msg.format(self)) + if not pattern_version and pattern_type == 'stix': + self._inner['pattern_version'] = self.get('spec_version') + class Infrastructure(STIXDomainObject): # TODO: Add link