Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions webview-ui/src/utils/__tests__/command-validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,41 @@ done`
parseCommand(problematicPart)
}).not.toThrow("Bad substitution")
})

it("should not throw an error when parsing commands with BASH_REMATCH array syntax", () => {
// This test reproduces the bug reported in issue #5978
const commandWithBashRematch = 'repo="${BASH_REMATCH[1]}"'

expect(() => {
parseCommand(commandWithBashRematch)
}).not.toThrow("Bad substitution")
})

it("should not throw an error when parsing the full gh alias command from issue #5978", () => {
// This is the exact command from the issue that causes the crash
// Using a regular string to avoid template literal evaluation issues
const ghAliasCommand =
"gh alias set pr-reviews --clobber '!f() { \n" +
' url="$1"\n' +
' if [[ "$url" =~ github\\.com/([^/]+/[^/]+)/pull/([0-9]+) ]]; then\n' +
' repo="${BASH_REMATCH[1]}"\n' +
' pr="${BASH_REMATCH[2]}"\n' +
' echo "=== Review Comments for $repo PR #$pr ==="\n' +
' gh api "repos/$repo/pulls/$pr/reviews" --jq ".[] | {user: .user.login, state: .state, body: .body, submitted_at: .submitted_at}"\n' +
" echo\n" +
' echo "=== Line Comments ==="\n' +
' gh api "repos/$repo/pulls/$pr/comments" --jq ".[] | {user: .user.login, body: .body, path: .path, line: .line, created_at: .created_at}"\n' +
" else\n" +
' echo "Error: Please provide a valid GitHub PR URL"\n' +
' echo "Example: gh pr-reviews https://github.com/owner/repo/pull/123"\n' +
' echo "Received: $url"\n' +
" fi\n" +
"}; f'"

expect(() => {
parseCommand(ghAliasCommand)
}).not.toThrow()
})
})

describe("isAutoApprovedCommand (legacy behavior)", () => {
Expand Down
18 changes: 16 additions & 2 deletions webview-ui/src/utils/command-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,22 @@ export function parseCommand(command: string): string[] {
return `__REDIR_${redirections.length - 1}__`
})

// Handle array indexing expressions: ${array[...]} pattern and partial expressions
processedCommand = processedCommand.replace(/\$\{[^}]*\[[^\]]*(\]([^}]*\})?)?/g, (match) => {
// Handle BASH_REMATCH specifically first to avoid shell-quote parsing issues
// Match ${BASH_REMATCH[n]} pattern
processedCommand = processedCommand.replace(/\$\{BASH_REMATCH\[[^\]]*\]\}/g, (match) => {
arrayIndexing.push(match)
return `__ARRAY_${arrayIndexing.length - 1}__`
})

// Also handle BASH_REMATCH without braces: $BASH_REMATCH[n]
processedCommand = processedCommand.replace(/\$BASH_REMATCH\[[^\]]*\]/g, (match) => {
arrayIndexing.push(match)
return `__ARRAY_${arrayIndexing.length - 1}__`
})

// Handle other array indexing expressions: ${array[...]} pattern and partial expressions
// This handles general array syntax but excludes BASH_REMATCH which was already handled
processedCommand = processedCommand.replace(/\$\{(?!BASH_REMATCH)[^}]*\[[^\]]*(\]([^}]*\})?)?/g, (match) => {
arrayIndexing.push(match)
return `__ARRAY_${arrayIndexing.length - 1}__`
})
Expand Down