add_visitor - take 2
parent
522e9cedd0
commit
03cceb827d
|
@ -68,3 +68,31 @@ cache.sqlite
|
||||||
# PyCharm
|
# PyCharm
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
### macOS template
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,350 @@
|
||||||
|
import importlib
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
import six
|
||||||
|
from stix2patterns.grammars.STIXPatternLexer import STIXPatternLexer
|
||||||
|
from stix2patterns.grammars.STIXPatternParser import (
|
||||||
|
STIXPatternParser, TerminalNode,
|
||||||
|
)
|
||||||
|
from stix2patterns.grammars.STIXPatternVisitor import STIXPatternVisitor
|
||||||
|
from stix2patterns.validator import STIXPatternErrorListener
|
||||||
|
from stix2.patterns import _BooleanExpression
|
||||||
|
|
||||||
|
from antlr4 import CommonTokenStream, InputStream
|
||||||
|
|
||||||
|
# need to import all classes because we need to access them via globals()
|
||||||
|
from .patterns import *
|
||||||
|
|
||||||
|
def collapse_lists(lists):
|
||||||
|
result = []
|
||||||
|
for c in lists:
|
||||||
|
if isinstance(c, list):
|
||||||
|
result.extend(c)
|
||||||
|
else:
|
||||||
|
result.append(c)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def values_only(things):
|
||||||
|
values = []
|
||||||
|
for x in things:
|
||||||
|
if not isinstance(x, TerminalNode):
|
||||||
|
values.append(x)
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# This class defines a complete generic visitor for a parse tree produced by STIXPatternParser.
|
||||||
|
|
||||||
|
|
||||||
|
class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
|
||||||
|
classes = {}
|
||||||
|
|
||||||
|
def __init__(self, module_suffix, module_name):
|
||||||
|
if module_suffix and module_name:
|
||||||
|
self.module_suffix = module_suffix
|
||||||
|
if STIXPatternVisitorForSTIX2.classes == {}:
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
for k, c in inspect.getmembers(module, inspect.isclass):
|
||||||
|
STIXPatternVisitorForSTIX2.classes[k] = c
|
||||||
|
else:
|
||||||
|
self.module_suffix = None
|
||||||
|
super(STIXPatternVisitor, self).__init__()
|
||||||
|
|
||||||
|
def get_class(self, class_name):
|
||||||
|
if class_name in STIXPatternVisitorForSTIX2.classes:
|
||||||
|
return STIXPatternVisitorForSTIX2.classes[class_name]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def instantiate(self, klass_name, *args):
|
||||||
|
klass_to_instantiate = None
|
||||||
|
if self.module_suffix:
|
||||||
|
klass_to_instantiate = self.get_class(klass_name + "For" + self.module_suffix)
|
||||||
|
if not klass_to_instantiate:
|
||||||
|
# use the classes in python_stix2
|
||||||
|
klass_to_instantiate = globals()[klass_name]
|
||||||
|
return klass_to_instantiate(*args)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#pattern.
|
||||||
|
def visitPattern(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return children[0]
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressions.
|
||||||
|
def visitObservationExpressions(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
if len(children) == 1:
|
||||||
|
return children[0]
|
||||||
|
else:
|
||||||
|
return FollowedByObservationExpression([children[0], children[2]])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressionOr.
|
||||||
|
def visitObservationExpressionOr(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
if len(children) == 1:
|
||||||
|
return children[0]
|
||||||
|
else:
|
||||||
|
return self.instantiate("OrObservationExpression", [children[0], children[2]])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressionAnd.
|
||||||
|
def visitObservationExpressionAnd(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
if len(children) == 1:
|
||||||
|
return children[0]
|
||||||
|
else:
|
||||||
|
return self.instantiate("AndObservationExpression", [children[0], children[2]])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressionRepeated.
|
||||||
|
def visitObservationExpressionRepeated(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("QualifiedObservationExpression", children[0], children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressionSimple.
|
||||||
|
def visitObservationExpressionSimple(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("ObservationExpression", children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressionCompound.
|
||||||
|
def visitObservationExpressionCompound(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("ObservationExpression", children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressionWithin.
|
||||||
|
def visitObservationExpressionWithin(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("QualifiedObservationExpression", children[0], children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#observationExpressionStartStop.
|
||||||
|
def visitObservationExpressionStartStop(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("QualifiedObservationExpression", children[0], children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#comparisonExpression.
|
||||||
|
def visitComparisonExpression(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
if len(children) == 1:
|
||||||
|
return children[0]
|
||||||
|
else:
|
||||||
|
if isinstance(children[0], _BooleanExpression):
|
||||||
|
children[0].operands.append(children[2])
|
||||||
|
return children[0]
|
||||||
|
else:
|
||||||
|
return self.instantiate("OrBooleanExpression", [children[0], children[2]])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#comparisonExpressionAnd.
|
||||||
|
def visitComparisonExpressionAnd(self, ctx):
|
||||||
|
# TODO: NOT
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
if len(children) == 1:
|
||||||
|
return children[0]
|
||||||
|
else:
|
||||||
|
if isinstance(children[0], _BooleanExpression):
|
||||||
|
children[0].operands.append(children[2])
|
||||||
|
return children[0]
|
||||||
|
else:
|
||||||
|
return self.instantiate("AndBooleanExpression", [children[0], children[2]])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestEqual.
|
||||||
|
def visitPropTestEqual(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
operator = children[1].symbol.type
|
||||||
|
negated = negated = operator != STIXPatternParser.EQ
|
||||||
|
return self.instantiate("EqualityComparisonExpression", children[0], children[3 if len(children) > 3 else 2],
|
||||||
|
negated)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestOrder.
|
||||||
|
def visitPropTestOrder(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
operator = children[1].symbol.type
|
||||||
|
if operator == STIXPatternParser.GT:
|
||||||
|
return self.instantiate("GreaterThanComparisonExpression", children[0],
|
||||||
|
children[3 if len(children) > 3 else 2], False)
|
||||||
|
elif operator == STIXPatternParser.LT:
|
||||||
|
return self.instantiate("LessThanComparisonExpression", children[0],
|
||||||
|
children[3 if len(children) > 3 else 2], False)
|
||||||
|
elif operator == STIXPatternParser.GE:
|
||||||
|
return self.instantiate("GreaterThanEqualComparisonExpression", children[0],
|
||||||
|
children[3 if len(children) > 3 else 2], False)
|
||||||
|
elif operator == STIXPatternParser.LE:
|
||||||
|
return self.instantiate("LessThanEqualComparisonExpression", children[0],
|
||||||
|
children[3 if len(children) > 3 else 2], False)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestSet.
|
||||||
|
def visitPropTestSet(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("InComparisonExpression", children[0], children[3 if len(children) > 3 else 2], False)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestLike.
|
||||||
|
def visitPropTestLike(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("LikeComparisonExpression", children[0], children[3 if len(children) > 3 else 2], False)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestRegex.
|
||||||
|
def visitPropTestRegex(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("MatchesComparisonExpression", children[0], children[3 if len(children) > 3 else 2],
|
||||||
|
False)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestIsSubset.
|
||||||
|
def visitPropTestIsSubset(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("IsSubsetComparisonExpression", children[0], children[3 if len(children) > 3 else 2])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestIsSuperset.
|
||||||
|
def visitPropTestIsSuperset(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("IsSupersetComparisonExpression", children[0], children[3 if len(children) > 3 else 2])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#propTestParen.
|
||||||
|
def visitPropTestParen(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("ParentheticalExpression", children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#startStopQualifier.
|
||||||
|
def visitStartStopQualifier(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return StartStopQualifier(children[1], children[3])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#withinQualifier.
|
||||||
|
def visitWithinQualifier(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return WithinQualifier(children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#repeatedQualifier.
|
||||||
|
def visitRepeatedQualifier(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return RepeatQualifier(children[1])
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#objectPath.
|
||||||
|
def visitObjectPath(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
flat_list = collapse_lists(children[2:])
|
||||||
|
property_path = []
|
||||||
|
i = 0
|
||||||
|
while i < len(flat_list):
|
||||||
|
current = flat_list[i]
|
||||||
|
if i == len(flat_list)-1:
|
||||||
|
property_path.append(current)
|
||||||
|
break
|
||||||
|
next = flat_list[i+1]
|
||||||
|
if isinstance(next, TerminalNode):
|
||||||
|
property_path.append(self.instantiate("ListObjectPathComponent", current.property_name, next.getText()))
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
property_path.append(current)
|
||||||
|
i += 1
|
||||||
|
return self.instantiate("ObjectPath", children[0].getText(), property_path)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#objectType.
|
||||||
|
def visitObjectType(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return children[0]
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#firstPathComponent.
|
||||||
|
def visitFirstPathComponent(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
step = children[0].getText()
|
||||||
|
# if step.endswith("_ref"):
|
||||||
|
# return stix2.ReferenceObjectPathComponent(step)
|
||||||
|
# else:
|
||||||
|
return self.instantiate("BasicObjectPathComponent", step, False)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#indexPathStep.
|
||||||
|
def visitIndexPathStep(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return children[1]
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#pathStep.
|
||||||
|
def visitPathStep(self, ctx):
|
||||||
|
return collapse_lists(self.visitChildren(ctx))
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#keyPathStep.
|
||||||
|
def visitKeyPathStep(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
if isinstance(children[1], StringConstant):
|
||||||
|
# special case for hashes
|
||||||
|
return children[1].value
|
||||||
|
else:
|
||||||
|
return self.instantiate("BasicObjectPathComponent", children[1].getText(), True)
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#setLiteral.
|
||||||
|
def visitSetLiteral(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return self.instantiate("ListConstant", values_only(children))
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#primitiveLiteral.
|
||||||
|
def visitPrimitiveLiteral(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return children[0]
|
||||||
|
|
||||||
|
# Visit a parse tree produced by STIXPatternParser#orderableLiteral.
|
||||||
|
def visitOrderableLiteral(self, ctx):
|
||||||
|
children = self.visitChildren(ctx)
|
||||||
|
return children[0]
|
||||||
|
|
||||||
|
def visitTerminal(self, node):
|
||||||
|
if node.symbol.type == STIXPatternParser.IntPosLiteral or node.symbol.type == STIXPatternParser.IntNegLiteral:
|
||||||
|
return IntegerConstant(node.getText())
|
||||||
|
elif node.symbol.type == STIXPatternParser.FloatPosLiteral or node.symbol.type == STIXPatternParser.FloatNegLiteral:
|
||||||
|
return FloatConstant(node.getText())
|
||||||
|
elif node.symbol.type == STIXPatternParser.HexLiteral:
|
||||||
|
return HexConstant(node.getText(), from_parse_tree=True)
|
||||||
|
elif node.symbol.type == STIXPatternParser.BinaryLiteral:
|
||||||
|
return BinaryConstant(node.getText(), from_parse_tree=True)
|
||||||
|
elif node.symbol.type == STIXPatternParser.StringLiteral:
|
||||||
|
return StringConstant(node.getText().strip('\''), from_parse_tree=True)
|
||||||
|
elif node.symbol.type == STIXPatternParser.BoolLiteral:
|
||||||
|
return BooleanConstant(node.getText())
|
||||||
|
elif node.symbol.type == STIXPatternParser.TimestampLiteral:
|
||||||
|
return TimestampConstant(node.getText())
|
||||||
|
else:
|
||||||
|
return node
|
||||||
|
|
||||||
|
def aggregateResult(self, aggregate, nextResult):
|
||||||
|
if aggregate:
|
||||||
|
aggregate.append(nextResult)
|
||||||
|
elif nextResult:
|
||||||
|
aggregate = [nextResult]
|
||||||
|
return aggregate
|
||||||
|
|
||||||
|
|
||||||
|
def create_pattern_object(pattern, module_suffix="", module_name=""):
|
||||||
|
'''
|
||||||
|
Validates a pattern against the STIX Pattern grammar. Error messages are
|
||||||
|
returned in a list. The test passed if the returned list is empty.
|
||||||
|
'''
|
||||||
|
|
||||||
|
start = ''
|
||||||
|
if isinstance(pattern, six.string_types):
|
||||||
|
start = pattern[:2]
|
||||||
|
pattern = InputStream(pattern)
|
||||||
|
|
||||||
|
if not start:
|
||||||
|
start = pattern.readline()[:2]
|
||||||
|
pattern.seek(0)
|
||||||
|
|
||||||
|
parseErrListener = STIXPatternErrorListener()
|
||||||
|
|
||||||
|
lexer = STIXPatternLexer(pattern)
|
||||||
|
# it always adds a console listener by default... remove it.
|
||||||
|
lexer.removeErrorListeners()
|
||||||
|
|
||||||
|
stream = CommonTokenStream(lexer)
|
||||||
|
|
||||||
|
parser = STIXPatternParser(stream)
|
||||||
|
parser.buildParseTrees = True
|
||||||
|
# it always adds a console listener by default... remove it.
|
||||||
|
parser.removeErrorListeners()
|
||||||
|
parser.addErrorListener(parseErrListener)
|
||||||
|
|
||||||
|
# To improve error messages, replace "<INVALID>" in the literal
|
||||||
|
# names with symbolic names. This is a hack, but seemed like
|
||||||
|
# the simplest workaround.
|
||||||
|
for i, lit_name in enumerate(parser.literalNames):
|
||||||
|
if lit_name == u"<INVALID>":
|
||||||
|
parser.literalNames[i] = parser.symbolicNames[i]
|
||||||
|
|
||||||
|
tree = parser.pattern()
|
||||||
|
builder = STIXPatternVisitorForSTIX2(module_suffix, module_name)
|
||||||
|
return builder.visit(tree)
|
|
@ -13,6 +13,14 @@ def escape_quotes_and_backslashes(s):
|
||||||
return s.replace(u'\\', u'\\\\').replace(u"'", u"\\'")
|
return s.replace(u'\\', u'\\\\').replace(u"'", u"\\'")
|
||||||
|
|
||||||
|
|
||||||
|
def quote_if_needed(x):
|
||||||
|
if isinstance(x, str):
|
||||||
|
if x.find("-") != -1:
|
||||||
|
if not x.startswith("'"):
|
||||||
|
return "'" + x + "'"
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
class _Constant(object):
|
class _Constant(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -23,11 +31,13 @@ class StringConstant(_Constant):
|
||||||
Args:
|
Args:
|
||||||
value (str): string value
|
value (str): string value
|
||||||
"""
|
"""
|
||||||
def __init__(self, value):
|
|
||||||
|
def __init__(self, value, from_parse_tree=False):
|
||||||
|
self.needs_to_be_quoted = not from_parse_tree
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "'%s'" % escape_quotes_and_backslashes(self.value)
|
return "'%s'" % (escape_quotes_and_backslashes(self.value) if self.needs_to_be_quoted else self.value)
|
||||||
|
|
||||||
|
|
||||||
class TimestampConstant(_Constant):
|
class TimestampConstant(_Constant):
|
||||||
|
@ -86,8 +96,8 @@ class BooleanConstant(_Constant):
|
||||||
self.value = value
|
self.value = value
|
||||||
return
|
return
|
||||||
|
|
||||||
trues = ['true', 't']
|
trues = ['true', 't', '1']
|
||||||
falses = ['false', 'f']
|
falses = ['false', 'f', '0']
|
||||||
try:
|
try:
|
||||||
if value.lower() in trues:
|
if value.lower() in trues:
|
||||||
self.value = True
|
self.value = True
|
||||||
|
@ -143,7 +153,7 @@ class HashConstant(StringConstant):
|
||||||
vocab_key = _HASH_REGEX[key][1]
|
vocab_key = _HASH_REGEX[key][1]
|
||||||
if not re.match(_HASH_REGEX[key][0], value):
|
if not re.match(_HASH_REGEX[key][0], value):
|
||||||
raise ValueError("'%s' is not a valid %s hash" % (value, vocab_key))
|
raise ValueError("'%s' is not a valid %s hash" % (value, vocab_key))
|
||||||
self.value = value
|
super(HashConstant, self).__init__(value)
|
||||||
|
|
||||||
|
|
||||||
class BinaryConstant(_Constant):
|
class BinaryConstant(_Constant):
|
||||||
|
@ -152,7 +162,13 @@ class BinaryConstant(_Constant):
|
||||||
Args:
|
Args:
|
||||||
value (str): base64 encoded string value
|
value (str): base64 encoded string value
|
||||||
"""
|
"""
|
||||||
def __init__(self, value):
|
|
||||||
|
def __init__(self, value, from_parse_tree=False):
|
||||||
|
# support with or without a 'b'
|
||||||
|
if from_parse_tree:
|
||||||
|
m = re.match("^b'(.+)'$", value)
|
||||||
|
if m:
|
||||||
|
value = m.group(1)
|
||||||
try:
|
try:
|
||||||
base64.b64decode(value)
|
base64.b64decode(value)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
@ -169,10 +185,17 @@ class HexConstant(_Constant):
|
||||||
Args:
|
Args:
|
||||||
value (str): hexadecimal value
|
value (str): hexadecimal value
|
||||||
"""
|
"""
|
||||||
def __init__(self, value):
|
|
||||||
if not re.match('^([a-fA-F0-9]{2})+$', value):
|
def __init__(self, value, from_parse_tree=False):
|
||||||
raise ValueError("must contain an even number of hexadecimal characters")
|
# support with or without an 'h'
|
||||||
self.value = value
|
if not from_parse_tree and re.match('^([a-fA-F0-9]{2})+$', value):
|
||||||
|
self.value = value
|
||||||
|
else:
|
||||||
|
m = re.match("^h'(([a-fA-F0-9]{2})+)'$", value)
|
||||||
|
if m:
|
||||||
|
self.value = m.group(1)
|
||||||
|
else:
|
||||||
|
raise ValueError("must contain an even number of hexadecimal characters")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "h'%s'" % self.value
|
return "h'%s'" % self.value
|
||||||
|
@ -185,10 +208,11 @@ class ListConstant(_Constant):
|
||||||
value (list): list of values
|
value (list): list of values
|
||||||
"""
|
"""
|
||||||
def __init__(self, values):
|
def __init__(self, values):
|
||||||
self.value = values
|
# handle _Constants or make a _Constant
|
||||||
|
self.value = [x if isinstance(x, _Constant) else make_constant(x) for x in values]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "(" + ", ".join([("%s" % make_constant(x)) for x in self.value]) + ")"
|
return "(" + ", ".join(["%s" % x for x in self.value]) + ")"
|
||||||
|
|
||||||
|
|
||||||
def make_constant(value):
|
def make_constant(value):
|
||||||
|
@ -229,7 +253,10 @@ class _ObjectPathComponent(object):
|
||||||
parse1 = component_name.split("[")
|
parse1 = component_name.split("[")
|
||||||
return ListObjectPathComponent(parse1[0], parse1[1][:-1])
|
return ListObjectPathComponent(parse1[0], parse1[1][:-1])
|
||||||
else:
|
else:
|
||||||
return BasicObjectPathComponent(component_name)
|
return BasicObjectPathComponent(component_name, False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return quote_if_needed(self.property_name)
|
||||||
|
|
||||||
|
|
||||||
class BasicObjectPathComponent(_ObjectPathComponent):
|
class BasicObjectPathComponent(_ObjectPathComponent):
|
||||||
|
@ -243,14 +270,11 @@ class BasicObjectPathComponent(_ObjectPathComponent):
|
||||||
property_name (str): object property name
|
property_name (str): object property name
|
||||||
is_key (bool): is dictionary key, default: False
|
is_key (bool): is dictionary key, default: False
|
||||||
"""
|
"""
|
||||||
def __init__(self, property_name, is_key=False):
|
def __init__(self, property_name, is_key):
|
||||||
self.property_name = property_name
|
self.property_name = property_name
|
||||||
# TODO: set is_key to True if this component is a dictionary key
|
# TODO: set is_key to True if this component is a dictionary key
|
||||||
# self.is_key = is_key
|
# self.is_key = is_key
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.property_name
|
|
||||||
|
|
||||||
|
|
||||||
class ListObjectPathComponent(_ObjectPathComponent):
|
class ListObjectPathComponent(_ObjectPathComponent):
|
||||||
"""List object path component (for an observation or expression)
|
"""List object path component (for an observation or expression)
|
||||||
|
@ -264,7 +288,7 @@ class ListObjectPathComponent(_ObjectPathComponent):
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s[%s]" % (self.property_name, self.index)
|
return "%s[%s]" % (quote_if_needed(self.property_name), self.index)
|
||||||
|
|
||||||
|
|
||||||
class ReferenceObjectPathComponent(_ObjectPathComponent):
|
class ReferenceObjectPathComponent(_ObjectPathComponent):
|
||||||
|
@ -276,9 +300,6 @@ class ReferenceObjectPathComponent(_ObjectPathComponent):
|
||||||
def __init__(self, reference_property_name):
|
def __init__(self, reference_property_name):
|
||||||
self.property_name = reference_property_name
|
self.property_name = reference_property_name
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.property_name
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectPath(object):
|
class ObjectPath(object):
|
||||||
"""Pattern operand object (property) path
|
"""Pattern operand object (property) path
|
||||||
|
@ -289,12 +310,14 @@ class ObjectPath(object):
|
||||||
"""
|
"""
|
||||||
def __init__(self, object_type_name, property_path):
|
def __init__(self, object_type_name, property_path):
|
||||||
self.object_type_name = object_type_name
|
self.object_type_name = object_type_name
|
||||||
self.property_path = [x if isinstance(x, _ObjectPathComponent) else
|
self.property_path = [
|
||||||
_ObjectPathComponent.create_ObjectPathComponent(x)
|
x if isinstance(x, _ObjectPathComponent) else
|
||||||
for x in property_path]
|
_ObjectPathComponent.create_ObjectPathComponent(x)
|
||||||
|
for x in property_path
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s:%s" % (self.object_type_name, ".".join(["%s" % x for x in self.property_path]))
|
return "%s:%s" % (self.object_type_name, ".".join(["%s" % quote_if_needed(x) for x in self.property_path]))
|
||||||
|
|
||||||
def merge(self, other):
|
def merge(self, other):
|
||||||
"""Extend the object property with that of the supplied object property path"""
|
"""Extend the object property with that of the supplied object property path"""
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
{
|
{
|
||||||
"id": "bundle--f64de948-7067-4534-8018-85f03d470625",
|
"id": "bundle--f64de948-7067-4534-8018-85f03d470625",
|
||||||
"objects": [
|
"objects": [
|
||||||
|
{
|
||||||
|
"created": "2017-05-31T21:32:58.226477Z",
|
||||||
|
"created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
|
||||||
|
"description": "Rover is malware suspected of being used for espionage purposes. It was used in 2015 in a targeted email sent to an Indian Ambassador to Afghanistan.[[Citation: Palo Alto Rover]]",
|
||||||
|
"external_references": [
|
||||||
{
|
{
|
||||||
"created": "2017-05-31T21:32:58.226477Z",
|
"external_id": "S0090",
|
||||||
"created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
|
"source_name": "mitre-attack",
|
||||||
"description": "Rover is malware suspected of being used for espionage purposes. It was used in 2015 in a targeted email sent to an Indian Ambassador to Afghanistan.[[Citation: Palo Alto Rover]]",
|
"url": "https://attack.mitre.org/wiki/Software/S0090"
|
||||||
"external_references": [
|
},
|
||||||
{
|
{
|
||||||
"external_id": "S0090",
|
"description": "Ray, V., Hayashi, K. (2016, February 29). New Malware \u2018Rover\u2019 Targets Indian Ambassador to Afghanistan. Retrieved February 29, 2016.",
|
||||||
"source_name": "mitre-attack",
|
"source_name": "Palo Alto Rover",
|
||||||
"url": "https://attack.mitre.org/wiki/Software/S0090"
|
"url": "http://researchcenter.paloaltonetworks.com/2016/02/new-malware-rover-targets-indian-ambassador-to-afghanistan/"
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Ray, V., Hayashi, K. (2016, February 29). New Malware \u2018Rover\u2019 Targets Indian Ambassador to Afghanistan. Retrieved February 29, 2016.",
|
|
||||||
"source_name": "Palo Alto Rover",
|
|
||||||
"url": "http://researchcenter.paloaltonetworks.com/2016/02/new-malware-rover-targets-indian-ambassador-to-afghanistan/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
|
|
||||||
"labels": [
|
|
||||||
"malware"
|
|
||||||
],
|
|
||||||
"modified": "2017-05-31T21:32:58.226477Z",
|
|
||||||
"name": "Rover",
|
|
||||||
"object_marking_refs": [
|
|
||||||
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
|
|
||||||
],
|
|
||||||
"type": "malware"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"spec_version": "2.0",
|
"id": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
|
||||||
"type": "bundle"
|
"labels": [
|
||||||
|
"malware"
|
||||||
|
],
|
||||||
|
"modified": "2017-05-31T21:32:58.226477Z",
|
||||||
|
"name": "Rover",
|
||||||
|
"object_marking_refs": [
|
||||||
|
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
|
||||||
|
],
|
||||||
|
"type": "malware"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spec_version": "2.0",
|
||||||
|
"type": "bundle"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
{
|
{
|
||||||
"type": "malware",
|
"type": "malware",
|
||||||
"id": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
|
"id": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
|
||||||
"created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
|
"created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
|
||||||
"created": "2017-05-31T21:32:58.226Z",
|
"created": "2017-05-31T21:32:58.226Z",
|
||||||
"modified": "2018-11-01T23:24:48.456Z",
|
"modified": "2018-11-01T23:24:48.456Z",
|
||||||
"name": "Rover",
|
"name": "Rover",
|
||||||
"description": "Rover is malware suspected of being used for espionage purposes. It was used in 2015 in a targeted email sent to an Indian Ambassador to Afghanistan.[[Citation: Palo Alto Rover]]",
|
"description": "Rover is malware suspected of being used for espionage purposes. It was used in 2015 in a targeted email sent to an Indian Ambassador to Afghanistan.[[Citation: Palo Alto Rover]]",
|
||||||
"labels": [
|
"labels": [
|
||||||
"version two"
|
"version two"
|
||||||
],
|
],
|
||||||
"external_references": [
|
"external_references": [
|
||||||
{
|
{
|
||||||
"source_name": "mitre-attack",
|
"source_name": "mitre-attack",
|
||||||
"url": "https://attack.mitre.org/wiki/Software/S0090",
|
"url": "https://attack.mitre.org/wiki/Software/S0090",
|
||||||
"external_id": "S0090"
|
"external_id": "S0090"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source_name": "Palo Alto Rover",
|
"source_name": "Palo Alto Rover",
|
||||||
"description": "Ray, V., Hayashi, K. (2016, February 29). New Malware \u2018Rover\u2019 Targets Indian Ambassador to Afghanistan. Retrieved February 29, 2016.",
|
"description": "Ray, V., Hayashi, K. (2016, February 29). New Malware \u2018Rover\u2019 Targets Indian Ambassador to Afghanistan. Retrieved February 29, 2016.",
|
||||||
"url": "http://researchcenter.paloaltonetworks.com/2016/02/new-malware-rover-targets-indian-ambassador-to-afghanistan/"
|
"url": "http://researchcenter.paloaltonetworks.com/2016/02/new-malware-rover-targets-indian-ambassador-to-afghanistan/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"object_marking_refs": [
|
"object_marking_refs": [
|
||||||
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
|
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
{
|
{
|
||||||
"type": "malware",
|
"type": "malware",
|
||||||
"id": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
|
"id": "malware--6b616fc1-1505-48e3-8b2c-0d19337bff38",
|
||||||
"created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
|
"created_by_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
|
||||||
"created": "2017-05-31T21:32:58.226Z",
|
"created": "2017-05-31T21:32:58.226Z",
|
||||||
"modified": "2018-11-01T23:24:48.457Z",
|
"modified": "2018-11-01T23:24:48.457Z",
|
||||||
"name": "Rover",
|
"name": "Rover",
|
||||||
"description": "Rover is malware suspected of being used for espionage purposes. It was used in 2015 in a targeted email sent to an Indian Ambassador to Afghanistan.[[Citation: Palo Alto Rover]]",
|
"description": "Rover is malware suspected of being used for espionage purposes. It was used in 2015 in a targeted email sent to an Indian Ambassador to Afghanistan.[[Citation: Palo Alto Rover]]",
|
||||||
"labels": [
|
"labels": [
|
||||||
"version three"
|
"version three"
|
||||||
],
|
],
|
||||||
"external_references": [
|
"external_references": [
|
||||||
{
|
{
|
||||||
"source_name": "mitre-attack",
|
"source_name": "mitre-attack",
|
||||||
"url": "https://attack.mitre.org/wiki/Software/S0090",
|
"url": "https://attack.mitre.org/wiki/Software/S0090",
|
||||||
"external_id": "S0090"
|
"external_id": "S0090"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source_name": "Palo Alto Rover",
|
"source_name": "Palo Alto Rover",
|
||||||
"description": "Ray, V., Hayashi, K. (2016, February 29). New Malware \u2018Rover\u2019 Targets Indian Ambassador to Afghanistan. Retrieved February 29, 2016.",
|
"description": "Ray, V., Hayashi, K. (2016, February 29). New Malware \u2018Rover\u2019 Targets Indian Ambassador to Afghanistan. Retrieved February 29, 2016.",
|
||||||
"url": "http://researchcenter.paloaltonetworks.com/2016/02/new-malware-rover-targets-indian-ambassador-to-afghanistan/"
|
"url": "http://researchcenter.paloaltonetworks.com/2016/02/new-malware-rover-targets-indian-ambassador-to-afghanistan/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"object_marking_refs": [
|
"object_marking_refs": [
|
||||||
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
|
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,42 +4,61 @@ import pytest
|
||||||
|
|
||||||
import stix2
|
import stix2
|
||||||
|
|
||||||
|
from stix2.STIXPatternVisitor import create_pattern_object
|
||||||
|
|
||||||
|
|
||||||
def test_create_comparison_expression():
|
def test_create_comparison_expression():
|
||||||
|
exp = stix2.EqualityComparisonExpression(
|
||||||
exp = stix2.EqualityComparisonExpression("file:hashes.'SHA-256'",
|
"file:hashes.'SHA-256'",
|
||||||
stix2.HashConstant("aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f", "SHA-256")) # noqa
|
stix2.HashConstant("aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f", "SHA-256"),
|
||||||
|
) # noqa
|
||||||
|
|
||||||
assert str(exp) == "file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f'"
|
assert str(exp) == "file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f'"
|
||||||
|
|
||||||
|
|
||||||
def test_boolean_expression():
|
def test_boolean_expression():
|
||||||
exp1 = stix2.MatchesComparisonExpression("email-message:from_ref.value",
|
exp1 = stix2.MatchesComparisonExpression(
|
||||||
stix2.StringConstant(".+\\@example\\.com$"))
|
"email-message:from_ref.value",
|
||||||
exp2 = stix2.MatchesComparisonExpression("email-message:body_multipart[*].body_raw_ref.name",
|
stix2.StringConstant(".+\\@example\\.com$"),
|
||||||
stix2.StringConstant("^Final Report.+\\.exe$"))
|
)
|
||||||
|
exp2 = stix2.MatchesComparisonExpression(
|
||||||
|
"email-message:body_multipart[*].body_raw_ref.name",
|
||||||
|
stix2.StringConstant("^Final Report.+\\.exe$"),
|
||||||
|
)
|
||||||
exp = stix2.AndBooleanExpression([exp1, exp2])
|
exp = 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
|
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():
|
def test_boolean_expression_with_parentheses():
|
||||||
exp1 = stix2.MatchesComparisonExpression(stix2.ObjectPath("email-message",
|
exp1 = stix2.MatchesComparisonExpression(
|
||||||
[stix2.ReferenceObjectPathComponent("from_ref"),
|
stix2.ObjectPath(
|
||||||
stix2.BasicObjectPathComponent("value")]),
|
"email-message",
|
||||||
stix2.StringConstant(".+\\@example\\.com$"))
|
[
|
||||||
exp2 = stix2.MatchesComparisonExpression("email-message:body_multipart[*].body_raw_ref.name",
|
stix2.ReferenceObjectPathComponent("from_ref"),
|
||||||
stix2.StringConstant("^Final Report.+\\.exe$"))
|
stix2.BasicObjectPathComponent("value", False),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
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]))
|
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
|
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():
|
def test_hash_followed_by_registryKey_expression_python_constant():
|
||||||
hash_exp = stix2.EqualityComparisonExpression("file:hashes.MD5",
|
hash_exp = stix2.EqualityComparisonExpression(
|
||||||
stix2.HashConstant("79054025255fb1a26e4bc422aef54eb4", "MD5"))
|
"file:hashes.MD5",
|
||||||
|
stix2.HashConstant("79054025255fb1a26e4bc422aef54eb4", "MD5"),
|
||||||
|
)
|
||||||
o_exp1 = stix2.ObservationExpression(hash_exp)
|
o_exp1 = stix2.ObservationExpression(hash_exp)
|
||||||
reg_exp = stix2.EqualityComparisonExpression(stix2.ObjectPath("windows-registry-key", ["key"]),
|
reg_exp = stix2.EqualityComparisonExpression(
|
||||||
stix2.StringConstant("HKEY_LOCAL_MACHINE\\foo\\bar"))
|
stix2.ObjectPath("windows-registry-key", ["key"]),
|
||||||
|
stix2.StringConstant("HKEY_LOCAL_MACHINE\\foo\\bar"),
|
||||||
|
)
|
||||||
o_exp2 = stix2.ObservationExpression(reg_exp)
|
o_exp2 = stix2.ObservationExpression(reg_exp)
|
||||||
fb_exp = stix2.FollowedByObservationExpression([o_exp1, o_exp2])
|
fb_exp = stix2.FollowedByObservationExpression([o_exp1, o_exp2])
|
||||||
para_exp = stix2.ParentheticalExpression(fb_exp)
|
para_exp = stix2.ParentheticalExpression(fb_exp)
|
||||||
|
@ -49,11 +68,15 @@ def test_hash_followed_by_registryKey_expression_python_constant():
|
||||||
|
|
||||||
|
|
||||||
def test_hash_followed_by_registryKey_expression():
|
def test_hash_followed_by_registryKey_expression():
|
||||||
hash_exp = stix2.EqualityComparisonExpression("file:hashes.MD5",
|
hash_exp = stix2.EqualityComparisonExpression(
|
||||||
stix2.HashConstant("79054025255fb1a26e4bc422aef54eb4", "MD5"))
|
"file:hashes.MD5",
|
||||||
|
stix2.HashConstant("79054025255fb1a26e4bc422aef54eb4", "MD5"),
|
||||||
|
)
|
||||||
o_exp1 = stix2.ObservationExpression(hash_exp)
|
o_exp1 = stix2.ObservationExpression(hash_exp)
|
||||||
reg_exp = stix2.EqualityComparisonExpression(stix2.ObjectPath("windows-registry-key", ["key"]),
|
reg_exp = stix2.EqualityComparisonExpression(
|
||||||
stix2.StringConstant("HKEY_LOCAL_MACHINE\\foo\\bar"))
|
stix2.ObjectPath("windows-registry-key", ["key"]),
|
||||||
|
stix2.StringConstant("HKEY_LOCAL_MACHINE\\foo\\bar"),
|
||||||
|
)
|
||||||
o_exp2 = stix2.ObservationExpression(reg_exp)
|
o_exp2 = stix2.ObservationExpression(reg_exp)
|
||||||
fb_exp = stix2.FollowedByObservationExpression([o_exp1, o_exp2])
|
fb_exp = stix2.FollowedByObservationExpression([o_exp1, o_exp2])
|
||||||
para_exp = stix2.ParentheticalExpression(fb_exp)
|
para_exp = stix2.ParentheticalExpression(fb_exp)
|
||||||
|
@ -63,31 +86,44 @@ def test_hash_followed_by_registryKey_expression():
|
||||||
|
|
||||||
|
|
||||||
def test_file_observable_expression():
|
def test_file_observable_expression():
|
||||||
exp1 = stix2.EqualityComparisonExpression("file:hashes.'SHA-256'",
|
exp1 = stix2.EqualityComparisonExpression(
|
||||||
stix2.HashConstant(
|
"file:hashes.'SHA-256'",
|
||||||
"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f",
|
stix2.HashConstant(
|
||||||
'SHA-256'))
|
"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f",
|
||||||
|
'SHA-256',
|
||||||
|
),
|
||||||
|
)
|
||||||
exp2 = stix2.EqualityComparisonExpression("file:mime_type", stix2.StringConstant("application/x-pdf"))
|
exp2 = stix2.EqualityComparisonExpression("file:mime_type", stix2.StringConstant("application/x-pdf"))
|
||||||
bool_exp = stix2.ObservationExpression(stix2.AndBooleanExpression([exp1, exp2]))
|
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
|
assert str(bool_exp) == "[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f' AND file:mime_type = 'application/x-pdf']" # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("observation_class, op", [
|
@pytest.mark.parametrize(
|
||||||
(stix2.AndObservationExpression, 'AND'),
|
"observation_class, op", [
|
||||||
(stix2.OrObservationExpression, 'OR'),
|
(stix2.AndObservationExpression, 'AND'),
|
||||||
])
|
(stix2.OrObservationExpression, 'OR'),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_multiple_file_observable_expression(observation_class, op):
|
def test_multiple_file_observable_expression(observation_class, op):
|
||||||
exp1 = stix2.EqualityComparisonExpression("file:hashes.'SHA-256'",
|
exp1 = stix2.EqualityComparisonExpression(
|
||||||
stix2.HashConstant(
|
"file:hashes.'SHA-256'",
|
||||||
"bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c",
|
stix2.HashConstant(
|
||||||
'SHA-256'))
|
"bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c",
|
||||||
exp2 = stix2.EqualityComparisonExpression("file:hashes.MD5",
|
'SHA-256',
|
||||||
stix2.HashConstant("cead3f77f6cda6ec00f57d76c9a6879f", "MD5"))
|
),
|
||||||
|
)
|
||||||
|
exp2 = stix2.EqualityComparisonExpression(
|
||||||
|
"file:hashes.MD5",
|
||||||
|
stix2.HashConstant("cead3f77f6cda6ec00f57d76c9a6879f", "MD5"),
|
||||||
|
)
|
||||||
bool1_exp = stix2.OrBooleanExpression([exp1, exp2])
|
bool1_exp = stix2.OrBooleanExpression([exp1, exp2])
|
||||||
exp3 = stix2.EqualityComparisonExpression("file:hashes.'SHA-256'",
|
exp3 = stix2.EqualityComparisonExpression(
|
||||||
stix2.HashConstant(
|
"file:hashes.'SHA-256'",
|
||||||
"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f",
|
stix2.HashConstant(
|
||||||
'SHA-256'))
|
"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f",
|
||||||
|
'SHA-256',
|
||||||
|
),
|
||||||
|
)
|
||||||
op1_exp = stix2.ObservationExpression(bool1_exp)
|
op1_exp = stix2.ObservationExpression(bool1_exp)
|
||||||
op2_exp = stix2.ObservationExpression(exp3)
|
op2_exp = stix2.ObservationExpression(exp3)
|
||||||
exp = observation_class([op1_exp, op2_exp])
|
exp = observation_class([op1_exp, op2_exp])
|
||||||
|
@ -97,34 +133,46 @@ def test_multiple_file_observable_expression(observation_class, op):
|
||||||
def test_root_types():
|
def test_root_types():
|
||||||
ast = stix2.ObservationExpression(
|
ast = stix2.ObservationExpression(
|
||||||
stix2.AndBooleanExpression(
|
stix2.AndBooleanExpression(
|
||||||
[stix2.ParentheticalExpression(
|
[
|
||||||
stix2.OrBooleanExpression([
|
stix2.ParentheticalExpression(
|
||||||
stix2.EqualityComparisonExpression("a:b", stix2.StringConstant("1")),
|
stix2.OrBooleanExpression([
|
||||||
stix2.EqualityComparisonExpression("b:c", stix2.StringConstant("2"))])),
|
stix2.EqualityComparisonExpression("a:b", stix2.StringConstant("1")),
|
||||||
stix2.EqualityComparisonExpression(u"b:d", stix2.StringConstant("3"))]))
|
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']"
|
assert str(ast) == "[(a:b = '1' OR b:c = '2') AND b:d = '3']"
|
||||||
|
|
||||||
|
|
||||||
def test_artifact_payload():
|
def test_artifact_payload():
|
||||||
exp1 = stix2.EqualityComparisonExpression("artifact:mime_type",
|
exp1 = stix2.EqualityComparisonExpression(
|
||||||
"application/vnd.tcpdump.pcap")
|
"artifact:mime_type",
|
||||||
exp2 = stix2.MatchesComparisonExpression("artifact:payload_bin",
|
"application/vnd.tcpdump.pcap",
|
||||||
stix2.StringConstant("\\xd4\\xc3\\xb2\\xa1\\x02\\x00\\x04\\x00"))
|
)
|
||||||
|
exp2 = stix2.MatchesComparisonExpression(
|
||||||
|
"artifact:payload_bin",
|
||||||
|
stix2.StringConstant("\\xd4\\xc3\\xb2\\xa1\\x02\\x00\\x04\\x00"),
|
||||||
|
)
|
||||||
and_exp = stix2.ObservationExpression(stix2.AndBooleanExpression([exp1, exp2]))
|
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
|
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
|
||||||
|
|
||||||
|
|
||||||
def test_greater_than_python_constant():
|
def test_greater_than_python_constant():
|
||||||
exp1 = stix2.GreaterThanComparisonExpression("file:extensions.windows-pebinary-ext.sections[*].entropy", 7.0)
|
exp1 = stix2.GreaterThanComparisonExpression("file:extensions.'windows-pebinary-ext'.sections[*].entropy", 7.0)
|
||||||
exp = stix2.ObservationExpression(exp1)
|
exp = stix2.ObservationExpression(exp1)
|
||||||
assert str(exp) == "[file:extensions.windows-pebinary-ext.sections[*].entropy > 7.0]"
|
assert str(exp) == "[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.0]"
|
||||||
|
|
||||||
|
|
||||||
def test_greater_than():
|
def test_greater_than():
|
||||||
exp1 = stix2.GreaterThanComparisonExpression("file:extensions.windows-pebinary-ext.sections[*].entropy",
|
exp1 = stix2.GreaterThanComparisonExpression(
|
||||||
stix2.FloatConstant(7.0))
|
"file:extensions.'windows-pebinary-ext'.sections[*].entropy",
|
||||||
|
stix2.FloatConstant(7.0),
|
||||||
|
)
|
||||||
exp = stix2.ObservationExpression(exp1)
|
exp = stix2.ObservationExpression(exp1)
|
||||||
assert str(exp) == "[file:extensions.windows-pebinary-ext.sections[*].entropy > 7.0]"
|
assert str(exp) == "[file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.0]"
|
||||||
|
|
||||||
|
|
||||||
def test_less_than():
|
def test_less_than():
|
||||||
|
@ -133,73 +181,123 @@ def test_less_than():
|
||||||
|
|
||||||
|
|
||||||
def test_greater_than_or_equal():
|
def test_greater_than_or_equal():
|
||||||
exp = stix2.GreaterThanEqualComparisonExpression("file:size",
|
exp = stix2.GreaterThanEqualComparisonExpression(
|
||||||
1024)
|
"file:size",
|
||||||
|
1024,
|
||||||
|
)
|
||||||
|
|
||||||
assert str(exp) == "file:size >= 1024"
|
assert str(exp) == "file:size >= 1024"
|
||||||
|
|
||||||
|
|
||||||
def test_less_than_or_equal():
|
def test_less_than_or_equal():
|
||||||
exp = stix2.LessThanEqualComparisonExpression("file:size",
|
exp = stix2.LessThanEqualComparisonExpression(
|
||||||
1024)
|
"file:size",
|
||||||
|
1024,
|
||||||
|
)
|
||||||
assert str(exp) == "file:size <= 1024"
|
assert str(exp) == "file:size <= 1024"
|
||||||
|
|
||||||
|
|
||||||
def test_not():
|
def test_not():
|
||||||
exp = stix2.LessThanComparisonExpression("file:size",
|
exp = stix2.LessThanComparisonExpression(
|
||||||
1024,
|
"file:size",
|
||||||
negated=True)
|
1024,
|
||||||
|
negated=True,
|
||||||
|
)
|
||||||
assert str(exp) == "file:size NOT < 1024"
|
assert str(exp) == "file:size NOT < 1024"
|
||||||
|
|
||||||
|
|
||||||
def test_and_observable_expression():
|
def test_and_observable_expression():
|
||||||
exp1 = stix2.AndBooleanExpression([stix2.EqualityComparisonExpression("user-account:account_type",
|
exp1 = stix2.AndBooleanExpression([
|
||||||
"unix"),
|
stix2.EqualityComparisonExpression(
|
||||||
stix2.EqualityComparisonExpression("user-account:user_id",
|
"user-account:account_type",
|
||||||
stix2.StringConstant("1007")),
|
"unix",
|
||||||
stix2.EqualityComparisonExpression("user-account:account_login",
|
),
|
||||||
"Peter")])
|
stix2.EqualityComparisonExpression(
|
||||||
exp2 = stix2.AndBooleanExpression([stix2.EqualityComparisonExpression("user-account:account_type",
|
"user-account:user_id",
|
||||||
"unix"),
|
stix2.StringConstant("1007"),
|
||||||
stix2.EqualityComparisonExpression("user-account:user_id",
|
),
|
||||||
stix2.StringConstant("1008")),
|
stix2.EqualityComparisonExpression(
|
||||||
stix2.EqualityComparisonExpression("user-account:account_login",
|
"user-account:account_login",
|
||||||
"Paul")])
|
"Peter",
|
||||||
exp3 = stix2.AndBooleanExpression([stix2.EqualityComparisonExpression("user-account:account_type",
|
),
|
||||||
"unix"),
|
])
|
||||||
stix2.EqualityComparisonExpression("user-account:user_id",
|
exp2 = stix2.AndBooleanExpression([
|
||||||
stix2.StringConstant("1009")),
|
stix2.EqualityComparisonExpression(
|
||||||
stix2.EqualityComparisonExpression("user-account:account_login",
|
"user-account:account_type",
|
||||||
"Mary")])
|
"unix",
|
||||||
exp = stix2.AndObservationExpression([stix2.ObservationExpression(exp1),
|
),
|
||||||
stix2.ObservationExpression(exp2),
|
stix2.EqualityComparisonExpression(
|
||||||
stix2.ObservationExpression(exp3)])
|
"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),
|
||||||
|
])
|
||||||
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
|
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
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_and_observable_expression():
|
def test_invalid_and_observable_expression():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.AndBooleanExpression([stix2.EqualityComparisonExpression("user-account:display_name",
|
stix2.AndBooleanExpression([
|
||||||
"admin"),
|
stix2.EqualityComparisonExpression(
|
||||||
stix2.EqualityComparisonExpression("email-addr:display_name",
|
"user-account:display_name",
|
||||||
stix2.StringConstant("admin"))])
|
"admin",
|
||||||
|
),
|
||||||
|
stix2.EqualityComparisonExpression(
|
||||||
|
"email-addr:display_name",
|
||||||
|
stix2.StringConstant("admin"),
|
||||||
|
),
|
||||||
|
])
|
||||||
assert "All operands to an 'AND' expression must have the same object type" in str(excinfo)
|
assert "All operands to an 'AND' expression must have the same object type" in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
def test_hex():
|
def test_hex():
|
||||||
exp_and = stix2.AndBooleanExpression([stix2.EqualityComparisonExpression("file:mime_type",
|
exp_and = stix2.AndBooleanExpression([
|
||||||
"image/bmp"),
|
stix2.EqualityComparisonExpression(
|
||||||
stix2.EqualityComparisonExpression("file:magic_number_hex",
|
"file:mime_type",
|
||||||
stix2.HexConstant("ffd8"))])
|
"image/bmp",
|
||||||
|
),
|
||||||
|
stix2.EqualityComparisonExpression(
|
||||||
|
"file:magic_number_hex",
|
||||||
|
stix2.HexConstant("ffd8"),
|
||||||
|
),
|
||||||
|
])
|
||||||
exp = stix2.ObservationExpression(exp_and)
|
exp = stix2.ObservationExpression(exp_and)
|
||||||
assert str(exp) == "[file:mime_type = 'image/bmp' AND file:magic_number_hex = h'ffd8']"
|
assert str(exp) == "[file:mime_type = 'image/bmp' AND file:magic_number_hex = h'ffd8']"
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_qualifiers():
|
def test_multiple_qualifiers():
|
||||||
exp_and = stix2.AndBooleanExpression([stix2.EqualityComparisonExpression("network-traffic:dst_ref.type",
|
exp_and = stix2.AndBooleanExpression([
|
||||||
"domain-name"),
|
stix2.EqualityComparisonExpression(
|
||||||
stix2.EqualityComparisonExpression("network-traffic:dst_ref.value",
|
"network-traffic:dst_ref.type",
|
||||||
"example.com")])
|
"domain-name",
|
||||||
|
),
|
||||||
|
stix2.EqualityComparisonExpression(
|
||||||
|
"network-traffic:dst_ref.value",
|
||||||
|
"example.com",
|
||||||
|
),
|
||||||
|
])
|
||||||
exp_ob = stix2.ObservationExpression(exp_and)
|
exp_ob = stix2.ObservationExpression(exp_and)
|
||||||
qual_rep = stix2.RepeatQualifier(5)
|
qual_rep = stix2.RepeatQualifier(5)
|
||||||
qual_within = stix2.WithinQualifier(stix2.IntegerConstant(1800))
|
qual_within = stix2.WithinQualifier(stix2.IntegerConstant(1800))
|
||||||
|
@ -208,8 +306,10 @@ def test_multiple_qualifiers():
|
||||||
|
|
||||||
|
|
||||||
def test_set_op():
|
def test_set_op():
|
||||||
exp = stix2.ObservationExpression(stix2.IsSubsetComparisonExpression("network-traffic:dst_ref.value",
|
exp = stix2.ObservationExpression(stix2.IsSubsetComparisonExpression(
|
||||||
"2001:0db8:dead:beef:0000:0000:0000:0000/64"))
|
"network-traffic:dst_ref.value",
|
||||||
|
"2001:0db8:dead:beef:0000:0000:0000:0000/64",
|
||||||
|
))
|
||||||
assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
|
assert str(exp) == "[network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64']"
|
||||||
|
|
||||||
|
|
||||||
|
@ -219,35 +319,45 @@ def test_timestamp():
|
||||||
|
|
||||||
|
|
||||||
def test_boolean():
|
def test_boolean():
|
||||||
exp = stix2.EqualityComparisonExpression("email-message:is_multipart",
|
exp = stix2.EqualityComparisonExpression(
|
||||||
True)
|
"email-message:is_multipart",
|
||||||
|
True,
|
||||||
|
)
|
||||||
assert str(exp) == "email-message:is_multipart = true"
|
assert str(exp) == "email-message:is_multipart = true"
|
||||||
|
|
||||||
|
|
||||||
def test_binary():
|
def test_binary():
|
||||||
const = stix2.BinaryConstant("dGhpcyBpcyBhIHRlc3Q=")
|
const = stix2.BinaryConstant("dGhpcyBpcyBhIHRlc3Q=")
|
||||||
exp = stix2.EqualityComparisonExpression("artifact:payload_bin",
|
exp = stix2.EqualityComparisonExpression(
|
||||||
const)
|
"artifact:payload_bin",
|
||||||
|
const,
|
||||||
|
)
|
||||||
assert str(exp) == "artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q='"
|
assert str(exp) == "artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q='"
|
||||||
|
|
||||||
|
|
||||||
def test_list():
|
def test_list():
|
||||||
exp = stix2.InComparisonExpression("process:name",
|
exp = stix2.InComparisonExpression(
|
||||||
['proccy', 'proximus', 'badproc'])
|
"process:name",
|
||||||
|
['proccy', 'proximus', 'badproc'],
|
||||||
|
)
|
||||||
assert str(exp) == "process:name IN ('proccy', 'proximus', 'badproc')"
|
assert str(exp) == "process:name IN ('proccy', 'proximus', 'badproc')"
|
||||||
|
|
||||||
|
|
||||||
def test_list2():
|
def test_list2():
|
||||||
# alternate way to construct an "IN" Comparison Expression
|
# alternate way to construct an "IN" Comparison Expression
|
||||||
exp = stix2.EqualityComparisonExpression("process:name",
|
exp = stix2.EqualityComparisonExpression(
|
||||||
['proccy', 'proximus', 'badproc'])
|
"process:name",
|
||||||
|
['proccy', 'proximus', 'badproc'],
|
||||||
|
)
|
||||||
assert str(exp) == "process:name IN ('proccy', 'proximus', 'badproc')"
|
assert str(exp) == "process:name IN ('proccy', 'proximus', 'badproc')"
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_constant_type():
|
def test_invalid_constant_type():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.EqualityComparisonExpression("artifact:payload_bin",
|
stix2.EqualityComparisonExpression(
|
||||||
{'foo': 'bar'})
|
"artifact:payload_bin",
|
||||||
|
{'foo': 'bar'},
|
||||||
|
)
|
||||||
assert 'Unable to create a constant' in str(excinfo)
|
assert 'Unable to create a constant' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,20 +379,22 @@ def test_invalid_float_constant():
|
||||||
assert 'must be a float' in str(excinfo)
|
assert 'must be a float' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("data, result", [
|
@pytest.mark.parametrize(
|
||||||
(True, True),
|
"data, result", [
|
||||||
(False, False),
|
(True, True),
|
||||||
('True', True),
|
(False, False),
|
||||||
('False', False),
|
('True', True),
|
||||||
('true', True),
|
('False', False),
|
||||||
('false', False),
|
('true', True),
|
||||||
('t', True),
|
('false', False),
|
||||||
('f', False),
|
('t', True),
|
||||||
('T', True),
|
('f', False),
|
||||||
('F', False),
|
('T', True),
|
||||||
(1, True),
|
('F', False),
|
||||||
(0, False),
|
(1, True),
|
||||||
])
|
(0, False),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_boolean_constant(data, result):
|
def test_boolean_constant(data, result):
|
||||||
boolean = stix2.BooleanConstant(data)
|
boolean = stix2.BooleanConstant(data)
|
||||||
assert boolean.value == result
|
assert boolean.value == result
|
||||||
|
@ -294,10 +406,12 @@ def test_invalid_boolean_constant():
|
||||||
assert 'must be a boolean' in str(excinfo)
|
assert 'must be a boolean' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("hashtype, data", [
|
@pytest.mark.parametrize(
|
||||||
('MD5', 'zzz'),
|
"hashtype, data", [
|
||||||
('ssdeep', 'zzz=='),
|
('MD5', 'zzz'),
|
||||||
])
|
('ssdeep', 'zzz=='),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_invalid_hash_constant(hashtype, data):
|
def test_invalid_hash_constant(hashtype, data):
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.HashConstant(data, hashtype)
|
stix2.HashConstant(data, hashtype)
|
||||||
|
@ -317,20 +431,26 @@ def test_invalid_binary_constant():
|
||||||
|
|
||||||
|
|
||||||
def test_escape_quotes_and_backslashes():
|
def test_escape_quotes_and_backslashes():
|
||||||
exp = stix2.MatchesComparisonExpression("file:name",
|
exp = stix2.MatchesComparisonExpression(
|
||||||
"^Final Report.+\\.exe$")
|
"file:name",
|
||||||
|
"^Final Report.+\\.exe$",
|
||||||
|
)
|
||||||
assert str(exp) == "file:name MATCHES '^Final Report.+\\\\.exe$'"
|
assert str(exp) == "file:name MATCHES '^Final Report.+\\\\.exe$'"
|
||||||
|
|
||||||
|
|
||||||
def test_like():
|
def test_like():
|
||||||
exp = stix2.LikeComparisonExpression("directory:path",
|
exp = stix2.LikeComparisonExpression(
|
||||||
"C:\\Windows\\%\\foo")
|
"directory:path",
|
||||||
|
"C:\\Windows\\%\\foo",
|
||||||
|
)
|
||||||
assert str(exp) == "directory:path LIKE 'C:\\\\Windows\\\\%\\\\foo'"
|
assert str(exp) == "directory:path LIKE 'C:\\\\Windows\\\\%\\\\foo'"
|
||||||
|
|
||||||
|
|
||||||
def test_issuperset():
|
def test_issuperset():
|
||||||
exp = stix2.IsSupersetComparisonExpression("ipv4-addr:value",
|
exp = stix2.IsSupersetComparisonExpression(
|
||||||
"198.51.100.0/24")
|
"ipv4-addr:value",
|
||||||
|
"198.51.100.0/24",
|
||||||
|
)
|
||||||
assert str(exp) == "ipv4-addr:value ISSUPERSET '198.51.100.0/24'"
|
assert str(exp) == "ipv4-addr:value ISSUPERSET '198.51.100.0/24'"
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,24 +472,32 @@ def test_invalid_within_qualifier():
|
||||||
|
|
||||||
|
|
||||||
def test_startstop_qualifier():
|
def test_startstop_qualifier():
|
||||||
qual = stix2.StartStopQualifier(stix2.TimestampConstant('2016-06-01T00:00:00Z'),
|
qual = stix2.StartStopQualifier(
|
||||||
datetime.datetime(2017, 3, 12, 8, 30, 0))
|
stix2.TimestampConstant('2016-06-01T00:00:00Z'),
|
||||||
|
datetime.datetime(2017, 3, 12, 8, 30, 0),
|
||||||
|
)
|
||||||
assert str(qual) == "START t'2016-06-01T00:00:00Z' STOP t'2017-03-12T08:30:00Z'"
|
assert str(qual) == "START t'2016-06-01T00:00:00Z' STOP t'2017-03-12T08:30:00Z'"
|
||||||
|
|
||||||
qual2 = stix2.StartStopQualifier(datetime.date(2016, 6, 1),
|
qual2 = stix2.StartStopQualifier(
|
||||||
stix2.TimestampConstant('2016-07-01T00:00:00Z'))
|
datetime.date(2016, 6, 1),
|
||||||
|
stix2.TimestampConstant('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 t'2016-06-01T00:00:00Z' STOP t'2016-07-01T00:00:00Z'"
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_startstop_qualifier():
|
def test_invalid_startstop_qualifier():
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.StartStopQualifier('foo',
|
stix2.StartStopQualifier(
|
||||||
stix2.TimestampConstant('2016-06-01T00:00:00Z'))
|
'foo',
|
||||||
|
stix2.TimestampConstant('2016-06-01T00:00:00Z'),
|
||||||
|
)
|
||||||
assert 'is not a valid argument for a Start/Stop Qualifier' in str(excinfo)
|
assert 'is not a valid argument for a Start/Stop Qualifier' in str(excinfo)
|
||||||
|
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
stix2.StartStopQualifier(datetime.date(2016, 6, 1),
|
stix2.StartStopQualifier(
|
||||||
'foo')
|
datetime.date(2016, 6, 1),
|
||||||
|
'foo',
|
||||||
|
)
|
||||||
assert 'is not a valid argument for a Start/Stop Qualifier' in str(excinfo)
|
assert 'is not a valid argument for a Start/Stop Qualifier' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
|
@ -377,3 +505,15 @@ def test_make_constant_already_a_constant():
|
||||||
str_const = stix2.StringConstant('Foo')
|
str_const = stix2.StringConstant('Foo')
|
||||||
result = stix2.patterns.make_constant(str_const)
|
result = stix2.patterns.make_constant(str_const)
|
||||||
assert result is str_const
|
assert result is str_const
|
||||||
|
|
||||||
|
|
||||||
|
def test_parsing_comparison_expression():
|
||||||
|
patt_obj = create_pattern_object("[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']")
|
||||||
|
assert str(patt_obj) == "[file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f']"
|
||||||
|
|
||||||
|
|
||||||
|
def test_parsing_qualified_expression():
|
||||||
|
patt_obj = create_pattern_object(
|
||||||
|
"[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS")
|
||||||
|
assert str(
|
||||||
|
patt_obj) == "[network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS"
|
||||||
|
|
Loading…
Reference in New Issue