Files
blue-team-tools/tools/sigma/backends/sqlite.py
T

125 lines
4.9 KiB
Python

from sigma.backends.sql import SQLBackend
from sigma.parser.condition import NodeSubexpression, ConditionAND, ConditionOR, ConditionNOT
import re
class SQLiteBackend(SQLBackend):
"""SQLiteBackend provides FullTextSearch functionality"""
identifier = "sqlite"
active = True
mapFullTextSearch = "%s MATCH ('\"%s\"')"
def __init__(self, sigmaconfig, table):
super().__init__(sigmaconfig, table)
self.mappingItem = False
def requireFTS(self, node):
return (not self.mappingItem and
(type(node) in (int, str) or all(isinstance(val, str) for val in node) or all(isinstance(val, int) for val in node)))
def generateFTS(self, value):
if re.search(r"((\\(\*|\?|\\))|\*|\?|_|%)", value):
raise NotImplementedError(
"Wildcards in SQlite Full Text Search not implemented")
self.countFTS += 1
return self.mapFullTextSearch % (self.table, value)
def generateANDNode(self, node):
if self.requireFTS(node):
fts = str('"' + self.andToken + '"').join(self.cleanValue(val)
for val in node)
return self.generateFTS(fts)
generated = [self.generateNode(val) for val in node]
filtered = [g for g in generated if g is not None]
if filtered:
return self.andToken.join(filtered)
else:
return None
def generateORNode(self, node):
if self.requireFTS(node):
fts = str('"' + self.orToken + '"').join(self.cleanValue(val)
for val in node)
return self.generateFTS(fts)
generated = [self.generateNode(val) for val in node]
filtered = [g for g in generated if g is not None]
if filtered:
return self.orToken.join(filtered)
else:
return None
def generateMapItemNode(self, node):
try:
self.mappingItem = True
fieldname, value = node
transformed_fieldname = self.fieldNameMapping(fieldname, value)
has_wildcard = re.search(
r"((\\(\*|\?|\\))|\*|\?|_|%)", self.generateNode(value))
if "," in self.generateNode(value) and not has_wildcard:
return self.mapMulti % (transformed_fieldname, self.generateNode(value))
elif "LENGTH" in transformed_fieldname:
return self.mapLength % (transformed_fieldname, value)
elif type(value) == list:
return self.generateMapItemListNode(transformed_fieldname, value)
elif self.mapListsSpecialHandling == False and type(value) in (str, int, list) or self.mapListsSpecialHandling == True and type(value) in (str, int):
if has_wildcard:
return self.mapWildcard % (transformed_fieldname, self.generateNode(value))
else:
return self.mapExpression % (transformed_fieldname, self.generateNode(value))
elif "sourcetype" in transformed_fieldname:
return self.mapSource % (transformed_fieldname, self.generateNode(value))
elif has_wildcard:
return self.mapWildcard % (transformed_fieldname, self.generateNode(value))
else:
raise TypeError(
"Backend does not support map values of type " + str(type(value)))
finally:
self.mappingItem = False
def generateValueNode(self, node):
if self.mappingItem:
return self.valueExpression % (self.cleanValue(str(node)))
else:
return self.generateFTS(self.cleanValue(str(node)))
def generateQuery(self, parsed):
self.countFTS = 0
result = self.generateNode(parsed.parsedSearch)
if self.countFTS > 1:
raise NotImplementedError(
"Match operator ({}) is allowed only once in SQLite, parse rule in a different way:\n{}".format(self.countFTS, result))
self.countFTS = 0
if parsed.parsedAgg:
# Handle aggregation
fro, whe = self.generateAggregation(parsed.parsedAgg, result)
return "SELECT * FROM {} WHERE {}".format(fro, whe)
return "SELECT * FROM {} WHERE {}".format(self.table, result)
def generateFullTextQuery(self, search, parsed):
search = search.replace('"', '')
search = '" OR "'.join(search.split(" OR "))
search = '" AND "'.join(search.split(" AND "))
search = '"{}"'.format(search)
search = search.replace('%', '')
search = search.replace('_', '')
search = '{} MATCH (\'{}\')'.format(self.table, search)
if parsed.parsedAgg:
# Handle aggregation
fro, whe = self.generateAggregation(parsed.parsedAgg, search)
return "SELECT * FROM {} WHERE {}".format(fro, whe)
return 'SELECT * FROM {} WHERE {}'.format(self.table, search)