Repository: oasis-open/cti-python-stix2
Pull request #268 (2019-06-12 18:28:29)

Update stix2.1 course-of-action support to the latest spec.


Patch:
diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py
index 37699a6..8e5cdc2 100644
--- a/stix2/v21/sdo.py
+++ b/stix2/v21/sdo.py
@@ -97,6 +97,10 @@ class CourseOfAction(STIXDomainObject):
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
         ('description', StringProperty()),
+        ('action_type', StringProperty()),
+        ('os_execution_envs', ListProperty(StringProperty)),
+        ('action_bin', StringProperty()),
+        ('action_reference', StringProperty()),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
@@ -106,6 +110,14 @@ class CourseOfAction(STIXDomainObject):
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
+    def _check_object_constraints(self):
+        super(CourseOfAction, self)._check_object_constraints()
+
+        self._check_mutually_exclusive_properties(
+            ["action_bin", "action_reference"],
+            at_least_one=False,
+        )
+
 
 class Identity(STIXDomainObject):
     # TODO: Add link


Test patch:
diff --git a/stix2/test/v21/test_course_of_action.py b/stix2/test/v21/test_course_of_action.py
index 73e8eca..5354d59 100644
--- a/stix2/test/v21/test_course_of_action.py
+++ b/stix2/test/v21/test_course_of_action.py
@@ -4,6 +4,7 @@ import pytest
 import pytz
 
 import stix2
+import stix2.exceptions
 
 from .constants import COURSE_OF_ACTION_ID
 
@@ -15,7 +16,14 @@ EXPECTED = """{
     "created": "2016-04-06T20:03:48.000Z",
     "modified": "2016-04-06T20:03:48.000Z",
     "name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
-    "description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ..."
+    "description": "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...",
+    "action_type": "textual:text/plain",
+    "os_execution_envs": [
+        "a",
+        "b",
+        "c"
+    ],
+    "action_bin": "aGVsbG8gd29ybGQ="
 }"""
 
 
@@ -27,6 +35,9 @@ def test_course_of_action_example():
         modified="2016-04-06T20:03:48.000Z",
         name="Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
         description="This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ...",
+        action_type="textual:text/plain",
+        os_execution_envs=["a", "b", "c"],
+        action_bin="aGVsbG8gd29ybGQ=",
     )
 
     assert str(coa) == EXPECTED
@@ -44,6 +55,9 @@ def test_course_of_action_example():
             "name": "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
             "spec_version": "2.1",
             "type": "course-of-action",
+            "action_type": "textual:text/plain",
+            "os_execution_envs": ["a", "b", "c"],
+            "action_bin": "aGVsbG8gd29ybGQ=",
         },
     ],
 )
@@ -58,5 +72,20 @@ def test_parse_course_of_action(data):
     assert coa.created_by_ref == "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff"
     assert coa.description == "This is how to add a filter rule to block inbound access to TCP port 80 to the existing UDP 1434 filter ..."
     assert coa.name == "Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter"
+    assert coa.action_type == "textual:text/plain"
+    assert coa.os_execution_envs == ["a", "b", "c"]
+    assert coa.action_bin == "aGVsbG8gd29ybGQ="
+
+
+def test_course_of_action_constraint():
+    with pytest.raises(stix2.exceptions.MutuallyExclusivePropertiesError):
+        stix2.v21.CourseOfAction(
+            name="Add TCP port 80 Filter Rule to the existing Block UDP 1434 Filter",
+            action_bin="aGVsbG8gd29ybGQ=",
+            action_reference=stix2.v21.ExternalReference(
+                source_name="a source",
+                description="description of a source",
+            ),
+        )
 
 # TODO: Add other examples