From 0fc2befd6ad82ceffb4c4bcf190bf96a99f5cf53 Mon Sep 17 00:00:00 2001 From: Rich Piazza Date: Sat, 25 Jul 2020 14:22:03 -0400 Subject: [PATCH 1/3] hack for issue_435 --- stix2/pattern_visitor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stix2/pattern_visitor.py b/stix2/pattern_visitor.py index c4deb641..10f70bba 100644 --- a/stix2/pattern_visitor.py +++ b/stix2/pattern_visitor.py @@ -259,6 +259,9 @@ def visitObjectPath(self, ctx): if isinstance(next, TerminalNode): property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.getText())) i += 2 + if isinstance(next, IntegerConstant): + property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.value)) + i += 2 else: property_path.append(current) i += 1 From b7a30befdcbb9a9c7dc93243cdee21eb695f4d2b Mon Sep 17 00:00:00 2001 From: Rich Piazza Date: Sat, 25 Jul 2020 14:47:40 -0400 Subject: [PATCH 2/3] add tests and fix introduced bug --- stix2/pattern_visitor.py | 2 +- stix2/test/v20/test_pattern_expressions.py | 9 +++++++-- stix2/test/v21/test_pattern_expressions.py | 5 +++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/stix2/pattern_visitor.py b/stix2/pattern_visitor.py index 10f70bba..6da41b5d 100644 --- a/stix2/pattern_visitor.py +++ b/stix2/pattern_visitor.py @@ -259,7 +259,7 @@ def visitObjectPath(self, ctx): if isinstance(next, TerminalNode): property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.getText())) i += 2 - if isinstance(next, IntegerConstant): + elif isinstance(next, IntegerConstant): property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.value)) i += 2 else: diff --git a/stix2/test/v20/test_pattern_expressions.py b/stix2/test/v20/test_pattern_expressions.py index 0e0a9ca5..cca327b1 100644 --- a/stix2/test/v20/test_pattern_expressions.py +++ b/stix2/test/v20/test_pattern_expressions.py @@ -512,15 +512,20 @@ def test_parsing_start_stop_qualified_expression(): def test_parsing_mixed_boolean_expression_1(): - patt_obj = create_pattern_object("[a:b = 1 AND a:b = 2 OR a:b = 3]",) + patt_obj = create_pattern_object("[a:b = 1 AND a:b = 2 OR a:b = 3]") assert str(patt_obj) == "[a:b = 1 AND a:b = 2 OR a:b = 3]" def test_parsing_mixed_boolean_expression_2(): - patt_obj = create_pattern_object("[a:b = 1 OR a:b = 2 AND a:b = 3]",) + patt_obj = create_pattern_object("[a:b = 1 OR a:b = 2 AND a:b = 3]") assert str(patt_obj) == "[a:b = 1 OR a:b = 2 AND a:b = 3]" +def test_parsing_integer_index(): + patt_obj = create_pattern_object("[a:b[1]=2]") + assert str(patt_obj) == "[a:b[1] = 2]" + + 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") diff --git a/stix2/test/v21/test_pattern_expressions.py b/stix2/test/v21/test_pattern_expressions.py index b574e059..56273c00 100644 --- a/stix2/test/v21/test_pattern_expressions.py +++ b/stix2/test/v21/test_pattern_expressions.py @@ -654,6 +654,11 @@ def test_parsing_mixed_boolean_expression_2(): assert str(patt_obj) == "[a:b = 1 OR a:b = 2 AND a:b = 3]" +def test_parsing_integer_index(): + patt_obj = create_pattern_object("[a:b[1]=2]") + assert str(patt_obj) == "[a:b[1] = 2]" + + def test_parsing_multiple_slashes_quotes(): patt_obj = create_pattern_object("[ file:name = 'weird_name\\'' ]", version="2.1") assert str(patt_obj) == "[file:name = 'weird_name\\'']" From 8f76a84bbfb5ea91bd97b96f2bccae02e3dd0f85 Mon Sep 17 00:00:00 2001 From: Rich Piazza Date: Thu, 30 Jul 2020 15:32:06 -0400 Subject: [PATCH 3/3] handle quoted path components --- stix2/pattern_visitor.py | 18 +++++++++++++----- stix2/patterns.py | 5 ++++- stix2/test/v20/test_pattern_expressions.py | 11 +++++++++++ stix2/test/v21/test_pattern_expressions.py | 10 ++++++++++ 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/stix2/pattern_visitor.py b/stix2/pattern_visitor.py index 6da41b5d..a9d43c50 100644 --- a/stix2/pattern_visitor.py +++ b/stix2/pattern_visitor.py @@ -2,6 +2,7 @@ import importlib import inspect +from six import text_type from stix2patterns.exceptions import ParseException from stix2patterns.grammars.STIXPatternParser import TerminalNode @@ -50,7 +51,7 @@ def check_for_valid_timetamp_syntax(timestamp_string): def same_boolean_operator(current_op, op_token): - return current_op == op_token.symbol.text + return current_op == op_token.getText() class STIXPatternVisitorForSTIX2(): @@ -260,7 +261,9 @@ def visitObjectPath(self, ctx): property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.getText())) i += 2 elif isinstance(next, IntegerConstant): - property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.value)) + property_path.append(self.instantiate("ListObjectPathComponent", + current.property_name if isinstance(current, BasicObjectPathComponent) else text_type(current), + next.value)) i += 2 else: property_path.append(current) @@ -275,7 +278,12 @@ def visitObjectType(self, ctx): # Visit a parse tree produced by STIXPatternParser#firstPathComponent. def visitFirstPathComponent(self, ctx): children = self.visitChildren(ctx) - step = children[0].getText() + first_component = children[0] + # hack for when the first component isn't a TerminalNode (see issue #438) + if isinstance(first_component, TerminalNode): + step = first_component.getText() + else: + step = text_type(first_component) # if step.endswith("_ref"): # return stix2.ReferenceObjectPathComponent(step) # else: @@ -294,8 +302,8 @@ def visitPathStep(self, ctx): def visitKeyPathStep(self, ctx): children = self.visitChildren(ctx) if isinstance(children[1], StringConstant): - # special case for hashes - return children[1].value + # special case for hashes and quoted steps + return children[1] else: return self.instantiate("BasicObjectPathComponent", children[1].getText(), True) diff --git a/stix2/patterns.py b/stix2/patterns.py index edcf0170..bbee7ac8 100644 --- a/stix2/patterns.py +++ b/stix2/patterns.py @@ -248,7 +248,10 @@ def make_constant(value): class _ObjectPathComponent(object): @staticmethod def create_ObjectPathComponent(component_name): - if component_name.endswith("_ref"): + # first case is to handle if component_name was quoted + if isinstance(component_name, StringConstant): + return BasicObjectPathComponent(component_name.value, False) + elif component_name.endswith("_ref"): return ReferenceObjectPathComponent(component_name) elif component_name.find("[") != -1: parse1 = component_name.split("[") diff --git a/stix2/test/v20/test_pattern_expressions.py b/stix2/test/v20/test_pattern_expressions.py index cca327b1..fa9000e0 100644 --- a/stix2/test/v20/test_pattern_expressions.py +++ b/stix2/test/v20/test_pattern_expressions.py @@ -526,6 +526,17 @@ def test_parsing_integer_index(): assert str(patt_obj) == "[a:b[1] = 2]" +# This should never occur, because the first component will always be a property_name, and they should not be quoted. +def test_parsing_quoted_first_path_component(): + patt_obj = create_pattern_object("[a:'b'[1]=2]") + assert str(patt_obj) == "[a:'b'[1] = 2]" + + +def test_parsing_quoted_second_path_component(): + patt_obj = create_pattern_object("[a:b.'b'[1]=2]") + assert str(patt_obj) == "[a:b.'b'[1] = 2]" + + 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") diff --git a/stix2/test/v21/test_pattern_expressions.py b/stix2/test/v21/test_pattern_expressions.py index 56273c00..3ba0aa62 100644 --- a/stix2/test/v21/test_pattern_expressions.py +++ b/stix2/test/v21/test_pattern_expressions.py @@ -658,6 +658,16 @@ def test_parsing_integer_index(): patt_obj = create_pattern_object("[a:b[1]=2]") assert str(patt_obj) == "[a:b[1] = 2]" +# This should never occur, because the first component will always be a property_name, and they should not be quoted. +def test_parsing_quoted_first_path_component(): + patt_obj = create_pattern_object("[a:'b'[1]=2]") + assert str(patt_obj) == "[a:'b'[1] = 2]" + + +def test_parsing_quoted_second_path_component(): + patt_obj = create_pattern_object("[a:b.'b'[1]=2]") + assert str(patt_obj) == "[a:b.'b'[1] = 2]" + def test_parsing_multiple_slashes_quotes(): patt_obj = create_pattern_object("[ file:name = 'weird_name\\'' ]", version="2.1")