1818
1919try :
2020 from .ddwaf_types import ddwaf_config
21- from .ddwaf_types import ddwaf_context_destroy
22- from .ddwaf_types import ddwaf_context_init
23- from .ddwaf_types import ddwaf_destroy
21+ from .ddwaf_types import ddwaf_context_capsule
2422 from .ddwaf_types import ddwaf_get_version
25- from .ddwaf_types import ddwaf_init
2623 from .ddwaf_types import ddwaf_object
24+ from .ddwaf_types import ddwaf_object_free
2725 from .ddwaf_types import ddwaf_result
28- from .ddwaf_types import ddwaf_result_free
2926 from .ddwaf_types import ddwaf_ruleset_info
3027 from .ddwaf_types import ddwaf_run
31- from .ddwaf_types import ddwaf_update
28+ from .ddwaf_types import py_ddwaf_context_init
29+ from .ddwaf_types import py_ddwaf_init
3230 from .ddwaf_types import py_ddwaf_required_addresses
31+ from .ddwaf_types import py_ddwaf_update
3332
3433 _DDWAF_LOADED = True
3534except OSError :
@@ -82,16 +81,18 @@ def __init__(self, ruleset_map, obfuscation_parameter_key_regexp, obfuscation_pa
8281 key_regex = obfuscation_parameter_key_regexp , value_regex = obfuscation_parameter_value_regexp
8382 )
8483 self ._info = ddwaf_ruleset_info ()
85- self ._ruleset_map = ddwaf_object .create_without_limits (ruleset_map )
86- self ._handle = ddwaf_init (self ._ruleset_map , ctypes .byref (config ), ctypes .byref (self ._info ))
87- self ._ctx = 0
84+ ruleset_map_object = ddwaf_object .create_without_limits (ruleset_map )
85+ self ._handle = py_ddwaf_init (ruleset_map_object , ctypes .byref (config ), ctypes .byref (self ._info ))
8886 if not self ._handle or self ._info .failed :
87+ # We keep the handle alive in case of errors, as some valid rules can be loaded
88+ # at the same time some invalid ones are rejected
8989 LOGGER .error (
9090 "DDWAF.__init__: invalid rules\n ruleset: %s\n loaded:%s\n errors:%s\n " ,
91- self . _ruleset_map .struct ,
91+ ruleset_map_object .struct ,
9292 self ._info .loaded ,
9393 self .info .errors ,
9494 )
95+ ddwaf_object_free (ctypes .byref (ruleset_map_object ))
9596
9697 @property
9798 def required_data (self ):
@@ -110,63 +111,52 @@ def update_rules(self, new_rules):
110111 # type: (dict[text_type, DDWafRulesType]) -> bool
111112 """update the rules of the WAF instance. return True if an error occurs."""
112113 rules = ddwaf_object .create_without_limits (new_rules )
113- result = ddwaf_update (self ._handle , rules , ctypes .byref (self ._info ))
114- if result == 0 :
115- LOGGER .error ("DDWAF.update_rules: invalid rules" )
116- return True
117- else :
114+ result = py_ddwaf_update (self ._handle , rules , self ._info )
115+ ddwaf_object_free (rules )
116+ if result :
118117 LOGGER .debug ("DDWAF.update_rules success.\n info %s" , self .info )
119118 self ._handle = result
119+ return True
120+ else :
121+ LOGGER .debug ("DDWAF.update_rules: keeping the previous handle." )
120122 return False
121123
122124 def _at_request_start (self ):
123- if self ._ctx :
124- ddwaf_context_destroy (self ._ctx )
125- self ._ctx = ddwaf_context_init (self ._handle )
126- if self ._ctx == 0 :
127- LOGGER .error ("DDWaf failure to create the context" )
125+ # type: () -> ddwaf_context_capsule
126+ if self ._handle :
127+ ctx = py_ddwaf_context_init (self ._handle )
128+ if not ctx :
129+ LOGGER .error ("DDWaf._at_request_start: failure to create the context." )
130+ return ctx
128131
129132 def _at_request_end (self ):
130- if self ._ctx :
131- ddwaf_context_destroy (self ._ctx )
132- self ._ctx = 0
133+ # () -> None
134+ pass
133135
134136 def run (
135137 self , # type: DDWaf
138+ ctx , # type: ddwaf_context_capsule
136139 data , # type: DDWafRulesType
137140 timeout_ms = DEFAULT_DDWAF_TIMEOUT_MS , # type:int
138141 ):
139142 # type: (...) -> DDWaf_result
140143 start = time .time ()
141144
142- if self ._ctx == 0 :
143- LOGGER .warning ("DDWaf failsafe to create the context" )
144- self ._ctx = ddwaf_context_init (self ._handle )
145-
146- if self ._ctx == 0 :
147- LOGGER .error ("DDWaf failure: no context created" )
145+ if not ctx :
146+ LOGGER .error ("DDWaf.run: dry run. no context created." )
148147 return DDWaf_result (None , [], 0 , (time .time () - start ) * 1e6 )
149148
150149 result = ddwaf_result ()
151150 wrapper = ddwaf_object (data )
152- error = ddwaf_run (self . _ctx , wrapper , ctypes .byref (result ), timeout_ms * 1000 )
151+ error = ddwaf_run (ctx . ctx , wrapper , ctypes .byref (result ), timeout_ms * 1000 )
153152 if error < 0 :
154153 LOGGER .warning ("run DDWAF error: %d\n input %s\n error %s" , error , wrapper .struct , self .info .errors )
155- try :
156- return DDWaf_result (
157- result .data .decode ("UTF-8" , errors = "ignore" ) if hasattr (result , "data" ) and result .data else None ,
158- [result .actions .array [i ].decode ("UTF-8" , errors = "ignore" ) for i in range (result .actions .size )],
159- result .total_runtime / 1e3 ,
160- (time .time () - start ) * 1e6 ,
161- )
162- finally :
163- ddwaf_result_free (ctypes .byref (result ))
164-
165- def __dealloc__ (self ):
166- if self ._ctx :
167- ddwaf_context_destroy (self ._ctx )
168- if self ._handle :
169- ddwaf_destroy (self ._handle )
154+ return DDWaf_result (
155+ result .data .decode ("UTF-8" , errors = "ignore" ) if hasattr (result , "data" ) and result .data else None ,
156+ [result .actions .array [i ].decode ("UTF-8" , errors = "ignore" ) for i in range (result .actions .size )],
157+ result .total_runtime / 1e3 ,
158+ (time .time () - start ) * 1e6 ,
159+ )
170160
171161 def version ():
172162 # type: () -> text_type
@@ -185,13 +175,25 @@ def __init__(self, rules, obfuscation_parameter_key_regexp, obfuscation_paramete
185175
186176 def run (
187177 self , # type: DDWaf
178+ ctx , # type: ddwaf_context_capsule
188179 data , # type: Union[None, int, text_type, list[Any], dict[text_type, Any]]
189180 timeout_ms = DEFAULT_DDWAF_TIMEOUT_MS , # type:int
190181 ):
191182 # type: (...) -> DDWaf_result
192183 LOGGER .warning ("DDWaf features disabled. dry run" )
193184 return DDWaf_result (None , [], 0.0 , 0.0 )
194185
186+ def update_rules (self , _ ):
187+ # type: (dict[text_type, DDWafRulesType]) -> bool
188+ LOGGER .warning ("DDWaf features disabled. dry update" )
189+ return False
190+
191+ def _at_request_start (self ):
192+ pass
193+
194+ def _at_request_end (self ):
195+ pass
196+
195197 def version ():
196198 # type: () -> text_type
197199 LOGGER .warning ("DDWaf features disabled. null version" )
0 commit comments