1"""`OrderChecker`."""
2
3import logging
4
5import idstools.rule
6
7from suricata_check.checkers.interface import CheckerInterface
8from suricata_check.utils.checker import (
9 are_rule_options_put_before,
10 count_rule_options,
11 get_rule_keyword_sequences,
12 get_rule_option_position,
13 is_rule_option_always_put_before,
14 is_rule_option_first,
15 is_rule_option_put_before,
16 is_rule_option_set,
17)
18from suricata_check.utils.checker_typing import ISSUES_TYPE, Issue
19from suricata_check.utils.regex import (
20 ALL_DETECTION_KEYWORDS,
21 ALL_TRANSFORMATION_KEYWORDS,
22 BUFFER_KEYWORDS,
23 CONTENT_KEYWORDS,
24 FLOW_STREAM_KEYWORDS,
25 MODIFIER_KEYWORDS,
26 OTHER_PAYLOAD_KEYWORDS,
27 PERFORMANCE_DETECTION_OPTIONS,
28 POINTER_MOVEMENT_KEYWORDS,
29 SIZE_KEYWORDS,
30 TRANSFORMATION_KEYWORDS,
31 get_regex_provider,
32 get_rule_body,
33)
34
35_regex_provider = get_regex_provider()
36
37
38# Regular expressions are placed here such that they are compiled only once.
39# This has a significant impact on the performance.
40REGEX_S210 = _regex_provider.compile(
41 r"^\(.*content\s*:.*;\s*content\s*:.*;.*(depth|offset)\s*:.*\)$",
42)
43
44
[docs]
45class OrderChecker(CheckerInterface):
46 """The `OrderChecker` contains several checks on the ordering Suricata options.
47
48 Note that the correct ordering of detection options is as follows:
49 1. Buffer
50 2. Size
51 3. Transformation
52 4. Coontent
53 5. Pointer movement
54 6. Fast pattern
55 7. Nocase
56 8. Other payload options
57
58 Codes S200-S209 report on the non-standard ordering of common options.
59
60 Codes S210-S219 report on the non-standard ordering of content matches.
61
62 Codes S220-S229 report on the non-standard ordering of flow options.
63
64 Codes S230-S239 report on the non-standard ordering of detection options.
65
66 Codes S240-S249 report on the non-standard ordering of threshold options.
67 """
68
69 codes = {
70 "S200": {"severity": logging.INFO},
71 "S201": {"severity": logging.INFO},
72 "S202": {"severity": logging.INFO},
73 "S203": {"severity": logging.INFO},
74 "S204": {"severity": logging.INFO},
75 "S205": {"severity": logging.INFO},
76 "S206": {"severity": logging.INFO},
77 "S207": {"severity": logging.INFO},
78 "S208": {"severity": logging.INFO},
79 "S210": {"severity": logging.INFO},
80 "S211": {"severity": logging.INFO},
81 "S212": {"severity": logging.INFO},
82 "S220": {"severity": logging.INFO},
83 "S221": {"severity": logging.INFO},
84 "S222": {"severity": logging.INFO},
85 "S223": {"severity": logging.INFO},
86 "S224": {"severity": logging.INFO},
87 "S230": {"severity": logging.INFO},
88 "S231": {"severity": logging.INFO},
89 "S232": {"severity": logging.INFO},
90 "S233": {"severity": logging.INFO},
91 "S234": {"severity": logging.INFO},
92 "S235": {"severity": logging.INFO},
93 "S236": {"severity": logging.INFO},
94 "S240": {"severity": logging.INFO},
95 "S241": {"severity": logging.INFO},
96 }
97
98 def _check_rule( # noqa: C901, PLR0912, PLR0915
99 self: "OrderChecker",
100 rule: idstools.rule.Rule,
101 ) -> ISSUES_TYPE:
102 issues: ISSUES_TYPE = []
103
104 body = get_rule_body(rule)
105
106 if is_rule_option_first(rule, "msg") is not True:
107 issues.append(
108 Issue(
109 code="S200",
110 message="""The rule body does not have msg as the first option.
111Consider reording to make msg the first option.""",
112 )
113 )
114
115 if is_rule_option_put_before(rule, "reference", ("content", "pcre")) is True:
116 issues.append(
117 Issue(
118 code="S201",
119 message="""The rule body contains the reference option before the detection logic.
120Consider reording to put the detection logic directly after the msg option.""",
121 )
122 )
123
124 if is_rule_option_put_before(rule, "classtype", ("reference",)) is True:
125 issues.append(
126 Issue(
127 code="S202",
128 message="""The rule body contains the classtype option before the reference option.
129Consider reording to put the classtype option directly after the reference option.""",
130 )
131 )
132
133 if is_rule_option_put_before(rule, "classtype", ("content", "pcre")) is True:
134 issues.append(
135 Issue(
136 code="S203",
137 message="""The rule body contains the classtype option before the detection logic.
138Consider reording to put the classtype option directly after the detection logic.""",
139 )
140 )
141
142 if is_rule_option_put_before(rule, "sid", ("classtype",)) is True:
143 issues.append(
144 Issue(
145 code="S204",
146 message="""The rule body contains the sid option before the classtype option.
147Consider reording to put the sid option directly after the classtype option.""",
148 )
149 )
150
151 if is_rule_option_put_before(rule, "sid", ("reference",)) is True:
152 issues.append(
153 Issue(
154 code="S205",
155 message="""The rule body contains the sid option before the reference option.
156Consider reording to put the sid option directly after the reference option.""",
157 )
158 )
159
160 if is_rule_option_put_before(rule, "sid", ("content", "pcre")) is True:
161 issues.append(
162 Issue(
163 code="S206",
164 message="""The rule body contains the sid option before the detection logic.
165Consider reording to put the sid option directly after the detection logic.""",
166 )
167 )
168
169 if is_rule_option_put_before(rule, "rev", ("sid",)) is True:
170 issues.append(
171 Issue(
172 code="S207",
173 message="""The rule body contains the rev option before the sid option.
174Consider reording to put the rev option directly after the sid option.""",
175 )
176 )
177
178 if is_rule_option_put_before(rule, "metadata", ("sid", "rev")) is True:
179 issues.append(
180 Issue(
181 code="S208",
182 message="""The rule body contains does not have the metadata option as the last option.
183Consider making metadata the last option.""",
184 )
185 )
186
187 if (
188 REGEX_S210.match(
189 body,
190 )
191 is not None
192 ):
193 issues.append(
194 Issue(
195 code="S210",
196 message="""The rule body contains a content matches modified by depth or offset \
197that is not the first content match.
198Consider moving the modified content match to the beginning of the detection options.""",
199 )
200 )
201
202 if count_rule_options(rule, "depth") > 1:
203 issues.append(
204 Issue(
205 code="S211",
206 message="""The rule body contains more than one content matche modified by depth.
207Consider making the second content match relative to the first using the within option.""",
208 )
209 )
210
211 if count_rule_options(rule, "offset") > 1:
212 issues.append(
213 Issue(
214 code="S212",
215 message="""The rule body contains more than one content matche modified by offset.
216Consider making the second content match relative to the first using the distance option.""",
217 )
218 )
219
220 if (
221 is_rule_option_set(rule, "flow")
222 and get_rule_option_position(rule, "flow") != 1
223 ):
224 issues.append(
225 Issue(
226 code="S220",
227 message="""The rule flow option is set but not directly following the msg option.
228Consider moving the flow option to directly after the msg option.""",
229 )
230 )
231
232 if (
233 is_rule_option_always_put_before(
234 rule,
235 "flow",
236 FLOW_STREAM_KEYWORDS,
237 )
238 is False
239 ):
240 issues.append(
241 Issue(
242 code="S221",
243 message="""The rule contains flow or stream keywords before the flow option in the rule body.
244Consider moving the flow option to before the flow and/or stream keywords.""",
245 )
246 )
247
248 if (
249 are_rule_options_put_before(
250 rule,
251 ("content", "pcre"),
252 FLOW_STREAM_KEYWORDS,
253 )
254 is True
255 ):
256 issues.append(
257 Issue(
258 code="S222",
259 message="""The rule contains flow or stream keywords after content buffers or detection logic.
260Consider moving the flow and/or stream keywords to before content buffers and detection options.""",
261 )
262 )
263
264 if (
265 is_rule_option_put_before(
266 rule,
267 "urilen",
268 FLOW_STREAM_KEYWORDS,
269 )
270 is True
271 ):
272 issues.append(
273 Issue(
274 code="S223",
275 message="""The rule contains the urilen option before the flow or stream keywords in the rule body.
276Consider moving the urilen option to after the flow and/or stream keywords.""",
277 )
278 )
279
280 if (
281 is_rule_option_always_put_before(
282 rule,
283 "urilen",
284 ("content", "pcre"),
285 )
286 is False
287 ):
288 issues.append(
289 Issue(
290 code="S224",
291 message="""The rule contains the urilen option after content buffers or detection logic.
292Consider moving the urilen option to before content buffers and detection options.""",
293 )
294 )
295
296 # Detects pointer movement before any content or buffer option or between a buffer and a content option.
297 for sequence in get_rule_keyword_sequences(
298 rule, seperator_keywords=CONTENT_KEYWORDS
299 ):
300 if (
301 are_rule_options_put_before(
302 rule,
303 POINTER_MOVEMENT_KEYWORDS,
304 set(CONTENT_KEYWORDS).union(BUFFER_KEYWORDS),
305 sequence=sequence,
306 )
307 is True
308 ):
309 issues.append(
310 Issue(
311 code="S230",
312 message="""The rule contains pointer movement before the content option in sequence {}.
313Consider moving the pointer movement options to after the content option.""".format(
314 sequence
315 ),
316 )
317 )
318
319 # Detects fast_pattern before any content or buffer option or between a buffer and a content option.
320 for sequence in get_rule_keyword_sequences(
321 rule, seperator_keywords=CONTENT_KEYWORDS
322 ):
323 if (
324 is_rule_option_put_before(
325 rule,
326 "fast_pattern",
327 set(SIZE_KEYWORDS)
328 .union(ALL_TRANSFORMATION_KEYWORDS)
329 .union(CONTENT_KEYWORDS)
330 .union(POINTER_MOVEMENT_KEYWORDS),
331 sequence=sequence,
332 )
333 is True
334 ):
335 issues.append(
336 Issue(
337 code="S231",
338 message="""The rule contains the fast_pattern option before \
339size options, transformation options, the content option or pointer movement options in sequence {}.
340Consider moving the fast_pattern option to after \
341size options, transformation options, the content option or pointer movement options.""".format(
342 sequence
343 ),
344 )
345 )
346
347 # Detects no_case before any content or buffer option or between a buffer and a content option.
348 for sequence in get_rule_keyword_sequences(
349 rule, seperator_keywords=CONTENT_KEYWORDS
350 ):
351 if (
352 is_rule_option_put_before(
353 rule,
354 "nocase",
355 set(SIZE_KEYWORDS)
356 .union(ALL_TRANSFORMATION_KEYWORDS)
357 .union(CONTENT_KEYWORDS)
358 .union(POINTER_MOVEMENT_KEYWORDS)
359 .union(PERFORMANCE_DETECTION_OPTIONS),
360 sequence=sequence,
361 )
362 is True
363 ):
364 issues.append(
365 Issue(
366 code="S232",
367 message="""The rule contains the nocase option before \
368size options, transformation options, the content option, pointer movement options, or fast_pattern option in sequence {}.
369Consider moving the nocase option to after \
370size options, transformation options, the content option, pointer movement options, or fast_pattern option.""".format(
371 sequence
372 ),
373 )
374 )
375
376 # Detects modifier options before any content or buffer option or between a buffer and a content option.
377 for sequence in get_rule_keyword_sequences(
378 rule, seperator_keywords=CONTENT_KEYWORDS
379 ):
380 if (
381 are_rule_options_put_before(
382 rule,
383 MODIFIER_KEYWORDS,
384 set(CONTENT_KEYWORDS),
385 sequence=sequence,
386 )
387 is True
388 ):
389 issues.append(
390 Issue(
391 code="S233",
392 message="""The rule contains modifier options before the content option.
393Consider moving the modifier options to after the content option.""",
394 )
395 )
396
397 # Detects other detection options before any content or buffer option or between a buffer and a content option.
398 for sequence in get_rule_keyword_sequences(
399 rule, seperator_keywords=CONTENT_KEYWORDS
400 ):
401 if (
402 are_rule_options_put_before(
403 rule,
404 OTHER_PAYLOAD_KEYWORDS,
405 set(CONTENT_KEYWORDS).union(BUFFER_KEYWORDS),
406 sequence=sequence,
407 )
408 is True
409 ):
410 issues.append(
411 Issue(
412 code="S234",
413 message="""The rule contains other detection options before \
414size options, transformation options, the content option, pointer movement options, nocase option, or fast_pattern option.
415Consider moving the other detection options to after \
416size options, transformation options, the content option, pointer movement options, nocase option, or fast_pattern option.""",
417 )
418 )
419
420 # Detects size options after any transformation options, content option or other detection options.
421 for sequence in get_rule_keyword_sequences(
422 rule, seperator_keywords=CONTENT_KEYWORDS
423 ):
424 if (
425 are_rule_options_put_before(
426 rule,
427 set(TRANSFORMATION_KEYWORDS)
428 .union(CONTENT_KEYWORDS)
429 .union(OTHER_PAYLOAD_KEYWORDS),
430 SIZE_KEYWORDS,
431 sequence=sequence,
432 )
433 is True
434 ):
435 issues.append(
436 Issue(
437 code="S235",
438 message="""The rule contains other size options after \
439any transformation options, content option or other detection options.
440Consider moving the size options to after any transformation options, content option or other detection options""",
441 )
442 )
443
444 # Detects transformation options after any content option or other detection options.
445 for sequence in get_rule_keyword_sequences(
446 rule, seperator_keywords=CONTENT_KEYWORDS
447 ):
448 if (
449 are_rule_options_put_before(
450 rule,
451 set(CONTENT_KEYWORDS).union(OTHER_PAYLOAD_KEYWORDS),
452 TRANSFORMATION_KEYWORDS,
453 sequence=sequence,
454 )
455 is True
456 ):
457 issues.append(
458 Issue(
459 code="S236",
460 message="""The rule contains other transformation options after \
461any content option or other detection options.
462Consider moving the transformation options to after any content option or other detection options""",
463 )
464 )
465
466 if (
467 is_rule_option_put_before(
468 rule,
469 "threshold",
470 ALL_DETECTION_KEYWORDS,
471 )
472 is True
473 ):
474 issues.append(
475 Issue(
476 code="S240",
477 message="""The rule contains the threshold option before some detection option.
478Consider moving the threshold option to after the detection options.""",
479 )
480 )
481
482 if (
483 is_rule_option_always_put_before(
484 rule,
485 "threshold",
486 ("reference", "sid"),
487 )
488 is False
489 ):
490 issues.append(
491 Issue(
492 code="S241",
493 message="""The rule contains the threshold option after the reference and/or sid option.
494Consider moving the threshold option to before the reference and sid options.""",
495 )
496 )
497
498 return issues