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.
This commit is contained in:
Syed Osama Ali Shah authored and GitHub committed 2026-07-09 03:54:33 +07:00
1 parent d3c04ab3e5
commit f9b07d0a9c
2 files changed
+6 -1

No files matched your search

+5 -1
View File
@@ -527,7 +527,11 @@ func StripToolPrefix(tmpl, name string) string {
if strings.Contains(tmpl, placeholder) {
parts := strings.SplitN(tmpl, placeholder, 2)
prefix, suffix := parts[0], parts[1]
if strings.HasPrefix(name, prefix) && strings.HasSuffix(name, suffix) {
// Require the name to be at least as long as prefix+suffix so an
// overlapping match (e.g. tmpl "pre_{tool_name}_suf", name "pre_suf")
// doesn't slice with a negative length and panic.
if strings.HasPrefix(name, prefix) && strings.HasSuffix(name, suffix) &&
len(prefix)+len(suffix) <= len(name) {
result := name[len(prefix):]
if suffix != "" {
result = result[:len(result)-len(suffix)]
+1
View File
@@ -16,6 +16,7 @@ func TestStripToolPrefix(t *testing.T) {
{"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"},