78 lines
3.0 KiB
Go
78 lines
3.0 KiB
Go
|
|
//go:build windows
|
||
|
|
|
||
|
|
package cursor
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"os"
|
||
|
|
"os/exec"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
func utf8BOM(text string) []byte { return append([]byte{0xef, 0xbb, 0xbf}, []byte(text)...) }
|
||
|
|
|
||
|
|
func TestPowerShellLauncherPreservesUnicodePathsAndArguments(t *testing.T) {
|
||
|
|
dir := filepath.Join(t.TempDir(), "中文 space O'Brien")
|
||
|
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
result := filepath.Join(dir, "result.json")
|
||
|
|
fake := `$payload=@{args=@($args);ca=$env:NODE_EXTRA_CA_CERTS;options=$env:NODE_OPTIONS;tls=$env:NODE_TLS_REJECT_UNAUTHORIZED};[IO.File]::WriteAllText(` + psLiteral(result) + `,($payload|ConvertTo-Json -Compress),[Text.Encoding]::UTF8);exit 0`
|
||
|
|
if err := os.WriteFile(filepath.Join(dir, "cursor-agent.ps1"), utf8BOM(fake), 0600); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
preload := filepath.Join(dir, "tls-redirect.cjs")
|
||
|
|
launcher := filepath.Join(dir, "launch.ps1")
|
||
|
|
if err := os.WriteFile(launcher, utf8BOM(buildWindowsLauncher("39092", filepath.Join(dir, "ca.crt"), "fixture-token", filepath.Join(dir, "agent.cmd"), preload)), 0600); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
cmd := exec.Command("powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", launcher, "--message", "one two", "a&b", "O'Brien")
|
||
|
|
cmd.Env = append(os.Environ(), "NODE_TLS_REJECT_UNAUTHORIZED=0", "NODE_OPTIONS=--require=old-tls-bypass.cjs")
|
||
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
|
t.Fatalf("launcher: %v %s", err, out)
|
||
|
|
}
|
||
|
|
data, err := os.ReadFile(result)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
data = []byte(strings.TrimPrefix(string(data), "\ufeff"))
|
||
|
|
var got struct {
|
||
|
|
Args []string `json:"args"`
|
||
|
|
CA string `json:"ca"`
|
||
|
|
Options string `json:"options"`
|
||
|
|
TLS string `json:"tls"`
|
||
|
|
}
|
||
|
|
if err = json.Unmarshal(data, &got); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
want := []string{"--message", "one two", "a&b", "O'Brien"}
|
||
|
|
if len(got.Args) != len(want) {
|
||
|
|
t.Fatalf("arguments: %#v", got.Args)
|
||
|
|
}
|
||
|
|
for i := range want {
|
||
|
|
if got.Args[i] != want[i] {
|
||
|
|
t.Fatalf("argument %d: %q", i, got.Args[i])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if got.CA != filepath.Join(dir, "ca.crt") || got.TLS != "" || strings.Contains(got.Options, "tls-bypass") {
|
||
|
|
t.Fatal("unsafe launcher environment")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestMigrationPowerShellSyntaxWithoutExecutingTrustChanges(t *testing.T) {
|
||
|
|
body := windowsCAMigrationBody(`C:\用户 space O'Brien\ca.crt`, strings.Repeat("A", 40), strings.Repeat("B", 40))
|
||
|
|
path := filepath.Join(t.TempDir(), "migration.ps1")
|
||
|
|
if err := os.WriteFile(path, utf8BOM(body), 0600); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
parse := `$tokens=$null;$errors=$null;$null=[Management.Automation.Language.Parser]::ParseFile(` + psLiteral(path) + `,[ref]$tokens,[ref]$errors);if($errors.Count){$errors|ForEach-Object{$_.Message};exit 1}`
|
||
|
|
if out, err := exec.Command("powershell.exe", "-NoProfile", "-NonInteractive", "-Command", parse).CombinedOutput(); err != nil {
|
||
|
|
t.Fatalf("invalid migration syntax: %v %s", err, out)
|
||
|
|
}
|
||
|
|
if strings.Contains(body, "CurrentUser") {
|
||
|
|
t.Fatal("elevated script must not target the alternate administrator's user store")
|
||
|
|
}
|
||
|
|
}
|