11from dataclasses import dataclass , field
2- from typing import Any , Dict , Optional , Tuple , List
2+ from typing import Any , Dict , Optional , Tuple , List , Type
33from uuid import UUID
44from datetime import date , datetime
5+ import datetime as dt
56import yaml
67import re
78import sigma
89import sigma .exceptions as sigma_exceptions
9- from sigma .exceptions import SigmaRuleLocation
10+ from sigma .exceptions import SigmaError , SigmaRuleLocation
1011from sigma .rule .attributes import SigmaLevel , SigmaRelated , SigmaRuleTag , SigmaStatus
12+ from sigma .conversion .state import ConversionState
1113
1214
1315class SigmaYAMLLoader (yaml .SafeLoader ):
1416 """Custom YAML loader implementing additional functionality for Sigma."""
1517
16- def construct_mapping (self , node , deep = ...) :
18+ def construct_mapping (self , node : yaml . MappingNode , deep : bool = False ) -> Dict [ Any , Any ] :
1719 keys = set ()
1820 for k , v in node .value :
19- key = self .construct_object (k , deep = deep )
21+ key = self .construct_object (k , deep = deep ) # type: ignore
2022 if key in keys :
2123 raise yaml .error .YAMLError ("Duplicate key '{k}'" )
2224 else :
@@ -38,8 +40,8 @@ class SigmaRuleBase:
3840 references : List [str ] = field (default_factory = list )
3941 tags : List ["sigma.rule.attributes.SigmaRuleTag" ] = field (default_factory = list )
4042 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
4345 fields : List [str ] = field (default_factory = list )
4446 falsepositives : List [str ] = field (default_factory = list )
4547 level : Optional ["sigma.rule.attributes.SigmaLevel" ] = None
@@ -55,12 +57,12 @@ class SigmaRuleBase:
5557 _conversion_result : Optional [List [Any ]] = field (
5658 init = False , default = None , repr = False , compare = False
5759 )
58- _conversion_states : Optional [List ["sigma.conversion.state. ConversionState" ]] = field (
60+ _conversion_states : Optional [List ["ConversionState" ]] = field (
5961 init = False , default = None , repr = False , compare = False
6062 )
6163 _output : bool = field (init = False , default = True , repr = False , compare = False )
6264
63- def __post_init__ (self ):
65+ def __post_init__ (self ) -> None :
6466 for field in ("references" , "tags" , "fields" , "falsepositives" ):
6567 if self .__getattribute__ (field ) is None :
6668 self .__setattr__ (field , [])
@@ -75,10 +77,10 @@ def __post_init__(self):
7577 @classmethod
7678 def from_dict (
7779 cls ,
78- rule : dict ,
80+ rule : Dict [ str , Any ] ,
7981 collect_errors : bool = False ,
8082 source : Optional [SigmaRuleLocation ] = None ,
81- ) -> Tuple [dict , List [Exception ]]:
83+ ) -> Tuple [Dict [ str , Any ], List [SigmaError ]]:
8284 """
8385 Convert Sigma rule base parsed in dict structure into kwargs dict that can be passed to the
8486 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
9092 """
9193 errors = []
9294
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 ]:
9496 """
9597 Accepted string based date formats are in range 1000-01-01 .. 3999-12-31:
9698 * XXXX-XX-XX -- fully corresponds to yaml date format
@@ -99,21 +101,21 @@ def get_rule_as_date(name: str, exception_class) -> Optional[date]:
99101 2024-01-1, 24-1-24, 24/1/1, ...
100102 """
101103 nonlocal errors , rule , source
102- result = rule .get (name )
104+ value = rule .get (name )
103105 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 )
107109 ):
108110 error = True
109111 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
111113 accepted_regexps = (
112114 "([1-3][0-9][0-9][0-9])-([01][0-9])-([0-3][0-9])" , # 1000-01-01 .. 3999-12-31
113115 "([1-3][0-9][0-9][0-9])/([01]?[0-9])/([0-3]?[0-9])" , # 1000/1/1, 1000/01/01 .. 3999/12/31
114116 )
115117 for date_regexp in accepted_regexps :
116- matcher = re .fullmatch (date_regexp , result )
118+ matcher = re .fullmatch (date_regexp , value )
117119 if matcher :
118120 result = date (int (matcher [1 ]), int (matcher [2 ]), int (matcher [3 ]))
119121 error = False
@@ -123,10 +125,13 @@ def get_rule_as_date(name: str, exception_class) -> Optional[date]:
123125 if error :
124126 errors .append (
125127 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
127129 )
128130 )
129- return result
131+ return None
132+ return result
133+ else :
134+ return value
130135
131136 # Rule identifier may be empty or must be valid UUID
132137 rule_id = rule .get ("id" )
@@ -358,12 +363,14 @@ def get_rule_as_date(name: str, exception_class) -> Optional[date]:
358363 )
359364
360365 @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 ]]:
362369 """Convert YAML input string with single document into SigmaRule object."""
363370 parsed_rule = yaml .load (rule , SigmaYAMLLoader )
364371 return cls .from_dict (parsed_rule , collect_errors )
365372
366- def to_dict (self ) -> dict :
373+ def to_dict (self ) -> Dict [ str , Any ] :
367374 """Convert rule object into dict."""
368375 d = {
369376 "title" : self .title ,
0 commit comments