def test_root_types(): ast = stix2.ObservationExpression( stix2.AndBooleanExpression( [stix2.ParentheticalExpression( stix2.OrBooleanExpression([ stix2.EqualityComparisonExpression("a:b", stix2.StringConstant("1")), stix2.EqualityComparisonExpression("b:c", stix2.StringConstant("2"))])), stix2.EqualityComparisonExpression(u"b:d", stix2.StringConstant("3"))])) assert str(ast) == "[(a:b = '1' OR b:c = '2') AND b:d = '3']"
def test_boolean_expression_with_parentheses(): exp1 = stix2.MatchesComparisonExpression(stix2.ObjectPath("email-message", [stix2.ReferenceObjectPathComponent("from_ref"), stix2.BasicObjectPathComponent("value")]), stix2.StringConstant(".+\\@example\\.com$")) exp2 = stix2.MatchesComparisonExpression("email-message:body_multipart[*].body_raw_ref.name", stix2.StringConstant("^Final Report.+\\.exe$")) exp = stix2.ParentheticalExpression(stix2.AndBooleanExpression([exp1, exp2])) assert str(exp) == "(email-message:from_ref.value MATCHES '.+\\\\@example\\\\.com$' AND email-message:body_multipart[*].body_raw_ref.name MATCHES '^Final Report.+\\\\.exe$')" # noqa
def test_hash_followed_by_registryKey_expression(): hash_exp = stix2.EqualityComparisonExpression("file:hashes.MD5", stix2.HashConstant("79054025255fb1a26e4bc422aef54eb4", "MD5")) o_exp1 = stix2.ObservationExpression(hash_exp) reg_exp = stix2.EqualityComparisonExpression(stix2.ObjectPath("windows-registry-key", ["key"]), stix2.StringConstant("HKEY_LOCAL_MACHINE\\foo\\bar")) o_exp2 = stix2.ObservationExpression(reg_exp) fb_exp = stix2.FollowedByObservationExpression([o_exp1, o_exp2]) para_exp = stix2.ParentheticalExpression(fb_exp) qual_exp = stix2.WithinQualifier(stix2.IntegerConstant(300)) exp = stix2.QualifiedObservationExpression(para_exp, qual_exp) assert str(exp) == "([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [windows-registry-key:key = 'HKEY_LOCAL_MACHINE\\\\foo\\\\bar']) WITHIN 300 SECONDS" # noqa
def __generate_observation_expression(self, size): """ Generate a random complex observation expression, which may consist of sub-expressions and qualifiers. :param size: The size of the desired observation expression, in terms of the number of simple comparison expressions it must contain :return: The observation expression AST """ assert size > 0 # The generation strategy is similar to that for comparison expressions # (see __generate_complex_comparison_expression()). It is generated in # two parts of random size; one side is constructed as a sub-expression. if size == 1: obs_expr = stix2.ObservationExpression( self.__generate_complex_comparison_expression(1)) else: lsize = random.randint(0, size) rsize = size - lsize if random.random() < 0.5: # Parenthesize right case obs_exprs = [ stix2.ObservationExpression( self.__generate_complex_comparison_expression(sz)) for sz in _rand_series(lsize) ] if rsize > 0: obs_exprs.append( stix2.ParentheticalExpression( self.__generate_observation_expression(rsize))) else: # Parenthesize left case if lsize == 0: obs_exprs = [] else: obs_exprs = [ stix2.ParentheticalExpression( self.__generate_observation_expression(lsize)) ] obs_exprs.extend( stix2.ObservationExpression( self.__generate_complex_comparison_expression(sz)) for sz in _rand_series(rsize)) ast_class = random.choice( (stix2.AndObservationExpression, stix2.OrObservationExpression, stix2.FollowedByObservationExpression)) obs_expr = ast_class(obs_exprs) if random.random() < self.__config.probability_qualifier: qualifier = self.__generate_random_qualifier() obs_expr = stix2.QualifiedObservationExpression( obs_expr, qualifier) return obs_expr
def __generate_complex_comparison_expression(self, size, type_constraint=None): """ Generates a "complex" comparison expression, i.e. one which may consist of sub-expressions connected via AND or OR. If a type constraint is given, the resulting expression will honor that constraint. :param size: The size of the desired complex comparison expression, in terms of the number of simple comparison expressions it must contain :param type_constraint: An SCO type, or None :return: """ assert size > 0 # This complex expression must be composed of N simple expressions. # This implementation builds the overall expression in two parts: a # left and right side. The location of the split between left and # right is random. A side is randomly chosen to just contain a series # of simple expressions, and the other side will have a nested # subexpression. # # One goal of the strategy is to avoid excessive nested parentheses. # Too many parentheses results in ugly crazy-looking patterns. This # algorithm still can generate some silly patterns, but I hope it helps # a little. if size == 1: expr = self.__generate_simple_comparison_expression_list( 1, type_constraint, False)[0] else: # Choose whether top-level operator will be AND or OR. # This will also determine how we handle the type constraint. is_and = random.random() < 0.5 # If AND, all operands *must* be type-constrained. if is_and and not type_constraint: type_constraint = self.__random_sco_type() # In the following, if type_constraint is None, both left and right # constraints will be None. No need for a special case. If we # have a type constraint, for 'AND', the constraint must be # enforced on both sides. For 'OR', we need only enforce it on one # side. if is_and: left_constraint = right_constraint = type_constraint else: left_constraint, right_constraint = type_constraint, None if random.random() < 0.5: left_constraint, right_constraint = \ right_constraint, left_constraint # Don't let either side be zero size here. Avoids the case where # we have an OR, and randomly choose to enforce the type constraint # on the zero-length side. That can result in an invalid pattern. lsize = random.randint(1, size - 1) rsize = size - lsize if random.random() < 0.5: # Parenthesize right case operands = self.__generate_simple_comparison_expression_list( lsize, left_constraint, is_and) operands.append( stix2.ParentheticalExpression( self.__generate_complex_comparison_expression( rsize, right_constraint))) else: # Parenthesize left case operands = [ stix2.ParentheticalExpression( self.__generate_complex_comparison_expression( lsize, left_constraint)) ] operands.extend( self.__generate_simple_comparison_expression_list( rsize, right_constraint, is_and)) if is_and: expr = stix2.AndBooleanExpression(operands) else: expr = stix2.OrBooleanExpression(operands) return expr