项目文件夹

文件
2026-07-13 13:00:08 +08:00

669 行
23 KiB
Go

//go:build windows
package winsandbox
import (
"context"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"testing"
"time"
"golang.org/x/sys/windows"
)
func TestMain(m *testing.M) {
waitMS := uint32((15 * time.Second).Milliseconds())
windowsSandboxWaitMilliseconds = waitMS
os.Setenv("WINDOWS_SANDBOX_WAIT_MS", strconv.FormatUint(uint64(waitMS), 10))
os.Exit(m.Run())
}
func TestWindowsAppContainerNameSeparatesForbidReadPolicies(t *testing.T) {
base := Spec{WritableRoots: []string{`C:\work`}, Network: true}
baseName := windowsAppContainerName(base)
forbidName := windowsAppContainerName(Spec{WritableRoots: []string{`C:\work`}, ForbidReadRoots: []string{`C:\work\secret`}, Network: true})
if baseName == forbidName {
t.Fatal("different forbid_read roots must not share an AppContainer profile")
}
for _, name := range []string{baseName, forbidName} {
if !strings.HasPrefix(name, "WinSandbox.") || len(name) > 64 {
t.Fatalf("unexpected AppContainer profile name: %q", name)
}
}
}
func TestWindowsAppContainerNetworkCapabilities(t *testing.T) {
withNetwork, err := prepareAppContainer(Spec{WritableRoots: []string{`C:\work`}, Network: true})
if err != nil {
t.Fatalf("prepare AppContainer with network: %v", err)
}
defer withNetwork.close()
if len(withNetwork.capabilities) == 0 {
t.Fatal("network-enabled AppContainer should include network capabilities")
}
withoutNetwork, err := prepareAppContainer(Spec{WritableRoots: []string{`C:\work`}, Network: false})
if err != nil {
t.Fatalf("prepare AppContainer without network: %v", err)
}
defer withoutNetwork.close()
if len(withoutNetwork.capabilities) != 0 {
t.Fatalf("network-disabled AppContainer capabilities = %d, want 0", len(withoutNetwork.capabilities))
}
}
func TestWindowsCleanupPathSecurityRemovesACEsBeforeRestore(t *testing.T) {
var calls []string
cleanup := cleanupPathSecurity(
func() { calls = append(calls, "restore") },
func() { calls = append(calls, "remove") },
func() { calls = append(calls, "after") },
)
cleanup()
if got := strings.Join(calls, ","); got != "remove,restore,after" {
t.Fatalf("cleanup order = %s, want remove,restore,after", got)
}
}
func TestWindowsUniqueNonZeroHandles(t *testing.T) {
got := uniqueNonZeroHandles([]windows.Handle{0, 10, 10, 0, 11, 10})
want := []windows.Handle{10, 11}
if len(got) != len(want) {
t.Fatalf("handles = %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("handles = %v, want %v", got, want)
}
}
}
func TestWindowsSandboxProcessCreationFlagsHideConsole(t *testing.T) {
flags := windowsSandboxProcessCreationFlags()
for _, want := range []uint32{
windows.CREATE_UNICODE_ENVIRONMENT,
windows.EXTENDED_STARTUPINFO_PRESENT,
windows.CREATE_SUSPENDED,
windows.CREATE_NO_WINDOW,
} {
if flags&want == 0 {
t.Fatalf("process creation flags %#x missing %#x", flags, want)
}
}
}
func TestWindowsSandboxStartupInfoHidesWindowAndKeepsStdHandles(t *testing.T) {
handles := [3]windows.Handle{11, 12, 13}
si := windowsSandboxStartupInfo(handles, nil)
if si.StartupInfo.Cb == 0 {
t.Fatal("startup info size was not initialized")
}
if si.StartupInfo.Flags&windows.STARTF_USESTDHANDLES == 0 {
t.Fatalf("startup flags %#x missing STARTF_USESTDHANDLES", si.StartupInfo.Flags)
}
if si.StartupInfo.Flags&windows.STARTF_USESHOWWINDOW == 0 {
t.Fatalf("startup flags %#x missing STARTF_USESHOWWINDOW", si.StartupInfo.Flags)
}
if si.StartupInfo.ShowWindow != windows.SW_HIDE {
t.Fatalf("ShowWindow = %d, want SW_HIDE", si.StartupInfo.ShowWindow)
}
if si.StartupInfo.StdInput != handles[0] || si.StartupInfo.StdOutput != handles[1] || si.StartupInfo.StdErr != handles[2] {
t.Fatalf("std handles = (%v,%v,%v), want %v", si.StartupInfo.StdInput, si.StartupInfo.StdOutput, si.StartupInfo.StdErr, handles)
}
}
func TestWindowsSandboxSystemCommandsAreHidden(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for _, cmd := range []*exec.Cmd{
hiddenWindowsSystemCommandContext(ctx, "icacls.exe", `C:\work`, "/C"),
hiddenWindowsSystemCommand("taskkill.exe", "/?"),
} {
if cmd.SysProcAttr == nil {
t.Fatal("system command SysProcAttr is nil")
}
if !cmd.SysProcAttr.HideWindow {
t.Fatal("system command did not set HideWindow")
}
if cmd.SysProcAttr.CreationFlags&windows.CREATE_NO_WINDOW == 0 {
t.Fatalf("system command creation flags %#x missing CREATE_NO_WINDOW", cmd.SysProcAttr.CreationFlags)
}
}
}
func TestWindowsSandboxAvailableOnCI(t *testing.T) {
if os.Getenv("CI") == "" {
t.Skip("only require AppContainer sandbox availability on CI")
}
if !Available() {
t.Fatal("windows sandbox APIs unavailable on CI")
}
}
func TestWindowsExecutableGrantDirResolvesPathTools(t *testing.T) {
dir := t.TempDir()
toolPath := filepath.Join(dir, "windows-sandbox-path-tool.exe")
if err := os.WriteFile(toolPath, []byte("not really an exe"), 0o644); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
if got := windowsExecutableGrantDir("windows-sandbox-path-tool.exe"); !sameWindowsPath(got, dir) {
t.Fatalf("grant dir = %q, want %q", got, dir)
}
}
func TestWindowsExecutableGrantRootsIncludeGitInstallRoot(t *testing.T) {
installRoot := filepath.Join(t.TempDir(), "Git")
bin := filepath.Join(installRoot, "usr", "bin")
if err := os.MkdirAll(bin, 0o755); err != nil {
t.Fatal(err)
}
bashPath := filepath.Join(bin, "bash.exe")
if err := os.WriteFile(bashPath, []byte("not really an exe"), 0o644); err != nil {
t.Fatal(err)
}
got := windowsExecutableGrantRoots(bashPath)
if len(got) != 2 {
t.Fatalf("grant roots = %v, want executable dir and Git install root", got)
}
if !sameWindowsPath(got[0], bin) || !sameWindowsPath(got[1], installRoot) {
t.Fatalf("grant roots = %v, want [%s %s]", got, bin, installRoot)
}
}
func TestWindowsWritableRootsIncludeCommandTempWithoutGlobalTemp(t *testing.T) {
workspace := t.TempDir()
commandTemp := t.TempDir()
got := windowsWritableRoots(Spec{WritableRoots: []string{workspace}}, commandTemp)
if len(got) != 2 {
t.Fatalf("writable roots = %v, want workspace and command temp only", got)
}
if !sameWindowsPath(got[0], workspace) || !sameWindowsPath(got[1], commandTemp) {
t.Fatalf("writable roots = %v, want [%s %s]", got, workspace, commandTemp)
}
if globalTemp := os.TempDir(); sameWindowsPath(globalTemp, workspace) || sameWindowsPath(globalTemp, commandTemp) {
t.Skip("test temp dirs are the global temp root")
}
for _, root := range got {
if sameWindowsPath(root, os.TempDir()) {
t.Fatalf("global temp root should not be auto-granted: %v", got)
}
}
}
func TestWindowsSandboxEnvRedirectsTemp(t *testing.T) {
env := setWindowsEnv([]string{"Path=C:\\Tools", "temp=C:\\old-temp", "TMP=C:\\old-tmp"}, map[string]string{
"TEMP": `C:\sandbox-temp`,
"TMP": `C:\sandbox-temp`,
"TMPDIR": `C:\sandbox-temp`,
})
joined := "\n" + strings.Join(env, "\n") + "\n"
for _, want := range []string{"\ntemp=C:\\sandbox-temp\n", "\nTMP=C:\\sandbox-temp\n", "\nTMPDIR=C:\\sandbox-temp\n"} {
if !strings.Contains(joined, want) {
t.Fatalf("env %q missing %q", joined, want)
}
}
}
func TestWindowsSandboxAllowsWorkspaceWriteAndDeniesOutside(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
workspace := t.TempDir()
outside := t.TempDir()
insideFile := filepath.Join(workspace, "inside.txt")
existingFile := filepath.Join(workspace, "existing.txt")
nestedDir := filepath.Join(workspace, "nested")
if err := os.Mkdir(nestedDir, 0o755); err != nil {
t.Fatal(err)
}
nestedExistingFile := filepath.Join(nestedDir, "existing.txt")
if err := os.WriteFile(existingFile, []byte("old"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(nestedExistingFile, []byte("old"), 0o644); err != nil {
t.Fatal(err)
}
outsideFile := filepath.Join(outside, "outside.txt")
t.Chdir(workspace)
script := "$ErrorActionPreference='Stop'; " +
psSandboxDiagnostics(workspace) +
psTrySetContent(insideFile, "ok") +
psTrySetContent(existingFile, "updated") +
psTrySetContent(nestedExistingFile, "nested") +
"if ((Split-Path -Leaf $env:TEMP) -notlike 'windows-sandbox-test-*') { exit 8 }; " +
"try { Set-Content -LiteralPath (Join-Path $env:TEMP 'sandbox-temp.txt') -Value temp } catch { Write-Host $_; __winsandbox_dump_diag; exit 1 }; " +
"try { Set-Content -LiteralPath " + psQuote(outsideFile) + " -Value nope; exit 9 } catch { exit 0 }"
result, err := Run(Spec{WritableRoots: []string{workspace}, Network: true, Writable: true, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 0 {
t.Fatalf("sandbox exit code = %d, want 0", result.ExitCode)
}
if got, err := os.ReadFile(insideFile); err != nil || !strings.Contains(string(got), "ok") {
t.Fatalf("inside write missing: %q err=%v", got, err)
}
if got, err := os.ReadFile(existingFile); err != nil || !strings.Contains(string(got), "updated") {
t.Fatalf("existing file write missing: %q err=%v", got, err)
}
if got, err := os.ReadFile(nestedExistingFile); err != nil || !strings.Contains(string(got), "nested") {
t.Fatalf("nested existing file write missing: %q err=%v", got, err)
}
if _, err := os.Stat(outsideFile); err == nil {
t.Fatalf("outside write unexpectedly succeeded: %s", outsideFile)
}
}
func TestWindowsSandboxReadOnlyAllowsReadsAndDeniesWrites(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
workspace := t.TempDir()
readableFile := filepath.Join(workspace, "readable.txt")
if err := os.WriteFile(readableFile, []byte("visible"), 0o644); err != nil {
t.Fatal(err)
}
writtenFile := filepath.Join(workspace, "written.txt")
script := "$ErrorActionPreference='Stop'; " +
"$value = Get-Content -Raw -LiteralPath " + psQuote(readableFile) + "; " +
"if ($value.Trim() -ne 'visible') { exit 8 }; " +
"try { Set-Content -LiteralPath " + psQuote(writtenFile) + " -Value nope; exit 9 } catch { exit 0 }"
result, err := Run(Spec{WritableRoots: []string{workspace}, Network: true, Writable: false, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 0 {
t.Fatalf("read-only sandbox exit code = %d, want 0", result.ExitCode)
}
if _, err := os.Stat(writtenFile); err == nil {
t.Fatalf("read-only sandbox unexpectedly wrote %s", writtenFile)
}
}
func TestWindowsSandboxDeniesForbidRead(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
workspace := t.TempDir()
secretDir := filepath.Join(workspace, "secret")
if err := os.Mkdir(secretDir, 0o755); err != nil {
t.Fatal(err)
}
secretFile := filepath.Join(secretDir, "token.txt")
if err := os.WriteFile(secretFile, []byte("secret"), 0o644); err != nil {
t.Fatal(err)
}
t.Chdir(workspace)
script := "$ErrorActionPreference='Stop'; " +
"try { Get-Content -LiteralPath " + psQuote(secretFile) + "; exit 9 } catch { exit 0 }"
result, err := Run(Spec{WritableRoots: []string{workspace}, ForbidReadRoots: []string{secretDir}, Network: true, Writable: true, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 0 {
t.Fatalf("forbid_read was not enforced, exit code = %d", result.ExitCode)
}
}
func TestWindowsSandboxDeniesForbidReadInReadOnlyAppContainer(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
workspace := t.TempDir()
secretDir := filepath.Join(workspace, "secret")
if err := os.Mkdir(secretDir, 0o755); err != nil {
t.Fatal(err)
}
secretFile := filepath.Join(secretDir, "token.txt")
if err := os.WriteFile(secretFile, []byte("secret"), 0o644); err != nil {
t.Fatal(err)
}
script := "$ErrorActionPreference='Stop'; " +
"try { Get-Content -LiteralPath " + psQuote(secretFile) + "; exit 9 } catch { exit 0 }"
result, err := Run(Spec{WritableRoots: []string{workspace}, ForbidReadRoots: []string{secretDir}, Network: true, Writable: false, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 0 {
t.Fatalf("forbid_read was not enforced in read-only AppContainer, exit code = %d", result.ExitCode)
}
}
func TestWindowsSandboxStdioEnvDirAndExitCode(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
root := t.TempDir()
workspace := filepath.Join(root, "workspace with spaces")
if err := os.Mkdir(workspace, 0o755); err != nil {
t.Fatal(err)
}
stdin := tempFileWithContent(t, "stdin.txt", "hello from stdin\n")
stdout := tempFileWithContent(t, "stdout.txt", "")
stderr := tempFileWithContent(t, "stderr.txt", "")
defer stdin.Close()
defer stdout.Close()
defer stderr.Close()
if _, err := stdin.Seek(0, 0); err != nil {
t.Fatal(err)
}
cwdMarker := filepath.Join(workspace, "cwd-marker.txt")
script := "$inputText = [Console]::In.ReadToEnd(); " +
"Write-Output ('OUT:' + $inputText.Trim()); " +
"[Console]::Error.WriteLine('ERR:' + $env:WINDOWS_SANDBOX_TEST_FLAG); " +
"try { Set-Content -LiteralPath 'cwd-marker.txt' -Value cwd } catch { exit 7 }; " +
"if ((Split-Path -Leaf $env:TEMP) -notlike 'windows-sandbox-test-*') { exit 8 }; " +
"exit 23"
result, err := Run(
Spec{WritableRoots: []string{workspace}, Network: true, Writable: true, TempPrefix: "windows-sandbox-test-"},
append(sh, script),
RunOptions{
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
Env: append(os.Environ(), "WINDOWS_SANDBOX_TEST_FLAG=flag-from-env"),
Dir: workspace,
},
)
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 23 {
t.Fatalf("exit code = %d, want 23", result.ExitCode)
}
if got := readWholeFile(t, stdout.Name()); !strings.Contains(got, "OUT:hello from stdin") {
t.Fatalf("stdout = %q, want stdin echo", got)
}
if got := readWholeFile(t, stderr.Name()); !strings.Contains(got, "ERR:flag-from-env") {
t.Fatalf("stderr = %q, want env echo", got)
}
if got, err := os.ReadFile(cwdMarker); err != nil || !strings.Contains(string(got), "cwd") {
t.Fatalf("cwd marker missing: %q err=%v", got, err)
}
}
func TestWindowsSandboxNetworkDisabledBlocksLoopbackConnect(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Skipf("loopback listener unavailable: %v", err)
}
defer listener.Close()
accepted := make(chan struct{}, 1)
go func() {
conn, err := listener.Accept()
if err == nil {
_ = conn.Close()
accepted <- struct{}{}
}
}()
_, port, err := net.SplitHostPort(listener.Addr().String())
if err != nil {
t.Fatal(err)
}
workspace := t.TempDir()
script := "$client = [Net.Sockets.TcpClient]::new(); " +
"$async = $client.BeginConnect('127.0.0.1', " + port + ", $null, $null); " +
"if ($async.AsyncWaitHandle.WaitOne(1500)) { " +
" try { $client.EndConnect($async); $client.Close(); exit 9 } catch { exit 0 } " +
"} else { $client.Close(); exit 0 }"
result, err := Run(Spec{WritableRoots: []string{workspace}, Network: false, Writable: false, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 0 {
t.Fatalf("network-disabled sandbox connected to loopback, exit code = %d", result.ExitCode)
}
select {
case <-accepted:
t.Fatal("network-disabled sandbox reached the loopback listener")
default:
}
}
func TestWindowsSandboxKillsChildProcessTreeOnReturn(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
workspace := t.TempDir()
marker := filepath.Join(workspace, "child-marker.txt")
childCommand := "Start-Sleep -Seconds 5; Set-Content -LiteralPath " + psQuote(marker) + " -Value alive"
script := "$exe = (Get-Process -Id $PID).Path; " +
"$child = Start-Process -FilePath $exe -ArgumentList @('-NoProfile','-NonInteractive','-Command'," + psQuote(childCommand) + ") -PassThru; " +
"if (-not $child.Id) { exit 8 }; " +
"exit 0"
result, err := Run(Spec{WritableRoots: []string{workspace}, Network: true, Writable: true, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 0 {
t.Fatalf("sandbox exit code = %d, want 0", result.ExitCode)
}
time.Sleep(7 * time.Second)
if _, err := os.Stat(marker); err == nil {
t.Fatalf("sandbox job object did not kill child process; marker exists: %s", marker)
}
}
func TestWindowsSandboxTimeoutTerminatesCommand(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
t.Setenv("WINDOWS_SANDBOX_WAIT_MS", "1000")
workspace := t.TempDir()
start := time.Now()
result, err := Run(Spec{WritableRoots: []string{workspace}, Network: true, Writable: true, TempPrefix: "windows-sandbox-test-"}, append(sh, "Start-Sleep -Seconds 5; exit 9"), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err == nil {
t.Fatalf("timed-out sandbox should fail, code=%d", result.ExitCode)
}
if !strings.Contains(err.Error(), "timed out") {
t.Fatalf("timeout error = %v", err)
}
if elapsed := time.Since(start); elapsed > 4*time.Second {
t.Fatalf("timeout took too long: %s", elapsed)
}
}
func TestWindowsSandboxCleansTouchedSecurityDescriptors(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
workspace := t.TempDir()
secretDir := filepath.Join(workspace, "secret")
if err := os.Mkdir(secretDir, 0o755); err != nil {
t.Fatal(err)
}
secretFile := filepath.Join(secretDir, "token.txt")
if err := os.WriteFile(secretFile, []byte("secret"), 0o644); err != nil {
t.Fatal(err)
}
t.Chdir(workspace)
script := "$ErrorActionPreference='Stop'; " +
psSandboxDiagnostics(workspace) +
psTrySetContent(filepath.Join(workspace, "inside.txt"), "ok") +
"try { Get-Content -LiteralPath " + psQuote(secretFile) + "; exit 9 } catch { exit 0 }"
result, err := Run(Spec{WritableRoots: []string{workspace}, ForbidReadRoots: []string{secretDir}, Network: true, Writable: true, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err != nil {
t.Fatalf("sandbox run failed: %v", err)
}
if result.ExitCode != 0 {
t.Fatalf("sandbox exit code = %d, want 0", result.ExitCode)
}
assertNoWindowsSandboxACEForTest(t, workspace)
assertNoWindowsSandboxACEForTest(t, secretDir)
}
func TestWindowsSandboxRejectsWritableNetworkDisabled(t *testing.T) {
if !Available() {
t.Skip("windows sandbox APIs unavailable")
}
sh := powershellArgvForTest(t, "")
if sh == nil {
t.Skip("PowerShell unavailable")
}
workspace := t.TempDir()
t.Chdir(workspace)
script := "$ErrorActionPreference='Stop'; Set-Content -LiteralPath " + psQuote(filepath.Join(workspace, "inside.txt")) + " -Value ok"
result, err := Run(Spec{WritableRoots: []string{workspace}, Network: false, Writable: true, TempPrefix: "windows-sandbox-test-"}, append(sh, script), RunOptions{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr})
if err == nil {
t.Fatalf("network=false writable sandbox should fail closed, code=%d", result.ExitCode)
}
if !strings.Contains(err.Error(), "network=false") {
t.Fatalf("error = %v, want network=false unsupported", err)
}
}
func psQuote(s string) string {
return "'" + strings.ReplaceAll(s, "'", "''") + "'"
}
func powershellArgvForTest(t *testing.T, command string) []string {
t.Helper()
for _, name := range []string{"pwsh", "powershell"} {
path, err := exec.LookPath(name)
if err != nil {
continue
}
args := []string{path, "-NoProfile", "-NonInteractive", "-Command"}
if command != "" {
args = append(args, command)
}
return args
}
return nil
}
func tempFileWithContent(t *testing.T, pattern string, content string) *os.File {
t.Helper()
f, err := os.CreateTemp(t.TempDir(), pattern)
if err != nil {
t.Fatal(err)
}
if _, err := f.WriteString(content); err != nil {
_ = f.Close()
t.Fatal(err)
}
return f
}
func readWholeFile(t *testing.T, path string) string {
t.Helper()
b, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
return string(b)
}
func psTrySetContent(path, value string) string {
return "try { Set-Content -LiteralPath " + psQuote(path) + " -Value " + psQuote(value) + " } catch { Write-Host $_; __winsandbox_dump_diag; exit 1 }; "
}
func psSandboxDiagnostics(root string) string {
return "$__winsandboxDiagRoot = " + psQuote(root) + "; " +
"function __winsandbox_dump_diag { " +
"Write-Host '--- windows sandbox diagnostics ---'; " +
"try { Write-Host ('USER=' + [Security.Principal.WindowsIdentity]::GetCurrent().Name) } catch {}; " +
"try { Write-Host ('SID=' + [Security.Principal.WindowsIdentity]::GetCurrent().User.Value) } catch {}; " +
"Write-Host ('TEMP=' + $env:TEMP); " +
"try { whoami /all } catch {}; " +
"try { icacls $__winsandboxDiagRoot } catch {}; " +
"try { icacls (Split-Path -Parent $__winsandboxDiagRoot) } catch {}; " +
"try { icacls $env:TEMP } catch {}; " +
"} "
}
func pathDACLSDDLForTest(t *testing.T, path string) string {
t.Helper()
sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.DACL_SECURITY_INFORMATION)
if err != nil {
t.Fatalf("GetNamedSecurityInfo(%s): %v", path, err)
}
if sd == nil {
return ""
}
return sd.String()
}
func assertNoWindowsSandboxACEForTest(t *testing.T, path string) {
t.Helper()
sddl := pathDACLSDDLForTest(t, path)
for _, forbidden := range []string{
allApplicationPackagesSID,
allRestrictedApplicationPackagesSID,
} {
if strings.Contains(sddl, forbidden) {
t.Fatalf("%s still contains sandbox SID %s: %s", path, forbidden, sddl)
}
}
userSID, err := currentProcessUserSIDString()
if err != nil {
t.Fatalf("current user SID: %v", err)
}
if strings.Contains(sddl, "(D") && strings.Contains(sddl, userSID) {
t.Fatalf("%s still contains current-user deny ACE: %s", path, sddl)
}
}
func sameWindowsPath(a, b string) bool {
if real, err := filepath.EvalSymlinks(a); err == nil {
a = real
}
if real, err := filepath.EvalSymlinks(b); err == nil {
b = real
}
return strings.EqualFold(filepath.Clean(a), filepath.Clean(b))
}