Skip to content

Commit 41a6419

Browse files
authored
waf: extract temp state from AppsecRuntimeConfig (#3952)
1 parent bcd7210 commit 41a6419

File tree

5 files changed

+192
-148
lines changed

5 files changed

+192
-148
lines changed

pkg/acquisition/modules/appsec/appsec_runner.go

Lines changed: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -132,66 +132,66 @@ func (r *AppsecRunner) Init(datadir string) error {
132132
return nil
133133
}
134134

135-
func (r *AppsecRunner) processRequest(tx appsec.ExtendedTransaction, request *appsec.ParsedRequest) error {
135+
func (r *AppsecRunner) processRequest(state *appsec.AppsecRequestState, request *appsec.ParsedRequest) error {
136136
var in *corazatypes.Interruption
137137
var err error
138138

139-
if request.Tx.IsRuleEngineOff() {
139+
if state.Tx.IsRuleEngineOff() {
140140
r.logger.Debugf("rule engine is off, skipping")
141141
return nil
142142
}
143143

144144
defer func() {
145-
request.Tx.ProcessLogging()
145+
state.Tx.ProcessLogging()
146146
//We don't close the transaction here, as it will reset coraza internal state and break variable tracking
147147

148-
err := r.AppsecRuntime.ProcessPostEvalRules(request)
148+
err := r.AppsecRuntime.ProcessPostEvalRules(state, request)
149149
if err != nil {
150150
r.logger.Errorf("unable to process PostEval rules: %s", err)
151151
}
152152
}()
153153

154154
//pre eval (expr) rules
155-
err = r.AppsecRuntime.ProcessPreEvalRules(request)
155+
err = r.AppsecRuntime.ProcessPreEvalRules(state, request)
156156
if err != nil {
157157
r.logger.Errorf("unable to process PreEval rules: %s", err)
158158
//FIXME: should we abort here ?
159159
}
160160

161-
request.Tx.ProcessConnection(request.ClientIP, 0, "", 0)
161+
state.Tx.ProcessConnection(request.ClientIP, 0, "", 0)
162162

163163
for k, v := range request.Args {
164164
for _, vv := range v {
165-
request.Tx.AddGetRequestArgument(k, vv)
165+
state.Tx.AddGetRequestArgument(k, vv)
166166
}
167167
}
168168

169-
request.Tx.ProcessURI(request.URI, request.Method, request.Proto)
169+
state.Tx.ProcessURI(request.URI, request.Method, request.Proto)
170170

171171
for k, vr := range request.Headers {
172172
for _, v := range vr {
173-
request.Tx.AddRequestHeader(k, v)
173+
state.Tx.AddRequestHeader(k, v)
174174
}
175175
}
176176

177177
if request.ClientHost != "" {
178-
request.Tx.AddRequestHeader("Host", request.ClientHost)
179-
request.Tx.SetServerName(request.ClientHost)
178+
state.Tx.AddRequestHeader("Host", request.ClientHost)
179+
state.Tx.SetServerName(request.ClientHost)
180180
}
181181

182182
if request.TransferEncoding != nil {
183-
request.Tx.AddRequestHeader("Transfer-Encoding", request.TransferEncoding[0])
183+
state.Tx.AddRequestHeader("Transfer-Encoding", request.TransferEncoding[0])
184184
}
185185

186-
in = request.Tx.ProcessRequestHeaders()
186+
in = state.Tx.ProcessRequestHeaders()
187187

188188
if in != nil {
189189
r.logger.Infof("inband rules matched for headers : %s", in.Action)
190190
return nil
191191
}
192192

193193
if len(request.Body) > 0 {
194-
in, _, err = request.Tx.WriteRequestBody(request.Body)
194+
in, _, err = state.Tx.WriteRequestBody(request.Body)
195195
if err != nil {
196196
r.logger.Errorf("unable to write request body : %s", err)
197197
return err
@@ -201,7 +201,7 @@ func (r *AppsecRunner) processRequest(tx appsec.ExtendedTransaction, request *ap
201201
}
202202
}
203203

204-
in, err = request.Tx.ProcessRequestBody()
204+
in, err = state.Tx.ProcessRequestBody()
205205
if err != nil {
206206
r.logger.Errorf("unable to process request body : %s", err)
207207
return err
@@ -214,70 +214,67 @@ func (r *AppsecRunner) processRequest(tx appsec.ExtendedTransaction, request *ap
214214
return nil
215215
}
216216

217-
func (r *AppsecRunner) ProcessInBandRules(request *appsec.ParsedRequest) error {
217+
func (r *AppsecRunner) ProcessInBandRules(state *appsec.AppsecRequestState, request *appsec.ParsedRequest) error {
218218
tx := appsec.NewExtendedTransaction(r.AppsecInbandEngine, request.UUID)
219-
r.AppsecRuntime.InBandTx = tx
220-
request.Tx = tx
219+
state.Tx = tx
221220
if len(r.AppsecRuntime.InBandRules) == 0 {
222221
return nil
223222
}
224-
err := r.processRequest(tx, request)
223+
err := r.processRequest(state, request)
225224
return err
226225
}
227226

228-
func (r *AppsecRunner) ProcessOutOfBandRules(request *appsec.ParsedRequest) error {
227+
func (r *AppsecRunner) ProcessOutOfBandRules(state *appsec.AppsecRequestState, request *appsec.ParsedRequest) error {
229228
tx := appsec.NewExtendedTransaction(r.AppsecOutbandEngine, request.UUID)
230-
r.AppsecRuntime.OutOfBandTx = tx
231-
request.Tx = tx
229+
state.Tx = tx
232230
if len(r.AppsecRuntime.OutOfBandRules) == 0 {
233231
return nil
234232
}
235-
err := r.processRequest(tx, request)
233+
err := r.processRequest(state, request)
236234
return err
237235
}
238236

239-
func (r *AppsecRunner) handleInBandInterrupt(request *appsec.ParsedRequest) {
237+
func (r *AppsecRunner) handleInBandInterrupt(state *appsec.AppsecRequestState, request *appsec.ParsedRequest) {
240238

241239
if allowed, reason := r.appsecAllowlistsClient.IsAllowlisted(request.ClientIP); allowed {
242240
r.logger.Infof("%s is allowlisted by %s, skipping", request.ClientIP, reason)
243241
return
244242
}
245243

246244
//create the associated event for crowdsec itself
247-
evt, err := EventFromRequest(request, r.Labels)
245+
evt, err := EventFromRequest(request, r.Labels, state.Tx.ID())
248246
if err != nil {
249247
//let's not interrupt the pipeline for this
250248
r.logger.Errorf("unable to create event from request : %s", err)
251249
}
252-
err = r.AccumulateTxToEvent(&evt, request)
253-
if err != nil {
254-
r.logger.Errorf("unable to accumulate tx to event : %s", err)
255-
}
256-
if in := request.Tx.Interruption(); in != nil {
250+
251+
r.AccumulateTxToEvent(&evt, state, request)
252+
253+
if in := state.Tx.Interruption(); in != nil {
257254
r.logger.Debugf("inband rules matched : %d", in.RuleID)
258-
r.AppsecRuntime.Response.InBandInterrupt = true
259-
r.AppsecRuntime.Response.BouncerHTTPResponseCode = r.AppsecRuntime.Config.BouncerBlockedHTTPCode
260-
r.AppsecRuntime.Response.UserHTTPResponseCode = r.AppsecRuntime.Config.UserBlockedHTTPCode
261-
r.AppsecRuntime.Response.Action = r.AppsecRuntime.DefaultRemediation
255+
state.Response.InBandInterrupt = true
256+
state.Response.BouncerHTTPResponseCode = r.AppsecRuntime.Config.BouncerBlockedHTTPCode
257+
state.Response.UserHTTPResponseCode = r.AppsecRuntime.Config.UserBlockedHTTPCode
258+
state.Response.Action = r.AppsecRuntime.DefaultRemediation
262259

263260
if _, ok := r.AppsecRuntime.RemediationById[in.RuleID]; ok {
264-
r.AppsecRuntime.Response.Action = r.AppsecRuntime.RemediationById[in.RuleID]
261+
state.Response.Action = r.AppsecRuntime.RemediationById[in.RuleID]
265262
}
266263

267264
for tag, remediation := range r.AppsecRuntime.RemediationByTag {
268265
if slices.Contains(in.Tags, tag) {
269-
r.AppsecRuntime.Response.Action = remediation
266+
state.Response.Action = remediation
270267
}
271268
}
272269

273-
err = r.AppsecRuntime.ProcessOnMatchRules(request, evt)
270+
err = r.AppsecRuntime.ProcessOnMatchRules(state, request, evt)
274271
if err != nil {
275272
r.logger.Errorf("unable to process OnMatch rules: %s", err)
276273
return
277274
}
278275

279276
// Should the in band match trigger an overflow ?
280-
if r.AppsecRuntime.Response.SendAlert {
277+
if state.Response.SendAlert {
281278
appsecOvlfw, err := AppsecEventGeneration(evt, request.HTTPRequest)
282279
if err != nil {
283280
r.logger.Errorf("unable to generate appsec event : %s", err)
@@ -288,34 +285,33 @@ func (r *AppsecRunner) handleInBandInterrupt(request *appsec.ParsedRequest) {
288285
}
289286
}
290287
// Should the in band match trigger an event ?
291-
if r.AppsecRuntime.Response.SendEvent {
288+
if state.Response.SendEvent {
292289
r.outChan <- evt
293290
}
294291

295292
}
296293
}
297294

298-
func (r *AppsecRunner) handleOutBandInterrupt(request *appsec.ParsedRequest) {
295+
func (r *AppsecRunner) handleOutBandInterrupt(state *appsec.AppsecRequestState, request *appsec.ParsedRequest) {
299296

300297
if allowed, reason := r.appsecAllowlistsClient.IsAllowlisted(request.ClientIP); allowed {
301298
r.logger.Infof("%s is allowlisted by %s, skipping", request.ClientIP, reason)
302299
return
303300
}
304301

305-
evt, err := EventFromRequest(request, r.Labels)
302+
evt, err := EventFromRequest(request, r.Labels, state.Tx.ID())
306303
if err != nil {
307304
//let's not interrupt the pipeline for this
308305
r.logger.Errorf("unable to create event from request : %s", err)
309306
}
310-
err = r.AccumulateTxToEvent(&evt, request)
311-
if err != nil {
312-
r.logger.Errorf("unable to accumulate tx to event : %s", err)
313-
}
314-
if in := request.Tx.Interruption(); in != nil {
307+
308+
r.AccumulateTxToEvent(&evt, state, request)
309+
310+
if in := state.Tx.Interruption(); in != nil {
315311
r.logger.Debugf("outband rules matched : %d", in.RuleID)
316-
r.AppsecRuntime.Response.OutOfBandInterrupt = true
312+
state.Response.OutOfBandInterrupt = true
317313

318-
err = r.AppsecRuntime.ProcessOnMatchRules(request, evt)
314+
err = r.AppsecRuntime.ProcessOnMatchRules(state, request, evt)
319315
if err != nil {
320316
r.logger.Errorf("unable to process OnMatch rules: %s", err)
321317
return
@@ -325,7 +321,7 @@ func (r *AppsecRunner) handleOutBandInterrupt(request *appsec.ParsedRequest) {
325321
// The event and the alert share the same internal map (parsed, meta, ...)
326322
// The event can be modified by the parsers, which might cause a concurrent map read/write
327323
// Should the match trigger an overflow ?
328-
if r.AppsecRuntime.Response.SendAlert {
324+
if state.Response.SendAlert {
329325
appsecOvlfw, err := AppsecEventGeneration(evt, request.HTTPRequest)
330326
if err != nil {
331327
r.logger.Errorf("unable to generate appsec event : %s", err)
@@ -337,17 +333,19 @@ func (r *AppsecRunner) handleOutBandInterrupt(request *appsec.ParsedRequest) {
337333
}
338334

339335
// Should the match trigger an event ?
340-
if r.AppsecRuntime.Response.SendEvent {
336+
if state.Response.SendEvent {
341337
r.outChan <- evt
342338
}
343339
}
344340
}
345341

346342
func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
347-
r.AppsecRuntime.Logger = r.AppsecRuntime.Logger.WithField("request_uuid", request.UUID)
343+
state := r.AppsecRuntime.NewRequestState()
344+
stateLogger := r.AppsecRuntime.Logger.WithField("request_uuid", request.UUID)
345+
r.AppsecRuntime.Logger = stateLogger
348346
logger := r.logger.WithField("request_uuid", request.UUID)
349347
logger.Debug("Request received in runner")
350-
r.AppsecRuntime.ClearResponse()
348+
r.AppsecRuntime.ClearResponse(&state)
351349

352350
request.IsInBand = true
353351
request.IsOutBand = false
@@ -356,11 +354,13 @@ func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
356354
startInBandParsing := time.Now()
357355
startGlobalParsing := time.Now()
358356

357+
state.CurrentPhase = appsec.PhaseInBand
358+
359359
//inband appsec rules
360-
err := r.ProcessInBandRules(request)
360+
err := r.ProcessInBandRules(&state, request)
361361
if err != nil {
362362
logger.Errorf("unable to process InBand rules: %s", err)
363-
err = request.Tx.Close()
363+
err = state.Tx.Close()
364364
if err != nil {
365365
logger.Errorf("unable to close inband transaction: %s", err)
366366
}
@@ -371,35 +371,36 @@ func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
371371
inBandParsingElapsed := time.Since(startInBandParsing)
372372
metrics.AppsecInbandParsingHistogram.With(prometheus.Labels{"source": request.RemoteAddrNormalized, "appsec_engine": request.AppsecEngine}).Observe(inBandParsingElapsed.Seconds())
373373

374-
if request.Tx.IsInterrupted() {
375-
r.handleInBandInterrupt(request)
374+
if state.Tx.IsInterrupted() {
375+
r.handleInBandInterrupt(&state, request)
376376
}
377377

378-
err = request.Tx.Close()
378+
err = state.Tx.Close()
379379
if err != nil {
380380
r.logger.Errorf("unable to close inband transaction: %s", err)
381381
}
382382

383383
// send back the result to the HTTP handler for the InBand part
384-
request.ResponseChannel <- r.AppsecRuntime.Response
384+
request.ResponseChannel <- state.Response
385385

386386
//Now let's process the out of band rules
387387

388388
request.IsInBand = false
389389
request.IsOutBand = true
390-
r.AppsecRuntime.Response.SendAlert = false
391-
r.AppsecRuntime.Response.SendEvent = true
390+
state.Response.SendAlert = false
391+
state.Response.SendEvent = true
392+
state.CurrentPhase = appsec.PhaseOutOfBand
392393

393394
//FIXME: This is a bit of a hack to avoid confusion with the transaction if we do not have any inband rules.
394395
//We should probably have different transaction (or even different request object) for inband and out of band rules
395396
if len(r.AppsecRuntime.OutOfBandRules) > 0 {
396397
//to measure the time spent in the Application Security Engine for OutOfBand rules
397398
startOutOfBandParsing := time.Now()
398399

399-
err = r.ProcessOutOfBandRules(request)
400+
err = r.ProcessOutOfBandRules(&state, request)
400401
if err != nil {
401402
logger.Errorf("unable to process OutOfBand rules: %s", err)
402-
err = request.Tx.Close()
403+
err = state.Tx.Close()
403404
if err != nil {
404405
logger.Errorf("unable to close outband transaction: %s", err)
405406
}
@@ -409,11 +410,11 @@ func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
409410
// time spent to process out of band rules
410411
outOfBandParsingElapsed := time.Since(startOutOfBandParsing)
411412
metrics.AppsecOutbandParsingHistogram.With(prometheus.Labels{"source": request.RemoteAddrNormalized, "appsec_engine": request.AppsecEngine}).Observe(outOfBandParsingElapsed.Seconds())
412-
if request.Tx.IsInterrupted() {
413-
r.handleOutBandInterrupt(request)
413+
if state.Tx.IsInterrupted() {
414+
r.handleOutBandInterrupt(&state, request)
414415
}
415416
}
416-
err = request.Tx.Close()
417+
err = state.Tx.Close()
417418
if err != nil {
418419
r.logger.Errorf("unable to close outband transaction: %s", err)
419420
}

0 commit comments

Comments
 (0)