pySigma-backend-sqlite 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pysigma_backend_sqlite-0.1.1 → pysigma_backend_sqlite-0.2.0}/PKG-INFO +4 -4
- {pysigma_backend_sqlite-0.1.1 → pysigma_backend_sqlite-0.2.0}/pyproject.toml +15 -13
- pysigma_backend_sqlite-0.2.0/sigma/backends/sqlite/__init__.py +5 -0
- pysigma_backend_sqlite-0.2.0/sigma/backends/sqlite/sqlite.py +431 -0
- pysigma_backend_sqlite-0.1.1/sigma/backends/sqlite/__init__.py +0 -5
- pysigma_backend_sqlite-0.1.1/sigma/backends/sqlite/sqlite.py +0 -260
- {pysigma_backend_sqlite-0.1.1 → pysigma_backend_sqlite-0.2.0}/LICENSE +0 -0
- {pysigma_backend_sqlite-0.1.1 → pysigma_backend_sqlite-0.2.0}/README.md +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pySigma-backend-sqlite
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: pySigma sqlite backend
|
|
5
5
|
Home-page: https://github.com/SigmaHQ/pySigma-backend-sqlite
|
|
6
6
|
License: LGPL-3.0-only
|
|
7
7
|
Author: wagga
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.9,<4.0
|
|
9
9
|
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Dist: pysigma (>=0.11.17,<0.12.0)
|
|
17
17
|
Project-URL: Repository, https://github.com/SigmaHQ/pySigma-backend-sqlite
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pySigma-backend-sqlite"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "pySigma sqlite backend"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = ["wagga"]
|
|
@@ -11,13 +11,14 @@ packages = [
|
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
[tool.poetry.dependencies]
|
|
14
|
-
python = "^3.
|
|
15
|
-
pysigma = "^0.
|
|
14
|
+
python = "^3.9"
|
|
15
|
+
pysigma = "^0.11.17"
|
|
16
16
|
|
|
17
17
|
[tool.poetry.group.dev.dependencies]
|
|
18
|
-
|
|
19
|
-
pytest
|
|
20
|
-
|
|
18
|
+
black = "^24.1"
|
|
19
|
+
pytest = "^8.0"
|
|
20
|
+
pytest-cov = "^4.1"
|
|
21
|
+
coverage = "^7.4"
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
[tool.pdm]
|
|
@@ -27,7 +28,7 @@ dev = []
|
|
|
27
28
|
[tool.pdm.build]
|
|
28
29
|
includes = ["sigma"]
|
|
29
30
|
[build-system]
|
|
30
|
-
requires = ["poetry-core>=1.
|
|
31
|
+
requires = ["poetry-core>=1.8.1"]
|
|
31
32
|
build-backend = "poetry.core.masonry.api"
|
|
32
33
|
|
|
33
34
|
[project]
|
|
@@ -35,18 +36,19 @@ authors = [
|
|
|
35
36
|
{name = "wagga", email = "wagga"},
|
|
36
37
|
]
|
|
37
38
|
license = {text = "LGPL-3.0-only"}
|
|
38
|
-
requires-python = ">=3.
|
|
39
|
+
requires-python = ">=3.9,<4.0"
|
|
39
40
|
dependencies = [
|
|
40
|
-
"pysigma>=0.
|
|
41
|
+
"pysigma>=0.11.17",
|
|
41
42
|
]
|
|
43
|
+
|
|
42
44
|
name = "pySigma-backend-sqlite"
|
|
43
|
-
version = "0.
|
|
45
|
+
version = "0.2.0"
|
|
44
46
|
description = "pySigma sqlite backend"
|
|
45
47
|
|
|
46
48
|
[project.urls]
|
|
47
49
|
repository = "https://github.com/SigmaHQ/pySigma-backend-sqlite"
|
|
48
50
|
|
|
49
51
|
[project.group.dev.dependencies]
|
|
50
|
-
pytest = "^
|
|
51
|
-
pytest-cov = "^4.1
|
|
52
|
-
coverage = "^7.
|
|
52
|
+
pytest = "^8.0"
|
|
53
|
+
pytest-cov = "^4.1"
|
|
54
|
+
coverage = "^7.4"
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
from sigma.conversion.deferred import DeferredQueryExpression
|
|
2
|
+
from sigma.conversion.state import ConversionState
|
|
3
|
+
from sigma.exceptions import SigmaFeatureNotSupportedByBackendError
|
|
4
|
+
from sigma.rule import SigmaRule
|
|
5
|
+
from sigma.conversion.base import TextQueryBackend
|
|
6
|
+
from sigma.conditions import (
|
|
7
|
+
ConditionItem,
|
|
8
|
+
ConditionAND,
|
|
9
|
+
ConditionOR,
|
|
10
|
+
ConditionNOT,
|
|
11
|
+
ConditionValueExpression,
|
|
12
|
+
ConditionFieldEqualsValueExpression,
|
|
13
|
+
)
|
|
14
|
+
from sigma.types import (
|
|
15
|
+
SigmaCompareExpression,
|
|
16
|
+
SigmaString,
|
|
17
|
+
SpecialChars,
|
|
18
|
+
SigmaRegularExpressionFlag,
|
|
19
|
+
SigmaCIDRExpression,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
import json
|
|
24
|
+
from typing import ClassVar, Dict, List, Optional, Pattern, Tuple, Union, Any
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class sqliteBackend(TextQueryBackend):
|
|
28
|
+
"""SQLite backend."""
|
|
29
|
+
|
|
30
|
+
# Operator precedence: tuple of Condition{AND,OR,NOT} in order of precedence.
|
|
31
|
+
# The backend generates grouping if required
|
|
32
|
+
name: ClassVar[str] = "SQLite backend"
|
|
33
|
+
formats: Dict[str, str] = {
|
|
34
|
+
"default": "Plain SQLite queries",
|
|
35
|
+
"zircolite": "Zircolite JSON format",
|
|
36
|
+
}
|
|
37
|
+
requires_pipeline: bool = (
|
|
38
|
+
False # TODO: does the backend requires that a processing pipeline is provided? This information can be used by user interface programs like Sigma CLI to warn users about inappropriate usage of the backend.
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
precedence: ClassVar[Tuple[ConditionItem, ConditionItem, ConditionItem]] = (
|
|
42
|
+
ConditionNOT,
|
|
43
|
+
ConditionAND,
|
|
44
|
+
ConditionOR,
|
|
45
|
+
)
|
|
46
|
+
parenthesize: bool = True
|
|
47
|
+
group_expression: ClassVar[str] = (
|
|
48
|
+
"({expr})" # Expression for precedence override grouping as format string with {expr} placeholder
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Generated query tokens
|
|
52
|
+
token_separator: str = " " # separator inserted between all boolean operators
|
|
53
|
+
or_token: ClassVar[str] = "OR"
|
|
54
|
+
and_token: ClassVar[str] = "AND"
|
|
55
|
+
not_token: ClassVar[str] = "NOT"
|
|
56
|
+
eq_token: ClassVar[str] = (
|
|
57
|
+
"=" # Token inserted between field and value (without separator)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# String output
|
|
61
|
+
## Fields
|
|
62
|
+
### Quoting
|
|
63
|
+
|
|
64
|
+
# SQLite correct way to handle field name is detailed here : https://sqlite.org/lang_keywords.html.
|
|
65
|
+
# Double-quoting should be the way to go. But since in some case quotes are interpreted as literals we need to find an alternative.
|
|
66
|
+
# Obviously, we cannot use "[" and "]", because it is 2 different characters, so we are left with "`" (MySQL).
|
|
67
|
+
field_quote: ClassVar[str] = (
|
|
68
|
+
"`" # Character used to quote field characters if field_quote_pattern matches (or not, depending on field_quote_pattern_negation). No field name quoting is done if not set.
|
|
69
|
+
)
|
|
70
|
+
field_quote_pattern: ClassVar[Pattern] = re.compile(
|
|
71
|
+
"^[a-zA-Z0-9_]*$"
|
|
72
|
+
) # Quote field names if this pattern (doesn't) matches, depending on field_quote_pattern_negation. Field name is always quoted if pattern is not set.
|
|
73
|
+
field_quote_pattern_negation: ClassVar[bool] = (
|
|
74
|
+
True # Negate field_quote_pattern result. Field name is quoted if pattern doesn't matches if set to True (default).
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
## Values
|
|
78
|
+
str_quote: ClassVar[str] = (
|
|
79
|
+
"'" # string quoting character (added as escaping character)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
escape_char: ClassVar[str] = (
|
|
83
|
+
"\\" # Escaping character for special characters inside string
|
|
84
|
+
)
|
|
85
|
+
wildcard_multi: ClassVar[str] = "%" # Character used as multi-character wildcard
|
|
86
|
+
wildcard_single: ClassVar[str] = "_" # Character used as single-character wildcard
|
|
87
|
+
|
|
88
|
+
# Special case for case sensitive string matching
|
|
89
|
+
wildcard_glob : ClassVar[str] = "*" # Character used as glob wildcard
|
|
90
|
+
wildcard_glob_single : ClassVar[str] = "?" # Character used as glob wildcard
|
|
91
|
+
|
|
92
|
+
add_escaped: ClassVar[str] = (
|
|
93
|
+
"\\" # Characters quoted in addition to wildcards and string quote
|
|
94
|
+
)
|
|
95
|
+
# filter_chars : ClassVar[str] = "" # Characters filtered
|
|
96
|
+
bool_values: ClassVar[Dict[bool, str]] = (
|
|
97
|
+
{ # Values to which boolean values are mapped.
|
|
98
|
+
True: "true",
|
|
99
|
+
False: "false",
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# String matching operators. if none is appropriate eq_token is used.
|
|
104
|
+
startswith_expression: ClassVar[str] = "{field} LIKE '{value}%' ESCAPE '\\'"
|
|
105
|
+
endswith_expression: ClassVar[str] = "{field} LIKE '%{value}' ESCAPE '\\'"
|
|
106
|
+
contains_expression: ClassVar[str] = "{field} LIKE '%{value}%' ESCAPE '\\'"
|
|
107
|
+
wildcard_match_expression: ClassVar[str] = (
|
|
108
|
+
"{field} LIKE '{value}' ESCAPE '\\'" # Special expression if wildcards can't be matched with the eq_token operator
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
field_exists_expression: ClassVar[str] = "{field} = {field}"
|
|
112
|
+
|
|
113
|
+
# Special expression if wildcards can't be matched with the eq_token operator
|
|
114
|
+
wildcard_match_str_expression: ClassVar[str] = "{field} LIKE '{value}' ESCAPE '\\'"
|
|
115
|
+
# wildcard_match_num_expression: ClassVar[str] = "{field} LIKE '%{value}%'"
|
|
116
|
+
|
|
117
|
+
# Regular expressions
|
|
118
|
+
# Regular expression query as format string with placeholders {field}, {regex}, {flag_x} where x
|
|
119
|
+
# is one of the flags shortcuts supported by Sigma (currently i, m and s) and refers to the
|
|
120
|
+
# token stored in the class variable re_flags.
|
|
121
|
+
re_expression: ClassVar[str] = "{field} REGEXP '{regex}'"
|
|
122
|
+
re_escape_char: ClassVar[str] = (
|
|
123
|
+
"" # Character used for escaping in regular expressions
|
|
124
|
+
)
|
|
125
|
+
re_escape: ClassVar[Tuple[str]] = () # List of strings that are escaped
|
|
126
|
+
re_escape_escape_char: bool = True # If True, the escape character is also escaped
|
|
127
|
+
re_flag_prefix: bool = (
|
|
128
|
+
True # If True, the flags are prepended as (?x) group at the beginning of the regular expression, e.g. (?i). If this is not supported by the target, it should be set to False.
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Mapping from SigmaRegularExpressionFlag values to static string templates that are used in
|
|
132
|
+
# flag_x placeholders in re_expression template.
|
|
133
|
+
# By default, i, m and s are defined. If a flag is not supported by the target query language,
|
|
134
|
+
# remove it from re_flags or don't define it to ensure proper error handling in case of appearance.
|
|
135
|
+
# re_flags : Dict[SigmaRegularExpressionFlag, str] = {}
|
|
136
|
+
|
|
137
|
+
case_sensitive_match_expression: ClassVar[str] = "{field} GLOB {value} ESCAPE '\\'"
|
|
138
|
+
|
|
139
|
+
# Numeric comparison operators
|
|
140
|
+
compare_op_expression: ClassVar[str] = (
|
|
141
|
+
"{field} {operator} {value}" # Compare operation query as format string with placeholders {field}, {operator} and {value}
|
|
142
|
+
)
|
|
143
|
+
# Mapping between CompareOperators elements and strings used as replacement for {operator} in compare_op_expression
|
|
144
|
+
compare_operators: ClassVar[Dict[SigmaCompareExpression.CompareOperators, str]] = {
|
|
145
|
+
SigmaCompareExpression.CompareOperators.LT: "<",
|
|
146
|
+
SigmaCompareExpression.CompareOperators.LTE: "<=",
|
|
147
|
+
SigmaCompareExpression.CompareOperators.GT: ">",
|
|
148
|
+
SigmaCompareExpression.CompareOperators.GTE: ">=",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Expression for comparing two event fields
|
|
152
|
+
field_equals_field_expression: ClassVar[Optional[str]] = (
|
|
153
|
+
None # Field comparison expression with the placeholders {field1} and {field2} corresponding to left field and right value side of Sigma detection item
|
|
154
|
+
)
|
|
155
|
+
field_equals_field_escaping_quoting: Tuple[bool, bool] = (
|
|
156
|
+
True,
|
|
157
|
+
True,
|
|
158
|
+
) # If regular field-escaping/quoting is applied to field1 and field2. A custom escaping/quoting can be implemented in the convert_condition_field_eq_field_escape_and_quote method.
|
|
159
|
+
|
|
160
|
+
# Null/None expressions
|
|
161
|
+
field_null_expression: ClassVar[str] = (
|
|
162
|
+
"{field} IS NULL" # Expression for field has null value as format string with {field} placeholder for field name
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Field existence condition expressions.
|
|
166
|
+
# field_exists_expression : ClassVar[str] = "exists({field})" # Expression for field existence as format string with {field} placeholder for field name
|
|
167
|
+
# field_not_exists_expression : ClassVar[str] = "notexists({field})" # Expression for field non-existence as format string with {field} placeholder for field name. If not set, field_exists_expression is negated with boolean NOT.
|
|
168
|
+
|
|
169
|
+
# Field value in list, e.g. "field in (value list)" or "field containsall (value list)"
|
|
170
|
+
convert_or_as_in: ClassVar[bool] = False # Convert OR as in-expression
|
|
171
|
+
convert_and_as_in: ClassVar[bool] = False # Convert AND as in-expression
|
|
172
|
+
in_expressions_allow_wildcards: ClassVar[bool] = (
|
|
173
|
+
False # Values in list can contain wildcards. If set to False (default) only plain values are converted into in-expressions.
|
|
174
|
+
)
|
|
175
|
+
field_in_list_expression: ClassVar[str] = (
|
|
176
|
+
"{field} {op} ({list})" # Expression for field in list of values as format string with placeholders {field}, {op} and {list}
|
|
177
|
+
)
|
|
178
|
+
or_in_operator: ClassVar[str] = (
|
|
179
|
+
"IN" # Operator used to convert OR into in-expressions. Must be set if convert_or_as_in is set
|
|
180
|
+
)
|
|
181
|
+
# and_in_operator : ClassVar[str] = "contains-all" # Operator used to convert AND into in-expressions. Must be set if convert_and_as_in is set
|
|
182
|
+
list_separator: ClassVar[str] = ", " # List element separator
|
|
183
|
+
|
|
184
|
+
# Value not bound to a field
|
|
185
|
+
|
|
186
|
+
# TODO : SQlite only handles FTS ("MATCH") with virtual tables. Not Handled for now.
|
|
187
|
+
# unbound_value_str_expression : ClassVar[str] = "MATCH {value}" # Expression for string value not bound to a field as format string with placeholder {value}
|
|
188
|
+
# unbound_value_num_expression : ClassVar[str] = 'MATCH {value}' # Expression for number value not bound to a field as format string with placeholder {value}
|
|
189
|
+
|
|
190
|
+
# Query finalization: appending and concatenating deferred query part
|
|
191
|
+
deferred_start: ClassVar[str] = (
|
|
192
|
+
"" # String used as separator between main query and deferred parts
|
|
193
|
+
)
|
|
194
|
+
deferred_separator: ClassVar[str] = (
|
|
195
|
+
"" # String used to join multiple deferred query parts
|
|
196
|
+
)
|
|
197
|
+
deferred_only_query: ClassVar[str] = (
|
|
198
|
+
"" # String used as query if final query only contains deferred expression
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
table = "<TABLE_NAME>"
|
|
202
|
+
|
|
203
|
+
def convert_value_str(
|
|
204
|
+
self, s: SigmaString, state: ConversionState, no_quote: bool = False, glob_wildcards: bool = False
|
|
205
|
+
) -> str:
|
|
206
|
+
"""Convert a SigmaString into a plain string which can be used in query."""
|
|
207
|
+
|
|
208
|
+
if glob_wildcards:
|
|
209
|
+
converted = s.convert(
|
|
210
|
+
escape_char=self.escape_char,
|
|
211
|
+
wildcard_multi=self.wildcard_glob,
|
|
212
|
+
wildcard_single=self.wildcard_glob_single,
|
|
213
|
+
add_escaped=self.add_escaped,
|
|
214
|
+
filter_chars=self.filter_chars,
|
|
215
|
+
)
|
|
216
|
+
else:
|
|
217
|
+
converted = s.convert(
|
|
218
|
+
escape_char=self.escape_char,
|
|
219
|
+
wildcard_multi=self.wildcard_multi,
|
|
220
|
+
wildcard_single=self.wildcard_single,
|
|
221
|
+
add_escaped=self.add_escaped,
|
|
222
|
+
filter_chars=self.filter_chars,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
converted = converted.replace(
|
|
226
|
+
"'", "''"
|
|
227
|
+
) # Doubling single quote in SQL is mandatory
|
|
228
|
+
|
|
229
|
+
if self.decide_string_quoting(s) and not no_quote:
|
|
230
|
+
return self.quote_string(converted)
|
|
231
|
+
else:
|
|
232
|
+
return converted
|
|
233
|
+
|
|
234
|
+
def convert_condition_field_eq_val_str(
|
|
235
|
+
self, cond: ConditionFieldEqualsValueExpression, state: ConversionState
|
|
236
|
+
) -> Union[str, DeferredQueryExpression]:
|
|
237
|
+
"""Conversion of field = string value expressions"""
|
|
238
|
+
try:
|
|
239
|
+
remove_quote = True # Expressions that use "LIKE" (starswith, endswith, ...) don't need supplemental quotes
|
|
240
|
+
|
|
241
|
+
if ( # Check conditions for usage of 'startswith' operator
|
|
242
|
+
self.startswith_expression
|
|
243
|
+
is not None # 'startswith' operator is defined in backend
|
|
244
|
+
and cond.value.endswith(
|
|
245
|
+
SpecialChars.WILDCARD_MULTI
|
|
246
|
+
) # String ends with wildcard
|
|
247
|
+
and not cond.value[
|
|
248
|
+
:-1
|
|
249
|
+
].contains_special() # Remainder of string doesn't contains special characters
|
|
250
|
+
):
|
|
251
|
+
expr = (
|
|
252
|
+
self.startswith_expression
|
|
253
|
+
) # If all conditions are fulfilled, use 'startswith' operartor instead of equal token
|
|
254
|
+
value = cond.value[:-1]
|
|
255
|
+
elif ( # Same as above but for 'endswith' operator: string starts with wildcard and doesn't contains further special characters
|
|
256
|
+
self.endswith_expression is not None
|
|
257
|
+
and cond.value.startswith(SpecialChars.WILDCARD_MULTI)
|
|
258
|
+
and not cond.value[1:].contains_special()
|
|
259
|
+
):
|
|
260
|
+
expr = self.endswith_expression
|
|
261
|
+
value = cond.value[1:]
|
|
262
|
+
elif ( # contains: string starts and ends with wildcard
|
|
263
|
+
self.contains_expression is not None
|
|
264
|
+
and cond.value.startswith(SpecialChars.WILDCARD_MULTI)
|
|
265
|
+
and cond.value.endswith(SpecialChars.WILDCARD_MULTI)
|
|
266
|
+
and not cond.value[1:-1].contains_special()
|
|
267
|
+
):
|
|
268
|
+
expr = self.contains_expression
|
|
269
|
+
value = cond.value[1:-1]
|
|
270
|
+
elif ( # wildcard match expression: string contains wildcard
|
|
271
|
+
self.wildcard_match_expression is not None
|
|
272
|
+
and (
|
|
273
|
+
cond.value.contains_special()
|
|
274
|
+
or self.wildcard_multi in cond.value
|
|
275
|
+
or self.wildcard_single in cond.value
|
|
276
|
+
or self.escape_char in cond.value
|
|
277
|
+
)
|
|
278
|
+
):
|
|
279
|
+
expr = self.wildcard_match_expression
|
|
280
|
+
value = cond.value
|
|
281
|
+
else:
|
|
282
|
+
expr = "{field}" + self.eq_token + "{value}"
|
|
283
|
+
value = cond.value
|
|
284
|
+
remove_quote = False
|
|
285
|
+
|
|
286
|
+
if remove_quote:
|
|
287
|
+
return expr.format(
|
|
288
|
+
field=self.escape_and_quote_field(cond.field),
|
|
289
|
+
value=self.convert_value_str(value, state, remove_quote),
|
|
290
|
+
)
|
|
291
|
+
else:
|
|
292
|
+
return expr.format(
|
|
293
|
+
field=self.escape_and_quote_field(cond.field),
|
|
294
|
+
value=self.convert_value_str(value, state),
|
|
295
|
+
)
|
|
296
|
+
except TypeError: # pragma: no cover
|
|
297
|
+
raise NotImplementedError(
|
|
298
|
+
"Field equals string value expressions with strings are not supported by the backend."
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
def convert_condition_field_eq_val_str_case_sensitive(
|
|
302
|
+
self, cond: ConditionFieldEqualsValueExpression, state: ConversionState
|
|
303
|
+
) -> Union[str, DeferredQueryExpression]:
|
|
304
|
+
"""Conversion of case-sensitive field = string value expressions"""
|
|
305
|
+
try:
|
|
306
|
+
if ( # Check conditions for usage of 'startswith' operator
|
|
307
|
+
self.case_sensitive_startswith_expression
|
|
308
|
+
is not None # 'startswith' operator is defined in backend
|
|
309
|
+
and cond.value.endswith(SpecialChars.WILDCARD_MULTI) # String ends with wildcard
|
|
310
|
+
and (
|
|
311
|
+
self.case_sensitive_startswith_expression_allow_special
|
|
312
|
+
or not cond.value[:-1].contains_special()
|
|
313
|
+
) # Remainder of string doesn't contains special characters or it's allowed
|
|
314
|
+
):
|
|
315
|
+
expr = (
|
|
316
|
+
self.case_sensitive_startswith_expression
|
|
317
|
+
) # If all conditions are fulfilled, use 'startswith' operator instead of equal token
|
|
318
|
+
value = cond.value[:-1]
|
|
319
|
+
elif ( # Same as above but for 'endswith' operator: string starts with wildcard and doesn't contains further special characters
|
|
320
|
+
self.case_sensitive_endswith_expression is not None
|
|
321
|
+
and cond.value.startswith(SpecialChars.WILDCARD_MULTI)
|
|
322
|
+
and (
|
|
323
|
+
self.case_sensitive_endswith_expression_allow_special
|
|
324
|
+
or not cond.value[1:].contains_special()
|
|
325
|
+
)
|
|
326
|
+
):
|
|
327
|
+
expr = self.case_sensitive_endswith_expression
|
|
328
|
+
value = cond.value[1:]
|
|
329
|
+
elif ( # contains: string starts and ends with wildcard
|
|
330
|
+
self.case_sensitive_contains_expression is not None
|
|
331
|
+
and cond.value.startswith(SpecialChars.WILDCARD_MULTI)
|
|
332
|
+
and cond.value.endswith(SpecialChars.WILDCARD_MULTI)
|
|
333
|
+
and (
|
|
334
|
+
self.case_sensitive_contains_expression_allow_special
|
|
335
|
+
or not cond.value[1:-1].contains_special()
|
|
336
|
+
)
|
|
337
|
+
):
|
|
338
|
+
expr = self.case_sensitive_contains_expression
|
|
339
|
+
value = cond.value[1:-1]
|
|
340
|
+
elif self.case_sensitive_match_expression is not None:
|
|
341
|
+
expr = self.case_sensitive_match_expression
|
|
342
|
+
value = cond.value
|
|
343
|
+
else:
|
|
344
|
+
raise NotImplementedError(
|
|
345
|
+
"Case-sensitive string matching is not supported by backend."
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
return expr.format(
|
|
349
|
+
field=self.escape_and_quote_field(cond.field),
|
|
350
|
+
value=self.convert_value_str(value, state, no_quote=False, glob_wildcards=True),
|
|
351
|
+
regex=self.convert_value_re(value.to_regex(self.add_escaped_re), state),
|
|
352
|
+
)
|
|
353
|
+
except TypeError: # pragma: no cover
|
|
354
|
+
raise NotImplementedError(
|
|
355
|
+
"Case-sensitive field equals string value expressions with strings are not supported by the backend."
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
def convert_condition_field_eq_val_cidr(
|
|
359
|
+
self, cond: ConditionFieldEqualsValueExpression, state: ConversionState
|
|
360
|
+
) -> Union[str, DeferredQueryExpression]:
|
|
361
|
+
"""Conversion of field matches CIDR value expressions."""
|
|
362
|
+
cidr: SigmaCIDRExpression = cond.value
|
|
363
|
+
expanded = cidr.expand()
|
|
364
|
+
expanded_cond = ConditionOR(
|
|
365
|
+
[
|
|
366
|
+
ConditionFieldEqualsValueExpression(cond.field, SigmaString(network))
|
|
367
|
+
for network in expanded
|
|
368
|
+
],
|
|
369
|
+
cond.source,
|
|
370
|
+
)
|
|
371
|
+
return self.convert_condition(expanded_cond, state)
|
|
372
|
+
|
|
373
|
+
def finalize_query_default(
|
|
374
|
+
self, rule: SigmaRule, query: str, index: int, state: ConversionState
|
|
375
|
+
) -> Any:
|
|
376
|
+
|
|
377
|
+
# TODO : fields support will be handled with a backend option (all fields by default)
|
|
378
|
+
# fields = "*" if len(rule.fields) == 0 else f"*, {', '.join(rule.fields)}"
|
|
379
|
+
|
|
380
|
+
# TODO : table name will be handled with a backend option
|
|
381
|
+
sqlite_query = f"SELECT * FROM {self.table} WHERE {query}"
|
|
382
|
+
|
|
383
|
+
return sqlite_query
|
|
384
|
+
|
|
385
|
+
def finalize_query_zircolite(
|
|
386
|
+
self, rule: SigmaRule, query: str, index: int, state: ConversionState
|
|
387
|
+
) -> Any:
|
|
388
|
+
|
|
389
|
+
sqlite_query = f"SELECT * FROM logs WHERE {query}"
|
|
390
|
+
|
|
391
|
+
rule_as_dict = rule.to_dict()
|
|
392
|
+
|
|
393
|
+
zircolite_rule = {
|
|
394
|
+
"title": rule_as_dict["title"],
|
|
395
|
+
"id": rule_as_dict["id"] if "id" in rule_as_dict else "",
|
|
396
|
+
"status": rule_as_dict["status"] if "status" in rule_as_dict else "",
|
|
397
|
+
"description": (
|
|
398
|
+
rule_as_dict["description"] if "description" in rule_as_dict else ""
|
|
399
|
+
),
|
|
400
|
+
"author": rule_as_dict["author"] if "author" in rule_as_dict else "",
|
|
401
|
+
"tags": rule_as_dict["tags"] if "tags" in rule_as_dict else [],
|
|
402
|
+
"falsepositives": (
|
|
403
|
+
rule_as_dict["falsepositives"]
|
|
404
|
+
if "falsepositives" in rule_as_dict
|
|
405
|
+
else []
|
|
406
|
+
),
|
|
407
|
+
"level": rule_as_dict["level"] if "level" in rule_as_dict else "",
|
|
408
|
+
"rule": [sqlite_query],
|
|
409
|
+
"filename": "",
|
|
410
|
+
}
|
|
411
|
+
return zircolite_rule
|
|
412
|
+
|
|
413
|
+
def finalize_output_zircolite(self, queries: List[Dict]) -> str:
|
|
414
|
+
return json.dumps(list(queries))
|
|
415
|
+
|
|
416
|
+
# TODO : SQlite only handles FTS ("MATCH") with virtual tables. Not Handled for now.
|
|
417
|
+
def convert_condition_val_str(
|
|
418
|
+
self, cond: ConditionValueExpression, state: ConversionState
|
|
419
|
+
) -> Union[str, DeferredQueryExpression]:
|
|
420
|
+
"""Conversion of value-only strings."""
|
|
421
|
+
raise SigmaFeatureNotSupportedByBackendError(
|
|
422
|
+
"Value-only string expressions (i.e Full Text Search or 'keywords' search) are not supported by the backend."
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
def convert_condition_val_num(
|
|
426
|
+
self, cond: ConditionValueExpression, state: ConversionState
|
|
427
|
+
) -> Union[str, DeferredQueryExpression]:
|
|
428
|
+
"""Conversion of value-only numbers."""
|
|
429
|
+
raise SigmaFeatureNotSupportedByBackendError(
|
|
430
|
+
"Value-only number expressions (i.e Full Text Search or 'keywords' search) are not supported by the backend."
|
|
431
|
+
)
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
from sigma.conversion.deferred import DeferredQueryExpression
|
|
2
|
-
from sigma.conversion.state import ConversionState
|
|
3
|
-
from sigma.exceptions import SigmaFeatureNotSupportedByBackendError
|
|
4
|
-
from sigma.rule import SigmaRule
|
|
5
|
-
from sigma.conversion.base import TextQueryBackend
|
|
6
|
-
from sigma.conditions import ConditionItem, ConditionAND, ConditionOR, ConditionNOT, ConditionValueExpression, ConditionFieldEqualsValueExpression
|
|
7
|
-
from sigma.types import SigmaCompareExpression, SigmaString, SpecialChars, SigmaRegularExpressionFlag, SigmaCIDRExpression
|
|
8
|
-
|
|
9
|
-
import re
|
|
10
|
-
import json
|
|
11
|
-
from typing import ClassVar, Dict, List, Optional, Pattern, Tuple, Union, Any
|
|
12
|
-
|
|
13
|
-
class sqliteBackend(TextQueryBackend):
|
|
14
|
-
"""SQLite backend."""
|
|
15
|
-
|
|
16
|
-
# Operator precedence: tuple of Condition{AND,OR,NOT} in order of precedence.
|
|
17
|
-
# The backend generates grouping if required
|
|
18
|
-
name : ClassVar[str] = "SQLite backend"
|
|
19
|
-
formats : Dict[str, str] = {
|
|
20
|
-
"default": "Plain SQLite queries",
|
|
21
|
-
"zircolite": "Zircolite JSON format",
|
|
22
|
-
}
|
|
23
|
-
requires_pipeline : bool = False # TODO: does the backend requires that a processing pipeline is provided? This information can be used by user interface programs like Sigma CLI to warn users about inappropriate usage of the backend.
|
|
24
|
-
|
|
25
|
-
precedence : ClassVar[Tuple[ConditionItem, ConditionItem, ConditionItem]] = (ConditionNOT, ConditionAND, ConditionOR)
|
|
26
|
-
parenthesize : bool = True
|
|
27
|
-
group_expression : ClassVar[str] = "({expr})" # Expression for precedence override grouping as format string with {expr} placeholder
|
|
28
|
-
|
|
29
|
-
# Generated query tokens
|
|
30
|
-
token_separator : str = " " # separator inserted between all boolean operators
|
|
31
|
-
or_token : ClassVar[str] = "OR"
|
|
32
|
-
and_token : ClassVar[str] = "AND"
|
|
33
|
-
not_token : ClassVar[str] = "NOT"
|
|
34
|
-
eq_token : ClassVar[str] = "=" # Token inserted between field and value (without separator)
|
|
35
|
-
|
|
36
|
-
# String output
|
|
37
|
-
## Fields
|
|
38
|
-
### Quoting
|
|
39
|
-
|
|
40
|
-
# SQLite correct way to handle field name is detailed here : https://sqlite.org/lang_keywords.html.
|
|
41
|
-
# Double-quoting should be the way to go. But since in some case quotes are interpreted as literals we need to find an alternative.
|
|
42
|
-
# Obviously, we cannot use "[" and "]", because it is 2 different characters, so we are left with "`" (MySQL).
|
|
43
|
-
field_quote : ClassVar[str] = "`" # Character used to quote field characters if field_quote_pattern matches (or not, depending on field_quote_pattern_negation). No field name quoting is done if not set.
|
|
44
|
-
field_quote_pattern : ClassVar[Pattern] = re.compile("^[a-zA-Z0-9_]*$") # Quote field names if this pattern (doesn't) matches, depending on field_quote_pattern_negation. Field name is always quoted if pattern is not set.
|
|
45
|
-
field_quote_pattern_negation : ClassVar[bool] = True # Negate field_quote_pattern result. Field name is quoted if pattern doesn't matches if set to True (default).
|
|
46
|
-
|
|
47
|
-
### Escaping
|
|
48
|
-
#field_escape : ClassVar[str] = "\\" # Character to escape particular parts defined in field_escape_pattern.
|
|
49
|
-
#field_escape_quote : ClassVar[bool] = True # Escape quote string defined in field_quote
|
|
50
|
-
#field_escape_pattern : ClassVar[Pattern] = re.compile("\\s") # All matches of this pattern are prepended with the string contained in field_escape.
|
|
51
|
-
|
|
52
|
-
## Values
|
|
53
|
-
str_quote : ClassVar[str] = "'" # string quoting character (added as escaping character)
|
|
54
|
-
#str_quote_pattern: ClassVar[Pattern] = re.compile(r"^.*%{1,}.*|.*_{1,}.*$")
|
|
55
|
-
#str_quote_pattern_negation: ClassVar[bool] = True
|
|
56
|
-
|
|
57
|
-
escape_char : ClassVar[str] = "\\" # Escaping character for special characters inside string
|
|
58
|
-
wildcard_multi : ClassVar[str] = "%" # Character used as multi-character wildcard
|
|
59
|
-
wildcard_single : ClassVar[str] = "_" # Character used as single-character wildcard
|
|
60
|
-
add_escaped : ClassVar[str] = "\\" # Characters quoted in addition to wildcards and string quote
|
|
61
|
-
#filter_chars : ClassVar[str] = "" # Characters filtered
|
|
62
|
-
bool_values : ClassVar[Dict[bool, str]] = { # Values to which boolean values are mapped.
|
|
63
|
-
True: "true",
|
|
64
|
-
False: "false",
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
# String matching operators. if none is appropriate eq_token is used.
|
|
68
|
-
startswith_expression : ClassVar[str] = "{field} LIKE '{value}%' ESCAPE '\\'"
|
|
69
|
-
endswith_expression : ClassVar[str] = "{field} LIKE '%{value}' ESCAPE '\\'"
|
|
70
|
-
contains_expression : ClassVar[str] = "{field} LIKE '%{value}%' ESCAPE '\\'"
|
|
71
|
-
wildcard_match_expression : ClassVar[str] = "{field} LIKE '{value}' ESCAPE '\\'" # Special expression if wildcards can't be matched with the eq_token operator
|
|
72
|
-
|
|
73
|
-
# Special expression if wildcards can't be matched with the eq_token operator
|
|
74
|
-
wildcard_match_str_expression: ClassVar[str] = "{field} LIKE '{value}' ESCAPE '\\'"
|
|
75
|
-
#wildcard_match_num_expression: ClassVar[str] = "{field} LIKE '%{value}%'"
|
|
76
|
-
|
|
77
|
-
# Regular expressions
|
|
78
|
-
# Regular expression query as format string with placeholders {field}, {regex}, {flag_x} where x
|
|
79
|
-
# is one of the flags shortcuts supported by Sigma (currently i, m and s) and refers to the
|
|
80
|
-
# token stored in the class variable re_flags.
|
|
81
|
-
re_expression : ClassVar[str] = "{field} REGEXP '{regex}'"
|
|
82
|
-
re_escape_char : ClassVar[str] = "" # Character used for escaping in regular expressions
|
|
83
|
-
re_escape : ClassVar[Tuple[str]] = () # List of strings that are escaped
|
|
84
|
-
re_escape_escape_char : bool = True # If True, the escape character is also escaped
|
|
85
|
-
re_flag_prefix : bool = True # If True, the flags are prepended as (?x) group at the beginning of the regular expression, e.g. (?i). If this is not supported by the target, it should be set to False.
|
|
86
|
-
|
|
87
|
-
# Mapping from SigmaRegularExpressionFlag values to static string templates that are used in
|
|
88
|
-
# flag_x placeholders in re_expression template.
|
|
89
|
-
# By default, i, m and s are defined. If a flag is not supported by the target query language,
|
|
90
|
-
# remove it from re_flags or don't define it to ensure proper error handling in case of appearance.
|
|
91
|
-
# re_flags : Dict[SigmaRegularExpressionFlag, str] = {}
|
|
92
|
-
|
|
93
|
-
# Numeric comparison operators
|
|
94
|
-
compare_op_expression : ClassVar[str] = "{field} {operator} {value}" # Compare operation query as format string with placeholders {field}, {operator} and {value}
|
|
95
|
-
# Mapping between CompareOperators elements and strings used as replacement for {operator} in compare_op_expression
|
|
96
|
-
compare_operators : ClassVar[Dict[SigmaCompareExpression.CompareOperators, str]] = {
|
|
97
|
-
SigmaCompareExpression.CompareOperators.LT : "<",
|
|
98
|
-
SigmaCompareExpression.CompareOperators.LTE : "<=",
|
|
99
|
-
SigmaCompareExpression.CompareOperators.GT : ">",
|
|
100
|
-
SigmaCompareExpression.CompareOperators.GTE : ">=",
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
# Expression for comparing two event fields
|
|
104
|
-
field_equals_field_expression : ClassVar[Optional[str]] = None # Field comparison expression with the placeholders {field1} and {field2} corresponding to left field and right value side of Sigma detection item
|
|
105
|
-
field_equals_field_escaping_quoting : Tuple[bool, bool] = (True, True) # If regular field-escaping/quoting is applied to field1 and field2. A custom escaping/quoting can be implemented in the convert_condition_field_eq_field_escape_and_quote method.
|
|
106
|
-
|
|
107
|
-
# Null/None expressions
|
|
108
|
-
field_null_expression : ClassVar[str] = "{field} IS NULL" # Expression for field has null value as format string with {field} placeholder for field name
|
|
109
|
-
|
|
110
|
-
# Field existence condition expressions.
|
|
111
|
-
#field_exists_expression : ClassVar[str] = "exists({field})" # Expression for field existence as format string with {field} placeholder for field name
|
|
112
|
-
#field_not_exists_expression : ClassVar[str] = "notexists({field})" # Expression for field non-existence as format string with {field} placeholder for field name. If not set, field_exists_expression is negated with boolean NOT.
|
|
113
|
-
|
|
114
|
-
# Field value in list, e.g. "field in (value list)" or "field containsall (value list)"
|
|
115
|
-
convert_or_as_in : ClassVar[bool] = False # Convert OR as in-expression
|
|
116
|
-
convert_and_as_in : ClassVar[bool] = False # Convert AND as in-expression
|
|
117
|
-
in_expressions_allow_wildcards : ClassVar[bool] = False # Values in list can contain wildcards. If set to False (default) only plain values are converted into in-expressions.
|
|
118
|
-
field_in_list_expression : ClassVar[str] = "{field} {op} ({list})" # Expression for field in list of values as format string with placeholders {field}, {op} and {list}
|
|
119
|
-
or_in_operator : ClassVar[str] = "IN" # Operator used to convert OR into in-expressions. Must be set if convert_or_as_in is set
|
|
120
|
-
#and_in_operator : ClassVar[str] = "contains-all" # Operator used to convert AND into in-expressions. Must be set if convert_and_as_in is set
|
|
121
|
-
list_separator : ClassVar[str] = ", " # List element separator
|
|
122
|
-
|
|
123
|
-
# Value not bound to a field
|
|
124
|
-
|
|
125
|
-
# TODO : SQlite only handles FTS ("MATCH") with virtual tables. Not Handled for now.
|
|
126
|
-
#unbound_value_str_expression : ClassVar[str] = "MATCH {value}" # Expression for string value not bound to a field as format string with placeholder {value}
|
|
127
|
-
#unbound_value_num_expression : ClassVar[str] = 'MATCH {value}' # Expression for number value not bound to a field as format string with placeholder {value}
|
|
128
|
-
|
|
129
|
-
# Query finalization: appending and concatenating deferred query part
|
|
130
|
-
deferred_start : ClassVar[str] = "" # String used as separator between main query and deferred parts
|
|
131
|
-
deferred_separator : ClassVar[str] = "" # String used to join multiple deferred query parts
|
|
132
|
-
deferred_only_query : ClassVar[str] = "" # String used as query if final query only contains deferred expression
|
|
133
|
-
|
|
134
|
-
table = '<TABLE_NAME>'
|
|
135
|
-
|
|
136
|
-
def convert_value_str(self, s: SigmaString, state: ConversionState, no_quote: bool=False) -> str:
|
|
137
|
-
"""Convert a SigmaString into a plain string which can be used in query."""
|
|
138
|
-
converted = s.convert(
|
|
139
|
-
self.escape_char,
|
|
140
|
-
self.wildcard_multi,
|
|
141
|
-
self.wildcard_single,
|
|
142
|
-
self.add_escaped,
|
|
143
|
-
self.filter_chars,
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
converted = converted.replace("\'", "\'\'") # Doubling single quote in SQL is mandatory
|
|
147
|
-
|
|
148
|
-
if self.decide_string_quoting(s) and not no_quote:
|
|
149
|
-
return self.quote_string(converted)
|
|
150
|
-
else:
|
|
151
|
-
return converted
|
|
152
|
-
|
|
153
|
-
def convert_condition_field_eq_val_str(
|
|
154
|
-
self, cond: ConditionFieldEqualsValueExpression, state: ConversionState
|
|
155
|
-
) -> Union[str, DeferredQueryExpression]:
|
|
156
|
-
"""Conversion of field = string value expressions"""
|
|
157
|
-
try:
|
|
158
|
-
remove_quote = True # Expressions that use "LIKE" (starswith, endswith, ...) don't need supplemental quotes
|
|
159
|
-
|
|
160
|
-
if ( # Check conditions for usage of 'startswith' operator
|
|
161
|
-
self.startswith_expression is not None # 'startswith' operator is defined in backend
|
|
162
|
-
and cond.value.endswith(SpecialChars.WILDCARD_MULTI) # String ends with wildcard
|
|
163
|
-
and not cond.value[:-1].contains_special() # Remainder of string doesn't contains special characters
|
|
164
|
-
):
|
|
165
|
-
expr = (self.startswith_expression) # If all conditions are fulfilled, use 'startswith' operartor instead of equal token
|
|
166
|
-
value = cond.value[:-1]
|
|
167
|
-
elif ( # Same as above but for 'endswith' operator: string starts with wildcard and doesn't contains further special characters
|
|
168
|
-
self.endswith_expression is not None
|
|
169
|
-
and cond.value.startswith(SpecialChars.WILDCARD_MULTI)
|
|
170
|
-
and not cond.value[1:].contains_special()
|
|
171
|
-
):
|
|
172
|
-
expr = self.endswith_expression
|
|
173
|
-
value = cond.value[1:]
|
|
174
|
-
elif ( # contains: string starts and ends with wildcard
|
|
175
|
-
self.contains_expression is not None
|
|
176
|
-
and cond.value.startswith(SpecialChars.WILDCARD_MULTI)
|
|
177
|
-
and cond.value.endswith(SpecialChars.WILDCARD_MULTI)
|
|
178
|
-
and not cond.value[1:-1].contains_special()
|
|
179
|
-
):
|
|
180
|
-
expr = self.contains_expression
|
|
181
|
-
value = cond.value[1:-1]
|
|
182
|
-
elif ( # wildcard match expression: string contains wildcard
|
|
183
|
-
self.wildcard_match_expression is not None and (
|
|
184
|
-
cond.value.contains_special()
|
|
185
|
-
or self.wildcard_multi in cond.value
|
|
186
|
-
or self.wildcard_single in cond.value
|
|
187
|
-
or self.escape_char in cond.value
|
|
188
|
-
)
|
|
189
|
-
):
|
|
190
|
-
expr = self.wildcard_match_expression
|
|
191
|
-
value = cond.value
|
|
192
|
-
else:
|
|
193
|
-
expr = "{field}" + self.eq_token + "{value}"
|
|
194
|
-
value = cond.value
|
|
195
|
-
remove_quote = False
|
|
196
|
-
|
|
197
|
-
if remove_quote:
|
|
198
|
-
return expr.format(
|
|
199
|
-
field=self.escape_and_quote_field(cond.field),
|
|
200
|
-
value=self.convert_value_str(value, state, remove_quote),
|
|
201
|
-
)
|
|
202
|
-
else:
|
|
203
|
-
return expr.format(
|
|
204
|
-
field=self.escape_and_quote_field(cond.field),
|
|
205
|
-
value=self.convert_value_str(value, state),
|
|
206
|
-
)
|
|
207
|
-
except TypeError: # pragma: no cover
|
|
208
|
-
raise NotImplementedError(
|
|
209
|
-
"Field equals string value expressions with strings are not supported by the backend."
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
def convert_condition_field_eq_val_cidr(self, cond : ConditionFieldEqualsValueExpression, state : ConversionState) -> Union[str, DeferredQueryExpression]:
|
|
213
|
-
"""Conversion of field matches CIDR value expressions."""
|
|
214
|
-
cidr : SigmaCIDRExpression = cond.value
|
|
215
|
-
expanded = cidr.expand()
|
|
216
|
-
expanded_cond = ConditionOR([ ConditionFieldEqualsValueExpression(cond.field, SigmaString(network)) for network in expanded ], cond.source)
|
|
217
|
-
return self.convert_condition(expanded_cond, state)
|
|
218
|
-
|
|
219
|
-
def finalize_query_default(self, rule: SigmaRule, query: str, index: int, state: ConversionState) -> Any:
|
|
220
|
-
|
|
221
|
-
# TODO : fields support will be handled with a backend option (all fields by default)
|
|
222
|
-
#fields = "*" if len(rule.fields) == 0 else f"*, {', '.join(rule.fields)}"
|
|
223
|
-
|
|
224
|
-
# TODO : table name will be handled with a backend option
|
|
225
|
-
sqlite_query = f'SELECT * FROM {self.table} WHERE {query}'
|
|
226
|
-
|
|
227
|
-
return sqlite_query
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def finalize_query_zircolite(self, rule: SigmaRule, query: str, index: int, state: ConversionState) -> Any:
|
|
231
|
-
|
|
232
|
-
sqlite_query = f'SELECT * FROM logs WHERE {query}'
|
|
233
|
-
|
|
234
|
-
rule_as_dict = rule.to_dict()
|
|
235
|
-
|
|
236
|
-
zircolite_rule = {
|
|
237
|
-
"title": rule_as_dict["title"],
|
|
238
|
-
"id": rule_as_dict["id"] if "id" in rule_as_dict else "",
|
|
239
|
-
"status": rule_as_dict["status"] if "status" in rule_as_dict else "",
|
|
240
|
-
"description": rule_as_dict["description"] if "description" in rule_as_dict else "",
|
|
241
|
-
"author": rule_as_dict["author"] if "author" in rule_as_dict else "",
|
|
242
|
-
"tags": rule_as_dict["tags"] if "tags" in rule_as_dict else [],
|
|
243
|
-
"falsepositives": rule_as_dict["falsepositives"] if "falsepositives" in rule_as_dict else [],
|
|
244
|
-
"level": rule_as_dict["level"] if "level" in rule_as_dict else "",
|
|
245
|
-
"rule": [sqlite_query],
|
|
246
|
-
"filename": ""
|
|
247
|
-
}
|
|
248
|
-
return zircolite_rule
|
|
249
|
-
|
|
250
|
-
def finalize_output_zircolite(self, queries: List[Dict]) -> str:
|
|
251
|
-
return json.dumps(list(queries))
|
|
252
|
-
|
|
253
|
-
# TODO : SQlite only handles FTS ("MATCH") with virtual tables. Not Handled for now.
|
|
254
|
-
def convert_condition_val_str(self, cond : ConditionValueExpression, state : ConversionState) -> Union[str, DeferredQueryExpression]:
|
|
255
|
-
"""Conversion of value-only strings."""
|
|
256
|
-
raise SigmaFeatureNotSupportedByBackendError("Value-only string expressions (i.e Full Text Search or 'keywords' search) are not supported by the backend.")
|
|
257
|
-
|
|
258
|
-
def convert_condition_val_num(self, cond : ConditionValueExpression, state : ConversionState) -> Union[str, DeferredQueryExpression]:
|
|
259
|
-
"""Conversion of value-only numbers."""
|
|
260
|
-
raise SigmaFeatureNotSupportedByBackendError("Value-only number expressions (i.e Full Text Search or 'keywords' search) are not supported by the backend.")
|
|
File without changes
|
|
File without changes
|