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
This commit is contained in:
Roo Code 2025-07-20 04:29:48 +00:00
parent de13d8a5cf
commit ff0ad63db2
2 changed files with 51 additions and 2 deletions

View file

@ -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)", () => {

View file

@ -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}__`
})