@@ -384,36 +384,42 @@ func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.
384
384
return types .ActionContinue
385
385
}
386
386
387
- if bodySize > 0 {
388
- b , err := proxywasm .GetHttpRequestBody (ctx .bodyReadIndex , bodySize )
389
- if err == nil {
390
- interruption , _ , err := tx .WriteRequestBody (b )
391
- if err != nil {
392
- ctx .logger .Error ().Err (err ).Msg ("Failed to write request body" )
393
- return types .ActionContinue
394
- }
395
-
396
- if interruption != nil {
397
- return ctx .handleInterruption (interruptionPhaseHttpRequestBody , interruption )
398
- }
399
-
400
- ctx .bodyReadIndex += bodySize
401
- } else if err != types .ErrorStatusNotFound {
402
- // When using FWT sometimes (it is inconsistent) we receive calls where ctx.bodyReadIndex == bodySize
403
- // meaning that the incoming size in the body is the same as the already read body.
404
- // When that happens, this code fails to retrieve the body through proxywasm.GetHttpRequestBody
405
- // as the total body is from 0 up to X bytes and since the last bodySize = X it attempts to read
406
- // from X up to X bytes and it returns a types.ErrorStatusNotFound. This could happen despite
407
- // endOfStream being true or false.
408
- // The tests in 920410 show this problem.
409
- // TODO(jcchavezs): Verify if this is a FTW problem.
410
- ctx .logger .Error ().
411
- Err (err ).
412
- Int ("body_read_index" , ctx .bodyReadIndex ).
387
+ // bodySize is the size of the whole body received so far, not the size of the current chunk
388
+ chunkSize := bodySize - ctx .bodyReadIndex
389
+ // OnHttpRequestBody might be called more than once with the same data, we check if there is new data available to be read
390
+ if chunkSize > 0 {
391
+ bodyChunk , err := proxywasm .GetHttpRequestBody (ctx .bodyReadIndex , chunkSize )
392
+ if err != nil {
393
+ ctx .logger .Error ().Err (err ).
413
394
Int ("body_size" , bodySize ).
395
+ Int ("body_read_index" , ctx .bodyReadIndex ).
396
+ Int ("chunk_size" , chunkSize ).
414
397
Msg ("Failed to read request body" )
415
398
return types .ActionContinue
416
399
}
400
+ readchunkSize := len (bodyChunk )
401
+ if readchunkSize != chunkSize {
402
+ ctx .logger .Warn ().Int ("read_chunk_size" , readchunkSize ).Int ("chunk_size" , chunkSize ).Msg ("Request chunk size read is different from the computed one" )
403
+ }
404
+ interruption , writtenBytes , err := tx .WriteRequestBody (bodyChunk )
405
+ if err != nil {
406
+ ctx .logger .Error ().Err (err ).Msg ("Failed to write request body" )
407
+ return types .ActionContinue
408
+ }
409
+ if interruption != nil {
410
+ return ctx .handleInterruption (interruptionPhaseHttpRequestBody , interruption )
411
+ }
412
+
413
+ // If not the whole chunk has been written, it implicitly means that we reached the waf request body limit.
414
+ // Internally ProcessRequestBody has been called and it did not raise any interruption (just checked in the condition above).
415
+ if writtenBytes < readchunkSize {
416
+ // No further body data will be processed
417
+ // Setting processedRequestBody avoid to call more than once ProcessRequestBody
418
+ ctx .processedRequestBody = true
419
+ return types .ActionContinue
420
+ }
421
+
422
+ ctx .bodyReadIndex += readchunkSize
417
423
}
418
424
419
425
if endOfStream {
@@ -531,6 +537,10 @@ func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types
531
537
return replaceResponseBodyWhenInterrupted (ctx .logger , bodySize )
532
538
}
533
539
540
+ if ctx .processedResponseBody {
541
+ return types .ActionContinue
542
+ }
543
+
534
544
if ctx .tx == nil {
535
545
return types .ActionContinue
536
546
}
@@ -562,33 +572,46 @@ func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types
562
572
return types .ActionContinue
563
573
}
564
574
565
- if bodySize > 0 {
566
- body , err := proxywasm .GetHttpResponseBody (ctx .bodyReadIndex , bodySize )
567
- if err == nil {
568
- interruption , _ , err := tx .WriteResponseBody (body )
569
- if err != nil {
570
- ctx .logger .Error ().Err (err ).Msg ("Failed to write response body" )
571
- return types .ActionContinue
572
- }
573
- // bodyReadIndex has to be updated before evaluating the interruption
574
- // it is internally needed to replace the full body if the tx is interrupted
575
- ctx .bodyReadIndex += bodySize
576
- if interruption != nil {
577
- return ctx .handleInterruption (interruptionPhaseHttpResponseBody , interruption )
578
- }
579
- } else if err != types .ErrorStatusNotFound {
575
+ chunkSize := bodySize - ctx .bodyReadIndex
576
+ if chunkSize > 0 {
577
+ bodyChunk , err := proxywasm .GetHttpResponseBody (ctx .bodyReadIndex , chunkSize )
578
+ if err != nil {
580
579
ctx .logger .Error ().
581
- Int ("body_read_index" , ctx .bodyReadIndex ).
582
580
Int ("body_size" , bodySize ).
581
+ Int ("body_read_index" , ctx .bodyReadIndex ).
582
+ Int ("chunk_size" , chunkSize ).
583
583
Err (err ).
584
584
Msg ("Failed to read response body" )
585
585
return types .ActionContinue
586
586
}
587
+
588
+ readchunkSize := len (bodyChunk )
589
+ if readchunkSize != chunkSize {
590
+ ctx .logger .Warn ().Int ("read_chunk_size" , readchunkSize ).Int ("chunk_size" , chunkSize ).Msg ("Response chunk size read is different from the computed one" )
591
+ }
592
+ interruption , writtenBytes , err := tx .WriteResponseBody (bodyChunk )
593
+ if err != nil {
594
+ ctx .logger .Error ().Err (err ).Msg ("Failed to write response body" )
595
+ return types .ActionContinue
596
+ }
597
+ // bodyReadIndex has to be updated before evaluating the interruption
598
+ // it is internally needed to replace the full body if the transaction is interrupted
599
+ ctx .bodyReadIndex += readchunkSize
600
+ if interruption != nil {
601
+ return ctx .handleInterruption (interruptionPhaseHttpResponseBody , interruption )
602
+ }
603
+ // If not the whole chunk has been written, it implicitly means that we reached the waf response body limit,
604
+ // internally ProcessResponseBody has been called and it did not raise any interruption (just checked in the condition above).
605
+ if writtenBytes < readchunkSize {
606
+ // no further body data will be processed
607
+ ctx .processedResponseBody = true
608
+ return types .ActionContinue
609
+ }
587
610
}
588
611
589
612
if endOfStream {
590
613
// We have already sent response headers, an unauthorized response can not be sent anymore,
591
- // but we can still drop the response to prevent leaking sensitive content.
614
+ // but we can still drop the response body to prevent leaking sensitive content.
592
615
// The error will also be logged by Coraza.
593
616
ctx .processedResponseBody = true
594
617
interruption , err := tx .ProcessResponseBody ()
@@ -604,7 +627,7 @@ func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types
604
627
return types .ActionContinue
605
628
}
606
629
// Wait until we see the entire body. It has to be buffered in order to check that it is fully legit
607
- // before sending it downstream
630
+ // before sending it downstream (to the client)
608
631
return types .ActionPause
609
632
}
610
633
0 commit comments