Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

85 lines
2.8 KiB
Go

package mcp
import (
"context"
"strings"
"testing"
"github.com/mark3labs/mcp-go/mcp"
)
func TestScrubControlChars(t *testing.T) {
cases := []struct {
name string
in string
want string
}{
{"plain text unchanged", "a normal description", "a normal description"},
{"pipes preserved", "Output format: json | gcx | toon", "Output format: json | gcx | toon"},
{"multibyte runes preserved", "café — résumé — déjà", "café — résumé — déjà"},
{"newline and tab kept", "line one\n\tindented", "line one\n\tindented"},
{"NUL dropped", "before\x00after", "beforeafter"},
{"carriage return dropped", "overwrite\rme", "overwriteme"},
{"DEL dropped", "a\x7fb", "ab"},
{"C1 control dropped", "a\u009bb", "ab"},
{"ANSI color stripped", "\x1b[31mred\x1b[0m text", "red text"},
{"ANSI cursor-move stripped", "x\x1b[2Ky", "xy"},
{"bare ESC sequence stripped", "a\x1bMb", "ab"},
{"empty string", "", ""},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := scrubControlChars(c.in); got != c.want {
t.Errorf("scrubControlChars(%q) = %q, want %q", c.in, got, c.want)
}
})
}
}
func TestScrubToolText(t *testing.T) {
tool := mcp.NewTool("demo",
mcp.WithDescription("does a thing\x1b[31m with color\x00 and a NUL"),
mcp.WithString("arg", mcp.Description("a param\x07 with a bell")),
)
scrubToolText(&tool)
if strings.ContainsAny(tool.Description, "\x00\x1b\x07") {
t.Errorf("tool description still carries control chars: %q", tool.Description)
}
if !strings.Contains(tool.Description, "does a thing") {
t.Errorf("scrub destroyed legitimate text: %q", tool.Description)
}
prop, ok := tool.InputSchema.Properties["arg"].(map[string]any)
if !ok {
t.Fatalf("arg property missing or wrong shape: %#v", tool.InputSchema.Properties["arg"])
}
desc, _ := prop["description"].(string)
if strings.ContainsRune(desc, '\x07') {
t.Errorf("param description still carries a control char: %q", desc)
}
if !strings.Contains(desc, "a param") {
t.Errorf("scrub destroyed legitimate param text: %q", desc)
}
}
// TestAddTool_ScrubsRegisteredDescription proves the scrub runs on the
// real registration path: a tool registered with a poisoned
// description lands in tools/list clean.
func TestAddTool_ScrubsRegisteredDescription(t *testing.T) {
t.Setenv("GORTEX_LAZY_TOOLS", "0")
srv := newFullTestServer(t)
srv.addTool(
mcp.NewTool("scrub_probe", mcp.WithDescription("poisoned\x1b[31m\x00description")),
func(ctx context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText("ok"), nil
},
)
st, ok := srv.mcpServer.ListTools()["scrub_probe"]
if !ok {
t.Fatal("scrub_probe was not registered")
}
if strings.ContainsAny(st.Tool.Description, "\x00\x1b") {
t.Errorf("registered description not scrubbed: %q", st.Tool.Description)
}
}