Skip to content

Do not keep the @validateSchema parser errors in the operator - #3636

Open
tomsommer wants to merge 1 commit into
owasp-modsecurity:v3/masterfrom
tomsommer:fix/validateschema-error-string-growth
Open

tomsommer wants to merge 1 commit into
owasp-modsecurity:v3/masterfrom
tomsommer:fix/validateschema-error-string-growth

Conversation

@tomsommer

@tomsommer tomsommer commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

what

  • ValidateSchema::evaluate() passes a local std::string as the libxml2 schema-parser error/warning context instead of the operator member m_err; the member is removed.
  • Regression case added to request-body-parser-xml.json: the existing bad-schema case with multiMatch, so the same operator instance is evaluated twice in one transaction; it asserts the error text is not repeated. It fails before the change and passes after it.

why

  • error_load() / warn_load() append into their context and nothing ever cleared m_err. An operator instance lives as long as the rule set, i.e. the process lifetime in nginx, and the schema is re-parsed on every evaluation. So every evaluation whose schema parse emits a warning or error grew the string without bound, the logged message for one transaction contained the messages of all previous ones, and concurrent transactions appended to the same string, which is a data race.
  • The accumulated text is per-evaluation state, so it belongs in a local. The check of it in the branch where the parser context cannot be created is dropped because no callback has run at that point.
  • The member sat inside the WITH_LIBXML2 guard and had no other reader; --without-libxml compilation of the translation unit was verified.

Evidence (unfixed tree, new test): the second evaluation logs

XML: Failed to load Schema: test-cases/data/SoapEnvelope-bad.xsd. XML Error: Failed to parse the XML resource '...'.
XML Error: Failed to parse the XML resource '...'.

with the message repeated once per previous evaluation. After the fix each evaluation logs it once. This is growth of a live string, so valgrind reports no lost blocks; the test is the evidence. make check on this branch: TOTAL 5044, PASS 5028, SKIP 16, FAIL 0.

references

Summary by CodeRabbit

  • Bug Fixes

    • Improved XML schema validation error reporting in debug logs.
    • Prevented repeated schema evaluations from producing duplicate XML error messages.
    • Preserved the expected request rejection behavior when an invalid SOAP schema cannot be loaded.
  • Tests

    • Added regression coverage for repeated XML schema validation and verified the resulting status and diagnostic output.

ValidateSchema::evaluate() installs ValidateSchema::error_load() and
ValidateSchema::warn_load() as the libxml2 schema parser callbacks and
passes the address of the ValidateSchema member m_err as their user
context. Those callbacks append to that string and nothing ever clears
it.

An operator instance is created while the rules are parsed and lives for
as long as the rule set does, which for a web server means the lifetime
of the process. Therefore:

 * every evaluation whose schema parse emits a warning or an error makes
   m_err grow by the size of the new message, without any bound, and
 * the message logged for one transaction contains the messages of all
   the previous transactions that used the same rule, and
 * concurrent transactions sharing the rule set append to the same
   std::string at the same time, which is a data race.

The accumulated text is per evaluation state, so it is moved to a local
std::string in evaluate() and the member is removed from the header. The
member was declared inside the WITH_LIBXML2 guard and is only used by
the guarded code, so builds configured with --without-libxml are
unaffected.

The check of the accumulated text in the branch where the parser
context itself cannot be created is dropped: at that point no callback
has run yet, so the local is always empty.

The unbounded part of the growth is cross request and cannot be observed
by the single transaction regression harness, but the accumulation is
visible within one transaction as soon as the same operator instance is
evaluated twice. The regression test added to
request-body-parser-xml.json therefore reuses the existing
SoapEnvelope-bad.xsd case, which fails to parse, and adds multiMatch so
that the operator runs once per transformation. Before this change the
debug log of the second evaluation reads

  XML: Failed to load Schema: test-cases/data/SoapEnvelope-bad.xsd. XML Error: Failed to parse the XML resource '...'.
  XML Error: Failed to parse the XML resource '...'.

with the message repeated once per previous evaluation; after it every
evaluation logs the message exactly once. The expected debug_log of the
new test asserts that no log line starts with a repeated "XML Error:",
so it fails before the change and passes after it.
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: f557aa37-4084-493f-84ff-860129973d64

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea9fef and 9e48194.

📒 Files selected for processing (3)
  • src/operators/validate_schema.cc
  • src/operators/validate_schema.h
  • test/test-cases/regression/request-body-parser-xml.json
💤 Files with no reviewable changes (1)
  • src/operators/validate_schema.h

Included review availability: Your plan provides up to 8 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The schema validation operator now stores parser errors in a local string instead of a persistent member. Failure logs use the local string. A regression test checks repeated bad-schema evaluations.

Changes

Schema Error Isolation

Layer / File(s) Summary
Local schema error handling
src/operators/validate_schema.cc, src/operators/validate_schema.h
ValidateSchema::evaluate collects parser errors in a local string and appends them to failure logs. The persistent m_err member was removed.
Repeated evaluation regression coverage
test/test-cases/regression/request-body-parser-xml.json
The XML request-body test checks error output across repeated bad-schema evaluations and uses the updated validation rule options.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

🚥 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%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (1 skipped: 1 … 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 clearly describes the main change: parser errors are no longer retained in the @validateSchema operator member.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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.

@sonarqubecloud

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant