diff --git a/stix2/equivalence/patterns/__init__.py b/stix2/equivalence/patterns/__init__.py index 0d0aa2a..c371ca3 100644 --- a/stix2/equivalence/patterns/__init__.py +++ b/stix2/equivalence/patterns/__init__.py @@ -1,18 +1,14 @@ -import stix2.pattern_visitor -from stix2.equivalence.patterns.transform import ( - ChainTransformer, SettleTransformer -) from stix2.equivalence.patterns.compare.observation import ( - observation_expression_cmp + observation_expression_cmp, +) +from stix2.equivalence.patterns.transform import ( + ChainTransformer, SettleTransformer, ) from stix2.equivalence.patterns.transform.observation import ( - CanonicalizeComparisonExpressionsTransformer, - AbsorptionTransformer, - FlattenTransformer, - DNFTransformer, - OrderDedupeTransformer + AbsorptionTransformer, CanonicalizeComparisonExpressionsTransformer, + DNFTransformer, FlattenTransformer, OrderDedupeTransformer, ) - +import stix2.pattern_visitor # Lazy-initialize _pattern_canonicalizer = None @@ -38,7 +34,7 @@ def _get_pattern_canonicalizer(): obs_expr_order = OrderDedupeTransformer() obs_expr_absorb = AbsorptionTransformer() obs_simplify = ChainTransformer( - obs_expr_flatten, obs_expr_order, obs_expr_absorb + obs_expr_flatten, obs_expr_order, obs_expr_absorb, ) obs_settle_simplify = SettleTransformer(obs_simplify) @@ -46,7 +42,7 @@ def _get_pattern_canonicalizer(): _pattern_canonicalizer = ChainTransformer( canonicalize_comp_expr, - obs_settle_simplify, obs_dnf, obs_settle_simplify + obs_settle_simplify, obs_dnf, obs_settle_simplify, ) return _pattern_canonicalizer @@ -86,12 +82,12 @@ def find_equivalent_patterns(search_pattern, patterns): patterns """ search_pattern_ast = stix2.pattern_visitor.create_pattern_object( - search_pattern + search_pattern, ) pattern_canonicalizer = _get_pattern_canonicalizer() canon_search_pattern_ast, _ = pattern_canonicalizer.transform( - search_pattern_ast + search_pattern_ast, ) for pattern in patterns: @@ -99,7 +95,7 @@ def find_equivalent_patterns(search_pattern, patterns): canon_pattern_ast, _ = pattern_canonicalizer.transform(pattern_ast) result = observation_expression_cmp( - canon_search_pattern_ast, canon_pattern_ast + canon_search_pattern_ast, canon_pattern_ast, ) if result == 0: diff --git a/stix2/equivalence/patterns/compare/__init__.py b/stix2/equivalence/patterns/compare/__init__.py index a80de4f..e4bcc8f 100644 --- a/stix2/equivalence/patterns/compare/__init__.py +++ b/stix2/equivalence/patterns/compare/__init__.py @@ -2,6 +2,7 @@ Some generic comparison utility functions. """ + def generic_cmp(value1, value2): """ Generic comparator of values which uses the builtin '<' and '>' operators. diff --git a/stix2/equivalence/patterns/compare/comparison.py b/stix2/equivalence/patterns/compare/comparison.py index 03b16f4..ed717fc 100644 --- a/stix2/equivalence/patterns/compare/comparison.py +++ b/stix2/equivalence/patterns/compare/comparison.py @@ -3,18 +3,18 @@ Comparison utilities for STIX pattern comparison expressions. """ import base64 import functools -from stix2.patterns import ( - _ComparisonExpression, AndBooleanExpression, OrBooleanExpression, - ListObjectPathComponent, IntegerConstant, FloatConstant, StringConstant, - BooleanConstant, TimestampConstant, HexConstant, BinaryConstant, - ListConstant -) -from stix2.equivalence.patterns.compare import generic_cmp, iter_lex_cmp +from stix2.equivalence.patterns.compare import generic_cmp, iter_lex_cmp +from stix2.patterns import ( + AndBooleanExpression, BinaryConstant, BooleanConstant, FloatConstant, + HexConstant, IntegerConstant, ListConstant, ListObjectPathComponent, + OrBooleanExpression, StringConstant, TimestampConstant, + _ComparisonExpression, +) _COMPARISON_OP_ORDER = ( "=", "!=", "<>", "<", "<=", ">", ">=", - "IN", "LIKE", "MATCHES", "ISSUBSET", "ISSUPERSET" + "IN", "LIKE", "MATCHES", "ISSUBSET", "ISSUPERSET", ) @@ -23,7 +23,7 @@ _CONSTANT_TYPE_ORDER = ( # treated equally as a generic "number" type. So they aren't in this list. # See constant_cmp(). StringConstant, BooleanConstant, - TimestampConstant, HexConstant, BinaryConstant, ListConstant + TimestampConstant, HexConstant, BinaryConstant, ListConstant, ) @@ -111,11 +111,11 @@ def list_cmp(value1, value2): # Achieve order-independence by sorting the lists first. sorted_value1 = sorted( - value1.value, key=functools.cmp_to_key(constant_cmp) + value1.value, key=functools.cmp_to_key(constant_cmp), ) sorted_value2 = sorted( - value2.value, key=functools.cmp_to_key(constant_cmp) + value2.value, key=functools.cmp_to_key(constant_cmp), ) result = iter_lex_cmp(sorted_value1, sorted_value2, constant_cmp) @@ -131,7 +131,7 @@ _CONSTANT_COMPARATORS = { TimestampConstant: generic_constant_cmp, HexConstant: hex_cmp, BinaryConstant: bin_cmp, - ListConstant: list_cmp + ListConstant: list_cmp, } @@ -214,7 +214,7 @@ def object_path_cmp(path1, path2): path_vals1 = object_path_to_raw_values(path1) path_vals2 = object_path_to_raw_values(path2) result = iter_lex_cmp( - path_vals1, path_vals2, object_path_component_cmp + path_vals1, path_vals2, object_path_component_cmp, ) return result @@ -345,7 +345,7 @@ def comparison_expression_cmp(expr1, expr2): # This will order according to recursive invocations of this comparator, # on sub-expressions. result = iter_lex_cmp( - expr1.operands, expr2.operands, comparison_expression_cmp + expr1.operands, expr2.operands, comparison_expression_cmp, ) return result diff --git a/stix2/equivalence/patterns/compare/observation.py b/stix2/equivalence/patterns/compare/observation.py index 66513da..227b8ae 100644 --- a/stix2/equivalence/patterns/compare/observation.py +++ b/stix2/equivalence/patterns/compare/observation.py @@ -3,24 +3,23 @@ Comparison utilities for STIX pattern observation expressions. """ from stix2.equivalence.patterns.compare import generic_cmp, iter_lex_cmp from stix2.equivalence.patterns.compare.comparison import ( - comparison_expression_cmp, generic_constant_cmp + comparison_expression_cmp, generic_constant_cmp, ) from stix2.patterns import ( - ObservationExpression, AndObservationExpression, OrObservationExpression, - QualifiedObservationExpression, _CompoundObservationExpression, - RepeatQualifier, WithinQualifier, StartStopQualifier, - FollowedByObservationExpression + AndObservationExpression, FollowedByObservationExpression, + ObservationExpression, OrObservationExpression, + QualifiedObservationExpression, RepeatQualifier, StartStopQualifier, + WithinQualifier, _CompoundObservationExpression, ) - _OBSERVATION_EXPRESSION_TYPE_ORDER = ( ObservationExpression, AndObservationExpression, OrObservationExpression, - FollowedByObservationExpression, QualifiedObservationExpression + FollowedByObservationExpression, QualifiedObservationExpression, ) _QUALIFIER_TYPE_ORDER = ( - RepeatQualifier, WithinQualifier, StartStopQualifier + RepeatQualifier, WithinQualifier, StartStopQualifier, ) @@ -36,7 +35,7 @@ def within_cmp(qual1, qual2): Compare WITHIN qualifiers. This orders by number of seconds. """ return generic_constant_cmp( - qual1.number_of_seconds, qual2.number_of_seconds + qual1.number_of_seconds, qual2.number_of_seconds, ) @@ -48,14 +47,14 @@ def startstop_cmp(qual1, qual2): return iter_lex_cmp( (qual1.start_time, qual1.stop_time), (qual2.start_time, qual2.stop_time), - generic_constant_cmp + generic_constant_cmp, ) _QUALIFIER_COMPARATORS = { RepeatQualifier: repeats_cmp, WithinQualifier: within_cmp, - StartStopQualifier: startstop_cmp + StartStopQualifier: startstop_cmp, } @@ -84,14 +83,14 @@ def observation_expression_cmp(expr1, expr2): # If they're simple, use contained comparison expression order elif type1 is ObservationExpression: result = comparison_expression_cmp( - expr1.operand, expr2.operand + expr1.operand, expr2.operand, ) elif isinstance(expr1, _CompoundObservationExpression): # Both compound, and of same type (and/or/followedby): sort according # to contents. result = iter_lex_cmp( - expr1.operands, expr2.operands, observation_expression_cmp + expr1.operands, expr2.operands, observation_expression_cmp, ) else: # QualifiedObservationExpression @@ -112,13 +111,13 @@ def observation_expression_cmp(expr1, expr2): result = qual_cmp(expr1.qualifier, expr2.qualifier) else: raise TypeError( - "Can't compare qualifier type: " + qual1_type.__name__ + "Can't compare qualifier type: " + qual1_type.__name__, ) if result == 0: # Same qualifier type and details; use qualified expression order result = observation_expression_cmp( - expr1.observation_expression, expr2.observation_expression + expr1.observation_expression, expr2.observation_expression, ) return result diff --git a/stix2/equivalence/patterns/transform/__init__.py b/stix2/equivalence/patterns/transform/__init__.py index 5df9061..84a993c 100644 --- a/stix2/equivalence/patterns/transform/__init__.py +++ b/stix2/equivalence/patterns/transform/__init__.py @@ -2,6 +2,7 @@ Generic AST transformation classes. """ + class Transformer: """ Base class for AST transformers. @@ -16,7 +17,7 @@ class Transformer: is useful in situations where a transformation needs to be repeated until the AST stops changing. """ - raise NotImplemented("transform") + raise NotImplementedError("transform") class ChainTransformer(Transformer): diff --git a/stix2/equivalence/patterns/transform/comparison.py b/stix2/equivalence/patterns/transform/comparison.py index 2848598..528cc9b 100644 --- a/stix2/equivalence/patterns/transform/comparison.py +++ b/stix2/equivalence/patterns/transform/comparison.py @@ -3,18 +3,19 @@ Transformation utilities for STIX pattern comparison expressions. """ import functools import itertools + +from stix2.equivalence.patterns.compare import iter_in, iter_lex_cmp +from stix2.equivalence.patterns.compare.comparison import ( + comparison_expression_cmp, +) from stix2.equivalence.patterns.transform import Transformer from stix2.equivalence.patterns.transform.specials import ( - windows_reg_key, ipv4_addr, ipv6_addr + ipv4_addr, ipv6_addr, windows_reg_key, ) from stix2.patterns import ( - _BooleanExpression, _ComparisonExpression, AndBooleanExpression, - OrBooleanExpression, ParentheticalExpression + AndBooleanExpression, OrBooleanExpression, ParentheticalExpression, + _BooleanExpression, _ComparisonExpression, ) -from stix2.equivalence.patterns.compare.comparison import ( - comparison_expression_cmp -) -from stix2.equivalence.patterns.compare import iter_lex_cmp, iter_in def _dupe_ast(ast): @@ -119,7 +120,7 @@ class ComparisonExpressionTransformer(Transformer): elif isinstance(ast, _ComparisonExpression): meth = getattr( - self, "transform_comparison", self.transform_default + self, "transform_comparison", self.transform_default, ) else: @@ -156,7 +157,7 @@ class OrderDedupeTransformer( :return: The same AST node, but with sorted children """ sorted_children = sorted( - ast.operands, key=functools.cmp_to_key(comparison_expression_cmp) + ast.operands, key=functools.cmp_to_key(comparison_expression_cmp), ) deduped_children = [ @@ -165,13 +166,13 @@ class OrderDedupeTransformer( # need key wrappers in our ASTs! k.obj for k, _ in itertools.groupby( sorted_children, key=functools.cmp_to_key( - comparison_expression_cmp - ) + comparison_expression_cmp, + ), ) ] changed = iter_lex_cmp( - ast.operands, deduped_children, comparison_expression_cmp + ast.operands, deduped_children, comparison_expression_cmp, ) != 0 ast.operands = deduped_children @@ -268,7 +269,7 @@ class AbsorptionTransformer( # The simple check: is child1 contained in child2? if iter_in( - child1, child2.operands, comparison_expression_cmp + child1, child2.operands, comparison_expression_cmp, ): to_delete.add(j) @@ -278,7 +279,7 @@ class AbsorptionTransformer( if all( iter_in( child1_operand, child2.operands, - comparison_expression_cmp + comparison_expression_cmp, ) for child1_operand in child1.operands ): @@ -326,7 +327,7 @@ class DNFTransformer(ComparisonExpressionTransformer): # we should ensure each repetition is independent of the # others. _dupe_ast(sub_ast) for sub_ast in itertools.chain( - other_children, prod_seq + other_children, prod_seq, ) ]) for prod_seq in itertools.product(*or_children) diff --git a/stix2/equivalence/patterns/transform/observation.py b/stix2/equivalence/patterns/transform/observation.py index 4470706..d4ee175 100644 --- a/stix2/equivalence/patterns/transform/observation.py +++ b/stix2/equivalence/patterns/transform/observation.py @@ -3,23 +3,30 @@ Transformation utilities for STIX pattern observation expressions. """ import functools import itertools -from stix2.patterns import ( - ObservationExpression, AndObservationExpression, OrObservationExpression, - QualifiedObservationExpression, _CompoundObservationExpression, - ParentheticalExpression, FollowedByObservationExpression + +from stix2.equivalence.patterns.compare import iter_in, iter_lex_cmp +from stix2.equivalence.patterns.compare.observation import ( + observation_expression_cmp, ) from stix2.equivalence.patterns.transform import ( - ChainTransformer, SettleTransformer, Transformer + ChainTransformer, SettleTransformer, Transformer, ) from stix2.equivalence.patterns.transform.comparison import ( - FlattenTransformer as CFlattenTransformer, - OrderDedupeTransformer as COrderDedupeTransformer, - AbsorptionTransformer as CAbsorptionTransformer, - DNFTransformer as CDNFTransformer, - SpecialValueCanonicalization + SpecialValueCanonicalization, +) +from stix2.equivalence.patterns.transform.comparison import \ + AbsorptionTransformer as CAbsorptionTransformer +from stix2.equivalence.patterns.transform.comparison import \ + DNFTransformer as CDNFTransformer +from stix2.equivalence.patterns.transform.comparison import \ + FlattenTransformer as CFlattenTransformer +from stix2.equivalence.patterns.transform.comparison import \ + OrderDedupeTransformer as COrderDedupeTransformer +from stix2.patterns import ( + AndObservationExpression, FollowedByObservationExpression, + ObservationExpression, OrObservationExpression, ParentheticalExpression, + QualifiedObservationExpression, _CompoundObservationExpression, ) -from stix2.equivalence.patterns.compare import iter_lex_cmp, iter_in -from stix2.equivalence.patterns.compare.observation import observation_expression_cmp def _dupe_ast(ast): @@ -52,7 +59,7 @@ def _dupe_ast(ast): elif isinstance(ast, QualifiedObservationExpression): # Don't need to dupe the qualifier object at this point result = QualifiedObservationExpression( - _dupe_ast(ast.observation_expression), ast.qualifier + _dupe_ast(ast.observation_expression), ast.qualifier, ) elif isinstance(ast, ObservationExpression): @@ -100,7 +107,7 @@ class ObservationExpressionTransformer(Transformer): AndObservationExpression: "and", OrObservationExpression: "or", FollowedByObservationExpression: "followedby", - QualifiedObservationExpression: "qualified" + QualifiedObservationExpression: "qualified", } def transform(self, ast): @@ -143,7 +150,7 @@ class ObservationExpressionTransformer(Transformer): else: raise TypeError("Not an observation expression: {}: {}".format( - type(ast).__name__, str(ast) + type(ast).__name__, str(ast), )) return result, changed @@ -228,7 +235,7 @@ class OrderDedupeTransformer( def __transform(self, ast): sorted_children = sorted( - ast.operands, key=functools.cmp_to_key(observation_expression_cmp) + ast.operands, key=functools.cmp_to_key(observation_expression_cmp), ) # Deduping only applies to ORs @@ -236,15 +243,15 @@ class OrderDedupeTransformer( deduped_children = [ key.obj for key, _ in itertools.groupby( sorted_children, key=functools.cmp_to_key( - observation_expression_cmp - ) + observation_expression_cmp, + ), ) ] else: deduped_children = sorted_children changed = iter_lex_cmp( - ast.operands, deduped_children, observation_expression_cmp + ast.operands, deduped_children, observation_expression_cmp, ) != 0 ast.operands = deduped_children @@ -376,12 +383,12 @@ class AbsorptionTransformer( if isinstance( child2, ( AndObservationExpression, - FollowedByObservationExpression - ) + FollowedByObservationExpression, + ), ): # The simple check: is child1 contained in child2? if iter_in( - child1, child2.operands, observation_expression_cmp + child1, child2.operands, observation_expression_cmp, ): to_delete.add(j) @@ -390,11 +397,11 @@ class AbsorptionTransformer( elif type(child1) is type(child2): if isinstance(child1, AndObservationExpression): can_simplify = self.__is_contained_and( - child1.operands, child2.operands + child1.operands, child2.operands, ) else: # child1 and 2 are followedby nodes can_simplify = self.__is_contained_followedby( - child1.operands, child2.operands + child1.operands, child2.operands, ) if can_simplify: @@ -434,7 +441,7 @@ class DNFTransformer(ObservationExpressionTransformer): distributed_children = [ root_type([ _dupe_ast(sub_ast) for sub_ast in itertools.chain( - other_children, prod_seq + other_children, prod_seq, ) ]) for prod_seq in itertools.product(*or_children) @@ -477,7 +484,7 @@ class CanonicalizeComparisonExpressionsTransformer( comp_special = SpecialValueCanonicalization() comp_dnf = CDNFTransformer() self.__comp_canonicalize = ChainTransformer( - comp_special, settle_simplify, comp_dnf, settle_simplify + comp_special, settle_simplify, comp_dnf, settle_simplify, ) def transform_observation(self, ast): diff --git a/stix2/equivalence/patterns/transform/specials.py b/stix2/equivalence/patterns/transform/specials.py index 0eba091..b95e6bf 100644 --- a/stix2/equivalence/patterns/transform/specials.py +++ b/stix2/equivalence/patterns/transform/specials.py @@ -2,10 +2,10 @@ Some simple comparison expression canonicalization functions. """ import socket -from stix2.equivalence.patterns.compare.comparison import ( - object_path_to_raw_values -) +from stix2.equivalence.patterns.compare.comparison import ( + object_path_to_raw_values, +) # Values we can use as wildcards in path patterns _ANY_IDX = object() diff --git a/stix2/pattern_visitor.py b/stix2/pattern_visitor.py index a9d43c5..c4b2ec2 100644 --- a/stix2/pattern_visitor.py +++ b/stix2/pattern_visitor.py @@ -2,8 +2,8 @@ import importlib import inspect -from six import text_type +from six import text_type from stix2patterns.exceptions import ParseException from stix2patterns.grammars.STIXPatternParser import TerminalNode from stix2patterns.v20.grammars.STIXPatternParser import \ @@ -261,9 +261,11 @@ class STIXPatternVisitorForSTIX2(): 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 if isinstance(current, BasicObjectPathComponent) else text_type(current), - 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) diff --git a/stix2/test/test_pattern_equivalence.py b/stix2/test/test_pattern_equivalence.py index 0488358..c75cc60 100644 --- a/stix2/test/test_pattern_equivalence.py +++ b/stix2/test/test_pattern_equivalence.py @@ -1,8 +1,8 @@ import pytest -from stix2.equivalence.patterns import ( - equivalent_patterns, find_equivalent_patterns -) +from stix2.equivalence.patterns import ( + equivalent_patterns, find_equivalent_patterns, +) # # # # # # Observation expression equivalence tests # # @@ -13,13 +13,13 @@ from stix2.equivalence.patterns import ( "patt1, patt2", [ ( "[a:b=1] OR [a:b=1]", - "[a:b=1]" + "[a:b=1]", ), ( "[a:b=1] OR [a:b=1] OR [a:b=1]", - "[a:b=1]" + "[a:b=1]", ), - ] + ], ) def test_obs_dupe_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -29,13 +29,13 @@ def test_obs_dupe_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1] AND [a:b=1]", - "[a:b=1]" + "[a:b=1]", ), ( "[a:b=1] FOLLOWEDBY [a:b=1]", - "[a:b=1]" + "[a:b=1]", ), - ] + ], ) def test_obs_dupe_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -72,8 +72,8 @@ def test_obs_dupe_not_equivalent(patt1, patt2): ( "[a:b=1] AND ([a:b=2] AND ([a:b=3] AND [a:b=4])) AND ([a:b=5])", "([a:b=1] AND ([a:b=2] AND [a:b=3]) AND ([a:b=4] AND [a:b=5]))", - ) - ] + ), + ], ) def test_obs_flatten_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -103,7 +103,7 @@ def test_obs_flatten_equivalent(patt1, patt2): "[a:b=1] FOLLOWEDBY ([a:b=2] FOLLOWEDBY [a:b=3]) WITHIN 2 SECONDS", "[a:b=1] WITHIN 2 SECONDS FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3]", ), - ] + ], ) def test_obs_flatten_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -113,21 +113,21 @@ def test_obs_flatten_not_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1] AND [a:b=2]", - "[a:b=2] AND [a:b=1]" + "[a:b=2] AND [a:b=1]", ), ( "[a:b=1] OR [a:b=2]", - "[a:b=2] OR [a:b=1]" + "[a:b=2] OR [a:b=1]", ), ( "[a:b=1] OR ([a:b=2] AND [a:b=3])", - "([a:b=3] AND [a:b=2]) OR [a:b=1]" + "([a:b=3] AND [a:b=2]) OR [a:b=1]", ), ( "[a:b=1] WITHIN 2 SECONDS AND [a:b=2] REPEATS 2 TIMES", - "[a:b=2] REPEATS 2 TIMES AND [a:b=1] WITHIN 2 SECONDS" - ) - ] + "[a:b=2] REPEATS 2 TIMES AND [a:b=1] WITHIN 2 SECONDS", + ), + ], ) def test_obs_order_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -137,13 +137,13 @@ def test_obs_order_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1] FOLLOWEDBY [a:b=2]", - "[a:b=2] FOLLOWEDBY [a:b=1]" + "[a:b=2] FOLLOWEDBY [a:b=1]", ), ( "[a:b=1] WITHIN 2 SECONDS AND [a:b=2] REPEATS 2 TIMES", - "[a:b=1] REPEATS 2 TIMES AND [a:b=2] WITHIN 2 SECONDS" - ) - ] + "[a:b=1] REPEATS 2 TIMES AND [a:b=2] WITHIN 2 SECONDS", + ), + ], ) def test_obs_order_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -153,29 +153,29 @@ def test_obs_order_not_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1] OR ([a:b=1] AND [a:b=2])", - "[a:b=1]" + "[a:b=1]", ), ( "[a:b=1] OR ([a:b=1] FOLLOWEDBY [a:b=2])", - "[a:b=1]" + "[a:b=1]", ), ( "([a:b=3] AND [a:b=1]) OR ([a:b=1] AND [a:b=2] AND [a:b=3])", - "[a:b=3] AND [a:b=1]" + "[a:b=3] AND [a:b=1]", ), ( "([a:b=1] FOLLOWEDBY [a:b=3]) OR ([a:b=4] FOLLOWEDBY [a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3])", - "[a:b=1] FOLLOWEDBY [a:b=3]" + "[a:b=1] FOLLOWEDBY [a:b=3]", ), ( "([a:b=1] FOLLOWEDBY [a:b=2]) OR (([a:b=1] FOLLOWEDBY [a:b=2]) AND [a:b=3])", - "[a:b=1] FOLLOWEDBY [a:b=2]" + "[a:b=1] FOLLOWEDBY [a:b=2]", ), ( "([a:b=1] AND [a:b=2]) OR (([a:b=1] AND [a:b=2]) FOLLOWEDBY [a:b=3])", - "[a:b=1] AND [a:b=2]" + "[a:b=1] AND [a:b=2]", ), - ] + ], ) def test_obs_absorb_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -185,13 +185,13 @@ def test_obs_absorb_equivalent(patt1, patt2): "patt1, patt2", [ ( "([a:b=1] AND [a:b=2]) OR ([a:b=2] AND [a:b=3] AND [a:b=4])", - "[a:b=1] AND [a:b=2]" + "[a:b=1] AND [a:b=2]", ), ( "([a:b=2] FOLLOWEDBY [a:b=1]) OR ([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3])", - "[a:b=2] FOLLOWEDBY [a:b=1]" - ) - ] + "[a:b=2] FOLLOWEDBY [a:b=1]", + ), + ], ) def test_obs_absorb_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -201,29 +201,29 @@ def test_obs_absorb_not_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1] AND ([a:b=2] OR [a:b=3])", - "([a:b=1] AND [a:b=2]) OR ([a:b=1] AND [a:b=3])" + "([a:b=1] AND [a:b=2]) OR ([a:b=1] AND [a:b=3])", ), ( "[a:b=1] FOLLOWEDBY ([a:b=2] OR [a:b=3])", - "([a:b=1] FOLLOWEDBY [a:b=2]) OR ([a:b=1] FOLLOWEDBY [a:b=3])" + "([a:b=1] FOLLOWEDBY [a:b=2]) OR ([a:b=1] FOLLOWEDBY [a:b=3])", ), ( "[a:b=1] AND ([a:b=2] AND ([a:b=3] OR [a:b=4]))", - "([a:b=1] AND [a:b=2] AND [a:b=3]) OR ([a:b=1] AND [a:b=2] AND [a:b=4])" + "([a:b=1] AND [a:b=2] AND [a:b=3]) OR ([a:b=1] AND [a:b=2] AND [a:b=4])", ), ( "[a:b=1] FOLLOWEDBY ([a:b=2] FOLLOWEDBY ([a:b=3] OR [a:b=4]))", - "([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=4])" + "([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=2] FOLLOWEDBY [a:b=4])", ), ( "([a:b=1] OR [a:b=2]) AND ([a:b=3] OR [a:b=4])", - "([a:b=1] AND [a:b=3]) OR ([a:b=1] AND [a:b=4]) OR ([a:b=2] AND [a:b=3]) OR ([a:b=2] AND [a:b=4])" + "([a:b=1] AND [a:b=3]) OR ([a:b=1] AND [a:b=4]) OR ([a:b=2] AND [a:b=3]) OR ([a:b=2] AND [a:b=4])", ), ( "([a:b=1] OR [a:b=2]) FOLLOWEDBY ([a:b=3] OR [a:b=4])", - "([a:b=1] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=4]) OR ([a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=2] FOLLOWEDBY [a:b=4])" + "([a:b=1] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=4]) OR ([a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=2] FOLLOWEDBY [a:b=4])", ), - ] + ], ) def test_obs_dnf_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -233,17 +233,17 @@ def test_obs_dnf_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1] AND [a:b=2]", - "[a:b=1] OR [a:b=2]" + "[a:b=1] OR [a:b=2]", ), ( "[a:b=1] AND ([a:b=2] OR [a:b=3])", - "([a:b=1] AND [a:b=2]) OR [a:b=3]" + "([a:b=1] AND [a:b=2]) OR [a:b=3]", ), ( "[a:b=1] WITHIN 2 SECONDS", - "[a:b=1] REPEATS 2 TIMES" - ) - ] + "[a:b=1] REPEATS 2 TIMES", + ), + ], ) def test_obs_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -258,21 +258,21 @@ def test_obs_not_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1 AND a:b=1]", - "[a:b=1]" + "[a:b=1]", ), ( "[a:b=1 AND a:b=1 AND a:b=1]", - "[a:b=1]" + "[a:b=1]", ), ( "[a:b=1 OR a:b=1]", - "[a:b=1]" + "[a:b=1]", ), ( "[a:b=1 OR a:b=1 OR a:b=1]", - "[a:b=1]" - ) - ] + "[a:b=1]", + ), + ], ) def test_comp_dupe_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -282,29 +282,29 @@ def test_comp_dupe_equivalent(patt1, patt2): "patt1, patt2", [ ( "[(a:b=1)]", - "[a:b=1]" + "[a:b=1]", ), ( "[(((((a:b=1)))))]", - "[(a:b=1)]" + "[(a:b=1)]", ), ( "[a:b=1 AND (a:b=2 AND a:b=3)]", - "[(a:b=1 AND a:b=2) AND a:b=3]" + "[(a:b=1 AND a:b=2) AND a:b=3]", ), ( "[a:b=1 OR (a:b=2 OR a:b=3)]", - "[(a:b=1 OR a:b=2) OR a:b=3]" + "[(a:b=1 OR a:b=2) OR a:b=3]", ), ( "[(((a:b=1 AND ((a:b=2) AND a:b=3) AND (a:b=4))))]", - "[a:b=1 AND a:b=2 AND a:b=3 AND a:b=4]" + "[a:b=1 AND a:b=2 AND a:b=3 AND a:b=4]", ), ( "[(((a:b=1 OR ((a:b=2) OR a:b=3) OR (a:b=4))))]", - "[a:b=1 OR a:b=2 OR a:b=3 OR a:b=4]" + "[a:b=1 OR a:b=2 OR a:b=3 OR a:b=4]", ), - ] + ], ) def test_comp_flatten_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -314,17 +314,17 @@ def test_comp_flatten_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1 AND a:b=2]", - "[a:b=2 AND a:b=1]" + "[a:b=2 AND a:b=1]", ), ( "[a:b=1 OR a:b=2]", - "[a:b=2 OR a:b=1]" + "[a:b=2 OR a:b=1]", ), ( "[(a:b=1 OR a:b=2) AND a:b=3]", "[a:b=3 AND (a:b=2 OR a:b=1)]", - ) - ] + ), + ], ) def test_comp_order_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -334,21 +334,21 @@ def test_comp_order_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1 OR (a:b=1 AND a:b=2)]", - "[a:b=1]" + "[a:b=1]", ), ( "[a:b=1 AND (a:b=1 OR a:b=2)]", - "[a:b=1]" + "[a:b=1]", ), ( "[(a:b=1 AND a:b=2) OR (a:b=3 AND a:b=2 AND a:b=1)]", - "[a:b=1 AND a:b=2]" + "[a:b=1 AND a:b=2]", ), ( "[(a:b=1 OR a:b=2) AND (a:b=3 OR a:b=2 OR a:b=1)]", - "[a:b=1 OR a:b=2]" - ) - ] + "[a:b=1 OR a:b=2]", + ), + ], ) def test_comp_absorb_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -358,25 +358,25 @@ def test_comp_absorb_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1 OR (a:b=2 AND a:b=3)]", - "[(a:b=1 OR a:b=2) AND (a:b=1 OR a:b=3)]" + "[(a:b=1 OR a:b=2) AND (a:b=1 OR a:b=3)]", ), ( "[a:b=1 AND (a:b=2 OR a:b=3)]", - "[(a:b=1 AND a:b=2) OR (a:b=1 AND a:b=3)]" + "[(a:b=1 AND a:b=2) OR (a:b=1 AND a:b=3)]", ), ( "[(a:b=1 AND a:b=2) OR (a:b=3 AND a:b=4)]", - "[(a:b=1 OR a:b=3) AND (a:b=1 OR a:b=4) AND (a:b=2 OR a:b=3) AND (a:b=2 OR a:b=4)]" + "[(a:b=1 OR a:b=3) AND (a:b=1 OR a:b=4) AND (a:b=2 OR a:b=3) AND (a:b=2 OR a:b=4)]", ), ( "[(a:b=1 OR a:b=2) AND (a:b=3 OR a:b=4)]", - "[(a:b=1 AND a:b=3) OR (a:b=1 AND a:b=4) OR (a:b=2 AND a:b=3) OR (a:b=2 AND a:b=4)]" + "[(a:b=1 AND a:b=3) OR (a:b=1 AND a:b=4) OR (a:b=2 AND a:b=3) OR (a:b=2 AND a:b=4)]", ), ( "[a:b=1 AND (a:b=2 AND (a:b=3 OR a:b=4))]", - "[(a:b=1 AND a:b=2 AND a:b=3) OR (a:b=1 AND a:b=2 AND a:b=4)]" - ) - ] + "[(a:b=1 AND a:b=2 AND a:b=3) OR (a:b=1 AND a:b=2 AND a:b=4)]", + ), + ], ) def test_comp_dnf_equivalent(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -386,17 +386,17 @@ def test_comp_dnf_equivalent(patt1, patt2): "patt1, patt2", [ ( "[a:b=1]", - "[a:b=2]" + "[a:b=2]", ), ( "[a:b=1 AND a:b=2]", - "[a:b=1 OR a:b=2]" + "[a:b=1 OR a:b=2]", ), ( "[(a:b=1 AND a:b=2) OR a:b=3]", - "[a:b=1 AND (a:b=2 OR a:b=3)]" + "[a:b=1 AND (a:b=2 OR a:b=3)]", ), - ] + ], ) def test_comp_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -406,41 +406,41 @@ def test_comp_not_equivalent(patt1, patt2): "patt1, patt2", [ ( "[ipv4-addr:value='1.2.3.4/32']", - "[ipv4-addr:value='1.2.3.4']" + "[ipv4-addr:value='1.2.3.4']", ), ( "[ipv4-addr:value='1.2.3.4/24']", - "[ipv4-addr:value='1.2.3.0/24']" + "[ipv4-addr:value='1.2.3.0/24']", ), ( "[ipv4-addr:value='1.2.255.4/23']", - "[ipv4-addr:value='1.2.254.0/23']" + "[ipv4-addr:value='1.2.254.0/23']", ), ( "[ipv4-addr:value='1.2.255.4/20']", - "[ipv4-addr:value='1.2.240.0/20']" + "[ipv4-addr:value='1.2.240.0/20']", ), ( "[ipv4-addr:value='1.2.255.4/0']", - "[ipv4-addr:value='0.0.0.0/0']" + "[ipv4-addr:value='0.0.0.0/0']", ), ( "[ipv4-addr:value='01.02.03.04']", - "[ipv4-addr:value='1.2.3.4']" + "[ipv4-addr:value='1.2.3.4']", ), ( "[ipv4-addr:value='1.2.3.4/-5']", - "[ipv4-addr:value='1.2.3.4/-5']" + "[ipv4-addr:value='1.2.3.4/-5']", ), ( "[ipv4-addr:value='1.2.3.4/99']", - "[ipv4-addr:value='1.2.3.4/99']" + "[ipv4-addr:value='1.2.3.4/99']", ), ( "[ipv4-addr:value='foo']", - "[ipv4-addr:value='foo']" + "[ipv4-addr:value='foo']", ), - ] + ], ) def test_comp_special_canonicalization_ipv4(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -450,17 +450,17 @@ def test_comp_special_canonicalization_ipv4(patt1, patt2): "patt1, patt2", [ ( "[ipv4-addr:value='1.2.3.4']", - "[ipv4-addr:value='1.2.3.5']" + "[ipv4-addr:value='1.2.3.5']", ), ( "[ipv4-addr:value='1.2.3.4/1']", - "[ipv4-addr:value='1.2.3.4/2']" + "[ipv4-addr:value='1.2.3.4/2']", ), ( "[ipv4-addr:value='foo']", - "[ipv4-addr:value='bar']" + "[ipv4-addr:value='bar']", ), - ] + ], ) def test_comp_special_canonicalization_ipv4_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -470,45 +470,45 @@ def test_comp_special_canonicalization_ipv4_not_equivalent(patt1, patt2): "patt1, patt2", [ ( "[ipv6-addr:value='1:2:3:4:5:6:7:8/128']", - "[ipv6-addr:value='1:2:3:4:5:6:7:8']" + "[ipv6-addr:value='1:2:3:4:5:6:7:8']", ), ( "[ipv6-addr:value='1:2:3:4:5:6:7:8/112']", - "[ipv6-addr:value='1:2:3:4:5:6:7:0/112']" + "[ipv6-addr:value='1:2:3:4:5:6:7:0/112']", ), ( "[ipv6-addr:value='1:2:3:4:5:6:ffff:8/111']", - "[ipv6-addr:value='1:2:3:4:5:6:fffe:0/111']" + "[ipv6-addr:value='1:2:3:4:5:6:fffe:0/111']", ), ( "[ipv6-addr:value='1:2:3:4:5:6:ffff:8/104']", - "[ipv6-addr:value='1:2:3:4:5:6:ff00:0/104']" + "[ipv6-addr:value='1:2:3:4:5:6:ff00:0/104']", ), ( "[ipv6-addr:value='1:2:3:4:5:6:7:8/0']", - "[ipv6-addr:value='0:0:0:0:0:0:0:0/0']" + "[ipv6-addr:value='0:0:0:0:0:0:0:0/0']", ), ( "[ipv6-addr:value='0001:0000:0000:0000:0000:0000:0000:0001']", - "[ipv6-addr:value='1::1']" + "[ipv6-addr:value='1::1']", ), ( "[ipv6-addr:value='0000:0000:0000:0000:0000:0000:0000:0000']", - "[ipv6-addr:value='::']" + "[ipv6-addr:value='::']", ), ( "[ipv6-addr:value='1:2:3:4:5:6:7:8/-5']", - "[ipv6-addr:value='1:2:3:4:5:6:7:8/-5']" + "[ipv6-addr:value='1:2:3:4:5:6:7:8/-5']", ), ( "[ipv6-addr:value='1:2:3:4:5:6:7:8/99']", - "[ipv6-addr:value='1:2:3:4:5:6:7:8/99']" + "[ipv6-addr:value='1:2:3:4:5:6:7:8/99']", ), ( "[ipv6-addr:value='foo']", - "[ipv6-addr:value='foo']" + "[ipv6-addr:value='foo']", ), - ] + ], ) def test_comp_special_canonicalization_ipv6(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -528,7 +528,7 @@ def test_comp_special_canonicalization_ipv6(patt1, patt2): "[ipv6-addr:value='foo']", "[ipv6-addr:value='bar']", ), - ] + ], ) def test_comp_special_canonicalization_ipv6_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -548,7 +548,7 @@ def test_comp_special_canonicalization_ipv6_not_equivalent(patt1, patt2): "[windows-registry-key:values[*].name = 'aaa']", "[windows-registry-key:values[*].name = 'AAA']", ), - ] + ], ) def test_comp_special_canonicalization_win_reg_key(patt1, patt2): assert equivalent_patterns(patt1, patt2) @@ -572,7 +572,7 @@ def test_comp_special_canonicalization_win_reg_key(patt1, patt2): "[windows-registry-key:values[*].data='foo']", "[windows-registry-key:values[*].data='FOO']", ), - ] + ], ) def test_comp_special_canonicalization_win_reg_key_not_equivalent(patt1, patt2): assert not equivalent_patterns(patt1, patt2) @@ -591,11 +591,11 @@ def test_find_equivalent_patterns(): "[a:b=1] OR ([a:b=2] AND [a:b=1])", "[(a:b=2 OR a:b=1) AND a:b=1]", "[c:d=1]", - "[a:b>1]" + "[a:b>1]", ] result = list( - find_equivalent_patterns(search_pattern, other_patterns) + find_equivalent_patterns(search_pattern, other_patterns), ) assert result == [ diff --git a/stix2/test/v21/test_pattern_expressions.py b/stix2/test/v21/test_pattern_expressions.py index 3ba0aa6..ac6a439 100644 --- a/stix2/test/v21/test_pattern_expressions.py +++ b/stix2/test/v21/test_pattern_expressions.py @@ -658,6 +658,7 @@ 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]")