Skip to content

fix(cli): split trailing punctuation off redacted URLs in linear time - #2541

Merged
cliffhall merged 1 commit into
v2/mainfrom
v2/fix/2540-linear-trailing-punctuation
Sep 30, 2026
Merged

cliffhall merged 1 commit into
v2/mainfrom
v2/fix/2540-linear-trailing-punctuation

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #2540

CodeQL flagged this on the v2.9.0 milestone-merge PR (#2536): "Polynomial regular expression used on uncontrolled data", high severity, at clients/cli/src/error-handler.ts:169.

Problem

redactUrlsInText (added for #2423 in PR #2488) split a URL's trailing sentence punctuation off with the unanchored /[.,;:!?)\]']+$/. When a punctuation run does not end the match, the engine rescans it from every start position, which is quadratic. The text is server-controlled: the CLI copies an HTTP error body into the error message.

Probe (server answers 400 with see http://a/?… + n × ! + x) Before After
n = 60,000 3 s (2.8.0: 0 s) 173 ms
n = 400,000 minutes (quadratic) 183 ms

The regex alone: 5k → 17 ms, 10k → 61 ms, 20k → 238 ms, 40k → 857 ms.

Fix

A backward scan over the same character set (trailingPunctuationStart) replaces the regex. Behavior is unchanged, and every existing redaction case still passes: the kept full stop, the single-quoted URL, the apostrophe inside a value, comma-joined URLs, and mixed-case schemes. Redaction still holds at both probe sizes: code=%5BREDACTED%5D, 0 secret hits.

Tests

clients/cli/__tests__/error-handler.test.ts adds a 50,000-character punctuation run inside a URL. Only the trailing !? is split off, and the secret parameter after the run is still redacted.

Verification

  • npm run format → clean
  • npm run local:gate → green (exit 0, 5m07s)

🤖 Generated with Claude Code

redactUrlsInText stripped a URL's trailing sentence punctuation with the
unanchored regex /[.,;:!?)\]']+$/, which rescans a punctuation run from
every start position when the run does not end the match: quadratic.
The text is server-controlled (an HTTP error body lands in the error
message), so a body of http://a/? plus a long run of '!' before an 'x'
stalled the CLI's error path: 60k characters took 3s, against 0s on
2.8.0. CodeQL flagged it high on the v2.9.0 merge PR (#2536).

Replace it with a backward scan over the same character set. Behavior is
unchanged; the same probe now answers in ~180ms at 60k and at 400k.

Closes #2540

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The linear scan preserves existing suffix behavior and the regression test covers the reported adversarial input.

Review effort: Balanced
Findings: None

What changed in this PR

Replaces a quadratic URL trailing-punctuation regex in the CLI error-redaction path with a behavior-equivalent linear scan.

Changes:

  • Adds a backward punctuation scan using a character set.
  • Adds regression coverage for a 50,000-character server-controlled punctuation run.
File Description
clients/​cli/​src/​error-handler.ts Implements linear trailing-punctuation detection.
clients/​cli/​__tests__/​error-handler.test.ts Tests long embedded punctuation while preserving secret redaction.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@cliffhall

Copy link
Copy Markdown
Member Author

Copilot review loop closed: round 1 was clean (approval recommended, no findings, no inline or suppressed comments), so no further round was requested.

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

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI error-envelope URL redaction uses a quadratic regex on server-controlled text (CodeQL, ReDoS)

2 participants