Skip to content

[ENG-4059] Screen the beginning of an oversized Node body - #306

Merged
patchstackdave merged 2 commits into
mainfrom
fix/node-body-and-inclusive
Sep 29, 2026
Merged

patchstackdave merged 2 commits into
mainfrom
fix/node-body-and-inclusive

Conversation

@patchstackdave

@patchstackdave patchstackdave commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Two small changes to how request bodies and rule conditions are handled.

Release gate: the inclusive check requires the matching server-side validation before release. A live rule update is accepted or rejected as a whole, so if the rules service still served a rule with a non-boolean inclusive, this version would reject that entire update and keep its previous rules until the rule was fixed.

Oversized Node bodies. protection.node() and the engine's Node middleware now behave like the Fetch path when a body is longer than maxBodyBytes:

  • the first maxBodyBytes are screened, where before the body was not screened at all;
  • the cap is reported as a body-cap skip, through coverage() on node() and through a new onSkip option on the engine middleware;
  • the cut-off body is no longer exposed as req.body. Before, the handler received a body that did not match the request.

The limit is in bytes, including when something upstream switched the stream to text with setEncoding(). A prefix the cap cuts partway through a UTF-8 character ends before that character. A chunk the reader cannot use (for example from an object-mode stream) is reported as a read-failed skip; the request continues unscreened and the body is not exposed. A body within the cap is handled as before.

inclusive validation. The rule validator now rejects a condition whose inclusive is anything other than a boolean. That includes the string "false", which the engine would treat as true. The published rule contract artifacts are unchanged.

Synthetic tests cover:

  • a marker at the start of an oversized body;
  • a marker ending exactly at the cap, and one byte past it, with the cap inside a chunk;
  • two-, three- and four-byte characters at every cap position inside them;
  • setEncoding('utf8' | 'latin1' | 'hex'), both within and past the cap, on both guards;
  • a complete body left as sent, and fail-open on an unusable chunk;
  • what req.body looks like after a cut-off body;
  • skip reporting on both guards, with a within-cap control;
  • every non-boolean spelling of inclusive, including inside a group.

Validation: full suite (3,467 passed, 7 skipped), typecheck, build, rule-contract:check and capabilities:check.

🤖 Generated with Claude Code

The Node guards (`protection.node()` and the engine's Node middleware)
now screen the first `maxBodyBytes` of a longer body, as the Fetch path
does, and report the cap as a `body-cap` skip. The cut-off body is no
longer exposed as `req.body`.

The rule validator also requires a condition's `inclusive` to be a
boolean, since any other value is read for its truthiness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderbuds

coderbuds Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Efficient streaming body prefix reader with thorough UTF-8 handling.

🎯 Quality: 100% Elite · 📦 Size: Medium

🤖 Authorship: Agent-written — Claude Code, going by its own attribution. Whether a person read it is unknown; coding agents can call the report-ai-usage tool to say.

📈 This month: Your 161st PR — above team average · Averaging Excellent

See how your team is trending →

When something upstream has called `setEncoding()`, the Node body
reader receives strings. They are now measured and retained as the
bytes they encode, so the cap stays a byte cap, and a prefix the cap
cuts inside a UTF-8 character ends before that character.

A chunk the reader cannot use is read past and reported as a
`read-failed` skip; the request continues unscreened and the body is
not exposed as `req.body`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@patchstackdave

Copy link
Copy Markdown
Contributor Author

/review

@patchstackdave
patchstackdave merged commit 9750a0f into main Sep 29, 2026
18 checks passed
@patchstackdave
patchstackdave deleted the fix/node-body-and-inclusive branch September 29, 2026 09:19
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.

2 participants