Skip to content

Fill ARGS_POST variable even if body bytes were invalid JSON when JSON body processor is running#1615

Open
HusseinKabbout wants to merge 11 commits into
corazawaf:mainfrom
HusseinKabbout:main
Open

Fill ARGS_POST variable even if body bytes were invalid JSON when JSON body processor is running#1615
HusseinKabbout wants to merge 11 commits into
corazawaf:mainfrom
HusseinKabbout:main

Conversation

@HusseinKabbout

@HusseinKabbout HusseinKabbout commented Apr 23, 2026

Copy link
Copy Markdown

Thank you for contributing to Coraza WAF, your effort is greatly appreciated
Before submitting check if what you want to add to coraza list meets quality standards before sending pull request. Thanks!

Dear maintainers, the link above is broken

Make sure that you've checked the boxes below before you submit PR:

Thanks for your contribution ❤️

What does this PR do?

This PR addresses #1611.

The JSON body parser underwent a behavior change (introduced around 2024, but only recently released) that broke the ProcessPartial flow. When SecRequestBodyLimitAction is set to ProcessPartial and the body limit is reached, Coraza now always returns a body processing error for JSON bodies without populating ARGS_POST. This makes using ProcessPartil difficult if you want to validate the body but only on a "best effort" basis.

The expected behavior (at least what I would expect) for ProcessPartial is to parse + validate as much of the body as possible, even if the body is truncated. While it is true that truncated bodies will always return an error for the JSON processor, Coraza should still populate ARGS_POST so that rules can be applied to whatever was successfully parsed. Then users can still decide if they want to block the request on REQBODY_ERROR != 0 or if they want to simply let it pass and validate whatever is in ARGS_POST.

This PR tries to enable that behavior. The parser will keep throwing the "invalid JSON" error but it will now also fill ARGS_POST with whatever values it managed to parse before the truncation.

The added test tries to simulate what I described above:

  • Make request with JSON body that goes over the limit
  • Check if ARGS and ARGS_POST contain what woudl be expected
  • Check if response is still what would be expected (did not change)

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of JSON request and response bodies so flattened JSON fields are populated from the parsed content before JSON validation errors are reported, enabling best-effort extraction even when parsing issues occur.
  • Tests

    • Added a JSON engine testing profile for partial/truncated JSON processing under a small request size limit, with updated expectations for which JSON-derived checks trigger and confirmation that the response JSON contains the expected additional field.

@HusseinKabbout HusseinKabbout requested a review from a team as a code owner April 23, 2026 14:38
@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3fb8ebd4-04fa-43c2-8db3-49f2bcbca84b

📥 Commits

Reviewing files that changed from the base of the PR and between b59a645 and d454321.

📒 Files selected for processing (1)
  • testing/engine/json.go
💤 Files with no reviewable changes (1)
  • testing/engine/json.go

📝 Walkthrough

Walkthrough

JSON body processing now populates flattened request and response arguments before returning read errors, and readJSON validates after parse-and-flatten work. A new engine profile exercises partial JSON processing with a 40-byte request-body limit.

Changes

JSON body processing

Layer / File(s) Summary
Populate flattened args before error return
internal/bodyprocessors/json.go
ProcessRequest and ProcessResponse now copy flattened data into ArgsPost and ResponseArgs before returning the error from readJSON.
Parse before validity check
internal/bodyprocessors/json.go
readJSON now calls gjson.Parse and readItems before gjson.Valid, and returns the flattened result alongside any readItems error.

JSON engine test profile

Layer / File(s) Summary
Add truncated JSON profile
testing/engine/json.go
Adds truncatedjsonyaml with JSON request/response processing, ProcessPartial, a 40-byte request-body limit, and rule expectations for truncated input and response augmentation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: fzipi

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main behavior change to populate ARGS_POST for invalid JSON during JSON body processing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
testing/engine/json.go (1)

78-162: Good coverage of the partial-body path.

The new profile correctly asserts both positive (test/test2/test3.0 parsed → 1100/1101/1104/1200/1201 fire) and negative (test3.1/test3.2 never reach ARGS/ARGS_POST → 1102/1103/1202/1203 don't fire) cases for truncation at 40 bytes, and 1010 validates the array-length semantic with only one element flattened. One small suggestion for robustness: consider adding a companion stage that keeps rule 1111 (REQBODY_ERROR !@eq 0) enabled to explicitly assert that the error flag is still raised alongside the populated args — that's the core contract of this PR and currently only the absence of blocking is checked.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@testing/engine/json.go` around lines 78 - 162, Add a companion stage to the
"truncatedjsonyaml" profile that uses the same StageInput as the existing "json"
stage but expects rule id 1111 to be triggered (i.e., include 1111 in
TriggeredRules) to assert the REQBODY_ERROR flag is set; also enable the SecRule
REQBODY_ERROR "!@eq 0" "id:1111, phase:2, log, block" in the Rules block (it is
currently commented out) so the test can observe that error rule. Locate the
profile by name "truncatedjsonyaml", the existing Stage under Tests -> Title
"json" to copy the input, and the SecRule with id 1111 to uncomment/activate.
Ensure the new stage's Output still includes the previously expected positive
rule ids (e.g., 1100,1101,1104,1200,1201) plus 1111 as a triggered rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/bodyprocessors/json.go`:
- Around line 83-88: The current logic calls readItems(...) and then overwrites
its error when gjson.Valid(s) is false, which hides specific errors from
readItems (e.g., max recursion); change the return logic so you preserve or wrap
the readItems error instead of discarding it: after err := readItems(json, key,
maxRecursion, res) check gjson.Valid(s) and if it's invalid, return the
validation error but if err != nil wrap the readItems error (e.g.,
fmt.Errorf("invalid JSON: %w", err)) or return err directly so the more specific
readItems error from readItems(...) is not lost; refer to the variables json :=
gjson.Parse(s), err, readItems and gjson.Valid(s) when implementing the change.

In `@testing/engine/json.go`:
- Around line 140-141: Fix the typo in the inline comment by changing "commented
our" to "commented out" in the commented lines above the SecRule (the comment
containing "This is commented our because we want to check if body processor can
work with partial JSON"); update that comment text so it reads "This is
commented out because we want to check if body processor can work with partial
JSON" while leaving the SecRule line unchanged.

---

Nitpick comments:
In `@testing/engine/json.go`:
- Around line 78-162: Add a companion stage to the "truncatedjsonyaml" profile
that uses the same StageInput as the existing "json" stage but expects rule id
1111 to be triggered (i.e., include 1111 in TriggeredRules) to assert the
REQBODY_ERROR flag is set; also enable the SecRule REQBODY_ERROR "!@eq 0"
"id:1111, phase:2, log, block" in the Rules block (it is currently commented
out) so the test can observe that error rule. Locate the profile by name
"truncatedjsonyaml", the existing Stage under Tests -> Title "json" to copy the
input, and the SecRule with id 1111 to uncomment/activate. Ensure the new
stage's Output still includes the previously expected positive rule ids (e.g.,
1100,1101,1104,1200,1201) plus 1111 as a triggered rule.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0c74c98-966d-43e9-b830-3fe903f66641

📥 Commits

Reviewing files that changed from the base of the PR and between 8b28b72 and 4915775.

📒 Files selected for processing (2)
  • internal/bodyprocessors/json.go
  • testing/engine/json.go

Comment thread internal/bodyprocessors/json.go Outdated
Comment thread testing/engine/json.go Outdated
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.77778% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.66%. Comparing base (43bd073) to head (d454321).

Files with missing lines Patch % Lines
internal/bodyprocessors/json.go 77.77% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1615   +/-   ##
=======================================
  Coverage   87.66%   87.66%           
=======================================
  Files         178      178           
  Lines        9134     9136    +2     
=======================================
+ Hits         8007     8009    +2     
  Misses        858      858           
  Partials      269      269           
Flag Coverage Δ
coraza.no_memoize 87.75% <77.77%> (+<0.01%) ⬆️
coraza.rule.case_sensitive_args_keys 87.63% <77.77%> (+<0.01%) ⬆️
coraza.rule.mandatory_rule_id_check 87.65% <77.77%> (+<0.01%) ⬆️
coraza.rule.multiphase_evaluation 87.42% <77.77%> (+<0.01%) ⬆️
coraza.rule.no_regex_multiline 87.62% <77.77%> (+<0.01%) ⬆️
coraza.rule.rx_prefilter 87.66% <77.77%> (+<0.01%) ⬆️
default 87.66% <77.77%> (+<0.01%) ⬆️
examples+ 16.28% <0.00%> (-0.01%) ⬇️
examples+coraza.no_memoize 85.61% <77.77%> (+<0.01%) ⬆️
examples+coraza.rule.case_sensitive_args_keys 85.59% <77.77%> (+<0.01%) ⬆️
examples+coraza.rule.mandatory_rule_id_check 85.70% <77.77%> (+<0.01%) ⬆️
examples+coraza.rule.multiphase_evaluation 87.42% <77.77%> (+<0.01%) ⬆️
examples+coraza.rule.no_regex_multiline 85.53% <77.77%> (+<0.01%) ⬆️
examples+coraza.rule.rx_prefilter 85.88% <77.77%> (+<0.01%) ⬆️
examples+no_fs_access 84.94% <77.77%> (+<0.01%) ⬆️
ftw 87.66% <77.77%> (+<0.01%) ⬆️
no_fs_access 87.00% <77.77%> (+<0.01%) ⬆️
tinygo 87.65% <77.77%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@fzipi

fzipi commented May 17, 2026

Copy link
Copy Markdown
Member

