mirror of
https://github.com/valitydev/SigmaHQ.git
synced 2024-11-07 17:58:52 +00:00
Merge branch 'devel-sigmac-config' into devel-sigmac
This commit is contained in:
commit
aaa3057769
@ -22,6 +22,11 @@ class BaseBackend:
|
||||
identifier = "base"
|
||||
active = False
|
||||
|
||||
def __init__(self, sigmaconfig):
|
||||
if not isinstance(sigmaconfig, (sigma.SigmaConfiguration, None)):
|
||||
raise TypeError("SigmaConfiguration object expected")
|
||||
self.sigmaconfig = sigmaconfig
|
||||
|
||||
def generate(self, parsed):
|
||||
return self.generateNode(parsed.getParseTree())
|
||||
|
||||
@ -96,7 +101,7 @@ class ElasticsearchQuerystringBackend(BaseBackend):
|
||||
key, value = node
|
||||
if type(value) not in (str, int, list):
|
||||
raise TypeError("Map values must be strings, numbers or lists, not " + str(type(value)))
|
||||
return "%s:%s" % (key, self.generateNode(value))
|
||||
return "%s:%s" % (self.sigmaconfig.get_fieldmapping(key), self.generateNode(value))
|
||||
|
||||
def generateValueNode(self, node):
|
||||
return "\"%s\"" % (self.cleanValue(str(node)))
|
||||
@ -140,11 +145,53 @@ class SplunkBackend(BaseBackend):
|
||||
def generateMapItemNode(self, node):
|
||||
key, value = node
|
||||
if type(value) in (str, int):
|
||||
return '%s=%s' % (key, self.generateNode(value))
|
||||
return '%s=%s' % (self.sigmaconfig.get_fieldmapping(key), self.generateNode(value))
|
||||
elif type(value) == list:
|
||||
return "(" + (" OR ".join(['%s=%s' % (key, self.generateValueNode(item)) for item in value])) + ")"
|
||||
return "(" + (" OR ".join(['%s=%s' % (self.sigmaconfig.get_fieldmapping(key), self.generateValueNode(item)) for item in value])) + ")"
|
||||
else:
|
||||
raise TypeError("Map values must be strings, numbers or lists, not " + str(type(value)))
|
||||
|
||||
def generateValueNode(self, node):
|
||||
return "\"%s\"" % (self.cleanValue(str(node)))
|
||||
|
||||
class FieldnameListBackend(BaseBackend):
|
||||
"""List all fieldnames from given Sigma rules for creation of a field mapping configuration."""
|
||||
identifier = "fieldlist"
|
||||
active = True
|
||||
|
||||
def generate(self, parsed):
|
||||
return "\n".join(sorted(set(list(flatten(self.generateNode(parsed.getParseTree()))))))
|
||||
|
||||
def generateANDNode(self, node):
|
||||
return [self.generateNode(val) for val in node]
|
||||
|
||||
def generateORNode(self, node):
|
||||
return self.generateANDNode(node)
|
||||
|
||||
def generateNOTNode(self, node):
|
||||
return self.generateNode(node.item)
|
||||
|
||||
def generateSubexpressionNode(self, node):
|
||||
return self.generateNode(node.items)
|
||||
|
||||
def generateListNode(self, node):
|
||||
if not set([type(value) for value in node]).issubset({str, int}):
|
||||
raise TypeError("List values must be strings or numbers")
|
||||
return [self.generateNode(value) for value in node]
|
||||
|
||||
def generateMapItemNode(self, node):
|
||||
key, value = node
|
||||
if type(value) not in (str, int, list):
|
||||
raise TypeError("Map values must be strings, numbers or lists, not " + str(type(value)))
|
||||
return [self.sigmaconfig.get_fieldmapping(key)]
|
||||
|
||||
def generateValueNode(self, node):
|
||||
return []
|
||||
|
||||
# Helpers
|
||||
def flatten(l):
|
||||
for i in l:
|
||||
if type(i) == list:
|
||||
yield from flatten(i)
|
||||
else:
|
||||
yield i
|
||||
|
@ -192,7 +192,7 @@ class SigmaConditionTokenizer:
|
||||
|
||||
def index(self, item):
|
||||
return self.tokens.index(item)
|
||||
|
||||
|
||||
class SigmaParseError(Exception):
|
||||
pass
|
||||
|
||||
@ -349,3 +349,52 @@ class SigmaConditionParser:
|
||||
|
||||
def getParseTree(self):
|
||||
return(self.parsedSearch[0])
|
||||
|
||||
# Configuration
|
||||
class SigmaConfiguration:
|
||||
"""Sigma converter configuration. Contains field mappings and logsource descriptions"""
|
||||
def __init__(self, configyaml=None):
|
||||
if configyaml == None:
|
||||
self.fieldmappings = dict()
|
||||
self.logsources = dict()
|
||||
else:
|
||||
config = yaml.safe_load(configyaml)
|
||||
|
||||
try:
|
||||
self.fieldmappings = config['fieldmappings']
|
||||
except KeyError:
|
||||
self.fieldmappings = dict()
|
||||
if type(self.fieldmappings) != dict:
|
||||
raise SigmaConfigParseError("Fieldmappings must be a map")
|
||||
|
||||
try:
|
||||
self.logsources = config['logsources']
|
||||
except KeyError:
|
||||
self.logsources = dict()
|
||||
|
||||
if type(self.logsources) != dict:
|
||||
raise SigmaConfigParseError("Logsources must be a map")
|
||||
for name, logsource in self.logsources.items():
|
||||
if type(logsource) != dict:
|
||||
raise SigmaConfigParseError("Logsource definitions must be maps")
|
||||
if 'category' in logsource and type(logsource['category']) != str \
|
||||
or 'product' in logsource and type(logsource['product']) != str \
|
||||
or 'service' in logsource and type(logsource['service']) != str:
|
||||
raise SigmaConfigParseError("Logsource category, product or service must be a string")
|
||||
if 'index' in logsource:
|
||||
if type(logsource['index']) not in (str, list):
|
||||
raise SigmaConfigParseError("Logsource index must be string or list of strings")
|
||||
if type(logsource['index']) == list and not set([type(index) for index in logsource['index']]).issubset({str}):
|
||||
raise SigmaConfigParseError("Logsource index patterns must be strings")
|
||||
if 'conditions' in logsource and type(logsource['conditions']) != dict:
|
||||
raise SigmaConfigParseError("Logsource conditions must be a map")
|
||||
|
||||
def get_fieldmapping(self, fieldname):
|
||||
"""Return mapped fieldname if mapping defined or field name given in parameter value"""
|
||||
try:
|
||||
return self.fieldmappings[fieldname]
|
||||
except KeyError:
|
||||
return fieldname
|
||||
|
||||
class SigmaConfigParseError(Exception):
|
||||
pass
|
||||
|
@ -7,7 +7,7 @@ import yaml
|
||||
import json
|
||||
import pathlib
|
||||
import itertools
|
||||
from sigma import SigmaParser, SigmaParseError
|
||||
from sigma import SigmaParser, SigmaParseError, SigmaConfiguration, SigmaConfigParseError
|
||||
import backends
|
||||
|
||||
def print_verbose(*args, **kwargs):
|
||||
@ -50,12 +50,22 @@ if cmdargs.target_list:
|
||||
if cmdargs.output:
|
||||
print("--output/-o not yet implemented", file=sys.stderr)
|
||||
sys.exit(99)
|
||||
|
||||
sigmaconfig = SigmaConfiguration()
|
||||
if cmdargs.config:
|
||||
print("--config/-c not yet implemented", file=sys.stderr)
|
||||
sys.exit(99)
|
||||
try:
|
||||
conffile = cmdargs.config
|
||||
f = open(conffile)
|
||||
sigmaconfig = SigmaConfiguration(f)
|
||||
except OSError as e:
|
||||
print("Failed to open Sigma configuration file %s: %s" % (conffile, str(e)))
|
||||
except yaml.parser.ParserError as e:
|
||||
print("Sigma configuration file %s is no valid YAML: %s" % (conffile, str(e)))
|
||||
except SigmaParseError as e:
|
||||
print("Sigma configuration parse error in %s: %s" % (conffile, str(e)))
|
||||
|
||||
try:
|
||||
backend = backends.getBackend(cmdargs.target)()
|
||||
backend = backends.getBackend(cmdargs.target)(sigmaconfig)
|
||||
except LookupError as e:
|
||||
print("Backend not found!")
|
||||
sys.exit(1)
|
||||
|
Loading…
Reference in New Issue
Block a user