Repository navigation
fix: avoid ReDoS when stripping fences from tool arguments (TypeScript, C#, Java, spec §9.8) - #585
Open
Arfath Ahmed Syed (ArfiArfath21) wants to merge 1 commit into
Conversation
…eScript, C# and Java runtimes The fence-stripping regex copied from spec section 9.8 backtracks cubically when tool-call arguments open a fence that never closes. The arguments string is model output, so a prompt injection can stall the agent loop. Replace it with plain string operations that return the same text, in TypeScript, C# and Java, and change the spec pseudocode so new implementations do not copy the regex. Python is covered by microsoft#582; Rust and Go use linear-time regex engines.
This branch has not been deployed
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.
Refs #581 (its table lists TypeScript, Java and C# as vulnerable; #582 fixes Python only, so this PR does not close it).
The fence-stripping regex from spec §9.8 is copied into the TypeScript, Java and C# runtimes:
\s*,\n?and the lazy group can all match the same whitespace, so an opening fence that never closes makes the engine try every split of that whitespace, which is cubic. The input is the model's tool-call arguments, so anything that can steer the model can stall the agent loop.Measured on the TypeScript regex from
main("```json" + " " * n + "x"): 16 ms at n=200, 262 ms at 800, 1,302 ms at 1,600; it takes minutes at 10,000. #581 has the same numbers for Python. I could not run the Java and C# builds here (see below), but their patterns are character-for-character the same.What changed:
runtime/typescript/.../core/tool-dispatch.ts,runtime/csharp/Prompty.Core/ToolDispatch.cs,runtime/java/.../ToolDispatch.java: a smallstripCodeFencehelper replaces the regex. It trims the whitespace, requires an opening and closing fence (at least 6 characters, so the two can't overlap), drops an optionaljsontag and trims again. Strategy order, warnings and fall-through to the block extractor are unchanged. The Java helper uses the ASCII whitespace set that Java's\smatches, so Unicode spaces behave as before.spec/spec.md§9.8 andweb/.../specification/agent-loop.mdx: Strategy 2 now callsstrip_code_fence, defined with string operations, with a note on why the regex form must not be used. Without this, anyone implementing the spec literally gets the same bug. Rust and Go use linear-time regex engines and are not affected.Verification:
vitestinpackages/core1,237 passed,tsc --noEmitandformat:checkclean. The slow-input test hangs on the old regex (I stopped it after 150 s) and passes in milliseconds now.ResilienceTests.csandRuntimeTest.java. If anything fails to compile I will fix it right away.