Hi @HusseinKabbout, is there any related issue for this one? Would like to understand which problem are you solving.

Also, I don't think making this the default would be my choice, as people might rely on the previous semantics.

@HusseinKabbout

Copy link
Copy Markdown
Author

Hi @HusseinKabbout, is there any related issue for this one? Would like to understand which problem are you solving.

The issue that I am trying to solve is described in the "What does this PR do?" section. There I also link to a existing GitHub issue. That should be the full context.

Also, I don't think making this the default would be my choice, as people might rely on the previous semantics.

The thing is that this was already the default before 3.4.0. The JSON syntax check was not in 3.3.3.

@HusseinKabbout

Copy link
Copy Markdown
Author

Any update on this PR? Is the provided context enough to understand what this PR is trying to solve?

@fzipi

fzipi commented Jun 2, 2026

Copy link
Copy Markdown
Member

I think you have. I'm afraid of changing the default dehaviour now because users might expect to only have valid json, and now it will be partial. That's why adding it behind a configuration flag make sense.

@fzipi

fzipi commented Jun 2, 2026

Copy link
Copy Markdown
Member

Ok, maybe this way it works: let's piggyback on SecRequestBodyLimitAction ProcessPartial, which will process partial JSON and populate ARGS_POST (your PR) + sets REQBODY_ERROR.

And make it clear on documentation that this is how it works for JSON.

@fzipi

fzipi commented Jun 2, 2026

Copy link
Copy Markdown
Member

Another option is to create a new directive SecRequestBodyJsonValidation, with these values:

SecRequestBodyJsonValidation On            # default — strict, only valid JSON populates ARGS_POST
SecRequestBodyJsonValidation Off           # no validity check at all (pre-3.3.3 behavior)
SecRequestBodyJsonValidation ProcessPartial # populate ARGS_POST even on error, still set REQBODY_ERROR

@HusseinKabbout

Copy link
Copy Markdown
Author

Another option is to create a new directive SecRequestBodyJsonValidation, with these values:

SecRequestBodyJsonValidation On            # default — strict, only valid JSON populates ARGS_POST
SecRequestBodyJsonValidation Off           # no validity check at all (pre-3.3.3 behavior)
SecRequestBodyJsonValidation ProcessPartial # populate ARGS_POST even on error, still set REQBODY_ERROR

This would make the configuration more explicit but is not needed, AFAIK. The behavior should be configurable with just my PR, it makes the population of ARGS_POST unconditional and REQBODY_ERROR will also always be set on invalid JSON. In the combination with SecRequestBodyLimitAction, users can decide what to do from here.

Also, since JSON is not the only body processor, maybe we should also check if this is also desirable for the other body processors.

@HusseinKabbout

Copy link
Copy Markdown
Author

Any update on this PR?

@M4tteoP M4tteoP left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments, but LGTM.
Just to recap: in the context of ProcessPartial, we used to ignore any validity checks and simply perform a best-effort parsing of the truncated body. This was a potential bypass issue.
Starting with #1110, we began enforcing validation and completely skipped any further best-effort inspection when validation failed. As a result, all JSON payloads larger than the configured limit would be blocked becuase REQBODY_ERROR is set, and disabling that rule would lead to 0 checks on the payload.

This PR brings back the best-effort inspection without removing validation. REQBODY_ERROR will still be set to 1 (we should add a test for this), but users will be able to write a rule that allows the request when running in ProcessPartial mode, relying on the best-effort parsing behavior.

It would be great to document this exclusion rule, perhaps even directly in the recommended configuration file alongside the REQBODY_ERROR comments.

Comment thread internal/bodyprocessors/json.go
Comment thread internal/bodyprocessors/json.go
Comment thread internal/bodyprocessors/json.go Outdated
Comment thread testing/engine/json.go Outdated
Comment thread testing/engine/json.go Outdated
@M4tteoP M4tteoP added the v3.8 label Jul 9, 2026
@HusseinKabbout

Copy link
Copy Markdown
Author

@M4tteoP Thanks for the review! I addressed your comments

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@testing/engine/json.go`:
- Around line 140-141: The stale inline note above SecRule REQBODY_ERROR is
misleading because the rule is now active, not commented out. Update the comment
in testing/engine/json.go to accurately describe the current intent of this test
block, keeping the explanation aligned with the active SecRule and the partial
JSON/body processor behavior it is meant to validate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60cfffbe-9861-4172-8c14-d94a145e03f5

📥 Commits

Reviewing files that changed from the base of the PR and between 5533954 and b59a645.

📒 Files selected for processing (2)
  • internal/bodyprocessors/json.go
  • testing/engine/json.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/bodyprocessors/json.go

Comment thread testing/engine/json.go Outdated

@M4tteoP M4tteoP left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! LGTM. I'll leave it approved for a bit, in case anyone else wants to chime in, and then I'll merge it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants