perf(unbounded-consumption): short-circuit once length proves a block - #7
Merged
Merged
Conversation
WHY: scanUnboundedConsumption runs on RAW (untruncated) input by design (the orchestrator bypasses maxInputLength truncation here so the size signal is not hidden). When input.length already exceeds maxLength the verdict is a block (score 90) — yet the detector still walked every character (longestCharRun) and built a Map over every whitespace- delimited token (maxTokenCount) and ran the UNBOUNDED_REQUEST regex over the full body. On a multi-MB body that is hundreds of ms of synchronous, event-loop-blocking CPU for no extra detection benefit. Measured: a ~7MB input of 1M distinct tokens ~190ms; a ~10MB single-char input ~113ms. WHAT: After the cheap length check trips, return immediately with score 90 and the length signal, skipping the O(n) char/token/regex scans. The verdict and score are unchanged (still blocked), so detection semantics are identical; only the worst-case per-request work is now bounded. Inputs within maxLength still get the full char-run / token-repeat / unbounded-request analysis. Adds regression tests pinning the short-circuit (still blocks, score 90, fast on a multi-MB body) and confirming within-limit detection paths still fire. Co-authored-by: mattia-mamini-gh <281593356+mattia-mamini-gh@users.noreply.github.com>
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.
Why
scanUnboundedConsumptionruns on RAW (untruncated) input by design — the orchestrator passes raw input here (src/aegis-guard.ts:217) so the oversized-payload signal isn't hidden bymaxInputLengthtruncation. But wheninput.length > maxLength(already a block, score 90), the detector still did full O(n) work over the entire body:longestCharRun(input)— walks every charactermaxTokenCount(input)— builds aMapover every whitespace-delimited token (and thesplitarray)UNBOUNDED_REQUESTregex over the full raw inputOn a multi-MB body that's hundreds of ms of synchronous, event-loop-blocking CPU for zero extra detection benefit — the verdict is already "block". Measured on
main:What
After the cheap
input.length > maxLengthcheck trips, return immediately withscore: 90and the length signal, skipping the char/token/regex scans. The block verdict and score are unchanged, so detection semantics are identical; only the worst-case per-request work is now bounded. Inputs withinmaxLength(≤ maxLength chars) still get the full char-run / token-repeat / unbounded-request analysis.One detail-only change: an oversized input that also had e.g. a char-run previously listed both signals in
details; it now lists justlength=.... Thesafe/score/threatTypeare identical and no test asserted on the combined string.Verification
npm run typecheck— cleannpm run lint— cleannpm test— 314 passed / 1 skipped (added 5 regression tests intests/unbounded-consumption.test.ts, incl. a<50msbound on a 1M-distinct-token body){safe:false, score:90}{safe:false, score:90}🤖 Generated with Claude Code