Repository: oasis-open/cti-python-stix2
Pull request #400 (2020-05-26 19:16:21)

fixed start/stop qualifier constant issue


Patch:
diff --git a/stix2/pattern_visitor.py b/stix2/pattern_visitor.py
index 317ffa1..4ec2b20 100644
--- a/stix2/pattern_visitor.py
+++ b/stix2/pattern_visitor.py
@@ -40,6 +40,11 @@ def remove_terminal_nodes(parse_tree_nodes):
     return values
 
 
+_TIMESTAMP_RE = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?Z')
+
+
+def check_for_valid_timetamp_syntax(timestamp_string):
+    return _TIMESTAMP_RE.match(timestamp_string)
 
 
 
@@ -214,6 +219,14 @@ class STIXPatternVisitorForSTIX2():
     # Visit a parse tree produced by STIXPatternParser#startStopQualifier.
     def visitStartStopQualifier(self, ctx):
         children = self.visitChildren(ctx)
+        # 2.0 parser will accept any string, need to make sure it is a full STIX timestamp
+        if isinstance(children[1], StringConstant):
+            if not check_for_valid_timetamp_syntax(children[1].value):
+                raise (ValueError("Start time is not a legal timestamp"))
+        if isinstance(children[3], StringConstant):
+            if not check_for_valid_timetamp_syntax(children[3].value):
+                raise (ValueError("Stop time is not a legal timestamp"))
+
         return StartStopQualifier(children[1], children[3])
 
     # Visit a parse tree produced by STIXPatternParser#withinQualifier.
diff --git a/stix2/patterns.py b/stix2/patterns.py
index 6592335..5d7c0a2 100644
--- a/stix2/patterns.py
+++ b/stix2/patterns.py
@@ -669,12 +669,16 @@ class StartStopQualifier(_ExpressionQualifier):
             self.start_time = start_time
         elif isinstance(start_time, datetime.date):
             self.start_time = TimestampConstant(start_time)
+        elif isinstance(start_time, StringConstant):
+            self.start_time = StringConstant(start_time.value)
         else:
             raise ValueError("%s is not a valid argument for a Start/Stop Qualifier" % start_time)
         if isinstance(stop_time, TimestampConstant):
             self.stop_time = stop_time
         elif isinstance(stop_time, datetime.date):
             self.stop_time = TimestampConstant(stop_time)
+        elif isinstance(stop_time, StringConstant):
+            self.stop_time = StringConstant(stop_time.value)
         else:
             raise ValueError("%s is not a valid argument for a Start/Stop Qualifier" % stop_time)
 


Test patch:
diff --git a/stix2/test/v20/test_pattern_expressions.py b/stix2/test/v20/test_pattern_expressions.py
index d5cbb5b..a96d3b8 100644
--- a/stix2/test/v20/test_pattern_expressions.py
+++ b/stix2/test/v20/test_pattern_expressions.py
@@ -312,8 +312,8 @@ def test_set_op():
 
 
 def test_timestamp():
-    ts = stix2.TimestampConstant('2014-01-13T07:03:17Z')
-    assert str(ts) == "t'2014-01-13T07:03:17Z'"
+    ts = stix2.StringConstant('2014-01-13T07:03:17Z')
+    assert str(ts) == "'2014-01-13T07:03:17Z'"
 
 
 def test_boolean():
@@ -363,11 +363,6 @@ def test_invalid_integer_constant():
         stix2.IntegerConstant('foo')
 
 
-def test_invalid_timestamp_constant():
-    with pytest.raises(ValueError):
-        stix2.TimestampConstant('foo')
-
-
 def test_invalid_float_constant():
     with pytest.raises(ValueError):
         stix2.FloatConstant('foo')
@@ -461,23 +456,23 @@ def test_invalid_within_qualifier():
 
 def test_startstop_qualifier():
     qual = stix2.StartStopQualifier(
-        stix2.TimestampConstant('2016-06-01T00:00:00Z'),
-        datetime.datetime(2017, 3, 12, 8, 30, 0),
+        stix2.StringConstant('2016-06-01T00:00:00Z'),
+        stix2.StringConstant('2017-03-12T08:30:00Z'),
     )
-    assert str(qual) == "START t'2016-06-01T00:00:00Z' STOP t'2017-03-12T08:30:00Z'"
+    assert str(qual) == "START '2016-06-01T00:00:00Z' STOP '2017-03-12T08:30:00Z'"
 
     qual2 = stix2.StartStopQualifier(
-        datetime.date(2016, 6, 1),
-        stix2.TimestampConstant('2016-07-01T00:00:00Z'),
+        stix2.StringConstant("2016-06-01T00:00:00Z"),
+        stix2.StringConstant('2016-07-01T00:00:00Z'),
     )
-    assert str(qual2) == "START t'2016-06-01T00:00:00Z' STOP t'2016-07-01T00:00:00Z'"
+    assert str(qual2) == "START '2016-06-01T00:00:00Z' STOP '2016-07-01T00:00:00Z'"
 
 
 def test_invalid_startstop_qualifier():
     with pytest.raises(ValueError):
         stix2.StartStopQualifier(
             'foo',
-            stix2.TimestampConstant('2016-06-01T00:00:00Z'),
+            stix2.StringConstant('2016-06-01T00:00:00Z'),
         )
 
     with pytest.raises(ValueError):
@@ -508,6 +503,19 @@ def test_parsing_qualified_expression():
     ) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS"
 
 
+def test_parsing_start_stop_qualified_expression():
+    patt_obj = create_pattern_object("[ipv4-addr:value = '1.2.3.4'] START '2016-06-01T00:00:00Z' STOP '2017-03-12T08:30:00Z'", version="2.0")
+
+    assert str(
+        patt_obj,
+    ) == "[ipv4-addr:value = '1.2.3.4'] START '2016-06-01T00:00:00Z' STOP '2017-03-12T08:30:00Z'"
+
+
+def test_parsing_illegal_start_stop_qualified_expression():
+    with pytest.raises(ValueError):
+        create_pattern_object("[ipv4-addr:value = '1.2.3.4'] START '2016-06-01' STOP '2017-03-12T08:30:00Z'", version="2.0")
+
+
 def test_list_constant():
     patt_obj = create_pattern_object("[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]", version="2.0")
     assert str(patt_obj) == "[network-traffic:src_ref.value IN ('10.0.0.0', '10.0.0.1', '10.0.0.2')]"