fix: exec brew directly instead of interpreting a shell string

runBrew built a command string and ran it through zsh -c, so the shell
interpreted the whole line — any metacharacter in an interpolated
package name (;, $(), backticks) would have been executed. Names come
from brew itself so exploitation was unlikely, but the injection class
is now gone: Process gets an argument array via /usr/bin/env, which
resolves brew on PATH (covering the bare "brew" fallback) and execs it
with no shell in between. Also slightly faster per invocation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
maxsoch 2026-07-06 21:34:28 +02:00
parent 99c243a51c
commit e41d7ac008

View file

@ -194,13 +194,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
DispatchQueue.global().async {
let brew = self.resolveBrewPath()
let cleaned = command.replacingOccurrences(of: "brew ", with: "")
let fullCommand = "\(brew) \(cleaned)"
let arguments = [brew] + cleaned.split(separator: " ").map(String.init)
self.log("\(fullCommand)\n")
self.log("\(arguments.joined(separator: " "))\n")
// env resolves brew via PATH (covers the bare "brew" fallback) and
// execs it directly no shell, so no metacharacter interpretation
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/zsh")
process.arguments = ["-c", fullCommand]
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = arguments
var environment = ProcessInfo.processInfo.environment
environment["SUDO_ASKPASS"] = Self.askpassURL.path