fix: avoid zsh process-substitution false positives in assignments (#11365)

This commit is contained in:
Hannes Rudolph 2026-02-10 08:01:48 -07:00 committed by GitHub
parent 7df1a18ccb
commit d4cf3211e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 2 deletions

View file

@ -0,0 +1,39 @@
import { containsDangerousSubstitution, getCommandDecision } from "../commands"
describe("containsDangerousSubstitution", () => {
describe("zsh array assignments (should NOT be flagged)", () => {
it("should return false for files=(a b c)", () => {
expect(containsDangerousSubstitution("files=(a b c)")).toBe(false)
})
it("should return false for var=(item1 item2)", () => {
expect(containsDangerousSubstitution("var=(item1 item2)")).toBe(false)
})
it("should return false for x=(hello)", () => {
expect(containsDangerousSubstitution("x=(hello)")).toBe(false)
})
})
describe("zsh process substitution (should be flagged)", () => {
it("should return true for standalone =(whoami)", () => {
expect(containsDangerousSubstitution("=(whoami)")).toBe(true)
})
it("should return true for =(ls) with leading space", () => {
expect(containsDangerousSubstitution(" =(ls)")).toBe(true)
})
it("should return true for echo =(cat /etc/passwd)", () => {
expect(containsDangerousSubstitution("echo =(cat /etc/passwd)")).toBe(true)
})
})
})
describe("getCommandDecision", () => {
it("should auto_approve array assignment command with wildcard allowlist", () => {
const command = 'files=(a.ts b.ts); for f in "${files[@]}"; do echo "$f"; done'
const result = getCommandDecision(command, ["*"])
expect(result).toBe("auto_approve")
})
})

View file

@ -13,7 +13,7 @@ import { parseCommand } from "../../shared/parse-command"
* - ${var=value} with escape sequences - Can embed commands via \140 (backtick), \x60, or \u0060
* - ${!var} - Indirect variable references
* - <<<$(...) or <<<`...` - Here-strings with command substitution
* - =(...) - Zsh process substitution that executes commands
* - =(...) - Zsh process substitution that executes commands (array assignments like `var=(...)` are excluded)
* - *(e:...:) or similar - Zsh glob qualifiers with code execution
*
* @param source - The command string to analyze
@ -46,7 +46,7 @@ export function containsDangerousSubstitution(source: string): boolean {
// Check for zsh process substitution =(...) which executes commands
// =(...) creates a temporary file containing the output of the command, but executes it
const zshProcessSubstitution = /=\([^)]+\)/.test(source)
const zshProcessSubstitution = /(?<![a-zA-Z0-9_])=\([^)]+\)/.test(source)
// Check for zsh glob qualifiers with code execution (e:...:)
// Patterns like *(e:whoami:) or ?(e:rm -rf /:) execute commands during glob expansion