From 8b0358af3bb7326b0e1ef6faa1abda9f0032ea35 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Fri, 1 May 2026 16:23:05 -0400 Subject: [PATCH] fix(parameterization): word-boundary check prevents over-eager value match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ParameterizationPreview.tokenize() matched highlight values via raw seg.text.startsWith(value, cursor) with no word-boundary check and no minimum length. A param value like "D" (e.g. a drive letter) lit up every capital D in the script body — Get-ADUser, Add-Type, Disable- all rendered as proposed-parameter pills. Add a word-boundary guard: a candidate match is only accepted if either side of the match either falls at start/end of the segment, OR the adjacent character is non-alphanumeric. The guard is conditional on whether the value itself starts/ends with a word char, so values that begin or end in punctuation (e.g. "D:\\Folder") still match cleanly when they sit next to whitespace or punctuation. Surfaced 2026-05-01 while testing the suggested-fix flow with a real PowerShell script. Co-Authored-By: Claude Opus 4.7 --- .../pilot/script/ParameterizationPreview.tsx | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/pilot/script/ParameterizationPreview.tsx b/frontend/src/components/pilot/script/ParameterizationPreview.tsx index 65b7d0a2..12ede89e 100644 --- a/frontend/src/components/pilot/script/ParameterizationPreview.tsx +++ b/frontend/src/components/pilot/script/ParameterizationPreview.tsx @@ -80,10 +80,27 @@ function tokenize(body: string, highlightValues: Record | undefi while (cursor < seg.text.length) { let matched: { key: string; value: string } | null = null for (const [key, value] of valueEntries) { - if (seg.text.startsWith(value, cursor)) { - matched = { key, value } - break - } + if (!seg.text.startsWith(value, cursor)) continue + // Word-boundary guard: a single-char value like "D" (drive letter) + // would otherwise light up every capital D in identifiers like + // `Get-ADUser`. We only require a boundary on a side of the value + // that itself starts/ends with a word char, so values that begin or + // end in punctuation (e.g. "D:\\Folder") still match cleanly. + const valueStartsWithWordChar = /^\w/.test(value) + const valueEndsWithWordChar = /\w$/.test(value) + const before = cursor > 0 ? seg.text[cursor - 1] : undefined + const after = cursor + value.length < seg.text.length + ? seg.text[cursor + value.length] + : undefined + const startBounded = !valueStartsWithWordChar + || before === undefined + || !/\w/.test(before) + const endBounded = !valueEndsWithWordChar + || after === undefined + || !/\w/.test(after) + if (!startBounded || !endBounded) continue + matched = { key, value } + break } if (matched) { flushPending()