Skip to content

fix(shared): bound the file-link label so bracket runs stop rescanning - #5782

Merged
t3dotgg merged 1 commit into
pingdotgg:mainfrom
tsouth89:fix/bound-file-link-label
Aug 9, 2026
Merged

fix(shared): bound the file-link label so bracket runs stop rescanning#5782
t3dotgg merged 1 commit into
pingdotgg:mainfrom
tsouth89:fix/bound-file-link-label

Conversation

@tsouth89

@tsouth89 tsouth89 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

fix(shared): bound the file-link label so bracket runs stop rescanning

What changed

FILE_LINK_TOKEN_REGEX in packages/shared/src/composerInlineTokens.ts let the
label body repeat without a bound:

/(^|\s)\[((?:\\.|[^\]\\])*)\]\(([^)\s]+)\)(?=\s)/g

This caps it at 512 characters. Two files, +36/-1.

Why it should exist

With an unbounded body, every whitespace in the composer is a candidate start:
the engine scans the rest of the text for a closing ], fails, then rescans
from the next whitespace. On a run of unterminated brackets that is quadratic,
and collectComposerInlineTokens runs on raw composer text — so pasting one
freezes the tab.

Measured on " [[" repeated, before → after:

input before after
30k chars 167ms 8ms
60k chars 676ms 17ms
120k chars 2838ms 33ms

Doubling the input quadruples the old time, which is the quadratic signature.

Scope, stated honestly: ordinary pastes are unaffected, because a ] anywhere
ahead bounds each attempt. A 104k-char markdown table of [a] [b] [c] cells and
a 172k-char code paste full of arr[i][j][k] both measure 0ms before and after.
The trigger is a long run of [ with no closing bracket following. So this is a
self-inflicted UI freeze on degenerate or hostile paste content, not a server-side
availability issue — worth fixing, not worth alarm.

Why the cap is safe

The cap cannot reject a link anyone could meaningfully write. Only a basename
survives the label !== basename check immediately below the match, and the
longest filename any common filesystem allows is 255.

How it was verified

Behavioural equivalence against the old pattern over 156,504 generated
inputs
— exhaustive triples over an alphabet of [ ] ( ) \ @ ", tab, newline,
space and path-ish fragments; structured [label](path) shapes across prefix and
suffix contexts; and 150,000 randomized fuzz strings. Compared match count, match
offsets, and every capture group.

Zero differences.

The single intended difference is at the cap itself: a 512-character label still
matches, 513 no longer does. The two new boundary tests pin exactly that, and a
third asserts the bracket-run case completes in under a second.

No UI change, so no screenshots — this is pure tokenizer logic behind the
composer.


Note

Low Risk
Localized tokenizer regex change with extensive equivalence testing; only rejects implausibly long link labels and improves client-side paste performance.

Overview
Fixes composer freezes when pasted text has long runs of unclosed [ brackets by capping how much of a [label](path) label the file-link regex will scan.

FILE_LINK_TOKEN_REGEX in composerInlineTokens.ts no longer uses an unbounded label repeat; it limits the label body to 512 characters via MAX_FILE_LINK_LABEL_LENGTH, so each match attempt is constant-bounded instead of rescanning the rest of the string from every whitespace (quadratic on inputs like repeated " [[").

Normal file links are unchanged in practice because only basenames pass validation and real filenames are far below the cap. Links with labels at 512 chars still tokenize; longer labels are left as plain text.

Tests add cap boundary cases and a performance guard on a 40k-char unterminated bracket run.

Reviewed by Cursor Bugbot for commit 9b71e34. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Bound file-link label length to 512 chars to prevent catastrophic bracket rescanning

  • Introduces MAX_FILE_LINK_LABEL_LENGTH = 512 in composerInlineTokens.ts and rebuilds FILE_LINK_TOKEN_REGEX with a bounded quantifier to cap label body length.
  • Prevents catastrophic backtracking when the tokenizer encounters long runs of unterminated brackets.
  • Behavioral Change: file link tokens with labels exceeding 512 characters are no longer recognized.

Macroscope summarized 9b71e34.

FILE_LINK_TOKEN_REGEX let the label body repeat without a bound. Every
whitespace in the composer is therefore a candidate start: the engine
scans the rest of the text for a closing "]", fails, and rescans from
the next whitespace. On a run of unterminated brackets that is
quadratic, and collectComposerInlineTokens runs on raw composer text,
so pasting one freezes the tab.

Capping the body makes each attempt constant-bounded:

  120k chars of " [["   2838ms -> 33ms
   60k chars            676ms  -> 17ms
   30k chars            167ms  ->  8ms

The cap cannot reject a link anyone could meaningfully write. Only a
basename survives the `label !== basename` check below it, and the
longest filename common filesystems allow is 255.

Verified equivalent to the old pattern over 156,504 generated inputs
built from [ ] ( ) \ @ " whitespace and path-ish fragments, comparing
match count, offsets and every capture group: zero differences. The
single intended difference is at the cap itself, which the two new
boundary tests pin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fc3c406f-97a8-4897-9218-c453f56299f0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 9, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved 9b71e34

This is a straightforward performance bug fix that bounds a regex to prevent quadratic backtracking on pathological input. The 512-character limit is generous (max filename is 255) and includes comprehensive tests verifying the boundary behavior.

You can customize Macroscope's approvability policy. Learn more.

@t3dotgg
t3dotgg merged commit 062b461 into pingdotgg:main Aug 9, 2026
16 of 17 checks passed
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 9, 2026
## What's Changed
* fix(server): favicon resolution no longer pins the event loop by @murenovich in pingdotgg/t3code#5538
* fix(shared): bound the file-link label so bracket runs stop rescanning by @tsouth89 in pingdotgg/t3code#5782

## New Contributors
* @murenovich made their first contribution in pingdotgg/t3code#5538
* @tsouth89 made their first contribution in pingdotgg/t3code#5782

**Full Changelog**: pingdotgg/t3code@v0.0.33-nightly.20260809.1045...v0.0.33-nightly.20260809.1047

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.33-nightly.20260809.1047
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 10, 2026
## What's Changed
* fix(mobile): reconnects no longer shift the thread list by @t3dotgg in pingdotgg/t3code#5372
* feat(web): drag pinned threads into your own order by @t3dotgg in pingdotgg/t3code#5581
* chore(ci): vouch StiensWout by @t3-code[bot] in pingdotgg/t3code#5637
* feat(desktop): remember recently used sites in the Browser panel by @chrisdeeming in pingdotgg/t3code#5270
* chore: vouch chrisdeeming by @t3-code[bot] in pingdotgg/t3code#5641
* feat(web): make sidebar artwork theme-aware by @maria-rcks in pingdotgg/t3code#5636
* fix(web): reconnect the composer seam for remote non-Git projects by @caezium in pingdotgg/t3code#5633
* fix(web): show Stop button while input is pending by @ipanasenko in pingdotgg/t3code#5554
* feat(web): fold plan mode and token-by-token output into Legacy features by @t3dotgg in pingdotgg/t3code#5664
* feat: sidebar v2 is now the default sidebar by @t3dotgg in pingdotgg/t3code#5672
* fix(server): stop PR status lookups amplifying GitHub rate limits by @t3dotgg in pingdotgg/t3code#5673
* fix(web): delay transient reconnect warnings by @t3-code[bot] in pingdotgg/t3code#5670
* fix(web): inherit terminal size in simple typography by @chrisdeeming in pingdotgg/t3code#5628
* fix(server): stop the reaper from silently killing live background subagents by @t3dotgg in pingdotgg/t3code#5677
* fix(desktop): zoom shortcuts no longer die when the preview browser has focus by @t3dotgg in pingdotgg/t3code#5691
* feat(mobile): one sheet for model and thread settings by @t3dotgg in pingdotgg/t3code#5625
* feat(usage): usage page reading provider transcripts across environments by @t3dotgg in pingdotgg/t3code#5684
* fix(web): usage chart no longer makes Claude look like the bigger spender by @t3dotgg in pingdotgg/t3code#5697
* fix(web): persist diff view mode by @leorivastech in pingdotgg/t3code#5731
* feat(web): show how many subagents are running at a glance by @t3dotgg in pingdotgg/t3code#5745
* fix(web): add missing cursor-pointer styling to dropdowns and interactive buttons by @naMqe-h in pingdotgg/t3code#5716
* fix(server): stop Claude resume handshakes from completing turns that never ran by @gfsaaser24 in pingdotgg/t3code#5710
* chore: vouch gfsaaser24 by @t3dotgg in pingdotgg/t3code#5761
* chore: vouch saphid by @t3dotgg in pingdotgg/t3code#5763
* fix(server): stop Codex threads with queued follow-ups by @t3dotgg in pingdotgg/t3code#5762
* fix(web): usage page loses the cost quality panel, gains a back button by @t3dotgg in pingdotgg/t3code#5756
* feat(server): agents can now open the images you paste into chat by @t3dotgg in pingdotgg/t3code#5757
* fix(web): pinned reorder no longer reshuffles while writes land by @t3dotgg in pingdotgg/t3code#5767
* feat(web): overhaul project settings into a real settings page by @t3dotgg in pingdotgg/t3code#5768
* fix(web): usage totals no longer jump while devices report in by @t3dotgg in pingdotgg/t3code#5772
* fix(server): settle no longer leaves monitors and dev servers running by @t3dotgg in pingdotgg/t3code#5774
* feat: pick worktree or current checkout per project by @t3dotgg in pingdotgg/t3code#5766
* fix(web): sidebar rows show the branch again, not a truncated plan step by @t3dotgg in pingdotgg/t3code#5776
* feat(server): vp run migrate-dev-db seeds worktree dev dbs with real data by @t3dotgg in pingdotgg/t3code#5773
* feat(web): keep unsent drafts one click away in the sidebar by @t3dotgg in pingdotgg/t3code#5777
* feat(web): project icons can be chosen manually by @t3dotgg in pingdotgg/t3code#5775
* fix(server): one greedy agent process no longer takes down the whole server by @t3dotgg in pingdotgg/t3code#5788
* ci: label-gated hosted-web preview deploys by @t3dotgg in pingdotgg/t3code#5465
* Add cross-platform mobile usage dashboard by @juliusmarminge in pingdotgg/t3code#5743
* fix(web): preserve desktop route during Clerk auth by @wobsoriano in pingdotgg/t3code#5770
* fix(web): match create theme and import theme buttons to the standard outline style by @UtkarshUsername in pingdotgg/t3code#5860
* fix(server): favicon resolution no longer pins the event loop by @murenovich in pingdotgg/t3code#5538
* fix(shared): bound the file-link label so bracket runs stop rescanning by @tsouth89 in pingdotgg/t3code#5782
* fix(web): thread title button no longer eats the drag area by @nathangerday in pingdotgg/t3code#5857
* fix(web): unify usage page chrome by @t3-code[bot] in pingdotgg/t3code#5823
* fix(shell): add ~/.local/bin to the Windows CLI resolver so native-installed providers are found by @arhxam in pingdotgg/t3code#5074
* fix(web): match settings search shortcut styling to command palette's by @UtkarshUsername in pingdotgg/t3code#5841
* fix(mobile): long-pressing a thread row no longer navigates into the thread by @juliusmarminge in pingdotgg/t3code#5901
* fix(server): usage no longer double-counts forked Codex sessions by @t3dotgg in pingdotgg/t3code#5887
* fix(server): sandbox user-provided SVGs by @t3dotgg in pingdotgg/t3code#5916
* fix(web): match usage titlebar text styling by @t3-code[bot] in pingdotgg/t3code#5897
* Move project settings to contextual project routes by @juliusmarminge in pingdotgg/t3code#5923
* Retain thread sidebar data when navigating to /settings so back navigation is instant by @juliusmarminge in pingdotgg/t3code#5930
* Automate production mobile EAS releases by @juliusmarminge in pingdotgg/t3code#5609
* Add settings and usage breadcrumbs by @juliusmarminge in pingdotgg/t3code#5929
* fix(web): correct model picker trigger padding by @Chrono-byte in pingdotgg/t3code#5935
* fix(web): show worktree icon in sidebar v2 by @tris203 in pingdotgg/t3code#5909
* fix(web): enable restore defaults after theme mix changes by @Lucenx9 in pingdotgg/t3code#5928
* fix(web): trait menu closes after you pick a level by @t3dotgg in pingdotgg/t3code#5879
* fix(web): align project name with headline by @carterwsmith in pingdotgg/t3code#5864
* fix(web): update pills use readable theme foregrounds by @chrisdeeming in pingdotgg/t3code#5938
* fix(web): use themed confirmation dialogs by @StiensWout in pingdotgg/t3code#5624
* fix(web): use import/export-appropriate icons for theme buttons by @UtkarshUsername in pingdotgg/t3code#5964
* fix(mobile): detect PowerShell cmdlet errors in work log rows by @myacoub91 in pingdotgg/t3code#5726
* fix(mobile): stop Android user bubbles with code blocks from overlapping by @Brechard in pingdotgg/t3code#5659
* fix(mobile): parse EAS fingerprint JSON by @juliusmarminge in pingdotgg/t3code#5991

## New Contributors
* @chrisdeeming made their first contribution in pingdotgg/t3code#5270
* @wobsoriano made their first contribution in pingdotgg/t3code#5770
* @murenovich made their first contribution in pingdotgg/t3code#5538
* @tsouth89 made their first contribution in pingdotgg/t3code#5782
* @nathangerday made their first contribution in pingdotgg/t3code#5857
* @carterwsmith made their first contribution in pingdotgg/t3code#5864
* @myacoub91 made their first contribution in pingdotgg/t3code#5726

**Full Changelog**: pingdotgg/t3code@v0.0.32...v0.0.33

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants