1
1
from dataclasses import dataclass , field
2
- from typing import Any , Dict , Optional , Tuple , List
2
+ from typing import Any , Dict , Optional , Tuple , List , Type
3
3
from uuid import UUID
4
4
from datetime import date , datetime
5
+ import datetime as dt
5
6
import yaml
6
7
import re
7
8
import sigma
8
9
import sigma .exceptions as sigma_exceptions
9
- from sigma .exceptions import SigmaRuleLocation
10
+ from sigma .exceptions import SigmaError , SigmaRuleLocation
10
11
from sigma .rule .attributes import SigmaLevel , SigmaRelated , SigmaRuleTag , SigmaStatus
12
+ from sigma .conversion .state import ConversionState
11
13
12
14
13
15
class SigmaYAMLLoader (yaml .SafeLoader ):
14
16
"""Custom YAML loader implementing additional functionality for Sigma."""
15
17
16
- def construct_mapping (self , node , deep = ...) :
18
+ def construct_mapping (self , node : yaml . MappingNode , deep : bool = False ) -> Dict [ Any , Any ] :
17
19
keys = set ()
18
20
for k , v in node .value :
19
- key = self .construct_object (k , deep = deep )
21
+ key = self .construct_object (k , deep = deep ) # type: ignore
20
22
if key in keys :
21
23
raise yaml .error .YAMLError ("Duplicate key '{k}'" )
22
24
else :
@@ -38,8 +40,8 @@ class SigmaRuleBase:
38
40
references : List [str ] = field (default_factory = list )
39
41
tags : List ["sigma.rule.attributes.SigmaRuleTag" ] = field (default_factory = list )
40
42
author : Optional [str ] = None
41
- date : Optional ["datetime .date" ] = None
42
- modified : Optional ["datetime .date" ] = None
43
+ date : Optional ["dt .date" ] = None
44
+ modified : Optional ["dt .date" ] = None
43
45
fields : List [str ] = field (default_factory = list )
44
46
falsepositives : List [str ] = field (default_factory = list )
45
47
level : Optional ["sigma.rule.attributes.SigmaLevel" ] = None
@@ -55,12 +57,12 @@ class SigmaRuleBase:
55
57
_conversion_result : Optional [List [Any ]] = field (
56
58
init = False , default = None , repr = False , compare = False
57
59
)
58
- _conversion_states : Optional [List ["sigma.conversion.state. ConversionState" ]] = field (
60
+ _conversion_states : Optional [List ["ConversionState" ]] = field (
59
61
init = False , default = None , repr = False , compare = False
60
62
)
61
63
_output : bool = field (init = False , default = True , repr = False , compare = False )
62
64
63
- def __post_init__ (self ):
65
+ def __post_init__ (self ) -> None :
64
66
for field in ("references" , "tags" , "fields" , "falsepositives" ):
65
67
if self .__getattribute__ (field ) is None :
66
68
self .__setattr__ (field , [])
@@ -75,10 +77,10 @@ def __post_init__(self):
75
77
@classmethod
76
78
def from_dict (
77
79
cls ,
78
- rule : dict ,
80
+ rule : Dict [ str , Any ] ,
79
81
collect_errors : bool = False ,
80
82
source : Optional [SigmaRuleLocation ] = None ,
81
- ) -> Tuple [dict , List [Exception ]]:
83
+ ) -> Tuple [Dict [ str , Any ], List [SigmaError ]]:
82
84
"""
83
85
Convert Sigma rule base parsed in dict structure into kwargs dict that can be passed to the
84
86
class instantiation of an object derived from the SigmaRuleBase class and the errors list.
@@ -90,7 +92,7 @@ class instantiation of an object derived from the SigmaRuleBase class and the er
90
92
"""
91
93
errors = []
92
94
93
- def get_rule_as_date (name : str , exception_class ) -> Optional [date ]:
95
+ def get_rule_as_date (name : str , exception_class : Type [ SigmaError ] ) -> Optional [date ]:
94
96
"""
95
97
Accepted string based date formats are in range 1000-01-01 .. 3999-12-31:
96
98
* XXXX-XX-XX -- fully corresponds to yaml date format
@@ -99,21 +101,21 @@ def get_rule_as_date(name: str, exception_class) -> Optional[date]:
99
101
2024-01-1, 24-1-24, 24/1/1, ...
100
102
"""
101
103
nonlocal errors , rule , source
102
- result = rule .get (name )
104
+ value = rule .get (name )
103
105
if (
104
- result is not None
105
- and not isinstance (result , date )
106
- and not isinstance (result , datetime )
106
+ value is not None
107
+ and not isinstance (value , date )
108
+ and not isinstance (value , datetime )
107
109
):
108
110
error = True
109
111
try :
110
- result = str (result ) # forcifully convert whatever the type is into string
112
+ value = str (value ) # forcifully convert whatever the type is into string
111
113
accepted_regexps = (
112
114
"([1-3][0-9][0-9][0-9])-([01][0-9])-([0-3][0-9])" , # 1000-01-01 .. 3999-12-31
113
115
"([1-3][0-9][0-9][0-9])/([01]?[0-9])/([0-3]?[0-9])" , # 1000/1/1, 1000/01/01 .. 3999/12/31
114
116
)
115
117
for date_regexp in accepted_regexps :
116
- matcher = re .fullmatch (date_regexp , result )
118
+ matcher = re .fullmatch (date_regexp , value )
117
119
if matcher :
118
120
result = date (int (matcher [1 ]), int (matcher [2 ]), int (matcher [3 ]))
119
121
error = False
@@ -123,10 +125,13 @@ def get_rule_as_date(name: str, exception_class) -> Optional[date]:
123
125
if error :
124
126
errors .append (
125
127
exception_class (
126
- f"Rule { name } '{ result } ' is invalid, use yyyy-mm-dd" , source = source
128
+ f"Rule { name } '{ value } ' is invalid, use yyyy-mm-dd" , source = source
127
129
)
128
130
)
129
- return result
131
+ return None
132
+ return result
133
+ else :
134
+ return value
130
135
131
136
# Rule identifier may be empty or must be valid UUID
132
137
rule_id = rule .get ("id" )
@@ -358,12 +363,14 @@ def get_rule_as_date(name: str, exception_class) -> Optional[date]:
358
363
)
359
364
360
365
@classmethod
361
- def from_yaml (cls , rule : str , collect_errors : bool = False ) -> "SigmaRuleBase" :
366
+ def from_yaml (
367
+ cls , rule : str , collect_errors : bool = False
368
+ ) -> Tuple [Dict [str , Any ], List [SigmaError ]]:
362
369
"""Convert YAML input string with single document into SigmaRule object."""
363
370
parsed_rule = yaml .load (rule , SigmaYAMLLoader )
364
371
return cls .from_dict (parsed_rule , collect_errors )
365
372
366
- def to_dict (self ) -> dict :
373
+ def to_dict (self ) -> Dict [ str , Any ] :
367
374
"""Convert rule object into dict."""
368
375
d = {
369
376
"title" : self .title ,
0 commit comments