Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

188 lines
6.1 KiB
Go

package runtimeprep
import (
"encoding/json"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestCursorBackgroundTaskGuardAllowsForegroundAndDeniesBackground(t *testing.T) {
nodePath, err := exec.LookPath("node")
if err != nil {
t.Skip("node is required to execute the generated Cursor hook")
}
pluginDir := t.TempDir()
if err := installCursorBackgroundTaskGuard(filepath.Join(pluginDir, "hooks")); err != nil {
t.Fatalf("installCursorBackgroundTaskGuard() error = %v", err)
}
hookPath := filepath.Join(pluginDir, "hooks", "guard-background-task.mjs")
tests := []struct {
name string
input string
wantDenied bool
}{
{
name: "foreground omitted",
input: `{"tool_name":"Task","tool_input":{"prompt":"run_in_background=true is mentioned only as text"}}`,
wantDenied: false,
},
{
name: "foreground false",
input: `{"tool_name":"Task","tool_input":{"run_in_background":false}}`,
wantDenied: false,
},
{
name: "background snake case",
input: `{"tool_name":"Task","tool_input":{"run_in_background":true}}`,
wantDenied: true,
},
{
name: "background camel case",
input: `{"toolName":"Task","toolInput":{"runInBackground":true}}`,
wantDenied: true,
},
{
name: "invalid payload fails closed",
input: `{`,
wantDenied: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
command := exec.Command(nodePath, hookPath)
command.Stdin = strings.NewReader(tt.input)
output, err := command.Output()
if err != nil {
t.Fatalf("generated hook error = %v", err)
}
var response struct {
Permission string `json:"permission"`
UserMessage string `json:"user_message"`
}
if err := json.Unmarshal(output, &response); err != nil {
t.Fatalf("generated hook output = %q: %v", output, err)
}
if tt.wantDenied {
if response.Permission != "deny" || response.UserMessage != cursorBackgroundTaskGuardDeniedMessage {
t.Fatalf("generated hook response = %#v, want background denial", response)
}
} else if response.Permission != "" || response.UserMessage != "" {
t.Fatalf("generated hook response = %#v, want empty allow response", response)
}
})
}
}
func TestCursorPromptPolicyUsesRelativeSearchRootsOnWindows(t *testing.T) {
base := "base runtime policy"
windowsPolicy := cursorPromptPolicyForGOOS(base, "windows")
for _, expected := range []string{
"## Windows file-search boundary",
"drive-qualified absolute Windows paths",
"workspace-relative path",
"native terminal",
"retry through the relative-path or native-terminal route",
} {
if !strings.Contains(windowsPolicy, expected) {
t.Fatalf("Windows Cursor policy missing %q: %s", expected, windowsPolicy)
}
}
if got := cursorPromptPolicyForGOOS(base, "linux"); got != base {
t.Fatalf("non-Windows Cursor policy = %q, want unchanged base policy", got)
}
}
func TestCursorBackgroundTaskGuardLauncherUsesCursorBundledNode(t *testing.T) {
nodePath, err := exec.LookPath("node")
if err != nil {
t.Skip("node is required to execute the generated Cursor hook")
}
pluginDir := t.TempDir()
if err := installCursorBackgroundTaskGuard(filepath.Join(pluginDir, "hooks")); err != nil {
t.Fatalf("installCursorBackgroundTaskGuard() error = %v", err)
}
installDir := filepath.Join(t.TempDir(), "cursor-version")
binDir := filepath.Join(t.TempDir(), "bin")
if err := os.MkdirAll(installDir, 0o700); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(binDir, 0o700); err != nil {
t.Fatal(err)
}
if err := os.Symlink(nodePath, filepath.Join(installDir, "node")); err != nil {
t.Fatal(err)
}
cursorPath := filepath.Join(installDir, "cursor-agent")
if err := os.WriteFile(cursorPath, []byte("#!/usr/bin/env bash\n"), 0o700); err != nil {
t.Fatal(err)
}
if err := os.Symlink(cursorPath, filepath.Join(binDir, "cursor-agent")); err != nil {
t.Fatal(err)
}
launcherPath := filepath.Join(pluginDir, "hooks", "guard-background-task.sh")
for _, tt := range []struct {
name string
input string
wantDenied bool
}{
{name: "foreground", input: `{"tool_name":"Task","tool_input":{"run_in_background":false}}`},
{name: "background", input: `{"tool_name":"Task","tool_input":{"run_in_background":true}}`, wantDenied: true},
} {
t.Run(tt.name, func(t *testing.T) {
command := exec.Command(launcherPath)
command.Stdin = strings.NewReader(tt.input)
command.Env = append(os.Environ(),
"CURSOR_INVOKED_AS=cursor-agent",
"PATH="+binDir+string(os.PathListSeparator)+os.Getenv("PATH"),
)
output, err := command.Output()
if err != nil {
t.Fatalf("generated launcher error = %v", err)
}
denied := strings.Contains(string(output), `"permission":"deny"`)
if denied != tt.wantDenied {
t.Fatalf("generated launcher output = %q, denied = %v want %v", output, denied, tt.wantDenied)
}
})
}
}
func TestCursorBackgroundTaskGuardLauncherFailsClosedWithoutCursorNode(t *testing.T) {
bashPath, err := exec.LookPath("bash")
if err != nil {
t.Skip("bash is required to execute the generated Cursor hook launcher")
}
pluginDir := t.TempDir()
if err := installCursorBackgroundTaskGuard(filepath.Join(pluginDir, "hooks")); err != nil {
t.Fatalf("installCursorBackgroundTaskGuard() error = %v", err)
}
binDir := t.TempDir()
if err := os.Symlink(bashPath, filepath.Join(binDir, "bash")); err != nil {
t.Fatal(err)
}
launcherPath := filepath.Join(pluginDir, "hooks", "guard-background-task.sh")
command := exec.Command(launcherPath)
command.Stdin = strings.NewReader(`{"tool_name":"Task","tool_input":{"run_in_background":false}}`)
command.Env = append(os.Environ(),
"CURSOR_INVOKED_AS=missing-cursor-agent",
"PATH="+strings.Join([]string{binDir, "/usr/bin", "/bin"}, string(os.PathListSeparator)),
)
output, err := command.CombinedOutput()
if err != nil {
t.Fatalf("generated launcher error = %v output = %q", err, output)
}
if !strings.Contains(string(output), `"permission":"deny"`) ||
!strings.Contains(string(output), cursorBackgroundTaskGuardDeniedMessage) {
t.Fatalf("generated launcher output = %q, want fail-closed denial", output)
}
}