diff --git a/cybox/objects/artifact_object.py b/cybox/objects/artifact_object.py index 9e440e85..c5393828 100644 --- a/cybox/objects/artifact_object.py +++ b/cybox/objects/artifact_object.py @@ -23,11 +23,27 @@ def validate_artifact_type(instance, value): raise ValueError(err) +def validate_byte_order_endianness(instance, value): + if value is None: + return + elif value in RawArtifact.ENDIANNESS: + return + else: + err = "Type must be one of %s. Received '%s'." % (RawArtifact.ENDIANNESS, value) + raise ValueError(err) + + class RawArtifact(String): - _binding_class = artifact_binding.RawArtifactType + _binding = artifact_binding + _binding_class = _binding.RawArtifactType _namespace = 'http://cybox.mitre.org/objects#ArtifactObject-2' - byte_order = fields.TypedField("byte_order") + BIG_ENDIAN = "Big-endian" + LITTLE_ENDIAN = "Little-endian" + MIDDLE_ENDIAN = "Middle-endian" + ENDIANNESS = (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN) + + byte_order = fields.TypedField("byte_order", postset_hook=validate_byte_order_endianness) class Packaging(entities.Entity): @@ -47,7 +63,8 @@ def unpack(self, packed_data): class Artifact(ObjectProperties): # Warning: Do not attempt to get or set Raw_Artifact directly. Use `data` - # or `packed_data` respectively. Raw_Artifact will be set on export. + # or `packed_data` respectively. The Raw_Artifact value will be set on + # export. You can set BaseObjectProperties or PatternFieldGroup attributes. _binding = artifact_binding _binding_class = _binding.ArtifactObjectType _namespace = 'http://cybox.mitre.org/objects#ArtifactObject-2' @@ -62,7 +79,8 @@ class Artifact(ObjectProperties): TYPES = (TYPE_FILE, TYPE_FILE_SYSTEM, TYPE_GENERIC, TYPE_MEMORY, TYPE_NETWORK) hashes = fields.TypedField("Hashes", HashList) - # packaging = fields.TypedField("Packaging", Packaging, multiple=True) # TODO: Support this as a TypedField + # TODO: Support packaging as a TypedField + # packaging = fields.TypedField("Packaging", Packaging, multiple=True) type_ = fields.TypedField("type_", key_name="type", preset_hook=validate_artifact_type) content_type = fields.TypedField("content_type") content_type_version = fields.TypedField("content_type_version") @@ -94,6 +112,7 @@ def __init__(self, data=None, type_=None): # for `data` has access to this attribute. self._packed_data = None self.data = data + self.raw_artifact = RawArtifact() @property def data(self): @@ -159,7 +178,8 @@ def to_obj(self, ns_info=None): artifact_obj.Packaging = packaging if self.packed_data: - artifact_obj.Raw_Artifact = RawArtifact(self.packed_data).to_obj(ns_info=ns_info) + self.raw_artifact.value = self.packed_data + artifact_obj.Raw_Artifact = self.raw_artifact.to_obj(ns_info=ns_info) return artifact_obj @@ -169,7 +189,8 @@ def to_dict(self): if self.packaging: artifact_dict['packaging'] = [p.to_dict() for p in self.packaging] if self.packed_data: - artifact_dict['raw_artifact'] = RawArtifact(self.packed_data).to_dict() + self.raw_artifact.value = self.packed_data + artifact_dict['raw_artifact'] = self.raw_artifact.to_dict() return artifact_dict @@ -179,7 +200,7 @@ def from_obj(cls, cls_obj): return None artifact = super(Artifact, cls).from_obj(cls_obj) - + packaging = cls_obj.Packaging if packaging: for c in packaging.Compression: @@ -191,8 +212,8 @@ def from_obj(cls, cls_obj): raw_artifact = cls_obj.Raw_Artifact if raw_artifact: - data = RawArtifact.from_obj(raw_artifact).value - artifact.packed_data = six.text_type(data) + artifact.raw_artifact = RawArtifact.from_obj(raw_artifact) + artifact.packed_data = six.text_type(artifact.raw_artifact.value) return artifact @@ -202,7 +223,7 @@ def from_dict(cls, cls_dict): return None artifact = super(Artifact, cls).from_dict(cls_dict) - + for layer in cls_dict.get('packaging', []): if layer.get('packaging_type') == "compression": artifact.packaging.append(CompressionFactory.from_dict(layer)) @@ -213,8 +234,8 @@ def from_dict(cls, cls_dict): raw_artifact = cls_dict.get('raw_artifact') if raw_artifact: - data = RawArtifact.from_dict(raw_artifact).value - artifact.packed_data = six.text_type(data) + artifact.raw_artifact = RawArtifact.from_dict(raw_artifact) + artifact.packed_data = six.text_type(artifact.raw_artifact.value) return artifact @@ -232,9 +253,10 @@ class Compression(Packaging): compression_mechanism = fields.TypedField("compression_mechanism") compression_mechanism_ref = fields.TypedField("compression_mechanism_ref") - def __init__(self, compression_mechanism=None): + def __init__(self, compression_mechanism=None, compression_mechanism_ref=None): super(Compression, self).__init__() self.compression_mechanism = compression_mechanism + self.compression_mechanism_ref = compression_mechanism_ref def to_dict(self): dict_ = super(Compression, self).to_dict() @@ -244,7 +266,7 @@ def to_dict(self): class ZlibCompression(Compression): def __init__(self): - super(ZlibCompression, self).__init__("zlib") + super(ZlibCompression, self).__init__(compression_mechanism="zlib") def pack(self, data): return zlib.compress(data) @@ -256,7 +278,7 @@ def unpack(self, packed_data): class Bz2Compression(Compression): def __init__(self): - super(Bz2Compression, self).__init__("bz2") + super(Bz2Compression, self).__init__(compression_mechanism="bz2") def pack(self, data): return bz2.compress(data) @@ -278,10 +300,13 @@ class Encryption(Packaging): encryption_key = fields.TypedField("encryption_key") encryption_key_ref = fields.TypedField("encryption_key_ref") - def __init__(self, encryption_mechanism=None, encryption_key=None): + def __init__(self, encryption_mechanism=None, encryption_key=None, + encryption_mechanism_ref=None, encryption_key_ref=None): super(Encryption, self).__init__() self.encryption_mechanism = encryption_mechanism self.encryption_key = encryption_key + self.encryption_mechanism_ref = encryption_mechanism_ref + self.encryption_key_ref = encryption_key_ref def to_dict(self): dict_ = super(Encryption, self).to_dict() @@ -292,7 +317,10 @@ def to_dict(self): class XOREncryption(Encryption): def __init__(self, key=None): - super(XOREncryption, self).__init__("xor", key) + super(XOREncryption, self).__init__( + encryption_mechanism="xor", + encryption_key=key + ) def pack(self, data): return xor(data, self.encryption_key) @@ -303,7 +331,10 @@ def unpack(self, packed_data): class PasswordProtectedZipEncryption(Encryption): def __init__(self, key=None): - super(PasswordProtectedZipEncryption, self).__init__("PasswordProtected", key) + super(PasswordProtectedZipEncryption, self).__init__( + encryption_mechanism="PasswordProtected", + encryption_key=key + ) # `pack` is not implemented @@ -330,6 +361,14 @@ class Encoding(Packaging): _binding_class = _binding.EncodingType algorithm = fields.TypedField("algorithm") + character_set = fields.TypedField("character_set") + custom_character_set_ref = fields.TypedField("custom_character_set_ref") + + def __init__(self, algorithm=None, character_set=None, custom_character_set_ref=None): + super(Encoding, self).__init__() + self.algorithm = algorithm + self.character_set = character_set + self.custom_character_set_ref = custom_character_set_ref def to_dict(self): dict_ = super(Encoding, self).to_dict() @@ -351,14 +390,14 @@ class EncryptionFactory(entities.EntityFactory): def entity_class(cls, key): if key == "xor": return XOREncryption - elif key == 'PasswordProtected': + elif key == "PasswordProtected": return PasswordProtectedZipEncryption else: raise ValueError("Unsupported encryption mechanism: %s" % key) @classmethod def dictkey(cls, mapping): - return mapping.get('encryption_mechanism') + return mapping.get("encryption_mechanism") @classmethod def objkey(cls, obj): @@ -377,7 +416,7 @@ def entity_class(cls, key): @classmethod def dictkey(cls, mapping): - return mapping.get('compression_mechanism') + return mapping.get("compression_mechanism") @classmethod def objkey(cls, obj): @@ -394,7 +433,7 @@ def entity_class(cls, key): @classmethod def dictkey(cls, mapping): - return mapping.get('algorithm', "Base64") # default is Base64 + return mapping.get("algorithm", "Base64") # default is Base64 @classmethod def objkey(cls, obj): diff --git a/cybox/test/objects/artifact_test.py b/cybox/test/objects/artifact_test.py index 96ff04f4..4292dd2c 100644 --- a/cybox/test/objects/artifact_test.py +++ b/cybox/test/objects/artifact_test.py @@ -135,6 +135,127 @@ def test_encryption(self): self.assertEqual(self.binary_data, a2.data) +class TestArtifactInstance(ObjectTestCase, unittest.TestCase): + object_type = "ArtifactObjectType" + klass = Artifact + + _full_dict = { + "packaging": [ + { + "packaging_type": "encoding", + "algorithm": "Base64" + } + ], + "xsi:type": object_type, + "raw_artifact": "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" + "AlgAECABBPgAAAACAAAAAAAAAA3ADAABAAEAAAJYAATMmLhYwWhKQrQ/CwBSAAAA" + "UgAAAADAnzJBjADgGLEMrQgARQAARAAAQABAEWU7wKiqCMCoqhSAHAA1ADACNVpT" + "AQAAAQAAAAAAAAExATABMAMxMjcHaW4tYWRkcgRhcnBhAAAMAAHBaEpCAEILAGkA" + "AABpAAAAAOAYsQytAMCfMkGMCABFAABb2PUAAIARjC7AqKoUwKiqCAA1gBwAR/kw" + "WlOFgAABAAEAAAAAATEBMAEwAzEyNwdpbi1hZGRyBGFycGEAAAwAAcAMAAwAAQAA" + "DhAACwlsb2NhbGhvc3QAwWhKQkZLCwBDAAAAQwAAAADAnzJBjADgGLEMrQgARQAA" + "NQAAQABAEWVKwKiqCMCoqhSAHQA1ACGYvSCKAQAAAQAAAAAAAANpc2MDb3JnAAAC" + "AAHBaEpC2ogLAIEAAACBAAAAABKpADIjAGAIReRVCABFAABzh94AAIARapXAqKo4" + "2Q0EGAarADUAXznwMm4BAAABAAAAAAAABV9sZGFwBF90Y3AXRGVmYXVsdC1GaXJz" + "dC1TaXRlLU5hbWUGX3NpdGVzAmRjBl9tc2Rjcwt1dGVsc3lzdGVtcwVsb2NhbAAA" + "IQABwWhKQrWSCwCmAAAApgAAAADgGLEMrQDAnzJBjAgARQAAmNj3AACAEYvvwKiq" + "FMCoqggANYAdAIR72CCKgYAAAQAEAAAAAANpc2MDb3JnAAACAAHADAACAAEAAA4Q" + "AA4GbnMtZXh0BG5ydDHADMAMAAIAAQAADhAADgZucy1leHQEc3RoMcAMwAwAAgAB" + "AAAOEAAJBm5zLWV4dMAMwAwAAgABAAAOEAAOBm5zLWV4dARsZ2ExwAzBaEpCPdYL" + "AIEAAACBAAAAAGAIReRVABKpADIjCABFAABzAABAADoR+HPZDQQYwKiqOAA1BqsA" + "X7VsMm6FgwABAAAAAAAABV9sZGFwBF90Y3AXRGVmYXVsdC1GaXJzdC1TaXRlLU5h" + "bWUGX3NpdGVzAmRjBl9tc2Rjcwt1dGVsc3lzdGVtcwVsb2NhbAAAIQABwWhKQszY" + "CwBiAAAAYgAAAAASqQAyIwBgCEXkVQgARQAAVIfwAACAEWqiwKiqONkNBBgGrAA1" + "AEB8UfFhAQAAAQAAAAAAAAVfbGRhcARfdGNwAmRjBl9tc2Rjcwt1dGVsc3lzdGVt" + "cwVsb2NhbAAAIQABwWhKQmEcDABiAAAAYgAAAABgCEXkVQASqQAyIwgARQAAVAAA" + "QAA6EfiS2Q0EGMCoqjgANQasAED3zfFhhYMAAQAAAAAAAAVfbGRhcARfdGNwAmRj" + "Bl9tc2Rjcwt1dGVsc3lzdGVtcwVsb2NhbAAAIQABwWhKQoAeDACMAAAAjAAAAAAS" + "qQAyIwBgCEXkVQgARQAAfofxAACAEWp3wKiqONkNBBgGrQA1AGp3mINhAQAAAQAA" + "AAAAAAVfbGRhcARfdGNwJDA1YjUyOTJiLTM0YjgtNGZiNy04NWEzLThiZWVmNWZk" + "MjA2OQdkb21haW5zBl9tc2Rjcwt1dGVsc3lzdGVtcwVsb2NhbAAAIQABwWhKQmRr" + "DACMAAAAjAAAAABgCEXkVQASqQAyIwgARQAAfgAAQAA6Efho2Q0EGMCoqjgANQat" + "AGrzFINhhYMAAQAAAAAAAAVfbGRhcARfdGNwJDA1YjUyOTJiLTM0YjgtNGZiNy04" + "NWEzLThiZWVmNWZkMjA2OQdkb21haW5zBl9tc2Rjcwt1dGVsc3lzdGVtcwVsb2Nh" + "bAAAIQABwWhKQvn4DQBTAAAAUwAAAAASqQAyIwBgCEXkVQgARQAARYf1AACAEWqs" + "wKiqONkNBBgGrgA1ADEajdBgAQAAAQAAAAAAAAVHUklNTQt1dGVsc3lzdGVtcwVs" + "b2NhbAAAAQABwWhKQhU7DgBTAAAAUwAAAABgCEXkVQASqQAyIwgARQAARQAAQAA6" + "Efih2Q0EGMCoqjgANQauADGWCdBghYMAAQAAAAAAAAVHUklNTQt1dGVsc3lzdGVt" + "cwVsb2NhbAAAAQAByWhKQuJzBQBTAAAAUwAAAAASqQAyIwBgCEXkVQgARQAARYf7" + "AACAEWqmwKiqONkNBBgGrwA1ADF0iXZjAQAAAQAAAAAAAAVHUklNTQt1dGVsc3lz" + "dGVtcwVsb2NhbAAAAQAByWhKQj+6BQBTAAAAUwAAAABgCEXkVQASqQAyIwgARQAA" + "RQAAQAA6Efih2Q0EGMCoqjgANQavADHwBXZjhYMAAQAAAAAAAAVHUklNTQt1dGVs" + "c3lzdGVtcwVsb2NhbAAAAQAB", + "type": "Network Traffic" + } + + +class TestArtifactPattern(ObjectTestCase, unittest.TestCase): + object_type = "ArtifactObjectType" + klass = Artifact + + _full_dict = { + "xsi:type": object_type, + "raw_artifact": { + "value": "777777076578616D706C6503636F6D", + "condition": "Contains" + }, + "type": "Network Traffic" + } + + def _get_data(artifact): return artifact.data