From 2b65f7205bf5070451a352a63cb855040fb0c7c8 Mon Sep 17 00:00:00 2001 From: Emmanuelle Vargas-Gonzalez Date: Fri, 6 Oct 2017 14:24:46 -0400 Subject: [PATCH 1/2] Mutex 'name' is required. Bunch of formatting, update test --- stix2/observables.py | 70 ++++++++++++++++++++++++++++++--------- stix2/test/test_custom.py | 8 +++++ 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/stix2/observables.py b/stix2/observables.py index 4caaaa5..7a92813 100644 --- a/stix2/observables.py +++ b/stix2/observables.py @@ -30,7 +30,6 @@ class ObservableProperty(Property): valid_refs = dict((k, v['type']) for (k, v) in dictified.items()) - # from .__init__ import parse_observable # avoid circular import for key, obj in dictified.items(): parsed_obj = parse_observable(obj, valid_refs) dictified[key] = parsed_obj @@ -73,6 +72,7 @@ class ExtensionsProperty(DictionaryProperty): class Artifact(_Observable): + _type = 'artifact' _properties = OrderedDict() _properties.update([ @@ -91,6 +91,7 @@ class Artifact(_Observable): class AutonomousSystem(_Observable): + _type = 'autonomous-system' _properties = OrderedDict() _properties.update([ @@ -103,6 +104,7 @@ class AutonomousSystem(_Observable): class Directory(_Observable): + _type = 'directory' _properties = OrderedDict() _properties.update([ @@ -119,6 +121,7 @@ class Directory(_Observable): class DomainName(_Observable): + _type = 'domain-name' _properties = OrderedDict() _properties.update([ @@ -130,6 +133,7 @@ class DomainName(_Observable): class EmailAddress(_Observable): + _type = 'email-addr' _properties = OrderedDict() _properties.update([ @@ -138,11 +142,11 @@ class EmailAddress(_Observable): ('display_name', StringProperty()), ('belongs_to_ref', ObjectReferenceProperty(valid_types='user-account')), ('extensions', ExtensionsProperty(enclosing_type=_type)), - ]) class EmailMIMEComponent(_STIXBase): + _properties = OrderedDict() _properties.update([ ('body', StringProperty()), @@ -157,6 +161,7 @@ class EmailMIMEComponent(_STIXBase): class EmailMessage(_Observable): + _type = 'email-message' _properties = OrderedDict() _properties.update([ @@ -187,6 +192,7 @@ class EmailMessage(_Observable): class ArchiveExt(_Extension): + _type = 'archive-ext' _properties = OrderedDict() _properties.update([ @@ -197,6 +203,7 @@ class ArchiveExt(_Extension): class AlternateDataStream(_STIXBase): + _properties = OrderedDict() _properties.update([ ('name', StringProperty(required=True)), @@ -206,6 +213,7 @@ class AlternateDataStream(_STIXBase): class NTFSExt(_Extension): + _type = 'ntfs-ext' _properties = OrderedDict() _properties.update([ @@ -215,6 +223,7 @@ class NTFSExt(_Extension): class PDFExt(_Extension): + _type = 'pdf-ext' _properties = OrderedDict() _properties.update([ @@ -227,6 +236,7 @@ class PDFExt(_Extension): class RasterImageExt(_Extension): + _type = 'raster-image-ext' _properties = OrderedDict() _properties.update([ @@ -239,6 +249,7 @@ class RasterImageExt(_Extension): class WindowsPEOptionalHeaderType(_STIXBase): + _properties = OrderedDict() _properties.update([ ('magic_hex', HexProperty()), @@ -280,6 +291,7 @@ class WindowsPEOptionalHeaderType(_STIXBase): class WindowsPESection(_STIXBase): + _properties = OrderedDict() _properties.update([ ('name', StringProperty(required=True)), @@ -290,6 +302,7 @@ class WindowsPESection(_STIXBase): class WindowsPEBinaryExt(_Extension): + _type = 'windows-pebinary-ext' _properties = OrderedDict() _properties.update([ @@ -309,11 +322,11 @@ class WindowsPEBinaryExt(_Extension): class File(_Observable): + _type = 'file' _properties = OrderedDict() _properties.update([ ('type', TypeProperty(_type)), - ('extensions', ExtensionsProperty(enclosing_type=_type)), ('hashes', HashesProperty()), ('size', IntegerProperty()), ('name', StringProperty()), @@ -330,6 +343,7 @@ class File(_Observable): ('decryption_key', StringProperty()), ('contains_refs', ListProperty(ObjectReferenceProperty)), ('content_ref', ObjectReferenceProperty(valid_types='artifact')), + ('extensions', ExtensionsProperty(enclosing_type=_type)), ]) def _check_object_constraints(self): @@ -339,6 +353,7 @@ class File(_Observable): class IPv4Address(_Observable): + _type = 'ipv4-addr' _properties = OrderedDict() _properties.update([ @@ -351,6 +366,7 @@ class IPv4Address(_Observable): class IPv6Address(_Observable): + _type = 'ipv6-addr' _properties = OrderedDict() _properties.update([ @@ -363,6 +379,7 @@ class IPv6Address(_Observable): class MACAddress(_Observable): + _type = 'mac-addr' _properties = OrderedDict() _properties.update([ @@ -373,16 +390,18 @@ class MACAddress(_Observable): class Mutex(_Observable): + _type = 'mutex' _properties = OrderedDict() _properties.update([ ('type', TypeProperty(_type)), - ('name', StringProperty()), + ('name', StringProperty(required=True)), ('extensions', ExtensionsProperty(enclosing_type=_type)), ]) class HTTPRequestExt(_Extension): + _type = 'http-request-ext' _properties = OrderedDict() _properties.update([ @@ -396,6 +415,7 @@ class HTTPRequestExt(_Extension): class ICMPExt(_Extension): + _type = 'icmp-ext' _properties = OrderedDict() _properties.update([ @@ -405,10 +425,11 @@ class ICMPExt(_Extension): class SocketExt(_Extension): + _type = 'socket-ext' _properties = OrderedDict() _properties.update([ - ('address_family', EnumProperty([ + ('address_family', EnumProperty(allowed=[ "AF_UNSPEC", "AF_INET", "AF_IPX", @@ -420,7 +441,7 @@ class SocketExt(_Extension): ], required=True)), ('is_blocking', BooleanProperty()), ('is_listening', BooleanProperty()), - ('protocol_family', EnumProperty([ + ('protocol_family', EnumProperty(allowed=[ "PF_INET", "PF_IPX", "PF_APPLETALK", @@ -429,7 +450,7 @@ class SocketExt(_Extension): "PF_NETROM" ])), ('options', DictionaryProperty()), - ('socket_type', EnumProperty([ + ('socket_type', EnumProperty(allowed=[ "SOCK_STREAM", "SOCK_DGRAM", "SOCK_RAW", @@ -440,6 +461,7 @@ class SocketExt(_Extension): class TCPExt(_Extension): + _type = 'tcp-ext' _properties = OrderedDict() _properties.update([ @@ -449,11 +471,11 @@ class TCPExt(_Extension): class NetworkTraffic(_Observable): + _type = 'network-traffic' _properties = OrderedDict() _properties.update([ ('type', TypeProperty(_type)), - ('extensions', ExtensionsProperty(enclosing_type=_type)), ('start', TimestampProperty()), ('end', TimestampProperty()), ('is_active', BooleanProperty()), @@ -471,6 +493,7 @@ class NetworkTraffic(_Observable): ('dst_payload_ref', ObjectReferenceProperty(valid_types='artifact')), ('encapsulates_refs', ListProperty(ObjectReferenceProperty(valid_types='network-traffic'))), ('encapsulates_by_ref', ObjectReferenceProperty(valid_types='network-traffic')), + ('extensions', ExtensionsProperty(enclosing_type=_type)), ]) def _check_object_constraints(self): @@ -479,6 +502,7 @@ class NetworkTraffic(_Observable): class WindowsProcessExt(_Extension): + _type = 'windows-process-ext' _properties = OrderedDict() _properties.update([ @@ -492,6 +516,7 @@ class WindowsProcessExt(_Extension): class WindowsServiceExt(_Extension): + _type = 'windows-service-ext' _properties = OrderedDict() _properties.update([ @@ -499,7 +524,7 @@ class WindowsServiceExt(_Extension): ('descriptions', ListProperty(StringProperty)), ('display_name', StringProperty()), ('group_name', StringProperty()), - ('start_type', EnumProperty([ + ('start_type', EnumProperty(allowed=[ "SERVICE_AUTO_START", "SERVICE_BOOT_START", "SERVICE_DEMAND_START", @@ -507,13 +532,13 @@ class WindowsServiceExt(_Extension): "SERVICE_SYSTEM_ALERT", ])), ('service_dll_refs', ListProperty(ObjectReferenceProperty(valid_types='file'))), - ('service_type', EnumProperty([ + ('service_type', EnumProperty(allowed=[ "SERVICE_KERNEL_DRIVER", "SERVICE_FILE_SYSTEM_DRIVER", "SERVICE_WIN32_OWN_PROCESS", "SERVICE_WIN32_SHARE_PROCESS", ])), - ('service_status', EnumProperty([ + ('service_status', EnumProperty(allowed=[ "SERVICE_CONTINUE_PENDING", "SERVICE_PAUSE_PENDING", "SERVICE_PAUSED", @@ -526,11 +551,11 @@ class WindowsServiceExt(_Extension): class Process(_Observable): + _type = 'process' _properties = OrderedDict() _properties.update([ ('type', TypeProperty(_type)), - ('extensions', ExtensionsProperty(enclosing_type=_type)), ('is_hidden', BooleanProperty()), ('pid', IntegerProperty()), ('name', StringProperty()), @@ -545,6 +570,7 @@ class Process(_Observable): ('binary_ref', ObjectReferenceProperty(valid_types='file')), ('parent_ref', ObjectReferenceProperty(valid_types='process')), ('child_refs', ListProperty(ObjectReferenceProperty('process'))), + ('extensions', ExtensionsProperty(enclosing_type=_type)), ]) def _check_object_constraints(self): @@ -563,6 +589,7 @@ class Process(_Observable): class Software(_Observable): + _type = 'software' _properties = OrderedDict() _properties.update([ @@ -577,6 +604,7 @@ class Software(_Observable): class URL(_Observable): + _type = 'url' _properties = OrderedDict() _properties.update([ @@ -587,6 +615,7 @@ class URL(_Observable): class UNIXAccountExt(_Extension): + _type = 'unix-account-ext' _properties = OrderedDict() _properties.update([ @@ -598,11 +627,11 @@ class UNIXAccountExt(_Extension): class UserAccount(_Observable): + _type = 'user-account' _properties = OrderedDict() _properties.update([ ('type', TypeProperty(_type)), - ('extensions', ExtensionsProperty(enclosing_type=_type)), ('user_id', StringProperty(required=True)), ('account_login', StringProperty()), ('account_type', StringProperty()), # open vocab @@ -616,16 +645,18 @@ class UserAccount(_Observable): ('password_last_changed', TimestampProperty()), ('account_first_login', TimestampProperty()), ('account_last_login', TimestampProperty()), + ('extensions', ExtensionsProperty(enclosing_type=_type)), ]) class WindowsRegistryValueType(_STIXBase): + _type = 'windows-registry-value-type' _properties = OrderedDict() _properties.update([ ('name', StringProperty(required=True)), ('data', StringProperty()), - ('data_type', EnumProperty([ + ('data_type', EnumProperty(allowed=[ 'REG_NONE', 'REG_SZ', 'REG_EXPAND_SZ', @@ -644,6 +675,7 @@ class WindowsRegistryValueType(_STIXBase): class WindowsRegistryKey(_Observable): + _type = 'windows-registry-key' _properties = OrderedDict() _properties.update([ @@ -664,6 +696,7 @@ class WindowsRegistryKey(_Observable): class X509V3ExtenstionsType(_STIXBase): + _type = 'x509-v3-extensions-type' _properties = OrderedDict() _properties.update([ @@ -687,6 +720,7 @@ class X509V3ExtenstionsType(_STIXBase): class X509Certificate(_Observable): + _type = 'x509-certificate' _properties = OrderedDict() _properties.update([ @@ -813,6 +847,7 @@ def CustomObservable(type='x-custom-observable', properties=None): def custom_builder(cls): class _Custom(cls, _Observable): + _type = type _properties = OrderedDict() _properties.update([ @@ -865,7 +900,7 @@ def _register_extension(observable, new_extension): EXT_MAP[observable_type] = {new_extension._type: new_extension} -def CustomExtension(observable=None, type='x-custom-observable', properties={}): +def CustomExtension(observable=None, type='x-custom-observable', properties=None): """Decorator for custom extensions to STIX Cyber Observables """ @@ -875,10 +910,15 @@ def CustomExtension(observable=None, type='x-custom-observable', properties={}): def custom_builder(cls): class _Custom(cls, _Extension): + _type = type _properties = { 'extensions': ExtensionsProperty(enclosing_type=_type), } + + if not isinstance(properties, dict): + raise ValueError("'properties' must be a dict!") + _properties.update(properties) def __init__(self, **kwargs): diff --git a/stix2/test/test_custom.py b/stix2/test/test_custom.py index a71650b..10baf7b 100644 --- a/stix2/test/test_custom.py +++ b/stix2/test/test_custom.py @@ -364,6 +364,14 @@ def test_custom_extension_invalid_observable(): assert "Custom observables must be created with the @CustomObservable decorator." in str(excinfo.value) +def test_custom_extension_no_properties(): + with pytest.raises(ValueError) as excinfo: + @stix2.observables.CustomExtension(stix2.DomainName, 'x-new-ext', None) + class BarExtension(): + pass + assert "'properties' must be a dict!" in str(excinfo.value) + + def test_parse_observable_with_custom_extension(): input_str = """{ "type": "domain-name", From e57c36f52553e4e3e350817558b963784b6e5c79 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 6 Oct 2017 15:05:39 -0400 Subject: [PATCH 2/2] Check custom extension properties is not empty --- stix2/observables.py | 2 +- stix2/test/test_custom.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/stix2/observables.py b/stix2/observables.py index 7a92813..137b388 100644 --- a/stix2/observables.py +++ b/stix2/observables.py @@ -916,7 +916,7 @@ def CustomExtension(observable=None, type='x-custom-observable', properties=None 'extensions': ExtensionsProperty(enclosing_type=_type), } - if not isinstance(properties, dict): + if not isinstance(properties, dict) or not properties: raise ValueError("'properties' must be a dict!") _properties.update(properties) diff --git a/stix2/test/test_custom.py b/stix2/test/test_custom.py index 10baf7b..77598ca 100644 --- a/stix2/test/test_custom.py +++ b/stix2/test/test_custom.py @@ -366,7 +366,15 @@ def test_custom_extension_invalid_observable(): def test_custom_extension_no_properties(): with pytest.raises(ValueError) as excinfo: - @stix2.observables.CustomExtension(stix2.DomainName, 'x-new-ext', None) + @stix2.observables.CustomExtension(stix2.DomainName, 'x-new-ext2', None) + class BarExtension(): + pass + assert "'properties' must be a dict!" in str(excinfo.value) + + +def test_custom_extension_empty_properties(): + with pytest.raises(ValueError) as excinfo: + @stix2.observables.CustomExtension(stix2.DomainName, 'x-new-ext2', {}) class BarExtension(): pass assert "'properties' must be a dict!" in str(excinfo.value)