Files
goclaw/internal/tools/policy_prefix_test.go
Syed Osama Ali Shah f9b07d0a9c fix(tools): avoid panic in StripToolPrefix on overlapping prefix+suffix (#1398)
When a tool_call_prefix template has both a prefix and a suffix
(e.g. "pre_{tool_name}_suf"), StripToolPrefix stripped the prefix and
then sliced off the suffix length without checking the two ends don't
overlap. A model-emitted name that satisfies both HasPrefix and
HasSuffix but is shorter than prefix+suffix (e.g. "pre_suf") produced a
negative slice length -> "slice bounds out of range [:-1]" panic. This
runs in the agent turn loop on LLM-controlled names
(resolveToolCallName -> StripToolPrefix), so a crafted/degenerate name
crashes the turn.

Guard with len(prefix)+len(suffix) <= len(name); overlapping names now
fall through to returning the name unchanged, consistent with the other
no-match fallbacks. Adds a regression row to the prefix test table.
2026-07-09 03:54:33 +07:00

40 lines
1.4 KiB
Go

package tools
import "testing"
func TestStripToolPrefix(t *testing.T) {
tests := []struct {
name string
tmpl string
input string
expect string
}{
// Template-based ({tool_name} placeholder)
{"template match", "proxy_{tool_name}", "proxy_exec", "exec"},
{"template with suffix", "{tool_name}_v2", "exec_v2", "exec"},
{"template prefix+suffix", "pre_{tool_name}_suf", "pre_exec_suf", "exec"},
{"template no match", "proxy_{tool_name}", "other_exec", "other_exec"},
{"template empty result", "proxy_{tool_name}", "proxy_", "proxy_"},
{"template exact placeholder", "{tool_name}", "exec", "exec"},
{"template prefix+suffix overlap (regression)", "pre_{tool_name}_suf", "pre_suf", "pre_suf"},
// Literal prefix
{"literal prefix with separator", "proxy_", "proxy_exec", "exec"},
{"literal prefix no match", "proxy_", "other_exec", "other_exec"},
{"literal prefix underscore join", "proxy", "proxy_exec", "exec"},
{"literal prefix single underscore strip", "proxy", "proxy__exec", "_exec"},
{"literal prefix empty after strip", "proxy_", "proxy_", "proxy_"},
{"literal name shorter than prefix", "proxy_", "pro", "pro"},
{"literal name equals prefix", "proxy", "proxy", "proxy"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := StripToolPrefix(tt.tmpl, tt.input)
if got != tt.expect {
t.Errorf("StripToolPrefix(%q, %q) = %q, want %q", tt.tmpl, tt.input, got, tt.expect)
}
})
}
}