protect: input-handling robustness pass (canonicalization + edge cases) - #114
Merged
Conversation
A batch of defensive hardening so matching stays correct on inputs that don't take the obvious shape. No public API changes. - Host/IP classification (internal_host) now canonicalizes before deciding: every IPv4 spelling inet_aton accepts, and expanded / IPv4-mapped IPv6 forms, are recognized (previously a string/prefix compare); adds 100.64.0.0/10. - Origin/redirect comparisons resolve against the request origin and normalize default ports (off_origin handles protocol-relative / backslash locations; cross_origin distinguishes an absent header from a present opaque one; cors_reflected covers ACAO: null + credentials). - Scalar matchers fan out over the leaves of a structured (nested / array-of- object) value instead of stringifying it; bounded + iterative so a pathological value can't fail a rule open. - Regex safety: the ReDoS detector catches nested quantified subgroups, and a rejected pattern now warns (the rule is unenforced) instead of failing silent. - Request body handling: permissive content-type parsing (+json / text/plain / no content-type still populate post.<field>); body inspection is no longer skipped on a declared Content-Length; `all` folds in the verbatim body. - Request normalization no longer deletes line-comment spans from the value it inspects (that hid payloads from parameter-scoped rules). - Response screening: exact content-type matching for live streams, a binary sniff so a textual octet-stream export is screened; a redactor whose rule decodes the body before matching now fails closed rather than serving a no-op mask; whitelist misconfig (no rule_id / unimplemented keys) warns. Adds tests/protect/security-hardening.test.ts plus updates to the normalizer / response-guards suites. 703 tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Robust input-handling improvements with extensive canonicalization and detailed edge-case tests. 🎯 Quality: 96% Elite · 📦 Size: Extra Large — strongly consider breaking this down 🛡️ Standards: no pre-flight fit check ran for this change — wire 📈 This month: Your 50th PR — above team average · Averaging Excellent |
…pth test The request normalizer recursed into nested objects unbounded, so a pathologically deep value could overflow the stack before matching ran — the per-rule catch would swallow that into a fail-open. Cap the walk (values below the bound are left un-normalized, still matched, never crashing). The regression test built its deep value via a JSON string, which overflowed JSON.parse/stringify on Node 18/20/22 (but not 25) — a test artifact, not the engine. Rebuild it in memory and assert it still matches past the normalize cap (a fail-open crash would return blocked:false). Verified on Node 20 and 22. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CI matrix tested 18/20/22, but publish.yml and release.yml build on Node 24 — so releases ran on a version CI never exercised. Add 24.x to close that gap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Author
|
/review |
daniloradovic
approved these changes
Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A defensive hardening pass so rule matching stays correct on inputs that don't take the obvious shape — alternate encodings, unusual structure, and content-type/edge-case handling — across the engine and the request/response runtime. No public API changes; existing behavior is preserved on ordinary inputs.
Highlights:
internal_host) canonicalizes before deciding — every IPv4 spellinginet_atonaccepts, plus expanded / IPv4-mapped IPv6 forms, are recognized (was a string/prefix compare). Adds100.64.0.0/10. False-positive guards for ordinary hostnames retained.off_originhandles protocol-relative and backslashLocations;cross_origindistinguishes a truly-absent header from a present opaque one and fixes a:443-vs-elided-port false positive;cors_reflectedcoversACAO: null+ credentials.[object Object]; the walk is iterative and bounded, so a pathological value can't throw aRangeErrorthat fails a rule open.((ab)+)+), and a rejected pattern now warns that its rule is unenforced instead of failing silently.*+json,text/plain, and no-content-type JSON bodies now populatepost.<field>); body inspection is no longer skipped based on a declaredContent-Length;allfolds in the verbatim body; and request normalization no longer deletes line-comment spans from the value it inspects.event-streamno longer disables screening), a binary sniff so a textualoctet-streamexport is screened while real binaries pass through untouched, and a redactor whose rule decodes the body before matching now fails closed rather than serving a no-op mask.rule_id-less entry, unimplementedwhitelist_keys) now warns.Tests
Adds
tests/protect/security-hardening.test.ts(canonicalization tables, structured-value matching, origin checks, content-type handling, regex guard, redactor fail-closed) and updates the normalizer / response-guards suites for the corrected behavior. 703 tests green, typecheck clean, build clean,examples/protectdemo passes.Notes
A few residual behaviors are intentional trade-offs (documented in-code): the response size cap still fails open above the screening ceiling (rule authors opt into
bypass_limit/max_bytes), a pre-gzipped body isn't decompressed for screening, and theencodeaction is HTML-text-context only (useblockfor URI-scheme contexts).