2018-07-03 13:00:18 +02:00
import datetime
import pytest
2020-09-13 01:11:00 +02:00
import pytz
2020-02-19 21:39:23 +01:00
from stix2patterns . exceptions import ParseException
2018-07-03 13:00:18 +02:00
import stix2
2018-12-11 19:07:53 +01:00
from stix2 . pattern_visitor import create_pattern_object
2020-09-13 01:11:00 +02:00
import stix2 . utils
2018-07-03 13:00:18 +02:00
def test_create_comparison_expression ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . EqualityComparisonExpression (
" file:hashes. ' SHA-256 ' " ,
stix2 . HashConstant ( " aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f " , " SHA-256 " ) ,
) # noqa
2018-12-11 19:07:53 +01:00
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " file:hashes. ' SHA-256 ' = ' aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f ' "
def test_boolean_expression ( ) :
2018-07-13 17:10:05 +02:00
exp1 = stix2 . MatchesComparisonExpression (
" email-message:from_ref.value " ,
stix2 . StringConstant ( " .+ \\ @example \\ .com$ " ) ,
)
exp2 = stix2 . MatchesComparisonExpression (
" email-message:body_multipart[*].body_raw_ref.name " ,
stix2 . StringConstant ( " ^Final Report.+ \\ .exe$ " ) ,
)
2018-07-03 13:00:18 +02:00
exp = stix2 . AndBooleanExpression ( [ exp1 , exp2 ] )
2018-12-11 19:07:53 +01:00
2018-07-03 13:00:18 +02:00
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_boolean_expression_with_parentheses ( ) :
2018-07-13 17:10:05 +02:00
exp1 = stix2 . MatchesComparisonExpression (
stix2 . ObjectPath (
" email-message " ,
[
stix2 . ReferenceObjectPathComponent ( " from_ref " ) ,
2018-12-11 19:07:53 +01:00
stix2 . BasicObjectPathComponent ( " value " , False ) ,
2018-07-13 17:10:05 +02:00
] ,
) ,
stix2 . StringConstant ( " .+ \\ @example \\ .com$ " ) ,
)
exp2 = stix2 . MatchesComparisonExpression (
" email-message:body_multipart[*].body_raw_ref.name " ,
stix2 . StringConstant ( " ^Final Report.+ \\ .exe$ " ) ,
)
2018-07-03 13:00:18 +02:00
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_python_constant ( ) :
2018-07-13 17:10:05 +02:00
hash_exp = stix2 . EqualityComparisonExpression (
" file:hashes.MD5 " ,
stix2 . HashConstant ( " 79054025255fb1a26e4bc422aef54eb4 " , " MD5 " ) ,
)
2018-07-03 13:00:18 +02:00
o_exp1 = stix2 . ObservationExpression ( hash_exp )
2018-07-13 17:10:05 +02:00
reg_exp = stix2 . EqualityComparisonExpression (
stix2 . ObjectPath ( " windows-registry-key " , [ " key " ] ) ,
stix2 . StringConstant ( " HKEY_LOCAL_MACHINE \\ foo \\ bar " ) ,
)
2018-07-03 13:00:18 +02:00
o_exp2 = stix2 . ObservationExpression ( reg_exp )
fb_exp = stix2 . FollowedByObservationExpression ( [ o_exp1 , o_exp2 ] )
para_exp = stix2 . ParentheticalExpression ( fb_exp )
qual_exp = stix2 . WithinQualifier ( 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 test_hash_followed_by_registryKey_expression ( ) :
2018-07-13 17:10:05 +02:00
hash_exp = stix2 . EqualityComparisonExpression (
" file:hashes.MD5 " ,
stix2 . HashConstant ( " 79054025255fb1a26e4bc422aef54eb4 " , " MD5 " ) ,
)
2018-07-03 13:00:18 +02:00
o_exp1 = stix2 . ObservationExpression ( hash_exp )
2018-07-13 17:10:05 +02:00
reg_exp = stix2 . EqualityComparisonExpression (
stix2 . ObjectPath ( " windows-registry-key " , [ " key " ] ) ,
stix2 . StringConstant ( " HKEY_LOCAL_MACHINE \\ foo \\ bar " ) ,
)
2018-07-03 13:00:18 +02:00
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 test_file_observable_expression ( ) :
2018-07-13 17:10:05 +02:00
exp1 = stix2 . EqualityComparisonExpression (
" file:hashes. ' SHA-256 ' " ,
stix2 . HashConstant (
" aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f " ,
' SHA-256 ' ,
) ,
)
2018-07-03 13:00:18 +02:00
exp2 = stix2 . EqualityComparisonExpression ( " file:mime_type " , stix2 . StringConstant ( " application/x-pdf " ) )
2018-12-11 19:07:53 +01:00
bool_exp = stix2 . ObservationExpression ( stix2 . AndBooleanExpression ( [ exp1 , exp2 ] ) )
assert str ( bool_exp ) == " [file:hashes. ' SHA-256 ' = ' aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f ' AND file:mime_type = ' application/x-pdf ' ] " # noqa
2018-07-03 13:00:18 +02:00
2018-07-13 17:10:05 +02:00
@pytest.mark.parametrize (
" observation_class, op " , [
( stix2 . AndObservationExpression , ' AND ' ) ,
( stix2 . OrObservationExpression , ' OR ' ) ,
] ,
)
2018-07-03 13:00:18 +02:00
def test_multiple_file_observable_expression ( observation_class , op ) :
2018-07-13 17:10:05 +02:00
exp1 = stix2 . EqualityComparisonExpression (
" file:hashes. ' SHA-256 ' " ,
stix2 . HashConstant (
" bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c " ,
' SHA-256 ' ,
) ,
)
exp2 = stix2 . EqualityComparisonExpression (
" file:hashes.MD5 " ,
stix2 . HashConstant ( " cead3f77f6cda6ec00f57d76c9a6879f " , " MD5 " ) ,
)
2018-07-03 13:00:18 +02:00
bool1_exp = stix2 . OrBooleanExpression ( [ exp1 , exp2 ] )
2018-07-13 17:10:05 +02:00
exp3 = stix2 . EqualityComparisonExpression (
" file:hashes. ' SHA-256 ' " ,
stix2 . HashConstant (
" aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f " ,
' SHA-256 ' ,
) ,
)
2018-07-03 13:00:18 +02:00
op1_exp = stix2 . ObservationExpression ( bool1_exp )
op2_exp = stix2 . ObservationExpression ( exp3 )
exp = observation_class ( [ op1_exp , op2_exp ] )
assert str ( exp ) == " [file:hashes. ' SHA-256 ' = ' bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c ' OR file:hashes.MD5 = ' cead3f77f6cda6ec00f57d76c9a6879f ' ] {} [file:hashes. ' SHA-256 ' = ' aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f ' ] " . format ( op ) # noqa
def test_root_types ( ) :
ast = stix2 . ObservationExpression (
stix2 . AndBooleanExpression (
2018-07-13 17:10:05 +02:00
[
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 " ) ) ,
] ,
) ,
)
2018-07-03 13:00:18 +02:00
assert str ( ast ) == " [(a:b = ' 1 ' OR b:c = ' 2 ' ) AND b:d = ' 3 ' ] "
def test_artifact_payload ( ) :
2018-07-13 17:10:05 +02:00
exp1 = stix2 . EqualityComparisonExpression (
" artifact:mime_type " ,
" application/vnd.tcpdump.pcap " ,
)
exp2 = stix2 . MatchesComparisonExpression (
" artifact:payload_bin " ,
stix2 . StringConstant ( " \\ xd4 \\ xc3 \\ xb2 \\ xa1 \\ x02 \\ x00 \\ x04 \\ x00 " ) ,
)
2018-12-11 19:07:53 +01:00
and_exp = stix2 . ObservationExpression ( stix2 . AndBooleanExpression ( [ exp1 , exp2 ] ) )
assert str ( and_exp ) == " [artifact:mime_type = ' application/vnd.tcpdump.pcap ' AND artifact:payload_bin MATCHES ' \\ \\ xd4 \\ \\ xc3 \\ \\ xb2 \\ \\ xa1 \\ \\ x02 \\ \\ x00 \\ \\ x04 \\ \\ x00 ' ] " # noqa
2018-07-03 13:00:18 +02:00
def test_greater_than_python_constant ( ) :
2018-12-11 19:07:53 +01:00
exp1 = stix2 . GreaterThanComparisonExpression ( " file:extensions. ' windows-pebinary-ext ' .sections[*].entropy " , 7.0 )
2018-07-03 13:00:18 +02:00
exp = stix2 . ObservationExpression ( exp1 )
2018-12-11 19:07:53 +01:00
assert str ( exp ) == " [file:extensions. ' windows-pebinary-ext ' .sections[*].entropy > 7.0] "
2018-07-03 13:00:18 +02:00
def test_greater_than ( ) :
2018-07-13 17:10:05 +02:00
exp1 = stix2 . GreaterThanComparisonExpression (
2018-12-11 19:07:53 +01:00
" file:extensions. ' windows-pebinary-ext ' .sections[*].entropy " ,
2018-07-13 17:10:05 +02:00
stix2 . FloatConstant ( 7.0 ) ,
)
2018-07-03 13:00:18 +02:00
exp = stix2 . ObservationExpression ( exp1 )
2018-12-11 19:07:53 +01:00
assert str ( exp ) == " [file:extensions. ' windows-pebinary-ext ' .sections[*].entropy > 7.0] "
2018-07-03 13:00:18 +02:00
2020-03-27 16:22:00 +01:00
def test_parsing_greater_than ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [file:extensions. ' windows-pebinary-ext ' .sections[*].entropy > 7.478901] " , version = " 2.1 " )
2020-03-27 16:22:00 +01:00
assert str ( patt_obj ) == " [file:extensions. ' windows-pebinary-ext ' .sections[*].entropy > 7.478901] "
2018-07-03 13:00:18 +02:00
def test_less_than ( ) :
2018-12-11 19:07:53 +01:00
exp = stix2 . LessThanComparisonExpression ( " file:size " , 1024 )
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " file:size < 1024 "
2020-03-27 16:22:00 +01:00
def test_parsing_less_than ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [file:size < 1024] " , version = " 2.1 " )
2020-03-27 16:22:00 +01:00
assert str ( patt_obj ) == " [file:size < 1024] "
2018-07-03 13:00:18 +02:00
def test_greater_than_or_equal ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . GreaterThanEqualComparisonExpression (
" file:size " ,
1024 ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " file:size >= 1024 "
2020-03-27 16:22:00 +01:00
def test_parsing_greater_than_or_equal ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [file:size >= 1024] " , version = " 2.1 " )
2020-03-27 16:22:00 +01:00
assert str ( patt_obj ) == " [file:size >= 1024] "
2018-07-03 13:00:18 +02:00
def test_less_than_or_equal ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . LessThanEqualComparisonExpression (
" file:size " ,
1024 ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " file:size <= 1024 "
2020-03-27 16:22:00 +01:00
def test_parsing_less_than_or_equal ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [file:size <= 1024] " , version = " 2.1 " )
2020-03-27 16:22:00 +01:00
assert str ( patt_obj ) == " [file:size <= 1024] "
2020-03-27 18:59:03 +01:00
def test_parsing_issubset ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [network-traffic:dst_ref.value ISSUBSET ' 2001:0db8:dead:beef:0000:0000:0000:0000/64 ' ] " , version = " 2.1 " )
2020-03-27 18:59:03 +01:00
assert str ( patt_obj ) == " [network-traffic:dst_ref.value ISSUBSET ' 2001:0db8:dead:beef:0000:0000:0000:0000/64 ' ] "
def test_parsing_issuperset ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [network-traffic:dst_ref.value ISSUPERSET ' 2001:0db8:dead:beef:0000:0000:0000:0000/64 ' ] " , version = " 2.1 " )
2020-03-27 18:59:03 +01:00
assert str ( patt_obj ) == " [network-traffic:dst_ref.value ISSUPERSET ' 2001:0db8:dead:beef:0000:0000:0000:0000/64 ' ] "
def test_parsing_like ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [directory:path LIKE ' C: \\ \\ Windows \\ \\ % \\ \\ foo ' ] " , version = " 2.1 " )
2020-03-27 18:59:03 +01:00
assert str ( patt_obj ) == " [directory:path LIKE ' C: \\ \\ Windows \\ \\ % \\ \\ foo ' ] "
def test_parsing_match ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [process:command_line MATCHES ' ^.+>-add GlobalSign.cer -c -s -r localMachine Root$ ' ] FOLLOWEDBY [process:command_line MATCHES ' ^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$ ' ] WITHIN 300 SECONDS " , version = " 2.1 " ) # noqa
2020-03-27 19:06:24 +01:00
assert str ( patt_obj ) == " [process:command_line MATCHES ' ^.+>-add GlobalSign.cer -c -s -r localMachine Root$ ' ] FOLLOWEDBY [process:command_line MATCHES ' ^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$ ' ] WITHIN 300 SECONDS " # noqa
2020-03-27 18:59:03 +01:00
def test_parsing_followed_by ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " ([file:hashes.MD5 = ' 79054025255fb1a26e4bc422aef54eb4 ' ] FOLLOWEDBY [windows-registry-key:key = ' HKEY_LOCAL_MACHINE \\ \\ foo \\ \\ bar ' ]) WITHIN 300 SECONDS " , version = " 2.1 " ) # noqa
2020-03-27 18:59:03 +01:00
assert str ( patt_obj ) == " ([file:hashes.MD5 = ' 79054025255fb1a26e4bc422aef54eb4 ' ] FOLLOWEDBY [windows-registry-key:key = ' HKEY_LOCAL_MACHINE \\ \\ foo \\ \\ bar ' ]) WITHIN 300 SECONDS " # noqa
2018-07-03 13:00:18 +02:00
def test_not ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . LessThanComparisonExpression (
" file:size " ,
1024 ,
negated = True ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " file:size NOT < 1024 "
def test_and_observable_expression ( ) :
2018-07-13 17:10:05 +02:00
exp1 = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" user-account:account_type " ,
" unix " ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:user_id " ,
stix2 . StringConstant ( " 1007 " ) ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:account_login " ,
" Peter " ,
) ,
] )
exp2 = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" user-account:account_type " ,
" unix " ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:user_id " ,
stix2 . StringConstant ( " 1008 " ) ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:account_login " ,
" Paul " ,
) ,
] )
exp3 = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" user-account:account_type " ,
" unix " ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:user_id " ,
stix2 . StringConstant ( " 1009 " ) ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:account_login " ,
" Mary " ,
) ,
] )
exp = stix2 . AndObservationExpression ( [
stix2 . ObservationExpression ( exp1 ) ,
stix2 . ObservationExpression ( exp2 ) ,
stix2 . ObservationExpression ( exp3 ) ,
] )
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " [user-account:account_type = ' unix ' AND user-account:user_id = ' 1007 ' AND user-account:account_login = ' Peter ' ] AND [user-account:account_type = ' unix ' AND user-account:user_id = ' 1008 ' AND user-account:account_login = ' Paul ' ] AND [user-account:account_type = ' unix ' AND user-account:user_id = ' 1009 ' AND user-account:account_login = ' Mary ' ] " # noqa
2020-03-27 16:22:00 +01:00
def test_parsing_and_observable_expression ( ) :
2020-04-03 16:45:36 +02:00
exp = create_pattern_object ( " [user-account:account_type = ' unix ' AND user-account:user_id = ' 1007 ' AND user-account:account_login = ' Peter ' ] AND [user-account:account_type = ' unix ' AND user-account:user_id = ' 1008 ' AND user-account:account_login = ' Paul ' ] " , version = " 2.1 " ) # noqa
2020-03-27 17:33:24 +01:00
assert str ( exp ) == " [user-account:account_type = ' unix ' AND user-account:user_id = ' 1007 ' AND user-account:account_login = ' Peter ' ] AND [user-account:account_type = ' unix ' AND user-account:user_id = ' 1008 ' AND user-account:account_login = ' Paul ' ] " # noqa
2020-03-27 16:22:00 +01:00
def test_or_observable_expression ( ) :
exp1 = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" user-account:account_type " ,
" unix " ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:user_id " ,
stix2 . StringConstant ( " 1007 " ) ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:account_login " ,
" Peter " ,
) ,
] )
exp2 = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" user-account:account_type " ,
" unix " ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:user_id " ,
stix2 . StringConstant ( " 1008 " ) ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:account_login " ,
" Paul " ,
) ,
] )
exp3 = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" user-account:account_type " ,
" unix " ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:user_id " ,
stix2 . StringConstant ( " 1009 " ) ,
) ,
stix2 . EqualityComparisonExpression (
" user-account:account_login " ,
" Mary " ,
) ,
] )
exp = stix2 . OrObservationExpression ( [
stix2 . ObservationExpression ( exp1 ) ,
stix2 . ObservationExpression ( exp2 ) ,
stix2 . ObservationExpression ( exp3 ) ,
] )
assert str ( exp ) == " [user-account:account_type = ' unix ' AND user-account:user_id = ' 1007 ' AND user-account:account_login = ' Peter ' ] OR [user-account:account_type = ' unix ' AND user-account:user_id = ' 1008 ' AND user-account:account_login = ' Paul ' ] OR [user-account:account_type = ' unix ' AND user-account:user_id = ' 1009 ' AND user-account:account_login = ' Mary ' ] " # noqa
def test_parsing_or_observable_expression ( ) :
2020-04-03 16:45:36 +02:00
exp = create_pattern_object ( " [user-account:account_type = ' unix ' AND user-account:user_id = ' 1007 ' AND user-account:account_login = ' Peter ' ] OR [user-account:account_type = ' unix ' AND user-account:user_id = ' 1008 ' AND user-account:account_login = ' Paul ' ] " , version = " 2.1 " ) # noqa
2020-03-27 16:22:00 +01:00
assert str ( exp ) == " [user-account:account_type = ' unix ' AND user-account:user_id = ' 1007 ' AND user-account:account_login = ' Peter ' ] OR [user-account:account_type = ' unix ' AND user-account:user_id = ' 1008 ' AND user-account:account_login = ' Paul ' ] " # noqa
2020-09-14 21:42:36 +02:00
def test_invalid_and_comparison_expression ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-13 17:10:05 +02:00
stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" user-account:display_name " ,
" admin " ,
) ,
stix2 . EqualityComparisonExpression (
" email-addr:display_name " ,
stix2 . StringConstant ( " admin " ) ,
) ,
] )
2018-07-03 13:00:18 +02:00
2020-09-14 21:42:36 +02:00
@pytest.mark.parametrize (
" pattern, root_types " , [
( " [a:a=1 AND a:b=1] " , { " a " } ) ,
( " [a:a=1 AND a:b=1 OR c:d=1] " , { " a " , " c " } ) ,
( " [a:a=1 AND (a:b=1 OR c:d=1)] " , { " a " } ) ,
( " [(a:a=1 OR b:a=1) AND (b:a=1 OR c:c=1)] " , { " b " } ) ,
( " [(a:a=1 AND a:b=1) OR (b:a=1 AND b:c=1)] " , { " a " , " b " } ) ,
] ,
)
def test_comparison_expression_root_types ( pattern , root_types ) :
ast = create_pattern_object ( pattern )
assert ast . operand . root_types == root_types
@pytest.mark.parametrize (
" pattern " , [
" [a:b=1 AND b:c=1] " ,
" [a:b=1 AND (b:c=1 OR c:d=1)] " ,
" [(a:b=1 OR b:c=1) AND (c:d=1 OR d:e=1)] " ,
" [(a:b=1 AND b:c=1) OR (b:c=1 AND c:d=1)] " ,
] ,
)
def test_comparison_expression_root_types_error ( pattern ) :
with pytest . raises ( ValueError ) :
create_pattern_object ( pattern )
2018-07-03 13:00:18 +02:00
def test_hex ( ) :
2018-07-13 17:10:05 +02:00
exp_and = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" file:mime_type " ,
" image/bmp " ,
) ,
stix2 . EqualityComparisonExpression (
" file:magic_number_hex " ,
stix2 . HexConstant ( " ffd8 " ) ,
) ,
] )
2018-07-03 13:00:18 +02:00
exp = stix2 . ObservationExpression ( exp_and )
assert str ( exp ) == " [file:mime_type = ' image/bmp ' AND file:magic_number_hex = h ' ffd8 ' ] "
2020-03-27 16:22:00 +01:00
def test_parsing_hex ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [file:magic_number_hex = h ' ffd8 ' ] " , version = " 2.1 " )
2020-03-27 16:22:00 +01:00
assert str ( patt_obj ) == " [file:magic_number_hex = h ' ffd8 ' ] "
2018-07-03 13:00:18 +02:00
def test_multiple_qualifiers ( ) :
2018-07-13 17:10:05 +02:00
exp_and = stix2 . AndBooleanExpression ( [
stix2 . EqualityComparisonExpression (
" network-traffic:dst_ref.type " ,
" domain-name " ,
) ,
stix2 . EqualityComparisonExpression (
" network-traffic:dst_ref.value " ,
" example.com " ,
) ,
] )
2018-07-03 13:00:18 +02:00
exp_ob = stix2 . ObservationExpression ( exp_and )
qual_rep = stix2 . RepeatQualifier ( 5 )
qual_within = stix2 . WithinQualifier ( stix2 . IntegerConstant ( 1800 ) )
exp = stix2 . QualifiedObservationExpression ( stix2 . QualifiedObservationExpression ( exp_ob , qual_rep ) , qual_within )
assert str ( exp ) == " [network-traffic:dst_ref.type = ' domain-name ' AND network-traffic:dst_ref.value = ' example.com ' ] REPEATS 5 TIMES WITHIN 1800 SECONDS " # noqa
def test_set_op ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . ObservationExpression ( stix2 . IsSubsetComparisonExpression (
" network-traffic:dst_ref.value " ,
" 2001:0db8:dead:beef:0000:0000:0000:0000/64 " ,
) )
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " [network-traffic:dst_ref.value ISSUBSET ' 2001:0db8:dead:beef:0000:0000:0000:0000/64 ' ] "
def test_timestamp ( ) :
ts = stix2 . TimestampConstant ( ' 2014-01-13T07:03:17Z ' )
assert str ( ts ) == " t ' 2014-01-13T07:03:17Z ' "
def test_boolean ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . EqualityComparisonExpression (
" email-message:is_multipart " ,
True ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " email-message:is_multipart = true "
def test_binary ( ) :
const = stix2 . BinaryConstant ( " dGhpcyBpcyBhIHRlc3Q= " )
2018-07-13 17:10:05 +02:00
exp = stix2 . EqualityComparisonExpression (
" artifact:payload_bin " ,
const ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " artifact:payload_bin = b ' dGhpcyBpcyBhIHRlc3Q= ' "
2020-03-27 16:22:00 +01:00
def test_parsing_binary ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [artifact:payload_bin = b ' dGhpcyBpcyBhIHRlc3Q= ' ] " , version = " 2.1 " )
2020-03-27 16:22:00 +01:00
assert str ( patt_obj ) == " [artifact:payload_bin = b ' dGhpcyBpcyBhIHRlc3Q= ' ] "
2018-07-03 13:00:18 +02:00
def test_list ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . InComparisonExpression (
" process:name " ,
[ ' proccy ' , ' proximus ' , ' badproc ' ] ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " process:name IN ( ' proccy ' , ' proximus ' , ' badproc ' ) "
def test_list2 ( ) :
# alternate way to construct an "IN" Comparison Expression
2018-07-13 17:10:05 +02:00
exp = stix2 . EqualityComparisonExpression (
" process:name " ,
[ ' proccy ' , ' proximus ' , ' badproc ' ] ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " process:name IN ( ' proccy ' , ' proximus ' , ' badproc ' ) "
def test_invalid_constant_type ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-13 17:10:05 +02:00
stix2 . EqualityComparisonExpression (
" artifact:payload_bin " ,
{ ' foo ' : ' bar ' } ,
)
2018-07-03 13:00:18 +02:00
def test_invalid_integer_constant ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . IntegerConstant ( ' foo ' )
def test_invalid_timestamp_constant ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . TimestampConstant ( ' foo ' )
def test_invalid_float_constant ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . FloatConstant ( ' foo ' )
2018-07-13 17:10:05 +02:00
@pytest.mark.parametrize (
" data, result " , [
( True , True ) ,
( False , False ) ,
( ' True ' , True ) ,
( ' False ' , False ) ,
( ' true ' , True ) ,
( ' false ' , False ) ,
( ' t ' , True ) ,
( ' f ' , False ) ,
( ' T ' , True ) ,
( ' F ' , False ) ,
( 1 , True ) ,
( 0 , False ) ,
] ,
)
2018-07-03 13:00:18 +02:00
def test_boolean_constant ( data , result ) :
boolean = stix2 . BooleanConstant ( data )
assert boolean . value == result
def test_invalid_boolean_constant ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . BooleanConstant ( ' foo ' )
2018-07-13 17:10:05 +02:00
@pytest.mark.parametrize (
" hashtype, data " , [
( ' MD5 ' , ' zzz ' ) ,
2020-05-14 00:17:17 +02:00
( ' SSDEEP ' , ' zzz== ' ) ,
2018-07-13 17:10:05 +02:00
] ,
)
2018-07-03 13:00:18 +02:00
def test_invalid_hash_constant ( hashtype , data ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . HashConstant ( data , hashtype )
def test_invalid_hex_constant ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . HexConstant ( ' mm ' )
def test_invalid_binary_constant ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . BinaryConstant ( ' foo ' )
def test_escape_quotes_and_backslashes ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . MatchesComparisonExpression (
" file:name " ,
" ^Final Report.+ \\ .exe$ " ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " file:name MATCHES ' ^Final Report.+ \\ \\ .exe$ ' "
def test_like ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . LikeComparisonExpression (
" directory:path " ,
" C: \\ Windows \\ % \\ foo " ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " directory:path LIKE ' C: \\ \\ Windows \\ \\ % \\ \\ foo ' "
def test_issuperset ( ) :
2018-07-13 17:10:05 +02:00
exp = stix2 . IsSupersetComparisonExpression (
" ipv4-addr:value " ,
" 198.51.100.0/24 " ,
)
2018-07-03 13:00:18 +02:00
assert str ( exp ) == " ipv4-addr:value ISSUPERSET ' 198.51.100.0/24 ' "
def test_repeat_qualifier ( ) :
qual = stix2 . RepeatQualifier ( stix2 . IntegerConstant ( 5 ) )
assert str ( qual ) == ' REPEATS 5 TIMES '
def test_invalid_repeat_qualifier ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . RepeatQualifier ( ' foo ' )
def test_invalid_within_qualifier ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-03 13:00:18 +02:00
stix2 . WithinQualifier ( ' foo ' )
def test_startstop_qualifier ( ) :
2018-07-13 17:10:05 +02:00
qual = stix2 . StartStopQualifier (
stix2 . TimestampConstant ( ' 2016-06-01T00:00:00Z ' ) ,
datetime . datetime ( 2017 , 3 , 12 , 8 , 30 , 0 ) ,
)
2018-07-03 13:00:18 +02:00
assert str ( qual ) == " START t ' 2016-06-01T00:00:00Z ' STOP t ' 2017-03-12T08:30:00Z ' "
2018-07-13 17:10:05 +02:00
qual2 = stix2 . StartStopQualifier (
datetime . date ( 2016 , 6 , 1 ) ,
stix2 . TimestampConstant ( ' 2016-07-01T00:00:00Z ' ) ,
)
2018-07-03 13:00:18 +02:00
assert str ( qual2 ) == " START t ' 2016-06-01T00:00:00Z ' STOP t ' 2016-07-01T00:00:00Z ' "
def test_invalid_startstop_qualifier ( ) :
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-13 17:10:05 +02:00
stix2 . StartStopQualifier (
' foo ' ,
stix2 . TimestampConstant ( ' 2016-06-01T00:00:00Z ' ) ,
)
2018-07-03 13:00:18 +02:00
2019-07-09 19:34:19 +02:00
with pytest . raises ( ValueError ) :
2018-07-13 17:10:05 +02:00
stix2 . StartStopQualifier (
datetime . date ( 2016 , 6 , 1 ) ,
' foo ' ,
)
2018-07-03 13:00:18 +02:00
2020-09-13 01:11:00 +02:00
@pytest.mark.parametrize (
" input_, expected_class, expected_value " , [
( 1 , stix2 . patterns . IntegerConstant , 1 ) ,
( 1.5 , stix2 . patterns . FloatConstant , 1.5 ) ,
( " abc " , stix2 . patterns . StringConstant , " abc " ) ,
( True , stix2 . patterns . BooleanConstant , True ) ,
(
" 2001-02-10T21:36:15Z " , stix2 . patterns . TimestampConstant ,
stix2 . utils . STIXdatetime ( 2001 , 2 , 10 , 21 , 36 , 15 , tzinfo = pytz . utc ) ,
) ,
(
datetime . datetime ( 2001 , 2 , 10 , 21 , 36 , 15 , tzinfo = pytz . utc ) ,
stix2 . patterns . TimestampConstant ,
stix2 . utils . STIXdatetime ( 2001 , 2 , 10 , 21 , 36 , 15 , tzinfo = pytz . utc ) ,
) ,
] ,
)
def test_make_constant_simple ( input_ , expected_class , expected_value ) :
const = stix2 . patterns . make_constant ( input_ )
assert isinstance ( const , expected_class )
assert const . value == expected_value
def test_make_constant_list ( ) :
2020-09-13 01:33:56 +02:00
list_const = stix2 . patterns . make_constant ( [ 1 , 2 , 3 ] )
2020-09-13 01:11:00 +02:00
assert isinstance ( list_const , stix2 . patterns . ListConstant )
assert all (
isinstance ( elt , stix2 . patterns . IntegerConstant )
for elt in list_const . value
)
assert all (
int_const . value == test_elt
2020-09-13 01:33:56 +02:00
for int_const , test_elt in zip ( list_const . value , [ 1 , 2 , 3 ] )
2020-09-13 01:11:00 +02:00
)
2018-07-03 13:00:18 +02:00
def test_make_constant_already_a_constant ( ) :
str_const = stix2 . StringConstant ( ' Foo ' )
result = stix2 . patterns . make_constant ( str_const )
assert result is str_const
2018-12-11 19:07:53 +01:00
def test_parsing_comparison_expression ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [file:hashes. ' SHA-256 ' = ' aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f ' ] " , version = " 2.1 " )
2018-12-11 19:07:53 +01:00
assert str ( patt_obj ) == " [file:hashes. ' SHA-256 ' = ' aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f ' ] "
2020-03-27 16:22:00 +01:00
def test_parsing_repeat_and_within_qualified_expression ( ) :
2018-12-11 19:07:53 +01:00
patt_obj = create_pattern_object (
2018-12-11 19:23:43 +01:00
" [network-traffic:dst_ref.type = ' domain-name ' AND network-traffic:dst_ref.value = ' example.com ' ] REPEATS 5 TIMES WITHIN 1800 SECONDS " ,
2020-04-03 16:45:36 +02:00
version = " 2.1 " ,
2018-12-11 19:23:43 +01:00
)
2018-12-11 19:07:53 +01:00
assert str (
2018-12-11 19:23:43 +01:00
patt_obj ,
) == " [network-traffic:dst_ref.type = ' domain-name ' AND network-traffic:dst_ref.value = ' example.com ' ] REPEATS 5 TIMES WITHIN 1800 SECONDS "
2018-12-11 19:07:53 +01:00
2020-03-27 16:22:00 +01:00
def test_parsing_start_stop_qualified_expression ( ) :
patt_obj = create_pattern_object (
2020-03-27 17:33:24 +01:00
" [network-traffic:dst_ref.type = ' domain-name ' AND network-traffic:dst_ref.value = ' example.com ' ] START t ' 2016-06-01T00:00:00Z ' STOP t ' 2017-03-12T08:30:00Z ' " , # noqa
2020-04-03 16:45:36 +02:00
version = " 2.1 " ,
2020-03-27 16:22:00 +01:00
)
assert str (
patt_obj ,
2020-03-27 17:33:24 +01:00
) == " [network-traffic:dst_ref.type = ' domain-name ' AND network-traffic:dst_ref.value = ' example.com ' ] START t ' 2016-06-01T00:00:00Z ' STOP t ' 2017-03-12T08:30:00Z ' " # noqa
2020-03-27 16:22:00 +01:00
2018-12-11 19:07:53 +01:00
def test_list_constant ( ) :
2020-04-03 16:45:36 +02:00
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.1 " )
2018-12-11 19:07:53 +01:00
assert str ( patt_obj ) == " [network-traffic:src_ref.value IN ( ' 10.0.0.0 ' , ' 10.0.0.1 ' , ' 10.0.0.2 ' )] "
2020-01-27 22:18:41 +01:00
2020-03-27 16:22:00 +01:00
def test_parsing_boolean ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [network-traffic:is_active = true] " , version = " 2.1 " )
2020-03-27 16:22:00 +01:00
assert str ( patt_obj ) == " [network-traffic:is_active = true] "
2020-07-24 17:40:21 +02:00
def test_parsing_mixed_boolean_expression_1 ( ) :
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] " , )
assert str ( patt_obj ) == " [a:b = 1 OR a:b = 2 AND a:b = 3] "
2020-07-25 20:47:40 +02:00
def test_parsing_integer_index ( ) :
patt_obj = create_pattern_object ( " [a:b[1]=2] " )
assert str ( patt_obj ) == " [a:b[1] = 2] "
2020-08-13 23:44:42 +02:00
2020-07-30 21:32:06 +02:00
# 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] "
2020-07-25 20:47:40 +02:00
2020-01-27 22:18:41 +01:00
def test_parsing_multiple_slashes_quotes ( ) :
2020-04-03 16:45:36 +02:00
patt_obj = create_pattern_object ( " [ file:name = ' weird_name \\ ' ' ] " , version = " 2.1 " )
2020-01-27 22:18:41 +01:00
assert str ( patt_obj ) == " [file:name = ' weird_name \\ ' ' ] "
2020-02-19 21:39:23 +01:00
def test_parse_error ( ) :
with pytest . raises ( ParseException ) :
2020-04-03 16:45:36 +02:00
create_pattern_object ( " [ file: name = ' weirdname] " , version = " 2.1 " )