From 75d0f91749f9565ae196cc3fa0bea0fa8c5a7a33 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 4 Nov 2026 01:56:07 -0800 Subject: [PATCH] fix(cli): match a dash-led id in linear time looksLikeAnId's single regex backtracked quadratically on a long dash token that ends in a non-id character (CodeQL js/polynomial-redos on the v0.9.43 release). Two linear checks with the same meaning replace it. --- packages/sim-cli/src/runtime/build.test.ts | 18 ++++++++++++++++++ packages/sim-cli/src/runtime/build.ts | 4 +--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/sim-cli/src/runtime/build.test.ts b/packages/sim-cli/src/runtime/build.test.ts index 9143c8e26af..e4168538376 100644 --- a/packages/sim-cli/src/runtime/build.test.ts +++ b/packages/sim-cli/src/runtime/build.test.ts @@ -1059,6 +1059,24 @@ describe('parse-error guidance for a value typed where a flag belongs', () => { expect(stderr).toContain('Example: sim move --workspace ') }) + it('points an id that opens with a dash past the option parser', () => { + expect(stderrOf(move(), ['-Ab3xYz', '--workspace', 'ws_1'])).toContain( + 'Example: sim move -- -Ab3xYz' + ) + }) + + it('treats a lowercase dash token as a misspelt flag, not an id', () => { + const stderr = stderrOf(move(), ['-abc', '--workspace', 'ws_1']) + expect(stderr).not.toContain('Example: sim move --') + expect(stderr).toContain('Options for sim move: -w, --workspace ') + }) + + it('answers a long dash token that is neither flag nor id in linear time', () => { + const started = performance.now() + stderrOf(move(), [`-${'0'.repeat(100_000)}!`, '--workspace', 'ws_1']) + expect(performance.now() - started).toBeLessThan(1000) + }) + it('names no flag when two required flags are missing', () => { const create = new Command('create') .requiredOption('--name ') diff --git a/packages/sim-cli/src/runtime/build.ts b/packages/sim-cli/src/runtime/build.ts index 1d3e4e9ff04..d918b5d976b 100644 --- a/packages/sim-cli/src/runtime/build.ts +++ b/packages/sim-cli/src/runtime/build.ts @@ -85,9 +85,7 @@ function commandPath(command: Command): string { */ function looksLikeAnId(token: string): boolean { return ( - token.length > 2 && - !token.startsWith('--') && - /^-[A-Za-z0-9_-]*[A-Z0-9][A-Za-z0-9_-]*$/.test(token) + token.length > 2 && !token.startsWith('--') && /^-[\w-]+$/.test(token) && /[A-Z0-9]/.test(token) ) }