diff --git a/internal/sandbox/validator.go b/internal/sandbox/validator.go index 28eb9cf3..0327dcf7 100644 --- a/internal/sandbox/validator.go +++ b/internal/sandbox/validator.go @@ -217,15 +217,18 @@ func (v *ScriptValidator) hasShellOperators(s string) bool { return false } +// Command-substitution patterns, compiled once. hasCommandSubstitution runs +// per argument in ValidateArgs, so recompiling these on every call wasted work +// proportional to the argument count. +var commandSubstitutionPatterns = []*regexp.Regexp{ + regexp.MustCompile(`\$\([^)]+\)`), // $(command) + regexp.MustCompile("`[^`]+`"), // `command` + regexp.MustCompile(`\$\{[^}]*\$\(`), // ${...$(command) +} + // hasCommandSubstitution checks for command substitution patterns func (v *ScriptValidator) hasCommandSubstitution(s string) bool { - patterns := []*regexp.Regexp{ - regexp.MustCompile(`\$\([^)]+\)`), // $(command) - regexp.MustCompile("`[^`]+`"), // `command` - regexp.MustCompile(`\$\{[^}]*\$\(`), // ${...$(command) - } - - for _, p := range patterns { + for _, p := range commandSubstitutionPatterns { if p.MatchString(s) { return true } diff --git a/internal/sandbox/validator_test.go b/internal/sandbox/validator_test.go index 67e18174..a8fd3a94 100644 --- a/internal/sandbox/validator_test.go +++ b/internal/sandbox/validator_test.go @@ -359,3 +359,12 @@ func containsHelper(s, substr string) bool { } return false } + +func BenchmarkValidateArgs(b *testing.B) { + v := NewScriptValidator() + args := []string{"--input", "file.txt", "--name", "report 2024", "--out", "/tmp/x", "--verbose", "--limit=50"} + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = v.ValidateArgs(args) + } +}