Summary
Three unsafe shell-construction patterns found in cmd/nixfleet/internal/. Actual risk is low — all inputs are operator-controlled config, not network/user input — but the patterns are fragile and should be cleaned up to prevent future exposure if input sources change.
Findings
1. pullmode/pullmode.go — unquoted config values in shell commands
setupRepository builds commands via fmt.Sprintf with unescaped config values:
fmt.Sprintf("git clone -b %s %s %s", config.Branch, config.RepoURL, config.RepoPath)
fmt.Sprintf("bash -c 'cd %s && git fetch origin && git reset --hard origin/%s'", config.RepoPath, config.Branch)
If config.RepoPath contains a single quote, it breaks out of the bash -c '...' context. These commands are sent over SSH to remote hosts as root.
Fix: Pass args as separate strings to exec.Command (no shell interpolation needed for git commands):
exec.Command("git", "clone", "-b", config.Branch, config.RepoURL, config.RepoPath)
exec.Command("git", "-C", config.RepoPath, "fetch", "origin")
exec.Command("git", "-C", config.RepoPath, "reset", "--hard", "origin/"+config.Branch)
2. pullmode/pullscript template — config values embedded in bash via text/template
Config values (WebhookSecret, HostName, DotfilesPath, etc.) are embedded directly into a bash script using text/template (no escaping):
WEBHOOK_SECRET="{{.WebhookSecret}}" # a " or $() in value = injection
Fix: Shell-quote embedded values using a template helper (e.g. shquote), or pass values as environment variables instead of inline substitution.
3. cache/cache.go — /bin/sh -c with joined args
execLocalCommand runs:
exec.CommandContext(ctx, "/bin/sh", "-c", strings.Join(args, " "))
Joining args into a single string and passing to /bin/sh -c re-introduces word-splitting. Should use exec.Command with separate args directly.
Risk assessment
Low — all three paths take input from operator-controlled configuration files, not from network requests or user-supplied input. There is no network-facing attack surface for these paths in the current architecture.
Recommended fix priority
Low — address in a normal dev cycle, not a hotfix.
Filed by security-bot via controller — 2026-04-22
Summary
Three unsafe shell-construction patterns found in
cmd/nixfleet/internal/. Actual risk is low — all inputs are operator-controlled config, not network/user input — but the patterns are fragile and should be cleaned up to prevent future exposure if input sources change.Findings
1.
pullmode/pullmode.go— unquoted config values in shell commandssetupRepositorybuilds commands viafmt.Sprintfwith unescaped config values:If
config.RepoPathcontains a single quote, it breaks out of thebash -c '...'context. These commands are sent over SSH to remote hosts as root.Fix: Pass args as separate strings to
exec.Command(no shell interpolation needed for git commands):2.
pullmode/pullscripttemplate — config values embedded in bash viatext/templateConfig values (
WebhookSecret,HostName,DotfilesPath, etc.) are embedded directly into a bash script usingtext/template(no escaping):Fix: Shell-quote embedded values using a template helper (e.g.
shquote), or pass values as environment variables instead of inline substitution.3.
cache/cache.go—/bin/sh -cwith joined argsexecLocalCommandruns:Joining args into a single string and passing to
/bin/sh -cre-introduces word-splitting. Should useexec.Commandwith separate args directly.Risk assessment
Low — all three paths take input from operator-controlled configuration files, not from network requests or user-supplied input. There is no network-facing attack surface for these paths in the current architecture.
Recommended fix priority
Low — address in a normal dev cycle, not a hotfix.
Filed by security-bot via controller — 2026-04-22