Skip to content

fix(frontend): replace polynomial-ReDoS regexes flagged by CodeQL - #51

Merged
rawkode merged 1 commit into
v3from
fix-redos-alerts
May 19, 2026
Merged

rawkode merged 1 commit into
v3from
fix-redos-alerts

Conversation

@rawkode

@rawkode rawkode commented May 19, 2026

Copy link
Copy Markdown
Member

Two CodeQL alerts at `high` security severity (`js/polynomial-redos`) were the actual blocker on PR #1 (v3 → main). Both rewritten to strictly linear scans.

Findings

Alert File Old pattern
#1 `sdk-core/route-registry.ts:54` `normalized.replace(//+$/, "")`
#2 `sdk-vue/parse-query.ts:54` `/([A-Za-z][\w-]):"([^"])"|([A-Za-z][\w-]):(\S)|(\S+)/g`

CodeQL's reasoning: each regex contains a quantifier reachable on user-controllable input where backtracking could push runtime into polynomial (or worse) time on adversarial inputs.

Fixes

route-registry.ts — hand loop that walks the trailing characters once, stopping at the first non-`/`. Linear, never collapses the single-slash root.

parse-query.ts — whitespace-bounded tokenizer + per-token `indexOf` split for `key:value`. Quoted runs (`is:"with spaces"`) handled by an inline state switch; key validation rewritten as a char-code loop. The `ParsedQuery` shape and `parseQueryFilters` signature are unchanged, so PullsQueue / IssuesList / EpicsList callers need no edits.

Verification

  • `bun test` — 73/73 green (existing parse-query.test.ts covers the kept behaviour).
  • `bun run typecheck` — clean.
  • `bun run build` — clean.

After this lands, PR #1 (v3 → main) should clear the `code_scanning` merge-queue rule (`security_alerts_threshold: high_or_higher` — these were the only two high-sev alerts on the merge ref).

Two CodeQL alerts at high security severity (js/polynomial-redos)
were blocking the v3 → main merge queue. Both rewritten to strictly
linear scans:

sdk-core/route-registry.ts:54
  Before: `normalized.replace(/\/+$/, "")` — `\/+` could backtrack on
  long unbroken slash inputs.
  After: hand loop that walks the string once, stopping at the first
  non-`/` from the end (and never collapsing the single-slash root).

sdk-vue/parse-query.ts:54
  Before: single alternation regex with `(\S+)` alongside narrower
  `key:"..."` and `key:\S*` cases — adversarial input could push the
  engine into superlinear time.
  After: hand-rolled whitespace-bounded tokenizer + per-token key:value
  split. Strictly linear, no nested regex. Quoted-value handling
  preserved (`is:"with spaces"` still keeps internal whitespace).
  Key validation (`[A-Za-z][\w-]*`) replaced with a per-character
  loop using char-code ranges so the engine never enters the ReDoS
  code path.

73/73 frontend tests pass; vue-tsc clean; build clean. The
ParsedQuery shape and `parseQueryFilters` signature are unchanged,
so callers (PullsQueue, IssuesList, EpicsList) need no edits.
Copilot AI review requested due to automatic review settings May 19, 2026 18:16

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.

Pull request overview

This PR addresses two high-severity CodeQL js/polynomial-redos alerts in the frontend SDK by replacing regex-based parsing/trimming with linear-time scans.

Changes:

  • Replaced the parseQueryFilters tokenization regex with a linear whitespace tokenizer + key:value split/validation.
  • Replaced trailing-slash trimming in buildExtensionUrl with a linear trailing-scan loop.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
frontend/packages/sdk-vue/src/parse-query.ts Swaps regex tokenization for a linear tokenizer and key validation logic.
frontend/packages/sdk-core/src/route-registry.ts Swaps trailing-slash regex trimming for a linear trailing-scan loop.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +58 to +63
// single-slash root").
let end = normalized.length;
while (end > 1 && normalized.charCodeAt(end - 1) === 0x2f /* '/' */) {
end -= 1;
}
const trimmed = normalized === "/" ? "" : normalized.slice(0, end);
Comment on lines +132 to +135
code === 0x20 || // space
code === 0x09 || // tab
code === 0x0a || // \n
code === 0x0d // \r
@rawkode
rawkode merged commit 9c189b0 into v3 May 19, 2026
17 checks passed
@rawkode
rawkode deleted the fix-redos-alerts branch May 19, 2026 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants