Repository: oasis-open/cti-python-stix2
Pull request #7 (2017-05-09 19:32:49)

Cyber observables
Not sure where the conflict(s) are


Patch:
diff --git a/stix2/base.py b/stix2/base.py
index c3a8944..a99805c 100644
--- a/stix2/base.py
+++ b/stix2/base.py
@@ -47,6 +47,12 @@ class _STIXBase(collections.Mapping):
             except ValueError as exc:
                 raise InvalidValueError(self.__class__, prop_name, reason=str(exc))
 
+    def _check_object_constaints(self):
+        if self.granular_markings:
+            for m in self.granular_markings:
+                # TODO: check selectors
+                pass
+
     def __init__(self, **kwargs):
         cls = self.__class__
 
@@ -75,10 +81,7 @@ class _STIXBase(collections.Mapping):
 
         self._inner = setting_kwargs
 
-        if self.granular_markings:
-            for m in self.granular_markings:
-                # TODO: check selectors
-                pass
+        self._check_object_constaints()
 
     def __getitem__(self, key):
         return self._inner[key]
@@ -114,6 +117,9 @@ class _STIXBase(collections.Mapping):
         cls = type(self)
         return cls(**new_inner)
 
+    def properties_populated(self):
+        return list(self._inner.keys())
+
 #  Versioning API
 
     def new_version(self, **kwargs):
diff --git a/stix2/exceptions.py b/stix2/exceptions.py
index fdd3a46..2b0cb7a 100644
--- a/stix2/exceptions.py
+++ b/stix2/exceptions.py
@@ -60,7 +60,7 @@ class DictionaryKeyError(STIXError, ValueError):
         self.reason = reason
 
     def __str__(self):
-        msg = "Invliad dictionary key {0.key}: ({0.reason})."
+        msg = "Invalid dictionary key {0.key}: ({0.reason})."
         return msg.format(self)
 
 
@@ -90,6 +90,20 @@ class UnmodifiablePropertyError(STIXError, ValueError):
         return msg.format(", ".join(self.unchangable_properties))
 
 
+class ObjectConstraintError(STIXError, TypeError):
+    """Violating some interproperty constraint of a STIX object type."""
+
+    def __init__(self, cls, fields):
+        super(ObjectConstraintError, self).__init__()
+        self.cls = cls
+        self.fields = sorted(list(fields))
+
+    def __str__(self):
+        msg = "The field(s) for {0}: ({1}) are not consistent."
+        return msg.format(self.cls.__name__,
+                          ", ".join(x for x in self.fields))
+
+
 class RevokeError(STIXError, ValueError):
     """Attempted to an operation on a revoked object"""
 
diff --git a/stix2/observables.py b/stix2/observables.py
index 3a5a49f..67ec7bb 100644
--- a/stix2/observables.py
+++ b/stix2/observables.py
@@ -6,6 +6,7 @@ and do not have a '_type' attribute.
 """
 
 from .base import Observable, _STIXBase
+from .exceptions import ObjectConstraintError
 from .properties import (BinaryProperty, BooleanProperty, DictionaryProperty,
                          EmbeddedObjectProperty, HashesProperty, HexProperty,
                          IntegerProperty, ListProperty,
@@ -114,12 +115,24 @@ class File(Observable):
         'accessed': TimestampProperty(),
         'parent_directory_ref': ObjectReferenceProperty(),
         'is_encrypted': BooleanProperty(),
-        'encyption_algorithm': StringProperty(),
+        'encryption_algorithm': StringProperty(),
         'decryption_key': StringProperty(),
         'contains_refs': ListProperty(ObjectReferenceProperty),
         'content_ref': ObjectReferenceProperty(),
     }
 
+    def _check_object_constaints(self):
+        super(File, self)._check_object_constaints()
+        illegal_properties = []
+        current_properties = self.properties_populated()
+        if not self.is_encrypted:
+            for p in ["encryption_algorithm", "decryption_key"]:
+                if p in current_properties:
+                    illegal_properties.append(p)
+        if illegal_properties:
+            illegal_properties.append("is_encrypted")
+            raise ObjectConstraintError(self.__class__, illegal_properties)
+
 
 class IPv4Address(Observable):
     _type = 'ipv4-addr'
@@ -259,7 +272,7 @@ class WindowsRegistryKey(Observable):
     _properties = {
         'type': TypeProperty(_type),
         'key': StringProperty(required=True),
-        'values': ListProperty(WindowsRegistryValueType),
+        'values': ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType)),
         # this is not the modified timestamps of the object itself
         'modified': TimestampProperty(),
         'creator_user_ref': ObjectReferenceProperty(),


Test patch:
diff --git a/stix2/test/test_observed_data.py b/stix2/test/test_observed_data.py
index fbafb53..80a5b14 100644
--- a/stix2/test/test_observed_data.py
+++ b/stix2/test/test_observed_data.py
@@ -328,7 +328,7 @@ def test_file_example():
                    modified="2016-12-24T19:00:00Z",
                    accessed="2016-12-21T20:00:00Z",
                    is_encrypted=True,
-                   encyption_algorithm="AES128-CBC",
+                   encryption_algorithm="AES128-CBC",
                    decryption_key="fred"
                    )
 
@@ -341,19 +341,19 @@ def test_file_example():
     assert f.modified == dt.datetime(2016, 12, 24, 19, 0, 0, tzinfo=pytz.utc)
     assert f.accessed == dt.datetime(2016, 12, 21, 20, 0, 0, tzinfo=pytz.utc)
     assert f.is_encrypted
-    assert f.encyption_algorithm == "AES128-CBC"
+    assert f.encryption_algorithm == "AES128-CBC"
     assert f.decryption_key == "fred"   # does the key have a format we can test for?
 
 
-# def test_file_example_encyption_error():
-#     f = stix2.File(name="qwerty.dll",
-#                    is_encrypted=False,
-#                    encyption_algorithm="AES128-CBC"
-#                    )
-#
-#     assert f.name == "qwerty.dll"
-#     assert f.is_encrypted == False
-#     assert f.encyption_algorithm == "AES128-CBC"
+def test_file_example_encryption_error():
+    with pytest.raises(stix2.exceptions.ObjectConstraintError) as excinfo:
+        stix2.File(name="qwerty.dll",
+                   is_encrypted=False,
+                   encryption_algorithm="AES128-CBC"
+                   )
+
+    assert excinfo.value.cls == stix2.File
+    assert excinfo.value.fields == ["encryption_algorithm", "is_encrypted"]
 
 
 def test_ip4_address_example():