Skip to content

Commit ff0ad63

Browse files
committed
fix: handle BASH_REMATCH syntax in command validation to prevent crashes
- Add specific handling for BASH_REMATCH array syntax in parseCommand - Handle both ${BASH_REMATCH[n]} and $BASH_REMATCH[n] patterns - Add comprehensive test cases for BASH_REMATCH parsing - Fixes issue #5978 where gh alias commands with BASH_REMATCH caused crashes
1 parent de13d8a commit ff0ad63

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

webview-ui/src/utils/__tests__/command-validation.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,41 @@ done`
276276
parseCommand(problematicPart)
277277
}).not.toThrow("Bad substitution")
278278
})
279+
280+
it("should not throw an error when parsing commands with BASH_REMATCH array syntax", () => {
281+
// This test reproduces the bug reported in issue #5978
282+
const commandWithBashRematch = 'repo="${BASH_REMATCH[1]}"'
283+
284+
expect(() => {
285+
parseCommand(commandWithBashRematch)
286+
}).not.toThrow("Bad substitution")
287+
})
288+
289+
it("should not throw an error when parsing the full gh alias command from issue #5978", () => {
290+
// This is the exact command from the issue that causes the crash
291+
// Using a regular string to avoid template literal evaluation issues
292+
const ghAliasCommand =
293+
"gh alias set pr-reviews --clobber '!f() { \n" +
294+
' url="$1"\n' +
295+
' if [[ "$url" =~ github\\.com/([^/]+/[^/]+)/pull/([0-9]+) ]]; then\n' +
296+
' repo="${BASH_REMATCH[1]}"\n' +
297+
' pr="${BASH_REMATCH[2]}"\n' +
298+
' echo "=== Review Comments for $repo PR #$pr ==="\n' +
299+
' gh api "repos/$repo/pulls/$pr/reviews" --jq ".[] | {user: .user.login, state: .state, body: .body, submitted_at: .submitted_at}"\n' +
300+
" echo\n" +
301+
' echo "=== Line Comments ==="\n' +
302+
' gh api "repos/$repo/pulls/$pr/comments" --jq ".[] | {user: .user.login, body: .body, path: .path, line: .line, created_at: .created_at}"\n' +
303+
" else\n" +
304+
' echo "Error: Please provide a valid GitHub PR URL"\n' +
305+
' echo "Example: gh pr-reviews https://github.com/owner/repo/pull/123"\n' +
306+
' echo "Received: $url"\n' +
307+
" fi\n" +
308+
"}; f'"
309+
310+
expect(() => {
311+
parseCommand(ghAliasCommand)
312+
}).not.toThrow()
313+
})
279314
})
280315

281316
describe("isAutoApprovedCommand (legacy behavior)", () => {

webview-ui/src/utils/command-validation.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,22 @@ export function parseCommand(command: string): string[] {
8383
return `__REDIR_${redirections.length - 1}__`
8484
})
8585

86-
// Handle array indexing expressions: ${array[...]} pattern and partial expressions
87-
processedCommand = processedCommand.replace(/\$\{[^}]*\[[^\]]*(\]([^}]*\})?)?/g, (match) => {
86+
// Handle BASH_REMATCH specifically first to avoid shell-quote parsing issues
87+
// Match ${BASH_REMATCH[n]} pattern
88+
processedCommand = processedCommand.replace(/\$\{BASH_REMATCH\[[^\]]*\]\}/g, (match) => {
89+
arrayIndexing.push(match)
90+
return `__ARRAY_${arrayIndexing.length - 1}__`
91+
})
92+
93+
// Also handle BASH_REMATCH without braces: $BASH_REMATCH[n]
94+
processedCommand = processedCommand.replace(/\$BASH_REMATCH\[[^\]]*\]/g, (match) => {
95+
arrayIndexing.push(match)
96+
return `__ARRAY_${arrayIndexing.length - 1}__`
97+
})
98+
99+
// Handle other array indexing expressions: ${array[...]} pattern and partial expressions
100+
// This handles general array syntax but excludes BASH_REMATCH which was already handled
101+
processedCommand = processedCommand.replace(/\$\{(?!BASH_REMATCH)[^}]*\[[^\]]*(\]([^}]*\})?)?/g, (match) => {
88102
arrayIndexing.push(match)
89103
return `__ARRAY_${arrayIndexing.length - 1}__`
90104
})

0 commit comments

Comments
 (0)