mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
fix(security): prevent command injection in setup wizard
The handleInstallDependency endpoint was vulnerable to command injection attacks. User-provided commands were split and executed directly without validation, allowing attackers to run arbitrary commands. Changes: - Add whitelist of allowed commands (package managers only) - Validate commands against whitelist before execution - Handle privilege escalation commands (sudo/pkexec/doas) by also validating the elevated command - Reject any command not in the whitelist with a clear error message The whitelist includes common package managers across platforms: - Linux: apt, dnf, pacman, zypper, emerge, eopkg, nix-env - macOS: brew, port - Windows: winget, choco, scoop Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8d28e2d06b
commit
330bc4e3de
1 changed files with 80 additions and 1 deletions
|
|
@ -544,6 +544,77 @@ type InstallResponse struct {
|
|||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// allowedCommands is a whitelist of commands that can be executed for dependency installation.
|
||||
// This prevents arbitrary command execution from user-controlled input.
|
||||
var allowedCommands = map[string]bool{
|
||||
// Package managers (may be called directly or via sudo)
|
||||
"apt": true,
|
||||
"apt-get": true,
|
||||
"dnf": true,
|
||||
"yum": true,
|
||||
"pacman": true,
|
||||
"zypper": true,
|
||||
"emerge": true,
|
||||
"eopkg": true,
|
||||
"nix-env": true,
|
||||
"brew": true,
|
||||
"port": true, // MacPorts
|
||||
"winget": true,
|
||||
"choco": true,
|
||||
"scoop": true,
|
||||
"sudo": true, // sudo is allowed as it prefixes package manager commands
|
||||
"pkexec": true, // polkit alternative to sudo
|
||||
"doas": true, // OpenBSD sudo alternative
|
||||
"xcode-select": true, // macOS Xcode CLI tools
|
||||
}
|
||||
|
||||
// allowedSudoCommands are commands allowed to be run after sudo/pkexec/doas
|
||||
var allowedSudoCommands = map[string]bool{
|
||||
"apt": true,
|
||||
"apt-get": true,
|
||||
"dnf": true,
|
||||
"yum": true,
|
||||
"pacman": true,
|
||||
"zypper": true,
|
||||
"emerge": true,
|
||||
"eopkg": true,
|
||||
"nix-env": true,
|
||||
"brew": true,
|
||||
"port": true,
|
||||
"xcode-select": true,
|
||||
}
|
||||
|
||||
// isCommandAllowed checks if a command is in the whitelist.
|
||||
// For sudo/pkexec/doas, it also validates the command being elevated.
|
||||
func isCommandAllowed(parts []string) bool {
|
||||
if len(parts) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
cmd := parts[0]
|
||||
if !allowedCommands[cmd] {
|
||||
return false
|
||||
}
|
||||
|
||||
// If it's a privilege escalation command, validate the actual command
|
||||
if cmd == "sudo" || cmd == "pkexec" || cmd == "doas" {
|
||||
if len(parts) < 2 {
|
||||
return false
|
||||
}
|
||||
// Skip sudo flags like -E, -S, etc.
|
||||
actualCmdIndex := 1
|
||||
for actualCmdIndex < len(parts) && strings.HasPrefix(parts[actualCmdIndex], "-") {
|
||||
actualCmdIndex++
|
||||
}
|
||||
if actualCmdIndex >= len(parts) {
|
||||
return false
|
||||
}
|
||||
return allowedSudoCommands[parts[actualCmdIndex]]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *Wizard) handleInstallDependency(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
|
|
@ -558,7 +629,6 @@ func (w *Wizard) handleInstallDependency(rw http.ResponseWriter, r *http.Request
|
|||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Execute the install command
|
||||
// Split the command into parts
|
||||
parts := strings.Fields(req.Command)
|
||||
if len(parts) == 0 {
|
||||
|
|
@ -569,6 +639,15 @@ func (w *Wizard) handleInstallDependency(rw http.ResponseWriter, r *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
// Validate command against whitelist to prevent arbitrary command execution
|
||||
if !isCommandAllowed(parts) {
|
||||
json.NewEncoder(rw).Encode(InstallResponse{
|
||||
Success: false,
|
||||
Error: "Command not allowed: only package manager commands are permitted",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command(parts[0], parts[1:]...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue