Repository: corazawaf/coraza
Pull request #560 (2023-01-09 01:11:39)

Append request body
Currently we were blindly copying the request body from the upstream call to the
RequestBodyWriter and then back to the downstream handler in many cases wasting resources
because body was beyong what coraza should analyse and or request end up being rejected
due to the size. This method allows to early check conditions around the body size to be
able to resolve a rejecting before copying the the body (or part of it).

The design is similar to https://github.com/SpiderLabs/ModSecurity/blob/8c409149c9e03c36d3feb767f74a07405bc1a685/src/transaction.cc#L1038 as suggested by @M4tteoP 

Missing:
- tests multiwrite
- calling ProcessRequestBody if the limit is hit

codecov-commenter: # [Codecov](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf) Report
Base: **78.41**% // Head: **78.11**% // Decreases project coverage by **`-0.29%`** :warning:
> Coverage data is based on head [(`28f6df4`)](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf) compared to base [(`46029c0`)](https://codecov.io/gh/corazawaf/coraza/commit/46029c0f0862049e5983e4ff55cff38b5b6ab80c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf).
> Patch coverage: 44.11% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           v3/dev     #560      +/-   ##
==========================================
- Coverage   78.41%   78.11%   -0.30%     
==========================================
  Files         145      145              
  Lines        6971     6987      +16     
==========================================
- Hits         5466     5458       -8     
- Misses       1274     1294      +20     
- Partials      231      235       +4     
```

| Flag | Coverage Δ | |
|---|---|---|
| default | `73.44% <41.17%> (-0.39%)` | :arrow_down: |
| examples | `27.10% <2.94%> (-0.08%)` | :arrow_down: |
| ftw | `56.41% <44.11%> (-0.12%)` | :arrow_down: |
| tinygo | `71.74% <0.00%> (-0.20%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf) | Coverage Δ | |
|---|---|---|
| [internal/corazawaf/body\_buffer.go](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-aW50ZXJuYWwvY29yYXphd2FmL2JvZHlfYnVmZmVyLmdv) | `71.69% <ø> (ø)` | |
| [internal/corazawaf/transaction.go](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-aW50ZXJuYWwvY29yYXphd2FmL3RyYW5zYWN0aW9uLmdv) | `78.88% <23.52%> (-0.97%)` | :arrow_down: |
| [http/middleware.go](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-aHR0cC9taWRkbGV3YXJlLmdv) | `82.65% <64.70%> (-6.13%)` | :arrow_down: |
| [bodyprocessors/multipart.go](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf#diff-Ym9keXByb2Nlc3NvcnMvbXVsdGlwYXJ0Lmdv) | `78.48% <0.00%> (-3.80%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/corazawaf/coraza/pull/560?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=corazawaf).

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [91 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


jcchavezs: Once there is consensus this helper can get in I will backfill tests.

On Mon, 9 Jan 2023, 02:15 sonarcloud[bot], ***@***.***> wrote:

> Kudos, SonarCloud Quality Gate passed!    [image: Quality Gate passed]
> <https://sonarcloud.io/dashboard?id=coraza&pullRequest=560>
>
> [image: Bug]
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG> [image:
> A]
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG> 0
> Bugs
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG>
> [image: Vulnerability]
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY> [image:
> A]
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY> 0
> Vulnerabilities
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY>
> [image: Security Hotspot]
> <https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT> [image:
> A]
> <https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT> 0
> Security Hotspots
> <https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT>
> [image: Code Smell]
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL> [image:
> A]
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL> 91
> Code Smells
> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL>
>
> [image: 0.0%]
> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list> 0.0%
> Coverage
> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list>
> [image: 0.1%]
> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list> 0.1%
> Duplication
> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list>
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/corazawaf/coraza/pull/560#issuecomment-1374993770>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAXOYAU6752HUZ3TYUQSC7TWRNRCZANCNFSM6AAAAAATU4MUOI>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>

jcchavezs: e.g. in proxy-wasm we would avoid copying the body or only copy the amount
of bytes we need.

On Mon, 9 Jan 2023, 02:23 José Carlos Chávez, ***@***.***> wrote:

> Once there is consensus this helper can get in I will backfill tests.
>
> On Mon, 9 Jan 2023, 02:15 sonarcloud[bot], ***@***.***>
> wrote:
>
>> Kudos, SonarCloud Quality Gate passed!    [image: Quality Gate passed]
>> <https://sonarcloud.io/dashboard?id=coraza&pullRequest=560>
>>
>> [image: Bug]
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG> [image:
>> A]
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG> 0
>> Bugs
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG>
>> [image: Vulnerability]
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY> [image:
>> A]
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY> 0
>> Vulnerabilities
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY>
>> [image: Security Hotspot]
>> <https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT> [image:
>> A]
>> <https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT> 0
>> Security Hotspots
>> <https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT>
>> [image: Code Smell]
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL> [image:
>> A]
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL> 91
>> Code Smells
>> <https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL>
>>
>> [image: 0.0%]
>> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list> 0.0%
>> Coverage
>> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list>
>> [image: 0.1%]
>> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list> 0.1%
>> Duplication
>> <https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list>
>>
>> —
>> Reply to this email directly, view it on GitHub
>> <https://github.com/corazawaf/coraza/pull/560#issuecomment-1374993770>,
>> or unsubscribe
>> <https://github.com/notifications/unsubscribe-auth/AAXOYAU6752HUZ3TYUQSC7TWRNRCZANCNFSM6AAAAAATU4MUOI>
>> .
>> You are receiving this because you authored the thread.Message ID:
>> ***@***.***>
>>
>

jcchavezs: @anuraaga I addressed most of your comments (thanks a lot). Please do re-check, once the final design is in place I will backfill with tests and then try the new API in proxy-wasm before merge. 
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [92 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [93 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [93 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [93 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [93 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [93 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [93 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=coraza&pullRequest=560)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=coraza&pullRequest=560&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL) [93 Code Smells](https://sonarcloud.io/project/issues?id=coraza&pullRequest=560&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_coverage&view=list)  
[![0.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.1%')](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list) [0.1% Duplication](https://sonarcloud.io/component_measures?id=coraza&pullRequest=560&metric=new_duplicated_lines_density&view=list)


jcchavezs: > We need to make the processing idempotent for it, which we should do anyways to allow phase catch up as described in https://github.com/corazawaf/coraza-proxy-wasm/pull/128#discussion_r1062070317

Right now it is idempotent, I would defer the phase catch up to a next discussion. 

Are you happy with the changes @anuraaga?
jcchavezs: Here we use the new API https://github.com/corazawaf/coraza-proxy-wasm/pull/131

Patch:
diff --git a/examples/http-server/go.mod b/examples/http-server/go.mod
index ad2880b..031c2d4 100644
--- a/examples/http-server/go.mod
+++ b/examples/http-server/go.mod
@@ -5,8 +5,8 @@ go 1.18
 require github.com/corazawaf/coraza/v3 v3.0.0-20220914101451-05d352c89b24
 
 require (
-	github.com/magefile/mage v1.13.0 // indirect
+	github.com/magefile/mage v1.14.0 // indirect
 	github.com/tidwall/gjson v1.14.3 // indirect
 	github.com/tidwall/match v1.1.1 // indirect
-	github.com/tidwall/pretty v1.2.0 // indirect
+	github.com/tidwall/pretty v1.2.1 // indirect
 )
diff --git a/examples/http-server/go.sum b/examples/http-server/go.sum
index 574ffec..75f20ff 100644
--- a/examples/http-server/go.sum
+++ b/examples/http-server/go.sum
@@ -1,10 +1,9 @@
 github.com/corazawaf/coraza/v3 v3.0.0-20220914101451-05d352c89b24 h1:dy3992o5ue40g1QWKupjsBwZTRWagsuiGcOsbV0b4xs=
 github.com/corazawaf/coraza/v3 v3.0.0-20220914101451-05d352c89b24/go.mod h1:xhc7feR6FUfYgmBmRw3UObvLiyzT3XPQtlJD+huy+Mc=
-github.com/magefile/mage v1.13.0 h1:XtLJl8bcCM7EFoO8FyH8XK3t7G5hQAeK+i4tq+veT9M=
-github.com/magefile/mage v1.13.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
 github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
 github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
 github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
diff --git a/go.mod b/go.mod
index 94c3008..c26e17f 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,7 @@ require (
 	github.com/magefile/mage v1.14.0
 	github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9
 	github.com/tidwall/gjson v1.14.3
-	golang.org/x/net v0.0.0-20221002022538-bcab6841153b
+	golang.org/x/net v0.1.0
 )
 
 require (
@@ -29,6 +29,6 @@ require (
 	github.com/tidwall/match v1.1.1 // indirect
 	github.com/tidwall/pretty v1.2.1 // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/sys v0.0.0-20220913175220-63ea55921009 // indirect
+	golang.org/x/sys v0.2.0 // indirect
 	golang.org/x/tools v0.1.12 // indirect
 )
diff --git a/go.sum b/go.sum
index be1e3e5..ce509fa 100644
--- a/go.sum
+++ b/go.sum
@@ -30,8 +30,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU=
-golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
@@ -44,8 +44,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw=
-golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
diff --git a/go.work.sum b/go.work.sum
index 45c810a..35b9894 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -3,6 +3,10 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
 github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
+github.com/anuraaga/libinjection-go v0.0.0-20230113044807-5fcb754bf705 h1:WJN215vzCLcGIhglWb+hJZJPNJgpTb7nhubqcGq8fnM=
+github.com/anuraaga/libinjection-go v0.0.0-20230113044807-5fcb754bf705/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw=
+github.com/anuraaga/libinjection-go v0.0.0-20230113045242-08ebd556b8eb h1:dknZh+W2enuX87pS8SF9efHRxPLFiXWGBKLTx434BlA=
+github.com/anuraaga/libinjection-go v0.0.0-20230113045242-08ebd556b8eb/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
 github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
diff --git a/http/middleware.go b/http/middleware.go
index 23e83a3..0a401c0 100644
--- a/http/middleware.go
+++ b/http/middleware.go
@@ -8,6 +8,7 @@
 package http
 
 import (
+	"fmt"
 	"io"
 	"net/http"
 	"strconv"
@@ -58,16 +59,21 @@ func processRequest(tx types.Transaction, req *http.Request) (*types.Interruptio
 		// body inspection, otherwise we just let the request follow its
 		// regular flow.
 		if req.Body != nil && req.Body != http.NoBody {
-			_, err := io.Copy(tx.RequestBodyWriter(), req.Body)
+			it, _, err := tx.ReadRequestBodyFrom(req.Body)
 			if err != nil {
-				return nil, err
+				return nil, fmt.Errorf("failed to append request body: %s", err.Error())
 			}
 
-			reader, err := tx.RequestBodyReader()
+			if it != nil {
+				return it, nil
+			}
+
+			rbr, err := tx.RequestBodyReader()
 			if err != nil {
-				return nil, err
+				return nil, fmt.Errorf("failed to get the request body: %s", err.Error())
 			}
-			reader = io.MultiReader(reader, req.Body)
+
+			body := io.MultiReader(rbr, req.Body)
 			// req.Body is transparently reinizialied with a new io.ReadCloser.
 			// The http handler will be able to read it.
 			// Prior to Go 1.19 NopCloser does not implement WriterTo if the reader implements it.
@@ -76,24 +82,22 @@ func processRequest(tx types.Transaction, req *http.Request) (*types.Interruptio
 			// This avoid errors like "failed to process request: malformed chunked encoding" when
 			// using io.Copy.
 			// In Go 1.19 we just do `req.Body = io.NopCloser(reader)`
-			if rwt, ok := reader.(io.WriterTo); ok {
+			if rwt, ok := body.(io.WriterTo); ok {
 				req.Body = struct {
 					io.Reader
 					io.WriterTo
 					io.Closer
-				}{reader, rwt, req.Body}
+				}{body, rwt, req.Body}
 			} else {
 				req.Body = struct {
 					io.Reader
 					io.Closer
-				}{reader, req.Body}
+				}{body, req.Body}
 			}
 		}
-
-		return tx.ProcessRequestBody()
 	}
 
-	return nil, nil
+	return tx.ProcessRequestBody()
 }
 
 func WrapHandler(waf coraza.WAF, l Logger, h http.Handler) http.Handler {
diff --git a/internal/corazawaf/body_buffer.go b/internal/corazawaf/body_buffer.go
index 1a476a9..4068a71 100644
--- a/internal/corazawaf/body_buffer.go
+++ b/internal/corazawaf/body_buffer.go
@@ -16,13 +16,33 @@ import (
 // It will handle memory usage for buffering and processing
 // It implements io.Copy(bodyBuffer, someReader) by inherit io.Writer
 type BodyBuffer struct {
-	io.Writer
 	options types.BodyBufferOptions
 	buffer  *bytes.Buffer
 	writer  *os.File
 	length  int64
 }
 
+var (
+	_ io.WriterTo = (*BodyBuffer)(nil)
+	_ io.Writer   = (*BodyBuffer)(nil)
+)
+
+func (br *BodyBuffer) WriteTo(w io.Writer) (int64, error) {
+	if br.writer == nil {
+		return br.buffer.WriteTo(w)
+	}
+
+	b := make([]byte, br.length)
+
+	n, err := br.writer.Read(b)
+	if err != nil {
+		return 0, err
+	}
+
+	n, err = w.Write(b[:n])
+	return int64(n), err
+}
+
 // Write appends data to the body buffer by chunks
 // You may dump io.Readers using io.Copy(br, reader)
 func (br *BodyBuffer) Write(data []byte) (n int, err error) {
diff --git a/internal/corazawaf/transaction.go b/internal/corazawaf/transaction.go
index 9aaba57..e122e2c 100644
--- a/internal/corazawaf/transaction.go
+++ b/internal/corazawaf/transaction.go
@@ -66,7 +66,7 @@ type Transaction struct {
 	LastPhase types.RulePhase
 
 	// Handles request body buffers
-	RequestBodyBuffer *BodyBuffer
+	requestBodyBuffer *BodyBuffer
 
 	// Handles response body buffers
 	ResponseBodyBuffer *BodyBuffer
@@ -327,7 +327,7 @@ func (tx *Transaction) ResponseBodyReader() (io.Reader, error) {
 }
 
 func (tx *Transaction) RequestBodyReader() (io.Reader, error) {
-	return tx.RequestBodyBuffer.Reader()
+	return tx.requestBodyBuffer.Reader()
 }
 
 // AddRequestHeader Adds a request header
@@ -447,14 +447,25 @@ func (tx *Transaction) ParseRequestReader(data io.Reader) (*types.Interruption,
 		ct, _, _ = strings.Cut(ctcol[0], ";")
 	}
 	for scanner.Scan() {
-		if _, err := tx.RequestBodyBuffer.Write(scanner.Bytes()); err != nil {
+		it, _, err := tx.WriteRequestBody(scanner.Bytes())
+		if err != nil {
 			return nil, fmt.Errorf("cannot write to request body to buffer: %s", err.Error())
 		}
+
+		if it != nil {
+			return it, nil
+		}
+
 		// urlencoded cannot end with CRLF
 		if ct != "application/x-www-form-urlencoded" {
-			if _, err := tx.RequestBodyBuffer.Write([]byte{'\r', '\n'}); err != nil {
+			it, _, err := tx.WriteRequestBody([]byte{'\r', '\n'})
+			if err != nil {
 				return nil, fmt.Errorf("cannot write to request body to buffer: %s", err.Error())
 			}
+
+			if it != nil {
+				return it, nil
+			}
 		}
 	}
 	return tx.ProcessRequestBody()
@@ -760,8 +771,134 @@ func (tx *Transaction) ProcessRequestHeaders() *types.Interruption {
 	return tx.interruption
 }
 
-func (tx *Transaction) RequestBodyWriter() io.Writer {
-	return tx.RequestBodyBuffer
+func setAndReturnBodyLimitInterruption(tx *Transaction) (*types.Interruption, int, error) {
+	tx.variables.inboundErrorData.Set("1")
+	tx.interruption = &types.Interruption{
+		Status: 403,
+		Action: "deny",
+	}
+	return tx.interruption, 0, nil
+}
+
+// WriteRequestBody writes bytes from a slice of bytes into the request body,
+// it returns an interuption if the writing bytes go beyond the request body limit.
+// It won't copy the bytes if the body access isn't accesible.
+func (tx *Transaction) WriteRequestBody(b []byte) (*types.Interruption, int, error) {
+	if tx.RuleEngine == types.RuleEngineOff {
+		return nil, 0, nil
+	}
+
+	if !tx.RequestBodyAccess {
+		return nil, 0, nil
+	}
+
+	if tx.RequestBodyLimit == tx.requestBodyBuffer.length {
+		// tx.RequestBodyLimit will never be zero so if this happened, we have an
+		// interruption for sure.
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionReject {
+			return tx.interruption, 0, nil
+		}
+
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionProcessPartial {
+			return nil, 0, nil
+		}
+	}
+
+	var (
+		writingBytes          = int64(len(b))
+		runProcessRequestBody = false
+	)
+
+	if tx.requestBodyBuffer.length+writingBytes >= tx.RequestBodyLimit {
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionReject {
+			// We interrupt this transaction in case RequestBodyLimitAction is Reject
+			return setAndReturnBodyLimitInterruption(tx)
+		}
+
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionProcessPartial {
+			writingBytes = tx.RequestBodyLimit - tx.requestBodyBuffer.length
+			runProcessRequestBody = true
+		}
+	}
+
+	w, err := tx.requestBodyBuffer.Write(b[:writingBytes])
+	if err != nil {
+		return nil, 0, err
+	}
+
+	if runProcessRequestBody {
+		_, err = tx.ProcessRequestBody()
+	}
+	return tx.interruption, int(w), err
+}
+
+// ByteLenger returns the length in bytes of a data stream.
+type ByteLenger interface {
+	Len() int
+}
+
+// ReadRequestBodyFrom writes bytes from a reader into the request body
+// it returns an interuption if the writing bytes go beyond the request body limit.
+// It won't read the reader if the body access isn't accesible.
+func (tx *Transaction) ReadRequestBodyFrom(r io.Reader) (*types.Interruption, int, error) {
+	if tx.RuleEngine == types.RuleEngineOff {
+		return nil, 0, nil
+	}
+
+	if !tx.RequestBodyAccess {
+		return nil, 0, nil
+	}
+
+	if tx.RequestBodyLimit == tx.requestBodyBuffer.length {
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionReject {
+			return tx.interruption, 0, nil
+		}
+
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionProcessPartial {
+			return nil, 0, nil
+		}
+	}
+
+	var (
+		writingBytes          int64
+		runProcessRequestBody = false
+	)
+	if l, ok := r.(ByteLenger); ok {
+		writingBytes = int64(l.Len())
+		if tx.requestBodyBuffer.length+writingBytes >= tx.RequestBodyLimit {
+			if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionReject {
+				return setAndReturnBodyLimitInterruption(tx)
+			}
+
+			if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionProcessPartial {
+				writingBytes = tx.RequestBodyLimit - tx.requestBodyBuffer.length
+				runProcessRequestBody = true
+			}
+		}
+	} else {
+		writingBytes = tx.RequestBodyLimit - tx.requestBodyBuffer.length
+	}
+
+	w, err := io.CopyN(tx.requestBodyBuffer, r, writingBytes)
+	if err != nil && err != io.EOF {
+		return nil, int(w), err
+	}
+
+	if tx.requestBodyBuffer.length == tx.RequestBodyLimit {
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionReject {
+			return setAndReturnBodyLimitInterruption(tx)
+		}
+
+		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionProcessPartial {
+			runProcessRequestBody = true
+		}
+	}
+
+	err = nil
+	if runProcessRequestBody {
+		_, err = tx.ProcessRequestBody()
+	}
+	return tx.interruption, int(w), err
 }
 
 // ProcessRequestBody Performs the request body (if any)
@@ -788,7 +925,7 @@ func (tx *Transaction) ProcessRequestBody() (*types.Interruption, error) {
 	}
 
 	// we won't process empty request bodies or disabled RequestBodyAccess
-	if !tx.RequestBodyAccess || tx.RequestBodyBuffer.Size() == 0 {
+	if !tx.RequestBodyAccess || tx.requestBodyBuffer.length == 0 {
 		tx.WAF.Rules.Eval(types.PhaseRequestBody, tx)
 		return tx.interruption, nil
 	}
@@ -797,30 +934,11 @@ func (tx *Transaction) ProcessRequestBody() (*types.Interruption, error) {
 		mime = m[0]
 	}
 
-	reader, err := tx.RequestBodyBuffer.Reader()
+	reader, err := tx.requestBodyBuffer.Reader()
 	if err != nil {
 		return nil, err
 	}
 
-	// Chunked requests will always be written to a temporary file
-	if tx.RequestBodyBuffer.Size() >= tx.RequestBodyLimit {
-		tx.variables.inboundErrorData.Set("1")
-		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionReject {
-			// We interrupt this transaction in case RequestBodyLimitAction is Reject
-			tx.interruption = &types.Interruption{
-				Status: 403,
-				Action: "deny",
-			}
-			return tx.interruption, nil
-		}
-
-		if tx.WAF.RequestBodyLimitAction == types.RequestBodyLimitActionProcessPartial {
-			tx.variables.inboundErrorData.Set("1")
-			// we limit our reader to tx.RequestBodyLimit bytes
-			reader = io.LimitReader(reader, tx.RequestBodyLimit)
-		}
-	}
-
 	rbp := tx.variables.reqbodyProcessor.String()
 
 	// Default variables.ReqbodyProcessor values
@@ -1127,7 +1245,7 @@ func (tx *Transaction) Close() error {
 	defer tx.WAF.txPool.Put(tx)
 	tx.variables.reset()
 	var errs []error
-	if err := tx.RequestBodyBuffer.Reset(); err != nil {
+	if err := tx.requestBodyBuffer.Reset(); err != nil {
 		errs = append(errs, err)
 	}
 	if err := tx.ResponseBodyBuffer.Reset(); err != nil {
diff --git a/internal/corazawaf/waf.go b/internal/corazawaf/waf.go
index 54531ca..ad16893 100644
--- a/internal/corazawaf/waf.go
+++ b/internal/corazawaf/waf.go
@@ -174,8 +174,8 @@ func (w *WAF) newTransactionWithID(id string) *Transaction {
 
 	// Always non-nil if buffers / collections were already initialized so we don't do any of them
 	// based on the presence of RequestBodyBuffer.
-	if tx.RequestBodyBuffer == nil {
-		tx.RequestBodyBuffer = NewBodyBuffer(types.BodyBufferOptions{
+	if tx.requestBodyBuffer == nil {
+		tx.requestBodyBuffer = NewBodyBuffer(types.BodyBufferOptions{
 			TmpPath:     w.TmpDir,
 			MemoryLimit: w.RequestBodyInMemoryLimit,
 		})
diff --git a/magefile.go b/magefile.go
index 593e7ae..eabf81c 100644
--- a/magefile.go
+++ b/magefile.go
@@ -104,14 +104,32 @@ func Coverage() error {
 // Fuzz runs fuzz tests
 func Fuzz() error {
 	// Go must be run once per test when fuzzing
-	transformationTests := []string{
-		"FuzzB64Decode",
-		"FuzzCMDLine",
-	}
-	for _, test := range transformationTests {
-		fmt.Println("Running", test)
-		if err := sh.RunV("go", "test", "-fuzz="+test, "-fuzztime=2m", "./transformations"); err != nil {
-			return err
+	tests := []struct {
+		pkg   string
+		tests []string
+	}{
+		{
+			pkg: "./operators",
+			tests: []string{
+				"FuzzSQLi",
+				"FuzzXSS",
+			},
+		},
+		{
+			pkg: "./transformations",
+			tests: []string{
+				"FuzzB64Decode",
+				"FuzzCMDLine",
+			},
+		},
+	}
+
+	for _, pkgTests := range tests {
+		for _, test := range pkgTests.tests {
+			fmt.Println("Running", test)
+			if err := sh.RunV("go", "test", "-fuzz="+test, "-fuzztime=2m", pkgTests.pkg); err != nil {
+				return err
+			}
 		}
 	}
 
diff --git a/operators/detect_sqli.go b/operators/detect_sqli.go
index 37f74d4..e27e90f 100644
--- a/operators/detect_sqli.go
+++ b/operators/detect_sqli.go
@@ -24,7 +24,7 @@ func (o *detectSQLi) Evaluate(tx rules.TransactionState, value string) bool {
 	if !res {
 		return false
 	}
-	tx.CaptureField(0, string(fingerprint))
+	tx.CaptureField(0, fingerprint)
 	return true
 }
 
diff --git a/types/transaction.go b/types/transaction.go
index 62f2919..afb1dbb 100644
--- a/types/transaction.go
+++ b/types/transaction.go
@@ -63,12 +63,8 @@ type Transaction interface {
 	// note: Remember to check for a possible intervention.
 	ProcessRequestHeaders() *Interruption
 
-	// RequestBodyWriter returns a io.Writer for writing the request body to.
-	// Contents will be buffered until the transaction is closed.
-	RequestBodyWriter() io.Writer
-
 	// RequestBodyReader returns a reader for content that has been written by
-	// RequestBodyWriter. This can be useful for buffering the request body
+	// request body buffer. This can be useful for buffering the request body
 	// within the Transaction while also passing it further in an HTTP framework.
 	RequestBodyReader() (io.Reader, error)
 
@@ -88,6 +84,22 @@ type Transaction interface {
 	// Remember to check for a possible intervention.
 	ProcessRequestBody() (*Interruption, error)
 
+	// WriteRequestBody attempts to write data into the body up to the buffer limit and
+	// returns an interruption if the body is bigger than the limit and the action is to
+	// reject. This is specially convenient to resolve an interruption before copying
+	// the body into the request body buffer.
+	//
+	// It returns the corresponding interruption, the number of bytes written an error if any.
+	WriteRequestBody(b []byte) (*Interruption, int, error)
+
+	// ReadRequestBodyFrom attempts to write data into the body up to the buffer limit and
+	// returns an interruption if the body is bigger than the limit and the action is to
+	// reject. This is specially convenient to resolve an interruption before copying
+	// the body into the request body buffer.
+	//
+	// It returns the corresponding interruption, the number of bytes written an error if any.
+	ReadRequestBodyFrom(io.Reader) (*Interruption, int, error)
+
 	// AddResponseHeader Adds a response header variable
 	//
 	// With this method it is possible to feed Coraza with a response header.


Test patch:
diff --git a/http/middleware_test.go b/http/middleware_test.go
index bafe8f8..e8db056 100644
--- a/http/middleware_test.go
+++ b/http/middleware_test.go
@@ -83,7 +83,7 @@ func TestProcessRequestMultipart(t *testing.T) {
 	}
 
 	if err := tx.Close(); err != nil {
-		t.Errorf("failed to close the transaction: %s", err.Error())
+		t.Fatal(err)
 	}
 }
 
@@ -289,16 +289,16 @@ func TestHttpServer(t *testing.T) {
 		t.Run(name, func(t *testing.T) {
 			conf := coraza.NewWAFConfig().
 				WithDirectives(`
-		# This is a comment
-		SecDebugLogLevel 5
-		SecRequestBodyAccess On
-		SecResponseBodyAccess On
-		SecResponseBodyMimeType text/plain
-		SecRule ARGS:id "@eq 0" "id:1, phase:1,deny, status:403,msg:'Invalid id',log,auditlog"
-		SecRule REQUEST_BODY "@contains eval" "id:100, phase:2,deny, status:403,msg:'Invalid request body',log,auditlog"
-		SecRule RESPONSE_HEADERS:Foo "@pm bar" "id:199,phase:3,deny,t:lowercase,deny, status:401,msg:'Invalid response header',log,auditlog"
-		SecRule RESPONSE_BODY "@contains password" "id:200, phase:4,deny, status:403,msg:'Invalid response body',log,auditlog"
-	`).WithErrorCallback(errLogger(t)).WithDebugLogger(&debugLogger{t: t})
+	# This is a comment
+	SecDebugLogLevel 5
+	SecRequestBodyAccess On
+	SecResponseBodyAccess On
+	SecResponseBodyMimeType text/plain
+	SecRule ARGS:id "@eq 0" "id:1, phase:1,deny, status:403,msg:'Invalid id',log,auditlog"
+	SecRule REQUEST_BODY "@contains eval" "id:100, phase:2,deny, status:403,msg:'Invalid request body',log,auditlog"
+	SecRule RESPONSE_HEADERS:Foo "@pm bar" "id:199,phase:3,deny,t:lowercase,deny, status:401,msg:'Invalid response header',log,auditlog"
+	SecRule RESPONSE_BODY "@contains password" "id:200, phase:4,deny, status:403,msg:'Invalid response body',log,auditlog"
+`).WithErrorCallback(errLogger(t)).WithDebugLogger(&debugLogger{t: t})
 			if l := tCase.reqBodyLimit; l > 0 {
 				conf = conf.WithRequestBodyAccess(coraza.NewRequestBodyConfig().WithLimit(l).WithInMemoryLimit(l))
 			}
@@ -306,7 +306,7 @@ func TestHttpServer(t *testing.T) {
 			if err != nil {
 				t.Fatal(err)
 			}
-			runAgainstWaf(t, tCase, waf)
+			runAgainstWAF(t, tCase, waf)
 		})
 	}
 }
@@ -349,12 +349,12 @@ func TestHttpServerWithRuleEngineOff(t *testing.T) {
 			if err != nil {
 				t.Fatal(err)
 			}
-			runAgainstWaf(t, tCase, waf)
+			runAgainstWAF(t, tCase, waf)
 		})
 	}
 }
 
-func runAgainstWaf(t *testing.T, tCase httpTest, waf coraza.WAF) {
+func runAgainstWAF(t *testing.T, tCase httpTest, waf coraza.WAF) {
 	t.Helper()
 	serverErrC := make(chan error, 1)
 	defer close(serverErrC)
diff --git a/internal/corazawaf/transaction_test.go b/internal/corazawaf/transaction_test.go
index 94c7d28..a085918 100644
--- a/internal/corazawaf/transaction_test.go
+++ b/internal/corazawaf/transaction_test.go
@@ -5,6 +5,7 @@ package corazawaf
 
 import (
 	"fmt"
+	"io"
 	"regexp"
 	"runtime/debug"
 	"strconv"
@@ -137,61 +138,213 @@ func TestTxResponse(t *testing.T) {
 	*/
 }
 
-func TestRequestBody(t *testing.T) {
+var requestBodyWriters = map[string]func(tx *Transaction, body string) (*types.Interruption, int, error){
+	"WriteRequestBody": func(tx *Transaction, body string) (*types.Interruption, int, error) {
+		return tx.WriteRequestBody([]byte(body))
+	},
+	"ReadRequestBodyFromKnownLen": func(tx *Transaction, body string) (*types.Interruption, int, error) {
+		return tx.ReadRequestBodyFrom(strings.NewReader(body))
+	},
+	"ReadRequestBodyFromUnknownLen": func(tx *Transaction, body string) (*types.Interruption, int, error) {
+		return tx.ReadRequestBodyFrom(struct{ io.Reader }{
+			strings.NewReader(body),
+		})
+	},
+}
+
+func TestWriteRequestBody(t *testing.T) {
+	const (
+		urlencodedBody    = "some=result&second=data"
+		urlencodedBodyLen = len(urlencodedBody)
+	)
+
 	testCases := []struct {
 		name                   string
-		requestBodyLimit       int64
+		requestBodyLimit       int
 		requestBodyLimitAction types.RequestBodyLimitAction
 		shouldInterrupt        bool
 	}{
 		{
-			name:                   "default",
-			requestBodyLimit:       200,
-			requestBodyLimitAction: types.RequestBodyLimitActionReject,
+			name:                   "LimitNotReached",
+			requestBodyLimit:       urlencodedBodyLen + 2,
+			requestBodyLimitAction: types.RequestBodyLimitAction(-1),
 		},
 		{
-			name:                   "limit rejects",
-			requestBodyLimit:       11,
+			name:                   "LimitReachedAndRejects",
+			requestBodyLimit:       urlencodedBodyLen - 3,
 			requestBodyLimitAction: types.RequestBodyLimitActionReject,
 			shouldInterrupt:        true,
 		},
 		{
-			name:                   "limit partial processing",
-			requestBodyLimit:       11,
+			name:                   "LimitReachedAndPartialProcessing",
+			requestBodyLimit:       urlencodedBodyLen - 3,
 			requestBodyLimitAction: types.RequestBodyLimitActionProcessPartial,
 		},
 	}
 
+	urlencodedBodyLenThird := urlencodedBodyLen / 3
+	bodyChunks := map[string][]string{
+		"BodyInOneShot":     {urlencodedBody},
+		"BodyInThreeChunks": {urlencodedBody[0:urlencodedBodyLenThird], urlencodedBody[urlencodedBodyLenThird : 2*urlencodedBodyLenThird], urlencodedBody[2*urlencodedBodyLenThird:]},
+	}
+
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			urlencoded := "some=result&second=data"
-			// xml := "<test><content>test</content></test>"
-			tx := NewWAF().NewTransaction()
-			tx.RequestBodyAccess = true
-			tx.RequestBodyLimit = testCase.requestBodyLimit
-			tx.WAF.RequestBodyLimitAction = testCase.requestBodyLimitAction
-
-			tx.AddRequestHeader("content-type", "application/x-www-form-urlencoded")
-			if _, err := tx.RequestBodyBuffer.Write([]byte(urlencoded)); err != nil {
-				t.Errorf("Failed to write body buffer: %s", err.Error())
-			}
-			tx.ProcessRequestHeaders()
-			if _, err := tx.ProcessRequestBody(); err != nil {
-				t.Errorf("Failed to process request body: %s", err.Error())
+			for name, writeRequestBody := range requestBodyWriters {
+				t.Run(name, func(t *testing.T) {
+					for name, chunks := range bodyChunks {
+						t.Run(name, func(t *testing.T) {
+							waf := NewWAF()
+							waf.RuleEngine = types.RuleEngineOn
+							waf.RequestBodyAccess = true
+							waf.RequestBodyLimit = int64(testCase.requestBodyLimit)
+							waf.RequestBodyInMemoryLimit = int64(testCase.requestBodyLimit)
+							waf.RequestBodyLimitAction = testCase.requestBodyLimitAction
+
+							tx := waf.NewTransaction()
+							tx.AddRequestHeader("content-type", "application/x-www-form-urlencoded")
+
+							it := tx.ProcessRequestHeaders()
+							if it != nil {
+								t.Fatal("Unexpected interruption on headers")
+							}
+
+							var err error
+
+							for _, c := range chunks {
+								if it, _, err = writeRequestBody(tx, c); err != nil {
+									t.Errorf("Failed to write body buffer: %s", err.Error())
+								}
+							}
+
+							if testCase.shouldInterrupt {
+								if it == nil {
+									t.Fatal("Expected interruption, got nil")
+								}
+							} else {
+								it, err := tx.ProcessRequestBody()
+								if err != nil {
+									t.Fatal(err)
+								}
+
+								if it != nil {
+									t.Fatalf("Unexpected interruption")
+								}
+
+								val := tx.variables.argsPost.Get("some")
+								if len(val) != 1 || val[0] != "result" {
+									t.Errorf("Failed to set urlencoded POST data with arguments: \"%s\"", strings.Join(val, "\", \""))
+								}
+							}
+
+							_ = tx.Close()
+						})
+					}
+
+				})
 			}
 
-			if testCase.shouldInterrupt {
-				if tx.interruption == nil {
-					t.Error("expected interruption")
-				}
-			} else {
-				val := tx.variables.argsPost.Get("some")
-				if len(val) != 1 || val[0] != "result" {
-					t.Error("Failed to set url encoded post data")
-				}
+		})
+	}
+}
+
+func TestWriteRequestBodyOnLimitReached(t *testing.T) {
+	testCases := map[string]struct {
+		requestBodyLimitAction  types.RequestBodyLimitAction
+		preexistingInterruption *types.Interruption
+	}{
+		"reject": {
+			requestBodyLimitAction: types.RequestBodyLimitActionReject,
+			preexistingInterruption: &types.Interruption{
+				RuleID: 123,
+			},
+		},
+		"partial processing": {
+			requestBodyLimitAction: types.RequestBodyLimitActionProcessPartial,
+		},
+	}
+
+	for tName, tCase := range testCases {
+		waf := NewWAF()
+		waf.RuleEngine = types.RuleEngineOn
+		waf.RequestBodyAccess = true
+		waf.RequestBodyLimit = 2
+		waf.RequestBodyInMemoryLimit = 2
+		waf.RequestBodyLimitAction = tCase.requestBodyLimitAction
+
+		t.Run(tName, func(t *testing.T) {
+			for wName, writer := range requestBodyWriters {
+				t.Run(wName, func(t *testing.T) {
+					tx := waf.NewTransaction()
+					_, err := tx.requestBodyBuffer.Write([]byte("ab"))
+					if err != nil {
+						t.Fatalf("unexpected error when writing to body buffer directly: %s", err.Error())
+					}
+					tx.interruption = tCase.preexistingInterruption
+
+					it, n, err := writer(tx, "c")
+					if err != nil {
+						t.Fatalf("unexpected error: %s", err.Error())
+					}
+
+					if it != tCase.preexistingInterruption {
+						t.Fatalf("unexpected interruption")
+					}
+
+					if n != 0 {
+						t.Fatalf("unexpected number of bytes written")
+					}
+
+					_ = tx.Close()
+				})
 			}
+		})
+	}
+}
+
+func TestWriteRequestBodyIsNopWhenBodyIsNotAccesible(t *testing.T) {
+	testCases := []struct {
+		ruleEngine        types.RuleEngineStatus
+		requestBodyAccess bool
+	}{
+		{
+			ruleEngine: types.RuleEngineOff,
+		},
+		{
+			ruleEngine:        types.RuleEngineOn,
+			requestBodyAccess: false,
+		},
+	}
 
-			_ = tx.Close()
+	for _, tCase := range testCases {
+		t.Run(fmt.Sprintf(
+			"ruleEngine = %s and requestBodyAccess = %t",
+			tCase.ruleEngine.String(),
+			tCase.requestBodyAccess,
+		), func(t *testing.T) {
+			waf := NewWAF()
+			waf.RuleEngine = tCase.ruleEngine
+			waf.RequestBodyAccess = tCase.requestBodyAccess
+
+			for wName, writer := range requestBodyWriters {
+				t.Run(wName, func(t *testing.T) {
+					tx := waf.NewTransaction()
+					it, n, err := writer(tx, "abc")
+					if err != nil {
+						t.Fatalf("unexpected error: %s", err.Error())
+					}
+
+					if it != nil {
+						t.Fatalf("unexpected interruption")
+					}
+
+					if n != 0 {
+						t.Fatalf("unexpected number of bytes written")
+					}
+
+					_ = tx.Close()
+				})
+			}
 		})
 	}
 }
@@ -408,7 +561,7 @@ func TestRequestBodyProcessingAlgorithm(t *testing.T) {
 	tx.ForceRequestBodyVariable = true
 	tx.AddRequestHeader("content-type", "text/plain")
 	tx.AddRequestHeader("content-length", "7")
-	if _, err := tx.RequestBodyBuffer.Write([]byte("test123")); err != nil {
+	if _, err := tx.requestBodyBuffer.Write([]byte("test123")); err != nil {
 		t.Error("Failed to write request body buffer")
 	}
 	if _, err := tx.ProcessRequestBody(); err != nil {
@@ -563,7 +716,7 @@ func TestTxReqBodyForce(t *testing.T) {
 	tx := waf.NewTransaction()
 	tx.RequestBodyAccess = true
 	tx.ForceRequestBodyVariable = true
-	if _, err := tx.RequestBodyBuffer.Write([]byte("test")); err != nil {
+	if _, err := tx.requestBodyBuffer.Write([]byte("test")); err != nil {
 		t.Error(err)
 	}
 	if _, err := tx.ProcessRequestBody(); err != nil {
@@ -579,7 +732,7 @@ func TestTxReqBodyForceNegative(t *testing.T) {
 	tx := waf.NewTransaction()
 	tx.RequestBodyAccess = true
 	tx.ForceRequestBodyVariable = false
-	if _, err := tx.RequestBodyBuffer.Write([]byte("test")); err != nil {
+	if _, err := tx.requestBodyBuffer.Write([]byte("test")); err != nil {
 		t.Error(err)
 	}
 	if _, err := tx.ProcessRequestBody(); err != nil {
@@ -866,5 +1019,4 @@ func TestProcessorsIdempotency(t *testing.T) {
 			l.Close()
 		})
 	}
-
 }
diff --git a/internal/corazawaf/waf_test.go b/internal/corazawaf/waf_test.go
index e244aa0..f6af2fc 100644
--- a/internal/corazawaf/waf_test.go
+++ b/internal/corazawaf/waf_test.go
@@ -14,6 +14,7 @@ func TestNewTransaction(t *testing.T) {
 	waf.RequestBodyAccess = true
 	waf.ResponseBodyAccess = true
 	waf.RequestBodyLimit = 1044
+
 	tx := waf.NewTransactionWithID("test")
 	if !tx.RequestBodyAccess {
 		t.Error("Request body access not enabled")
diff --git a/internal/seclang/body_test.go b/internal/seclang/body_test.go
index 59d0c04..2376570 100644
--- a/internal/seclang/body_test.go
+++ b/internal/seclang/body_test.go
@@ -20,7 +20,7 @@ func TestRequestBodyAccessOff(t *testing.T) {
 	}
 	tx := waf.NewTransaction()
 	tx.ProcessURI("/", "POST", "http/1.1")
-	tx.RequestBodyBuffer.Write([]byte("test=123"))
+	tx.requestBodyBuffer.Write([]byte("test=123"))
 	tx.AddRequestHeader("Content-Type", "application/x-www-form-urlencoded")
 	tx.ProcessRequestHeaders()
 	tx.ProcessRequestBody()
@@ -39,7 +39,7 @@ func TestRequestBodyAccessOn(t *testing.T) {
 	}
 	tx := waf.NewTransaction()
 	tx.ProcessURI("/", "POST", "http/1.1")
-	if _, err := tx.RequestBodyBuffer.Write([]byte("test=123")); err != nil {
+	if _, _, err := tx.WriteRequestBody([]byte("test=123")); err != nil {
 		t.Error(err)
 	}
 	tx.AddRequestHeader("Content-Type", "application/x-www-form-urlencoded")
diff --git a/internal/seclang/rules_test.go b/internal/seclang/rules_test.go
index d2474bf..6f848ec 100644
--- a/internal/seclang/rules_test.go
+++ b/internal/seclang/rules_test.go
@@ -4,7 +4,6 @@
 package seclang
 
 import (
-	"io"
 	"regexp"
 	"strings"
 	"testing"
@@ -957,12 +956,17 @@ Content-Type: application/octet-stream
 
 BINARYDATA
 ------WebKitFormBoundaryABCDEFGIJKLMNOPQ--`)
-	_, err = io.Copy(tx.RequestBodyBuffer, body)
+	it, _, err := tx.ReadRequestBodyFrom(body)
 	if err != nil {
 		t.Error(err)
 		return
 	}
-	it, err := tx.ProcessRequestBody()
+
+	if it != nil {
+		t.Fatal(err)
+	}
+
+	it, err = tx.ProcessRequestBody()
 	if err != nil {
 		t.Error(err)
 		return
diff --git a/operators/detect_sqli_test.go b/operators/detect_sqli_test.go
new file mode 100644
index 0000000..d94ee5f
--- /dev/null
+++ b/operators/detect_sqli_test.go
@@ -0,0 +1,30 @@
+// Copyright 2023 Juan Pablo Tosso and the OWASP Coraza contributors
+// SPDX-License-Identifier: Apache-2.0
+
+package operators
+
+import (
+	"testing"
+
+	"github.com/corazawaf/coraza/v3/internal/corazawaf"
+)
+
+var sqliTests = []string{
+	"",
+	"this is not isqli",
+	"ascii(substring(version() from 1 for 1))",
+	"this\nis a ' or ''='\nsql injection",
+}
+
+func FuzzSQLi(f *testing.F) {
+	for _, tc := range sqliTests {
+		f.Add(tc)
+	}
+	sqli := &detectSQLi{}
+	waf := corazawaf.NewWAF()
+	f.Fuzz(func(t *testing.T, tc string) {
+		tx := waf.NewTransaction()
+		defer tx.Close()
+		_ = sqli.Evaluate(tx, tc)
+	})
+}
diff --git a/operators/detect_xss_test.go b/operators/detect_xss_test.go
new file mode 100644
index 0000000..700f15f
--- /dev/null
+++ b/operators/detect_xss_test.go
@@ -0,0 +1,31 @@
+// Copyright 2023 Juan Pablo Tosso and the OWASP Coraza contributors
+// SPDX-License-Identifier: Apache-2.0
+
+package operators
+
+import (
+	"testing"
+
+	"github.com/corazawaf/coraza/v3/internal/corazawaf"
+)
+
+var xssTests = []string{
+	"",
+	"this is not an XSS",
+	"<a href=\"javascript:alert(1)\">)",
+	"href=&#",
+	"href=&#X",
+}
+
+func FuzzXSS(f *testing.F) {
+	for _, tc := range xssTests {
+		f.Add(tc)
+	}
+	xss := &detectXSS{}
+	waf := corazawaf.NewWAF()
+	f.Fuzz(func(t *testing.T, tc string) {
+		tx := waf.NewTransaction()
+		defer tx.Close()
+		_ = xss.Evaluate(tx, tc)
+	})
+}
diff --git a/testing/coreruleset/coreruleset_test.go b/testing/coreruleset/coreruleset_test.go
index 23d40d5..d51187f 100644
--- a/testing/coreruleset/coreruleset_test.go
+++ b/testing/coreruleset/coreruleset_test.go
@@ -110,7 +110,7 @@ func BenchmarkCRSSimplePOST(b *testing.B) {
 		tx.AddRequestHeader("Accept", "application/json")
 		tx.AddRequestHeader("Content-Type", "application/x-www-form-urlencoded")
 		tx.ProcessRequestHeaders()
-		if _, err := tx.RequestBodyWriter().Write([]byte("parameters2=and&other2=Stuff")); err != nil {
+		if _, _, err := tx.WriteRequestBody([]byte("parameters2=and&other2=Stuff")); err != nil {
 			b.Error(err)
 		}
 		if _, err := tx.ProcessRequestBody(); err != nil {
@@ -144,7 +144,7 @@ func BenchmarkCRSLargePOST(b *testing.B) {
 		tx.AddRequestHeader("Accept", "application/json")
 		tx.AddRequestHeader("Content-Type", "application/x-www-form-urlencoded")
 		tx.ProcessRequestHeaders()
-		if _, err := tx.RequestBodyWriter().Write(postPayload); err != nil {
+		if _, _, err := tx.WriteRequestBody(postPayload); err != nil {
 			b.Error(err)
 		}
 		if _, err := tx.ProcessRequestBody(); err != nil {
diff --git a/testing/coreruleset/go.mod b/testing/coreruleset/go.mod
index 0533573..c0faa4f 100644
--- a/testing/coreruleset/go.mod
+++ b/testing/coreruleset/go.mod
@@ -13,7 +13,7 @@ require (
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver v1.5.0 // indirect
 	github.com/Masterminds/sprig v2.22.0+incompatible // indirect
-	github.com/corazawaf/libinjection-go v0.1.1 // indirect
+	github.com/corazawaf/libinjection-go v0.1.2 // indirect
 	github.com/fatih/color v1.13.0 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/goccy/go-yaml v1.8.10 // indirect
diff --git a/testing/coreruleset/go.sum b/testing/coreruleset/go.sum
index 4d1f64b..8f7d184 100644
--- a/testing/coreruleset/go.sum
+++ b/testing/coreruleset/go.sum
@@ -40,8 +40,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/corazawaf/coraza/v3 v3.0.0-20221004054810-060cedcb166d h1:e7nLsrnie6309FYWPZg2kY2yQWhHslmfkzZTPVnpeqg=
 github.com/corazawaf/coraza/v3 v3.0.0-20221004054810-060cedcb166d/go.mod h1:+ypLPFkX5j1GwKi+rqRZ57W3lSHReBdeVLh0o8qirI4=
-github.com/corazawaf/libinjection-go v0.1.1 h1:N/SMuy9Q4wPL72pU/OsoYjIIjfvUbsVwHf8A3tWMLKg=
-github.com/corazawaf/libinjection-go v0.1.1/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw=
+github.com/corazawaf/libinjection-go v0.1.2 h1:oeiV9pc5rvJ+2oqOqXEAMJousPpGiup6f7Y3nZj5GoM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
diff --git a/testing/engine.go b/testing/engine.go
index e039e8d..63a4fc8 100644
--- a/testing/engine.go
+++ b/testing/engine.go
@@ -134,7 +134,7 @@ func (t *Test) SetRequestBody(body interface{}) error {
 	if t.magic {
 		t.RequestHeaders["content-length"] = strconv.Itoa(lbody)
 	}
-	if _, err := t.transaction.RequestBodyWriter().Write([]byte(data)); err != nil {
+	if _, _, err := t.transaction.WriteRequestBody([]byte(data)); err != nil {
 		return err
 	}
 	return nil
diff --git a/testing/modsecurity_test.go b/testing/modsecurity_test.go
index 806a99b..76b0059 100644
--- a/testing/modsecurity_test.go
+++ b/testing/modsecurity_test.go
@@ -133,7 +133,7 @@ func BenchmarkModSecurityCRSSimplePOST(b *testing.B) {
 		if err := tx.ProcessRequestHeaders(); err != nil {
 			b.Error(err)
 		}
-		if err := tx.AppendRequestBody(body); err != nil {
+		if err := tx.ReadRequestBodyFrom(body); err != nil {
 			b.Error(err)
 		}
 		if err := tx.ProcessRequestBody(); err != nil {