diff --git a/stix2/hashes.py b/stix2/hashes.py new file mode 100644 index 0000000..198cef6 --- /dev/null +++ b/stix2/hashes.py @@ -0,0 +1,95 @@ +""" +Library support for hash algorithms, independent of STIX specs. +""" + +import enum +import re + + +class Hash(enum.Enum): + """ + Instances represent a hash algorithm, independent of STIX spec version. + Different spec versions may have different requirements for naming; this + allows us to refer to and use hash algorithms in a spec-agnostic way. + """ + MD5 = 0 + MD6 = 1 + RIPEMD160 = 2 + SHA1 = 3 + SHA224 = 4 + SHA256 = 5 + SHA384 = 6 + SHA512 = 7 + SHA3224 = 8 + SHA3256 = 9 + SHA3384 = 10 + SHA3512 = 11 + SSDEEP = 12 + WHIRLPOOL = 13 + TLSH = 14 + + +# Regexes used to sanity check hash values. Could also be combined with the +# enum values themselves using enum definition tricks, but... this seems +# simpler. +_HASH_REGEXES = { + Hash.MD5: r"^[a-f0-9]{32}$", + Hash.MD6: r"^[a-f0-9]{32}|[a-f0-9]{40}|[a-f0-9]{56}|[a-f0-9]{64}|[a-f0-9]{96}|[a-f0-9]{128}$", + Hash.RIPEMD160: r"^[a-f0-9]{40}$", + Hash.SHA1: r"^[a-f0-9]{40}$", + Hash.SHA224: r"^[a-f0-9]{56}$", + Hash.SHA256: r"^[a-f0-9]{64}$", + Hash.SHA384: r"^[a-f0-9]{96}$", + Hash.SHA512: r"^[a-f0-9]{128}$", + Hash.SHA3224: r"^[a-f0-9]{56}$", + Hash.SHA3256: r"^[a-f0-9]{64}$", + Hash.SHA3384: r"^[a-f0-9]{96}$", + Hash.SHA3512: r"^[a-f0-9]{128}$", + Hash.SSDEEP: r"^[a-z0-9/+:.]{1,128}$", + Hash.WHIRLPOOL: r"^[a-f0-9]{128}$", + Hash.TLSH: r"^[a-f0-9]{70}$", +} + + +# compile all the regexes; be case-insensitive +for hash_, re_str in _HASH_REGEXES.items(): + _HASH_REGEXES[hash_] = re.compile(re_str, re.I) + + +def infer_hash_algorithm(name): + """ + Given a hash algorithm name, try to figure out which hash algorithm it + refers to. This primarily enables some user flexibility in naming hash + algorithms when creating STIX content. + + :param name: A hash algorithm name + :return: A Hash enum value if the name was recognized, or None if it was + not recognized. + """ + enum_name = name.replace("-", "").upper() + + try: + enum_obj = Hash[enum_name] + except KeyError: + enum_obj = None + + return enum_obj + + +def check_hash(hash_, value): + """ + Sanity check the given hash value, against the given hash algorithm. + + :param hash_: The hash algorithm, as one of the Hash enums + :param value: A hash value as string + :return: True if the value seems okay; False if not + """ + + # I guess there's no need to require a regex mapping for the algorithm... + # Just assume it's okay if we have no way to check it. + result = True + regex = _HASH_REGEXES.get(hash_) + if regex: + result = bool(regex.match(value)) + + return result diff --git a/stix2/properties.py b/stix2/properties.py index 735bc79..ef7246c 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -7,6 +7,9 @@ import inspect import re import uuid +import stix2 +import stix2.hashes + from .base import _STIXBase from .exceptions import CustomContentError, DictionaryKeyError, STIXError from .parsing import parse, parse_observable @@ -417,54 +420,65 @@ class DictionaryProperty(Property): return dictified, False -HASHES_REGEX = { - "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"), - "TLSH": (r"^[a-fA-F0-9]{70}$", "TLSH"), -} - - class HashesProperty(DictionaryProperty): + def __init__(self, spec_hash_names, **kwargs): + super().__init__(**kwargs) + + self.__spec_hash_names = spec_hash_names + + # Map hash algorithm enum to the given spec mandated name, for those + # names which are recognized as hash algorithms by this library. + self.__alg_to_spec_name = {} + for spec_hash_name in spec_hash_names: + alg = stix2.hashes.infer_hash_algorithm(spec_hash_name) + if alg: + self.__alg_to_spec_name[alg] = spec_hash_name + def clean(self, value, allow_custom): # ignore the has_custom return value here; there is no customization # of DictionaryProperties. - clean_dict, _ = super(HashesProperty, self).clean( - value, allow_custom, - ) + clean_dict, _ = super().clean(value, allow_custom) + + spec_dict = {} has_custom = False - for k, v in copy.deepcopy(clean_dict).items(): - key = k.upper().replace('-', '') - if key in HASHES_REGEX: - vocab_key = HASHES_REGEX[key][1] - if vocab_key == "SSDEEP" and self.spec_version == "2.0": - vocab_key = vocab_key.lower() - if not re.match(HASHES_REGEX[key][0], v): - raise ValueError("'{0}' is not a valid {1} hash".format(v, vocab_key)) - if k != vocab_key: - clean_dict[vocab_key] = clean_dict[k] - del clean_dict[k] + for hash_k, hash_v in clean_dict.items(): + hash_alg = stix2.hashes.infer_hash_algorithm(hash_k) + + if hash_alg: + # Library-supported hash algorithm: sanity check the value. + if not stix2.hashes.check_hash(hash_alg, hash_v): + raise ValueError( + "'{0}' is not a valid {1} hash".format( + hash_v, hash_alg.name, + ), + ) + + spec_name = self.__alg_to_spec_name.get(hash_alg) + if not spec_name: + # There is library support for the hash algorithm, but it's + # not in the spec. So it's custom. Just use the user's + # name as-is. + has_custom = True + spec_name = hash_k else: - has_custom = True + # Unrecognized hash algorithm; use as-is. Hash algorithm name + # must be an exact match from spec, or it will be considered + # custom. + spec_name = hash_k + if spec_name not in self.__spec_hash_names: + has_custom = True if not allow_custom and has_custom: - raise CustomContentError("custom hash found: " + k) + raise CustomContentError( + "custom hash algorithm: " + hash_k, + ) - return clean_dict, has_custom + spec_dict[spec_name] = hash_v + + return spec_dict, has_custom class BinaryProperty(Property): @@ -654,19 +668,49 @@ class EmbeddedObjectProperty(Property): class EnumProperty(StringProperty): + """ + Used for enumeration type properties. Properties of this type do not allow + customization. + """ def __init__(self, allowed, **kwargs): - if type(allowed) is not list: - allowed = list(allowed) + if isinstance(allowed, str): + allowed = [allowed] self.allowed = allowed super(EnumProperty, self).__init__(**kwargs) def clean(self, value, allow_custom): cleaned_value, _ = super(EnumProperty, self).clean(value, allow_custom) + + if cleaned_value not in self.allowed: + raise ValueError("value '{}' is not valid for this enumeration.".format(cleaned_value)) + + return cleaned_value, False + + +class OpenVocabProperty(StringProperty): + """ + Used for open vocab type properties. + """ + + def __init__(self, allowed, **kwargs): + super(OpenVocabProperty, self).__init__(**kwargs) + + if isinstance(allowed, str): + allowed = [allowed] + self.allowed = allowed + + def clean(self, value, allow_custom): + cleaned_value, _ = super(OpenVocabProperty, self).clean( + value, allow_custom, + ) + has_custom = cleaned_value not in self.allowed if not allow_custom and has_custom: - raise ValueError("value '{}' is not valid for this enumeration.".format(cleaned_value)) + raise CustomContentError( + "custom value in open vocab: '{}'".format(cleaned_value), + ) return cleaned_value, has_custom diff --git a/stix2/test/test_properties.py b/stix2/test/test_properties.py index ea9a003..f19d1b5 100644 --- a/stix2/test/test_properties.py +++ b/stix2/test/test_properties.py @@ -9,8 +9,9 @@ from stix2.exceptions import ( ) from stix2.properties import ( BinaryProperty, BooleanProperty, EmbeddedObjectProperty, EnumProperty, - FloatProperty, HexProperty, IntegerProperty, ListProperty, Property, - StringProperty, TimestampProperty, TypeProperty, + FloatProperty, HashesProperty, HexProperty, IntegerProperty, ListProperty, + OpenVocabProperty, Property, StringProperty, TimestampProperty, + TypeProperty, ) @@ -363,8 +364,86 @@ def test_enum_property_invalid(): with pytest.raises(ValueError): enum_prop.clean('z', False) + with pytest.raises(ValueError): + enum_prop.clean('z', True) -def test_enum_property_custom(): - enum_prop = EnumProperty(['a', 'b', 'c']) - result = enum_prop.clean("z", True) - assert result == ("z", True) + +@pytest.mark.parametrize( + "vocab", [ + ['a', 'b', 'c'], + ('a', 'b', 'c'), + 'b', + ], +) +def test_openvocab_property(vocab): + ov_prop = OpenVocabProperty(vocab) + + assert ov_prop.clean("b", False) == ("b", False) + assert ov_prop.clean("b", True) == ("b", False) + + with pytest.raises(CustomContentError): + ov_prop.clean("d", False) + + assert ov_prop.clean("d", True) == ("d", True) + + +@pytest.mark.parametrize( + "value", [ + {"sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b"}, + [('MD5', '2dfb1bcc980200c6706feee399d41b3f'), ('RIPEMD-160', 'b3a8cd8a27c90af79b3c81754f267780f443dfef')], + ], +) +def test_hashes_property_valid(value): + hash_prop = HashesProperty(["sha256", "md5", "ripemd160"]) + _, has_custom = hash_prop.clean(value, False) + assert not has_custom + + +@pytest.mark.parametrize( + "value", [ + {"MD5": "a"}, + {"SHA-256": "2dfb1bcc980200c6706feee399d41b3f"}, + ], +) +def test_hashes_property_invalid(value): + hash_prop = HashesProperty(["sha256", "md5"]) + + with pytest.raises(ValueError): + hash_prop.clean(value, False) + + +def test_hashes_property_custom(): + value = { + "sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b", + "abc-123": "aaaaaaaaaaaaaaaaaaaaa", + } + expected_cleaned_value = { + # cleaning transforms recognized hash algorithm names to the spec- + # mandated name. + "SHA-256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b", + "abc-123": "aaaaaaaaaaaaaaaaaaaaa", + } + + hash_prop = HashesProperty(["SHA-256"]) + result = hash_prop.clean(value, True) + assert result == (expected_cleaned_value, True) + + with pytest.raises(CustomContentError): + hash_prop.clean(value, False) + + +def test_hashes_no_library_support(): + prop = HashesProperty(["foo"]) + + result = prop.clean({"foo": "bar"}, False) + assert result == ({"foo": "bar"}, False) + + result = prop.clean({"foo": "bar"}, True) + assert result == ({"foo": "bar"}, False) + + with pytest.raises(CustomContentError): + # require exact name match for unsupported hash algorithms + prop.clean({"FOO": "bar"}, False) + + result = prop.clean({"FOO": "bar"}, True) + assert result == ({"FOO": "bar"}, True) diff --git a/stix2/test/v20/conftest.py b/stix2/test/v20/conftest.py index 6bb8fae..0e4bc4c 100644 --- a/stix2/test/v20/conftest.py +++ b/stix2/test/v20/conftest.py @@ -55,7 +55,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -67,7 +67,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -79,7 +79,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.936Z", "name": "Malicious site hosting downloader", @@ -91,7 +91,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -103,7 +103,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -156,7 +156,7 @@ def stix_objs2(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-31T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -168,7 +168,7 @@ def stix_objs2(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -180,7 +180,7 @@ def stix_objs2(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", diff --git a/stix2/test/v20/test_datastore_filters.py b/stix2/test/v20/test_datastore_filters.py index e8945d1..bcbd08f 100644 --- a/stix2/test/v20/test_datastore_filters.py +++ b/stix2/test/v20/test_datastore_filters.py @@ -20,7 +20,7 @@ stix_objs = [ "created": "2014-05-08T09:00:00.000Z", "id": "indicator--a932fcc6-e032-476c-826f-cb970a5a1ade", "labels": [ - "file-hash-watchlist", + "compromised", ], "modified": "2014-05-08T09:00:00.000Z", "name": "File hash for Poison Ivy variant", diff --git a/stix2/test/v20/test_datastore_memory.py b/stix2/test/v20/test_datastore_memory.py index 3357e2c..23b3966 100644 --- a/stix2/test/v20/test_datastore_memory.py +++ b/stix2/test/v20/test_datastore_memory.py @@ -19,7 +19,7 @@ IND1 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -31,7 +31,7 @@ IND2 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -43,7 +43,7 @@ IND3 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.936Z", "name": "Malicious site hosting downloader", @@ -55,7 +55,7 @@ IND4 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -67,7 +67,7 @@ IND5 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -79,7 +79,7 @@ IND6 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-31T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -91,7 +91,7 @@ IND7 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -103,7 +103,7 @@ IND8 = { "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "labels": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -285,7 +285,7 @@ def test_memory_store_object_creator_of_present(mem_store): iden = Identity( id=IDENTITY_ID, name="Foo Corp.", - identity_class="corporation", + identity_class="organization", ) mem_store.add(camp) diff --git a/stix2/test/v20/test_pickle.py b/stix2/test/v20/test_pickle.py index 416341c..36e4337 100644 --- a/stix2/test/v20/test_pickle.py +++ b/stix2/test/v20/test_pickle.py @@ -13,7 +13,7 @@ def test_pickling(): id=IDENTITY_ID, name="alice", description="this is a pickle test", - identity_class="some_class", + identity_class="individual", ) pickle.loads(pickle.dumps(identity)) diff --git a/stix2/test/v20/test_properties.py b/stix2/test/v20/test_properties.py index 0158f9a..d760363 100644 --- a/stix2/test/v20/test_properties.py +++ b/stix2/test/v20/test_properties.py @@ -8,9 +8,8 @@ from stix2.exceptions import ( ExtraPropertiesError, ParseError, ) from stix2.properties import ( - DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty, - HashesProperty, IDProperty, ListProperty, ObservableProperty, - ReferenceProperty, STIXObjectProperty, + DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty, IDProperty, + ListProperty, ObservableProperty, ReferenceProperty, STIXObjectProperty, ) from stix2.v20.common import MarkingProperty @@ -354,45 +353,6 @@ def test_property_list_of_dictionary(): assert test_obj.property1[0]['foo'] == 'bar' -@pytest.mark.parametrize( - "value", [ - {"sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b"}, - [('MD5', '2dfb1bcc980200c6706feee399d41b3f'), ('RIPEMD-160', 'b3a8cd8a27c90af79b3c81754f267780f443dfef')], - ], -) -def test_hashes_property_valid(value): - hash_prop = HashesProperty() - _, has_custom = hash_prop.clean(value, False) - assert not has_custom - - -@pytest.mark.parametrize( - "value", [ - {"MD5": "a"}, - {"SHA-256": "2dfb1bcc980200c6706feee399d41b3f"}, - ], -) -def test_hashes_property_invalid(value): - hash_prop = HashesProperty() - - with pytest.raises(ValueError): - hash_prop.clean(value, False) - - -def test_hashes_property_custom(): - value = { - "sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b", - "abc-123": "aaaaaaaaaaaaaaaaaaaaa", - } - - hash_prop = HashesProperty() - result = hash_prop.clean(value, True) - assert result == (value, True) - - with pytest.raises(CustomContentError): - hash_prop.clean(value, False) - - def test_embedded_property(): emb_prop = EmbeddedObjectProperty(type=stix2.v20.EmailMIMEComponent) mime = stix2.v20.EmailMIMEComponent( diff --git a/stix2/test/v20/test_versioning.py b/stix2/test/v20/test_versioning.py index 665ac4c..d3973f0 100644 --- a/stix2/test/v20/test_versioning.py +++ b/stix2/test/v20/test_versioning.py @@ -433,7 +433,7 @@ def test_version_marking(): def test_version_disable_custom(): m = stix2.v20.Malware( - name="foo", labels=["label"], description="Steals your identity!", + name="foo", labels=["spyware"], description="Steals your identity!", x_custom=123, allow_custom=True, ) @@ -450,7 +450,7 @@ def test_version_disable_custom(): def test_version_enable_custom(): m = stix2.v20.Malware( - name="foo", labels=["label"], description="Steals your identity!", + name="foo", labels=["spyware"], description="Steals your identity!", ) # Add a custom property to an object for which it was previously disallowed @@ -464,7 +464,7 @@ def test_version_enable_custom(): def test_version_propagate_custom(): m = stix2.v20.Malware( - name="foo", labels=["label"], + name="foo", labels=["spyware"], ) # Remember custom-not-allowed setting from original; produce error @@ -476,7 +476,7 @@ def test_version_propagate_custom(): assert m2.description == "Steals your identity!" m_custom = stix2.v20.Malware( - name="foo", labels=["label"], x_custom=123, allow_custom=True, + name="foo", labels=["spyware"], x_custom=123, allow_custom=True, ) # Remember custom-allowed setting from original; should work diff --git a/stix2/test/v21/conftest.py b/stix2/test/v21/conftest.py index 6efcf39..9e37d92 100644 --- a/stix2/test/v21/conftest.py +++ b/stix2/test/v21/conftest.py @@ -66,7 +66,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -80,7 +80,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -94,7 +94,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.936Z", "name": "Malicious site hosting downloader", @@ -108,7 +108,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -122,7 +122,7 @@ def stix_objs1(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -183,7 +183,7 @@ def stix_objs2(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000001", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-31T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -197,7 +197,7 @@ def stix_objs2(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", @@ -211,7 +211,7 @@ def stix_objs2(): "created": "2017-01-27T13:49:53.935Z", "id": "indicator--00000000-0000-4000-8000-000000000002", "indicator_types": [ - "url-watchlist", + "malicious-activity", ], "modified": "2017-01-27T13:49:53.935Z", "name": "Malicious site hosting downloader", diff --git a/stix2/test/v21/test_datastore_filters.py b/stix2/test/v21/test_datastore_filters.py index a6a50a7..0e531fd 100644 --- a/stix2/test/v21/test_datastore_filters.py +++ b/stix2/test/v21/test_datastore_filters.py @@ -24,7 +24,7 @@ stix_objs = [ "created": "2014-05-08T09:00:00.000Z", "id": "indicator--a932fcc6-e032-476c-826f-cb970a5a1ade", "indicator_types": [ - "file-hash-watchlist", + "compromised", ], "modified": "2014-05-08T09:00:00.000Z", "name": "File hash for Poison Ivy variant", diff --git a/stix2/test/v21/test_datastore_memory.py b/stix2/test/v21/test_datastore_memory.py index 870f82e..f2c9a8c 100644 --- a/stix2/test/v21/test_datastore_memory.py +++ b/stix2/test/v21/test_datastore_memory.py @@ -300,7 +300,7 @@ def test_memory_store_object_creator_of_present(mem_store): iden = Identity( id=IDENTITY_ID, name="Foo Corp.", - identity_class="corporation", + identity_class="organization", ) mem_store.add(camp) diff --git a/stix2/test/v21/test_deterministic_ids.py b/stix2/test/v21/test_deterministic_ids.py index 56b2e8a..f8a60e4 100644 --- a/stix2/test/v21/test_deterministic_ids.py +++ b/stix2/test/v21/test_deterministic_ids.py @@ -14,6 +14,7 @@ from stix2.properties import ( TypeProperty, ) import stix2.v21 +from stix2.v21.vocab import HASHING_ALGORITHM SCO_DET_ID_NAMESPACE = uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7") @@ -175,7 +176,7 @@ def test_empty_hash(): spec_version='2.1', enclosing_type=_type, ), ), - ('hashes', HashesProperty()), + ('hashes', HashesProperty(HASHING_ALGORITHM)), )) _id_contributing_properties = ['hashes'] diff --git a/stix2/test/v21/test_environment.py b/stix2/test/v21/test_environment.py index 7f6b71c..883ff01 100644 --- a/stix2/test/v21/test_environment.py +++ b/stix2/test/v21/test_environment.py @@ -490,7 +490,7 @@ def test_object_similarity_on_same_identity2(): IDEN_KWARGS = dict( name="John Smith", identity_class="individual", - sectors=["government", "critical-infrastructure"], + sectors=["government", "infrastructure"], ) iden1 = stix2.v21.Identity(id=IDENTITY_ID, **IDEN_KWARGS) iden2 = stix2.v21.Identity(id=IDENTITY_ID, **IDEN_KWARGS) @@ -723,7 +723,7 @@ def test_object_similarity_different_spec_version_raises(): def test_object_similarity_zero_match(): IND_KWARGS = dict( - indicator_types=["malicious-activity", "bar"], + indicator_types=["anomalous-activity"], pattern="[ipv4-addr:value = '192.168.1.1']", pattern_type="stix", valid_from="2019-01-01T12:34:56Z", @@ -743,14 +743,14 @@ def test_object_similarity_zero_match(): ind1 = stix2.v21.Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS) ind2 = stix2.v21.Indicator(id=INDICATOR_ID, **IND_KWARGS) env = stix2.Environment().object_similarity(ind1, ind2, **weights) - assert round(env) == 8 + assert round(env) == 0 env = stix2.Environment().object_similarity(ind2, ind1, **weights) - assert round(env) == 8 + assert round(env) == 0 def test_object_similarity_different_spec_version(): IND_KWARGS = dict( - labels=["APTX"], + labels=["malicious-activity"], pattern="[ipv4-addr:value = '192.168.1.1']", ) weights = { diff --git a/stix2/test/v21/test_location.py b/stix2/test/v21/test_location.py index cf9c101..e912fba 100644 --- a/stix2/test/v21/test_location.py +++ b/stix2/test/v21/test_location.py @@ -36,7 +36,7 @@ EXPECTED_LOCATION_2 = """{ "id": "location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64", "created": "2016-04-06T20:03:00.000Z", "modified": "2016-04-06T20:03:00.000Z", - "region": "north-america" + "region": "northern-america" } """ @@ -47,8 +47,7 @@ EXPECTED_LOCATION_2_REPR = "Location(" + " ".join( id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64', created='2016-04-06T20:03:00.000Z', modified='2016-04-06T20:03:00.000Z', - region='north-america'""".split(), -) + ")" + region='northern-america'""".split()) + ")" def test_location_with_some_required_properties(): @@ -76,7 +75,7 @@ def test_location_with_some_required_properties(): "id": LOCATION_ID, "created": "2016-04-06T20:03:00.000Z", "modified": "2016-04-06T20:03:00.000Z", - "region": "north-america", + "region": "northern-america", }, ], ) @@ -88,7 +87,7 @@ def test_parse_location(data): assert location.id == LOCATION_ID assert location.created == dt.datetime(2016, 4, 6, 20, 3, 0, tzinfo=pytz.utc) assert location.modified == dt.datetime(2016, 4, 6, 20, 3, 0, tzinfo=pytz.utc) - assert location.region == 'north-america' + assert location.region == 'northern-america' rep = re.sub(r"(\[|=| )u('|\"|\\\'|\\\")", r"\g<1>\g<2>", repr(location)) assert rep == EXPECTED_LOCATION_2_REPR @@ -302,6 +301,7 @@ def test_google_map_url_multiple_props_no_long_lat_provided(): region="North America", country="United States of America", street_address="1410 Museum Campus Drive, Chicago, IL 60605", + allow_custom=True, ) loc_url = loc.to_maps_url() @@ -312,7 +312,7 @@ def test_google_map_url_multiple_props_and_long_lat_provided(): expected_url = "https://www.google.com/maps/search/?api=1&query=41.862401%2C-87.616001" loc = stix2.v21.Location( - region="North America", + region="northern-america", country="United States of America", street_address="1410 Museum Campus Drive, Chicago, IL 60605", latitude=41.862401, @@ -354,6 +354,7 @@ def test_bing_map_url_multiple_props_no_long_lat_provided(): region="North America", country="United States of America", street_address="1410 Museum Campus Drive, Chicago, IL 60605", + allow_custom=True, ) loc_url = loc.to_maps_url("Bing Maps") @@ -364,7 +365,7 @@ def test_bing_map_url_multiple_props_and_long_lat_provided(): expected_url = "https://bing.com/maps/default.aspx?where1=41.862401%2C-87.616001&lvl=16" loc = stix2.v21.Location( - region="North America", + region="northern-america", country="United States of America", street_address="1410 Museum Campus Drive, Chicago, IL 60605", latitude=41.862401, diff --git a/stix2/test/v21/test_malware.py b/stix2/test/v21/test_malware.py index f111826..0425204 100644 --- a/stix2/test/v21/test_malware.py +++ b/stix2/test/v21/test_malware.py @@ -181,7 +181,7 @@ def test_malware_family_no_name(): "id": MALWARE_ID, "spec_version": "2.1", "is_family": True, - "malware_types": ["a type"], + "malware_types": ["spyware"], }) @@ -191,7 +191,7 @@ def test_malware_non_family_no_name(): "id": MALWARE_ID, "spec_version": "2.1", "is_family": False, - "malware_types": ["something"], + "malware_types": ["spyware"], }) @@ -207,7 +207,7 @@ def test_malware_with_os_refs(): "id": MALWARE_ID, "spec_version": "2.1", "is_family": False, - "malware_types": ["something"], + "malware_types": ["spyware"], "operating_system_refs": [software], }) diff --git a/stix2/test/v21/test_pickle.py b/stix2/test/v21/test_pickle.py index faef4c4..55e8139 100644 --- a/stix2/test/v21/test_pickle.py +++ b/stix2/test/v21/test_pickle.py @@ -13,7 +13,7 @@ def test_pickling(): id=IDENTITY_ID, name="alice", description="this is a pickle test", - identity_class="some_class", + identity_class="individual", ) pickle.loads(pickle.dumps(identity)) diff --git a/stix2/test/v21/test_properties.py b/stix2/test/v21/test_properties.py index 413aa0d..7ccf6f2 100644 --- a/stix2/test/v21/test_properties.py +++ b/stix2/test/v21/test_properties.py @@ -6,9 +6,9 @@ from stix2.exceptions import ( ExtraPropertiesError, ParseError, ) from stix2.properties import ( - DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty, - HashesProperty, IDProperty, ListProperty, ObservableProperty, - ReferenceProperty, STIXObjectProperty, StringProperty, + DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty, IDProperty, + ListProperty, ObservableProperty, ReferenceProperty, STIXObjectProperty, + StringProperty, ) from stix2.v21.common import MarkingProperty @@ -367,53 +367,6 @@ def test_property_list_of_dictionary(): assert test_obj.property1[0]['foo'] == 'bar' -@pytest.mark.parametrize( - "value", [ - {"sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b"}, - [('MD5', '2dfb1bcc980200c6706feee399d41b3f'), ('RIPEMD-160', 'b3a8cd8a27c90af79b3c81754f267780f443dfef')], - [('TLSH', '6FF02BEF718027B0160B4391212923ED7F1A463D563B1549B86CF62973B197AD2731F8')], - ], -) -def test_hashes_property_valid(value): - hash_prop = HashesProperty() - _, has_custom = hash_prop.clean(value, False) - assert not has_custom - - _, has_custom = hash_prop.clean(value, True) - assert not has_custom - - -@pytest.mark.parametrize( - "value", [ - {"MD5": "a"}, - {"SHA-256": "2dfb1bcc980200c6706feee399d41b3f"}, - {"TLSH": "6FF02BEF718027B0160B4391212923ED7F1A463D563B1549B86CF62973B197AD2731F"}, - ], -) -def test_hashes_property_invalid(value): - hash_prop = HashesProperty() - - with pytest.raises(ValueError): - hash_prop.clean(value, False) - - with pytest.raises(ValueError): - hash_prop.clean(value, True) - - -def test_hashes_property_custom(): - value = { - "sha256": "6db12788c37247f2316052e142f42f4b259d6561751e5f401a1ae2a6df9c674b", - "abc-123": "aaaaaaaaaaaaaaaaaaaaa", - } - - hash_prop = HashesProperty() - result = hash_prop.clean(value, True) - assert result == (value, True) - - with pytest.raises(CustomContentError): - hash_prop.clean(value, False) - - def test_embedded_property(): emb_prop = EmbeddedObjectProperty(type=stix2.v21.EmailMIMEComponent) mime = stix2.v21.EmailMIMEComponent( diff --git a/stix2/test/v21/test_threat_actor.py b/stix2/test/v21/test_threat_actor.py index 6a782ef..899c300 100644 --- a/stix2/test/v21/test_threat_actor.py +++ b/stix2/test/v21/test_threat_actor.py @@ -76,7 +76,7 @@ def test_seen_ordering_constraint(): with pytest.raises(ValueError): stix2.v21.ThreatActor( name="Bad Person", - threat_actor_types=["bad person", "evil person"], + threat_actor_types=["hacker", "criminal"], first_seen="2010-04-21T09:31:11Z", last_seen="2009-02-06T03:39:31Z", ) @@ -84,7 +84,7 @@ def test_seen_ordering_constraint(): # equal timestamps is okay. stix2.v21.ThreatActor( name="Bad Person", - threat_actor_types=["bad person", "evil person"], + threat_actor_types=["hacker", "criminal"], first_seen="2010-04-21T09:31:11Z", last_seen="2010-04-21T09:31:11Z", ) diff --git a/stix2/v20/common.py b/stix2/v20/common.py index fe151fb..acff911 100644 --- a/stix2/v20/common.py +++ b/stix2/v20/common.py @@ -12,6 +12,7 @@ from ..properties import ( ) from ..utils import NOW, _get_dict from .base import _STIXBase20 +from .vocab import HASHING_ALGORITHM def _should_set_millisecond(cr, marking_type): @@ -38,7 +39,7 @@ class ExternalReference(_STIXBase20): ('source_name', StringProperty(required=True)), ('description', StringProperty()), ('url', StringProperty()), - ('hashes', HashesProperty(spec_version='2.0')), + ('hashes', HashesProperty(HASHING_ALGORITHM, spec_version='2.0')), ('external_id', StringProperty()), ]) diff --git a/stix2/v20/observables.py b/stix2/v20/observables.py index 56262b0..793b683 100644 --- a/stix2/v20/observables.py +++ b/stix2/v20/observables.py @@ -17,6 +17,7 @@ from ..properties import ( ObjectReferenceProperty, StringProperty, TimestampProperty, TypeProperty, ) from .base import _Extension, _Observable, _STIXBase20 +from .vocab import HASHING_ALGORITHM class Artifact(_Observable): @@ -30,7 +31,7 @@ class Artifact(_Observable): ('mime_type', StringProperty()), ('payload_bin', BinaryProperty()), ('url', StringProperty()), - ('hashes', HashesProperty(spec_version='2.0')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('extensions', ExtensionsProperty(spec_version="2.0", enclosing_type=_type)), ]) @@ -173,7 +174,7 @@ class AlternateDataStream(_STIXBase20): _properties = OrderedDict([ ('name', StringProperty(required=True)), - ('hashes', HashesProperty(spec_version='2.0')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('size', IntegerProperty()), ]) @@ -256,7 +257,7 @@ class WindowsPEOptionalHeaderType(_STIXBase20): ('size_of_heap_commit', IntegerProperty()), ('loader_flags_hex', HexProperty()), ('number_of_rva_and_sizes', IntegerProperty()), - ('hashes', HashesProperty(spec_version='2.0')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ]) def _check_object_constraints(self): @@ -273,7 +274,7 @@ class WindowsPESection(_STIXBase20): ('name', StringProperty(required=True)), ('size', IntegerProperty()), ('entropy', FloatProperty()), - ('hashes', HashesProperty(spec_version='2.0')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ]) @@ -293,7 +294,7 @@ class WindowsPEBinaryExt(_Extension): ('number_of_symbols', IntegerProperty()), ('size_of_optional_header', IntegerProperty()), ('characteristics_hex', HexProperty()), - ('file_header_hashes', HashesProperty(spec_version='2.0')), + ('file_header_hashes', HashesProperty(HASHING_ALGORITHM)), ('optional_header', EmbeddedObjectProperty(type=WindowsPEOptionalHeaderType)), ('sections', ListProperty(EmbeddedObjectProperty(type=WindowsPESection))), ]) @@ -307,7 +308,7 @@ class File(_Observable): _type = 'file' _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.0')), - ('hashes', HashesProperty(spec_version='2.0')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('size', IntegerProperty()), ('name', StringProperty()), ('name_enc', StringProperty()), @@ -771,7 +772,7 @@ class X509Certificate(_Observable): _properties = OrderedDict([ ('type', TypeProperty(_type, spec_version='2.0')), ('is_self_signed', BooleanProperty()), - ('hashes', HashesProperty(spec_version='2.0')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('version', StringProperty()), ('serial_number', StringProperty()), ('signature_algorithm', StringProperty()), diff --git a/stix2/v20/sdo.py b/stix2/v20/sdo.py index 3c854e0..d08063e 100644 --- a/stix2/v20/sdo.py +++ b/stix2/v20/sdo.py @@ -9,12 +9,17 @@ from ..custom import _custom_object_builder from ..exceptions import InvalidValueError from ..properties import ( BooleanProperty, IDProperty, IntegerProperty, ListProperty, - ObservableProperty, PatternProperty, ReferenceProperty, StringProperty, - TimestampProperty, TypeProperty, + ObservableProperty, OpenVocabProperty, PatternProperty, ReferenceProperty, + StringProperty, TimestampProperty, TypeProperty, ) from ..utils import NOW from .base import _DomainObject from .common import ExternalReference, GranularMarking, KillChainPhase +from .vocab import ( + ATTACK_MOTIVATION, ATTACK_RESOURCE_LEVEL, IDENTITY_CLASS, INDICATOR_LABEL, + INDUSTRY_SECTOR, MALWARE_LABEL, REPORT_LABEL, THREAT_ACTOR_LABEL, + THREAT_ACTOR_ROLE, THREAT_ACTOR_SOPHISTICATION, TOOL_LABEL, +) class AttackPattern(_DomainObject): @@ -102,8 +107,8 @@ class Identity(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('identity_class', StringProperty(required=True)), - ('sectors', ListProperty(StringProperty)), + ('identity_class', OpenVocabProperty(IDENTITY_CLASS, required=True)), + ('sectors', ListProperty(OpenVocabProperty(INDUSTRY_SECTOR))), ('contact_information', StringProperty()), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), @@ -132,7 +137,7 @@ class Indicator(_DomainObject): ('valid_until', TimestampProperty()), ('kill_chain_phases', ListProperty(KillChainPhase)), ('revoked', BooleanProperty(default=lambda: False)), - ('labels', ListProperty(StringProperty, required=True)), + ('labels', ListProperty(OpenVocabProperty(INDICATOR_LABEL), required=True)), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), ('granular_markings', ListProperty(GranularMarking)), @@ -163,8 +168,8 @@ class IntrusionSet(_DomainObject): ('last_seen', TimestampProperty()), ('goals', ListProperty(StringProperty)), ('resource_level', StringProperty()), - ('primary_motivation', StringProperty()), - ('secondary_motivations', ListProperty(StringProperty)), + ('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)), + ('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), ('external_references', ListProperty(ExternalReference)), @@ -189,7 +194,7 @@ class Malware(_DomainObject): ('description', StringProperty()), ('kill_chain_phases', ListProperty(KillChainPhase)), ('revoked', BooleanProperty(default=lambda: False)), - ('labels', ListProperty(StringProperty, required=True)), + ('labels', ListProperty(OpenVocabProperty(MALWARE_LABEL), required=True)), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), ('granular_markings', ListProperty(GranularMarking)), @@ -237,7 +242,7 @@ class Report(_DomainObject): ('published', TimestampProperty(required=True)), ('object_refs', ListProperty(ReferenceProperty(invalid_types=[], spec_version='2.0'), required=True)), ('revoked', BooleanProperty(default=lambda: False)), - ('labels', ListProperty(StringProperty, required=True)), + ('labels', ListProperty(OpenVocabProperty(REPORT_LABEL), required=True)), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), ('granular_markings', ListProperty(GranularMarking)), @@ -259,15 +264,15 @@ class ThreatActor(_DomainObject): ('name', StringProperty(required=True)), ('description', StringProperty()), ('aliases', ListProperty(StringProperty)), - ('roles', ListProperty(StringProperty)), + ('roles', ListProperty(OpenVocabProperty(THREAT_ACTOR_ROLE))), ('goals', ListProperty(StringProperty)), - ('sophistication', StringProperty()), - ('resource_level', StringProperty()), - ('primary_motivation', StringProperty()), - ('secondary_motivations', ListProperty(StringProperty)), - ('personal_motivations', ListProperty(StringProperty)), + ('sophistication', OpenVocabProperty(THREAT_ACTOR_SOPHISTICATION)), + ('resource_level', OpenVocabProperty(ATTACK_RESOURCE_LEVEL)), + ('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)), + ('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))), + ('personal_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))), ('revoked', BooleanProperty(default=lambda: False)), - ('labels', ListProperty(StringProperty, required=True)), + ('labels', ListProperty(OpenVocabProperty(THREAT_ACTOR_LABEL), required=True)), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), ('granular_markings', ListProperty(GranularMarking)), @@ -291,7 +296,7 @@ class Tool(_DomainObject): ('kill_chain_phases', ListProperty(KillChainPhase)), ('tool_version', StringProperty()), ('revoked', BooleanProperty(default=lambda: False)), - ('labels', ListProperty(StringProperty, required=True)), + ('labels', ListProperty(OpenVocabProperty(TOOL_LABEL), required=True)), ('external_references', ListProperty(ExternalReference)), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))), ('granular_markings', ListProperty(GranularMarking)), diff --git a/stix2/v20/vocab.py b/stix2/v20/vocab.py new file mode 100644 index 0000000..b0f9a70 --- /dev/null +++ b/stix2/v20/vocab.py @@ -0,0 +1,178 @@ +""" +STIX 2.0 open vocabularies and enums +""" + +ATTACK_MOTIVATION = [ + "accidental", + "coercion", + "dominance", + "ideology", + "notoriety", + "organizational-gain", + "personal-gain", + "personal-satisfaction", + "revenge", + "unpredictable", +] + + +ATTACK_RESOURCE_LEVEL = [ + "individual", + "club", + "contest", + "team", + "organization", + "government", +] + + +HASHING_ALGORITHM = [ + "MD5", + "MD6", + "RIPEMD-160", + "SHA-1", + "SHA-224", + "SHA-256", + "SHA-384", + "SHA-512", + "SHA3-224", + "SHA3-256", + "SHA3-384", + "SHA3-512", + "ssdeep", + "WHIRLPOOL", +] + + +IDENTITY_CLASS = [ + "individual", + "group", + "organization", + "class", + "unknown", +] + + +INDICATOR_LABEL = [ + "anomalous-activity", + "anonymization", + "benign", + "compromised", + "malicious-activity", + "attribution", +] + + +INDUSTRY_SECTOR = [ + "agriculture", + "aerospace", + "automotive", + "communications", + "construction", + "defence", + "education", + "energy", + "entertainment", + "financial-services", + "government-national", + "government-regional", + "government-local", + "government-public-services", + "healthcare", + "hospitality-leisure", + "infrastructure", + "insurance", + "manufacturing", + "mining", + "non-profit", + "pharmaceuticals", + "retail", + "technology", + "telecommunications", + "transportation", + "utilities", +] + + +MALWARE_LABEL = [ + "adware", + "backdoor", + "bot", + "ddos", + "dropper", + "exploit-kit", + "keylogger", + "ransomware", + "remote-access-trojan", + "resource-exploitation", + "rogue-security-software", + "rootkit", + "screen-capture", + "spyware", + "trojan", + "virus", + "worm", +] + + +REPORT_LABEL = [ + "threat-report", + "attack-pattern", + "campaign", + "identity", + "indicator", + "intrusion-set", + "malware", + "observed-data", + "threat-actor", + "tool", + "vulnerability", +] + + +THREAT_ACTOR_LABEL = [ + "activist", + "competitor", + "crime-syndicate", + "criminal", + "hacker", + "insider-accidental", + "insider-disgruntled", + "nation-state", + "sensationalist", + "spy", + "terrorist", +] + + +THREAT_ACTOR_ROLE = [ + "agent", + "director", + "independent", + "infrastructure-architect", + "infrastructure-operator", + "malware-author", + "sponsor", +] + + +THREAT_ACTOR_SOPHISTICATION = [ + "none", + "minimal", + "intermediate", + "advanced", + "expert", + "innovator", + "strategic", +] + + +TOOL_LABEL = [ + "denial-of-service", + "exploitation", + "information-gathering", + "network-capture", + "credential-exploitation", + "remote-access", + "vulnerability-scanning", +] diff --git a/stix2/v21/common.py b/stix2/v21/common.py index af0a758..49fdf87 100644 --- a/stix2/v21/common.py +++ b/stix2/v21/common.py @@ -13,6 +13,7 @@ from ..properties import ( ) from ..utils import NOW, _get_dict from .base import _STIXBase21 +from .vocab import HASHING_ALGORITHM class ExternalReference(_STIXBase21): @@ -24,7 +25,7 @@ class ExternalReference(_STIXBase21): ('source_name', StringProperty(required=True)), ('description', StringProperty()), ('url', StringProperty()), - ('hashes', HashesProperty(spec_version='2.1')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('external_id', StringProperty()), ]) diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py index 430afea..9963d49 100644 --- a/stix2/v21/observables.py +++ b/stix2/v21/observables.py @@ -14,10 +14,17 @@ from ..properties import ( BinaryProperty, BooleanProperty, DictionaryProperty, EmbeddedObjectProperty, EnumProperty, ExtensionsProperty, FloatProperty, HashesProperty, HexProperty, IDProperty, IntegerProperty, ListProperty, - ReferenceProperty, StringProperty, TimestampProperty, TypeProperty, + OpenVocabProperty, ReferenceProperty, StringProperty, TimestampProperty, + TypeProperty, ) from .base import _Extension, _Observable, _STIXBase21 from .common import GranularMarking +from .vocab import ( + ACCOUNT_TYPE, ENCRYPTION_ALGORITHM, HASHING_ALGORITHM, + NETWORK_SOCKET_ADDRESS_FAMILY, NETWORK_SOCKET_TYPE, + WINDOWS_INTEGRITY_LEVEL, WINDOWS_PEBINARY_TYPE, WINDOWS_REGISTRY_DATATYPE, + WINDOWS_SERVICE_START_TYPE, WINDOWS_SERVICE_STATUS, WINDOWS_SERVICE_TYPE, +) class Artifact(_Observable): @@ -33,8 +40,8 @@ class Artifact(_Observable): ('mime_type', StringProperty()), ('payload_bin', BinaryProperty()), ('url', StringProperty()), - ('hashes', HashesProperty(spec_version='2.1')), - ('encryption_algorithm', StringProperty()), + ('hashes', HashesProperty(HASHING_ALGORITHM)), + ('encryption_algorithm', EnumProperty(ENCRYPTION_ALGORITHM)), ('decryption_key', StringProperty()), ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))), ('granular_markings', ListProperty(GranularMarking)), @@ -212,7 +219,7 @@ class AlternateDataStream(_STIXBase21): _properties = OrderedDict([ ('name', StringProperty(required=True)), - ('hashes', HashesProperty(spec_version='2.1')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('size', IntegerProperty()), ]) @@ -294,7 +301,7 @@ class WindowsPEOptionalHeaderType(_STIXBase21): ('size_of_heap_commit', IntegerProperty()), ('loader_flags_hex', HexProperty()), ('number_of_rva_and_sizes', IntegerProperty()), - ('hashes', HashesProperty(spec_version='2.1')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ]) def _check_object_constraints(self): @@ -311,7 +318,7 @@ class WindowsPESection(_STIXBase21): ('name', StringProperty(required=True)), ('size', IntegerProperty(min=0)), ('entropy', FloatProperty()), - ('hashes', HashesProperty(spec_version='2.1')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ]) @@ -322,7 +329,7 @@ class WindowsPEBinaryExt(_Extension): _type = 'windows-pebinary-ext' _properties = OrderedDict([ - ('pe_type', StringProperty(required=True)), # open_vocab + ('pe_type', OpenVocabProperty(WINDOWS_PEBINARY_TYPE, required=True)), ('imphash', StringProperty()), ('machine_hex', HexProperty()), ('number_of_sections', IntegerProperty(min=0)), @@ -331,7 +338,7 @@ class WindowsPEBinaryExt(_Extension): ('number_of_symbols', IntegerProperty(min=0)), ('size_of_optional_header', IntegerProperty(min=0)), ('characteristics_hex', HexProperty()), - ('file_header_hashes', HashesProperty(spec_version='2.1')), + ('file_header_hashes', HashesProperty(HASHING_ALGORITHM)), ('optional_header', EmbeddedObjectProperty(type=WindowsPEOptionalHeaderType)), ('sections', ListProperty(EmbeddedObjectProperty(type=WindowsPESection))), ]) @@ -347,7 +354,7 @@ class File(_Observable): ('type', TypeProperty(_type, spec_version='2.1')), ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), - ('hashes', HashesProperty(spec_version='2.1')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('size', IntegerProperty(min=0)), ('name', StringProperty()), ('name_enc', StringProperty()), @@ -487,34 +494,11 @@ class SocketExt(_Extension): _type = 'socket-ext' _properties = OrderedDict([ - ( - 'address_family', EnumProperty( - allowed=[ - "AF_UNSPEC", - "AF_INET", - "AF_IPX", - "AF_APPLETALK", - "AF_NETBIOS", - "AF_INET6", - "AF_IRDA", - "AF_BTH", - ], required=True, - ), - ), + ('address_family', EnumProperty(NETWORK_SOCKET_ADDRESS_FAMILY, required=True)), ('is_blocking', BooleanProperty()), ('is_listening', BooleanProperty()), ('options', DictionaryProperty(spec_version='2.1')), - ( - 'socket_type', EnumProperty( - allowed=[ - "SOCK_STREAM", - "SOCK_DGRAM", - "SOCK_RAW", - "SOCK_RDM", - "SOCK_SEQPACKET", - ], - ), - ), + ('socket_type', EnumProperty(NETWORK_SOCKET_TYPE)), ('socket_descriptor', IntegerProperty(min=0)), ('socket_handle', IntegerProperty()), ]) @@ -613,16 +597,7 @@ class WindowsProcessExt(_Extension): ('owner_sid', StringProperty()), ('window_title', StringProperty()), ('startup_info', DictionaryProperty(spec_version='2.1')), - ( - 'integrity_level', EnumProperty( - allowed=[ - "low", - "medium", - "high", - "system", - ], - ), - ), + ('integrity_level', EnumProperty(WINDOWS_INTEGRITY_LEVEL)), ]) @@ -637,41 +612,10 @@ class WindowsServiceExt(_Extension): ('descriptions', ListProperty(StringProperty)), ('display_name', StringProperty()), ('group_name', StringProperty()), - ( - 'start_type', EnumProperty( - allowed=[ - "SERVICE_AUTO_START", - "SERVICE_BOOT_START", - "SERVICE_DEMAND_START", - "SERVICE_DISABLED", - "SERVICE_SYSTEM_ALERT", - ], - ), - ), + ('start_type', EnumProperty(WINDOWS_SERVICE_START_TYPE)), ('service_dll_refs', ListProperty(ReferenceProperty(valid_types='file', spec_version="2.1"))), - ( - 'service_type', EnumProperty( - allowed=[ - "SERVICE_KERNEL_DRIVER", - "SERVICE_FILE_SYSTEM_DRIVER", - "SERVICE_WIN32_OWN_PROCESS", - "SERVICE_WIN32_SHARE_PROCESS", - ], - ), - ), - ( - 'service_status', EnumProperty( - allowed=[ - "SERVICE_CONTINUE_PENDING", - "SERVICE_PAUSE_PENDING", - "SERVICE_PAUSED", - "SERVICE_RUNNING", - "SERVICE_START_PENDING", - "SERVICE_STOP_PENDING", - "SERVICE_STOPPED", - ], - ), - ), + ('service_type', EnumProperty(WINDOWS_SERVICE_TYPE)), + ('service_status', EnumProperty(WINDOWS_SERVICE_STATUS)), ]) @@ -789,7 +733,7 @@ class UserAccount(_Observable): ('user_id', StringProperty()), ('credential', StringProperty()), ('account_login', StringProperty()), - ('account_type', StringProperty()), # open vocab + ('account_type', OpenVocabProperty(ACCOUNT_TYPE)), ('display_name', StringProperty()), ('is_service_account', BooleanProperty()), ('is_privileged', BooleanProperty()), @@ -817,25 +761,7 @@ class WindowsRegistryValueType(_STIXBase21): _properties = OrderedDict([ ('name', StringProperty()), ('data', StringProperty()), - ( - 'data_type', EnumProperty( - allowed=[ - "REG_NONE", - "REG_SZ", - "REG_EXPAND_SZ", - "REG_BINARY", - "REG_DWORD", - "REG_DWORD_BIG_ENDIAN", - "REG_LINK", - "REG_MULTI_SZ", - "REG_RESOURCE_LIST", - "REG_FULL_RESOURCE_DESCRIPTION", - "REG_RESOURCE_REQUIREMENTS_LIST", - "REG_QWORD", - "REG_INVALID_TYPE", - ], - ), - ), + ('data_type', EnumProperty(WINDOWS_REGISTRY_DATATYPE)), ]) @@ -900,7 +826,7 @@ class X509Certificate(_Observable): ('spec_version', StringProperty(fixed='2.1')), ('id', IDProperty(_type, spec_version='2.1')), ('is_self_signed', BooleanProperty()), - ('hashes', HashesProperty(spec_version='2.1')), + ('hashes', HashesProperty(HASHING_ALGORITHM)), ('version', StringProperty()), ('serial_number', StringProperty()), ('signature_algorithm', StringProperty()), diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py index 3b81e43..bba531e 100644 --- a/stix2/v21/sdo.py +++ b/stix2/v21/sdo.py @@ -13,12 +13,20 @@ from ..exceptions import ( ) from ..properties import ( BooleanProperty, EnumProperty, FloatProperty, IDProperty, IntegerProperty, - ListProperty, ObservableProperty, PatternProperty, ReferenceProperty, - StringProperty, TimestampProperty, TypeProperty, + ListProperty, ObservableProperty, OpenVocabProperty, PatternProperty, + ReferenceProperty, StringProperty, TimestampProperty, TypeProperty, ) from ..utils import NOW from .base import _DomainObject from .common import ExternalReference, GranularMarking, KillChainPhase +from .vocab import ( + ATTACK_MOTIVATION, ATTACK_RESOURCE_LEVEL, GROUPING_CONTEXT, IDENTITY_CLASS, + IMPLEMENTATION_LANGUAGE, INDICATOR_TYPE, INDUSTRY_SECTOR, + INFRASTRUCTURE_TYPE, MALWARE_CAPABILITIES, MALWARE_RESULT, MALWARE_TYPE, + OPINION, PATTERN_TYPE, PROCESSOR_ARCHITECTURE, REGION, REPORT_TYPE, + THREAT_ACTOR_ROLE, THREAT_ACTOR_SOPHISTICATION, THREAT_ACTOR_TYPE, + TOOL_TYPE, +) class AttackPattern(_DomainObject): @@ -127,7 +135,7 @@ class Grouping(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('name', StringProperty()), ('description', StringProperty()), - ('context', StringProperty(required=True)), + ('context', OpenVocabProperty(GROUPING_CONTEXT, required=True)), ('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), @@ -155,8 +163,8 @@ class Identity(_DomainObject): ('name', StringProperty(required=True)), ('description', StringProperty()), ('roles', ListProperty(StringProperty)), - ('identity_class', StringProperty()), - ('sectors', ListProperty(StringProperty)), + ('identity_class', OpenVocabProperty(IDENTITY_CLASS)), + ('sectors', ListProperty(OpenVocabProperty(INDUSTRY_SECTOR))), ('contact_information', StringProperty()), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), @@ -183,9 +191,9 @@ class Indicator(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('name', StringProperty()), ('description', StringProperty()), - ('indicator_types', ListProperty(StringProperty)), + ('indicator_types', ListProperty(OpenVocabProperty(INDICATOR_TYPE))), ('pattern', PatternProperty(required=True)), - ('pattern_type', StringProperty(required=True)), + ('pattern_type', OpenVocabProperty(PATTERN_TYPE, required=True)), ('pattern_version', StringProperty()), ('valid_from', TimestampProperty(default=lambda: NOW)), ('valid_until', TimestampProperty()), @@ -242,7 +250,7 @@ class Infrastructure(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('infrastructure_types', ListProperty(StringProperty)), + ('infrastructure_types', ListProperty(OpenVocabProperty(INFRASTRUCTURE_TYPE))), ('aliases', ListProperty(StringProperty)), ('kill_chain_phases', ListProperty(KillChainPhase)), ('first_seen', TimestampProperty()), @@ -286,9 +294,9 @@ class IntrusionSet(_DomainObject): ('first_seen', TimestampProperty()), ('last_seen', TimestampProperty()), ('goals', ListProperty(StringProperty)), - ('resource_level', StringProperty()), - ('primary_motivation', StringProperty()), - ('secondary_motivations', ListProperty(StringProperty)), + ('resource_level', OpenVocabProperty(ATTACK_RESOURCE_LEVEL)), + ('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)), + ('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), ('confidence', IntegerProperty()), @@ -327,7 +335,7 @@ class Location(_DomainObject): ('latitude', FloatProperty(min=-90.0, max=90.0)), ('longitude', FloatProperty(min=-180.0, max=180.0)), ('precision', FloatProperty(min=0.0)), - ('region', StringProperty()), + ('region', OpenVocabProperty(REGION)), ('country', StringProperty()), ('administrative_area', StringProperty()), ('city', StringProperty()), @@ -431,16 +439,16 @@ class Malware(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('name', StringProperty()), ('description', StringProperty()), - ('malware_types', ListProperty(StringProperty)), + ('malware_types', ListProperty(OpenVocabProperty(MALWARE_TYPE))), ('is_family', BooleanProperty(required=True)), ('aliases', ListProperty(StringProperty)), ('kill_chain_phases', ListProperty(KillChainPhase)), ('first_seen', TimestampProperty()), ('last_seen', TimestampProperty()), ('operating_system_refs', ListProperty(ReferenceProperty(valid_types='software', spec_version='2.1'))), - ('architecture_execution_envs', ListProperty(StringProperty)), - ('implementation_languages', ListProperty(StringProperty)), - ('capabilities', ListProperty(StringProperty)), + ('architecture_execution_envs', ListProperty(OpenVocabProperty(PROCESSOR_ARCHITECTURE))), + ('implementation_languages', ListProperty(OpenVocabProperty(IMPLEMENTATION_LANGUAGE))), + ('capabilities', ListProperty(OpenVocabProperty(MALWARE_CAPABILITIES))), ('sample_refs', ListProperty(ReferenceProperty(valid_types=['artifact', 'file'], spec_version='2.1'))), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), @@ -494,7 +502,7 @@ class MalwareAnalysis(_DomainObject): ('analysis_started', TimestampProperty()), ('analysis_ended', TimestampProperty()), ('result_name', StringProperty()), - ('result', StringProperty()), + ('result', OpenVocabProperty(MALWARE_RESULT)), ('analysis_sco_refs', ListProperty(ReferenceProperty(valid_types="SCO", spec_version='2.1'))), ('sample_ref', ReferenceProperty(valid_types="SCO", spec_version='2.1')), ('revoked', BooleanProperty(default=lambda: False)), @@ -607,17 +615,7 @@ class Opinion(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('explanation', StringProperty()), ('authors', ListProperty(StringProperty)), - ( - 'opinion', EnumProperty( - allowed=[ - 'strongly-disagree', - 'disagree', - 'neutral', - 'agree', - 'strongly-agree', - ], required=True, - ), - ), + ('opinion', EnumProperty(OPINION, required=True)), ('object_refs', ListProperty(ReferenceProperty(valid_types=["SCO", "SDO", "SRO"], spec_version='2.1'), required=True)), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), @@ -644,7 +642,7 @@ class Report(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('report_types', ListProperty(StringProperty)), + ('report_types', ListProperty(OpenVocabProperty(REPORT_TYPE))), ('published', TimestampProperty(required=True)), ('object_refs', ListProperty(ReferenceProperty(invalid_types=[], spec_version='2.1'), required=True)), ('revoked', BooleanProperty(default=lambda: False)), @@ -672,17 +670,17 @@ class ThreatActor(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('threat_actor_types', ListProperty(StringProperty)), + ('threat_actor_types', ListProperty(OpenVocabProperty(THREAT_ACTOR_TYPE))), ('aliases', ListProperty(StringProperty)), ('first_seen', TimestampProperty()), ('last_seen', TimestampProperty()), - ('roles', ListProperty(StringProperty)), + ('roles', ListProperty(OpenVocabProperty(THREAT_ACTOR_ROLE))), ('goals', ListProperty(StringProperty)), - ('sophistication', StringProperty()), - ('resource_level', StringProperty()), - ('primary_motivation', StringProperty()), - ('secondary_motivations', ListProperty(StringProperty)), - ('personal_motivations', ListProperty(StringProperty)), + ('sophistication', OpenVocabProperty(THREAT_ACTOR_SOPHISTICATION)), + ('resource_level', OpenVocabProperty(ATTACK_RESOURCE_LEVEL)), + ('primary_motivation', OpenVocabProperty(ATTACK_MOTIVATION)), + ('secondary_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))), + ('personal_motivations', ListProperty(OpenVocabProperty(ATTACK_MOTIVATION))), ('revoked', BooleanProperty(default=lambda: False)), ('labels', ListProperty(StringProperty)), ('confidence', IntegerProperty()), @@ -718,7 +716,7 @@ class Tool(_DomainObject): ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond', precision_constraint='min')), ('name', StringProperty(required=True)), ('description', StringProperty()), - ('tool_types', ListProperty(StringProperty)), + ('tool_types', ListProperty(OpenVocabProperty(TOOL_TYPE))), ('aliases', ListProperty(StringProperty)), ('kill_chain_phases', ListProperty(KillChainPhase)), ('tool_version', StringProperty()), diff --git a/stix2/v21/vocab.py b/stix2/v21/vocab.py new file mode 100644 index 0000000..c18c320 --- /dev/null +++ b/stix2/v21/vocab.py @@ -0,0 +1,452 @@ +""" +STIX 2.1 open vocabularies and enums +""" + +ACCOUNT_TYPE = [ + "facebook", + "ldap", + "nis", + "openid", + "radius", + "skype", + "tacacs", + "twitter", + "unix", + "windows-local", + "windows-domain", +] + + +ATTACK_MOTIVATION = [ + "accidental", + "coercion", + "dominance", + "ideology", + "notoriety", + "organizational-gain", + "personal-gain", + "personal-satisfaction", + "revenge", + "unpredictable", +] + + +ATTACK_RESOURCE_LEVEL = [ + "individual", + "club", + "contest", + "team", + "organization", + "government", +] + + +ENCRYPTION_ALGORITHM = [ + "AES-256-GCM", + "ChaCha20-Poly1305", + "mime-type-indicated", +] + + +GROUPING_CONTEXT = [ + "suspicious-activity", + "malware-analysis", + "unspecified", +] + + +HASHING_ALGORITHM = [ + "MD5", + "SHA-1", + "SHA-256", + "SHA-512", + "SHA3-256", + "SHA3-512", + "SSDEEP", + "TLSH", +] + + +IDENTITY_CLASS = [ + "individual", + "group", + "system", + "organization", + "class", + "unknown", +] + + +IMPLEMENTATION_LANGUAGE = [ + "applescript", + "bash", + "c", + "c++", + "c#", + "go", + "java", + "javascript", + "lua", + "objective-c", + "perl", + "php", + "powershell", + "python", + "ruby", + "scala", + "swift", + "typescript", + "visual-basic", + "x86-32", + "x86-64", +] + + +INDICATOR_TYPE = [ + "anomalous-activity", + "anonymization", + "benign", + "compromised", + "malicious-activity", + "attribution", + "unknown", +] + + +INDUSTRY_SECTOR = [ + "agriculture", + "aerospace", + "automotive", + "chemical", + "commercial", + "communications", + "construction", + "defense", + "education", + "energy", + "entertainment", + "financial-services", + "government", + "emergency-services", + "government-national", + "government-regional", + "government-local", + "government-public-services", + "healthcare", + "hospitality-leisure", + "infrastructure", + "dams", + "nuclear", + "water", + "insurance", + "manufacturing", + "mining", + "non-profit", + "pharmaceuticals", + "retail", + "technology", + "telecommunications", + "transportation", + "utilities", +] + + +INFRASTRUCTURE_TYPE = [ + "amplification", + "anonymization", + "botnet", + "command-and-control", + "exfiltration", + "hosting-malware", + "hosting-target-lists", + "phishing", + "reconnaissance", + "staging", + "unknown", +] + + +MALWARE_RESULT = [ + "malicious", + "suspicious", + "benign", + "unknown", +] + + +MALWARE_CAPABILITIES = [ + "accesses-remote-machines", + "anti-debugging", + "anti-disassembly", + "anti-emulation", + "anti-memory-forensics", + "anti-sandbox", + "anti-vm", + "captures-input-peripherals", + "captures-output-peripherals", + "captures-system-state-data", + "cleans-traces-of-infection", + "commits-fraud", + "communicates-with-c2", + "compromises-data-availability", + "compromises-data-integrity", + "compromises-system-availability", + "controls-local-machine", + "degrades-security-software", + "degrades-system-updates", + "determines-c2-server", + "emails-spam", + "escalates-privileges", + "evades-av", + "exfiltrates-data", + "fingerprints-host", + "hides-artifacts", + "hides-executing-code", + "infects-files", + "infects-remote-machines", + "installs-other-components", + "persists-after-system-reboot", + "prevents-artifact-access", + "prevents-artifact-deletion", + "probes-network-environment", + "self-modifies", + "steals-authentication-credentials", + "violates-system-operational-integrity", +] + + +MALWARE_TYPE = [ + "adware", + "backdoor", + "bot", + "bootkit", + "ddos", + "downloader", + "dropper", + "exploit-kit", + "keylogger", + "ransomware", + "remote-access-trojan", + "resource-exploitation", + "rogue-security-software", + "rootkit", + "screen-capture", + "spyware", + "trojan", + "unknown", + "virus", + "webshell", + "wiper", + "worm", +] + + +NETWORK_SOCKET_ADDRESS_FAMILY = [ + "AF_UNSPEC", + "AF_INET", + "AF_IPX", + "AF_APPLETALK", + "AF_NETBIOS", + "AF_INET6", + "AF_IRDA", + "AF_BTH", +] + + +NETWORK_SOCKET_TYPE = [ + "SOCK_STREAM", + "SOCK_DGRAM", + "SOCK_RAW", + "SOCK_RDM", + "SOCK_SEQPACKET", +] + + +OPINION = [ + "strongly-disagree", + "disagree", + "neutral", + "agree", + "strongly-agree", +] + + +PATTERN_TYPE = [ + "stix", + "pcre", + "sigma", + "snort", + "suricata", + "yara", +] + + +PROCESSOR_ARCHITECTURE = [ + "alpha", + "arm", + "ia-64", + "mips", + "powerpc", + "sparc", + "x86", + "x86-64", +] + + +REGION = [ + "africa", + "eastern-africa", + "middle-africa", + "northern-africa", + "southern-africa", + "western-africa", + "americas", + "latin-america-caribbean", + "south-america", + "caribbean", + "central-america", + "northern-america", + "asia", + "central-asia", + "eastern-asia", + "southern-asia", + "south-eastern-asia", + "western-asia", + "europe", + "eastern-europe", + "northern-europe", + "southern-europe", + "western-europe", + "oceania", + "antarctica", + "australia-new-zealand", + "melanesia", + "micronesia", + "polynesia", +] + + +REPORT_TYPE = [ + "attack-pattern", + "campaign", + "identity", + "indicator", + "intrusion-set", + "malware", + "observed-data", + "threat-actor", + "threat-report", + "tool", + "vulnerability", +] + + +THREAT_ACTOR_TYPE = [ + "activist", + "competitor", + "crime-syndicate", + "criminal", + "hacker", + "insider-accidental", + "insider-disgruntled", + "nation-state", + "sensationalist", + "spy", + "terrorist", + "unknown", +] + + +THREAT_ACTOR_ROLE = [ + "agent", + "director", + "independent", + "infrastructure-architect", + "infrastructure-operator", + "malware-author", + "sponsor", +] + + +THREAT_ACTOR_SOPHISTICATION = [ + "none", + "minimal", + "intermediate", + "advanced", + "expert", + "innovator", + "strategic", +] + + +TOOL_TYPE = [ + "denial-of-service", + "exploitation", + "information-gathering", + "network-capture", + "credential-exploitation", + "remote-access", + "vulnerability-scanning", + "unknown", +] + + +WINDOWS_INTEGRITY_LEVEL = [ + "low", + "medium", + "high", + "system", +] + + +WINDOWS_PEBINARY_TYPE = [ + "dll", + "exe", + "sys", +] + + +WINDOWS_REGISTRY_DATATYPE = [ + "REG_NONE", + "REG_SZ", + "REG_EXPAND_SZ", + "REG_BINARY", + "REG_DWORD", + "REG_DWORD_BIG_ENDIAN", + "REG_DWORD_LITTLE_ENDIAN", + "REG_LINK", + "REG_MULTI_SZ", + "REG_RESOURCE_LIST", + "REG_FULL_RESOURCE_DESCRIPTION", + "REG_RESOURCE_REQUIREMENTS_LIST", + "REG_QWORD", + "REG_INVALID_TYPE", +] + + +WINDOWS_SERVICE_START_TYPE = [ + "SERVICE_AUTO_START", + "SERVICE_BOOT_START", + "SERVICE_DEMAND_START", + "SERVICE_DISABLED", + "SERVICE_SYSTEM_ALERT", +] + + +WINDOWS_SERVICE_TYPE = [ + "SERVICE_KERNEL_DRIVER", + "SERVICE_FILE_SYSTEM_DRIVER", + "SERVICE_WIN32_OWN_PROCESS", + "SERVICE_WIN32_SHARE_PROCESS", +] + + +WINDOWS_SERVICE_STATUS = [ + "SERVICE_CONTINUE_PENDING", + "SERVICE_PAUSE_PENDING", + "SERVICE_PAUSED", + "SERVICE_RUNNING", + "SERVICE_START_PENDING", + "SERVICE_STOP_PENDING", + "SERVICE_STOPPED", +]