Parsing of detections

Transformation of detections into internal data structures. Parsing must
be changed later to on-demand parsing because condition can change
default behavior of lists.
This commit is contained in:
Thomas Patzke 2017-02-16 00:40:08 +01:00
parent 3821e59db1
commit ce43dce7ef
2 changed files with 75 additions and 1 deletions

View File

@ -3,7 +3,80 @@
import yaml
import re
COND_NONE = 0
COND_AND = 1
COND_OR = 2
COND_NOT = 3
class SigmaParser:
def __init__(self, sigma):
self.definitions = dict()
self.parsedyaml = yaml.safe_load(sigma)
try:
for definitionName in self.parsedyaml["detection"]:
if definitionName in ("condition", "timeframe"): # skip non-identifiers here
continue
if definitionName in self.definitions:
raise SigmaParseError("Definition '%s' was already defined" % (definitionName))
self.definitions[definitionName] = self.parse_definition(self.parsedyaml["detection"][definitionName])
except KeyError:
raise SigmaParseError("No detection definitions found")
def parse_definition(self, definition, condOverride=None):
if type(definition) not in (dict, list):
raise SigmaParseError("Expected map or list, got type %s: '%s'" % (type(definition), str(definition)))
if type(definition) == list: # list of values or maps
if condOverride: # condition given through rule detection condition, e.g. 1 of x
cond = condOverride
else: # no condition given, use default from spec
cond = ConditionOR()
for value in definition:
if type(value) in (str, int, dict):
cond.add(value)
else:
raise SigmaParseError("Definition list may only contain plain values or maps")
elif type(definition) == dict: # map
cond = definition
return cond
class SigmaParseError(Exception):
pass
### Parse Tree Node Classes ###
class ConditionBase:
"""Base class for conditional operations"""
op = COND_NONE
items = None
def __init__(self):
raise NotImplementedError("ConditionBase is no usable class")
def add(self, item):
self.items.append(item)
class ConditionAND(ConditionBase):
"""AND Condition"""
op = COND_AND
def __init__(self):
self.items = list()
class ConditionOR(ConditionAND):
"""OR Condition"""
op = COND_OR
class ConditionNOT(ConditionBase):
"""NOT Condition"""
op = COND_NOT
def __init__(self):
self.items = None
def add(self, item):
if self.items == None:
super.add(item)
else:
raise ValueError("Only one element allowed in NOT condition")

View File

@ -20,7 +20,7 @@ argparser = argparse.ArgumentParser(description="Convert Sigma rules into SIEM s
argparser.add_argument("--recurse", "-r", help="Recurse into subdirectories")
argparser.add_argument("--target", "-t", default="null", choices=backends.getBackendDict().keys(), help="Output target format")
argparser.add_argument("--target-list", "-l", action="store_true", help="List available output target formats")
argparser.add_argument("--fieldmapping", "-f", help="File with mappings between generic Sigma field names and configured field names (format: sigma_field: field1, field2, ...)")
argparser.add_argument("--config", "-c", help="Configuration mit field name and index mapping for target environment")
argparser.add_argument("--output", "-o", help="Output file or filename prefix if multiple files are generated")
argparser.add_argument("--verbose", "-v", action="store_true", help="Be verbose")
argparser.add_argument("--debug", "-d", action="store_true", help="Debugging output")
@ -38,6 +38,7 @@ for sigmafile in cmdargs.inputs:
f = open(sigmafile)
parser = SigmaParser(f)
print_debug(json.dumps(parser.parsedyaml, indent=2))
#print_debug(json.dumps(parser.definitions, indent=2))
except OSError as e:
print("Failed to open Sigma file %s: %s" % (sigmafile, str(e)))
except yaml.parser.ParserError as e: