Files
ThothII/tools/thothctl/internal/safeio/files_windows_test.go
T

101 lines
3.0 KiB
Go

//go:build windows
package safeio
import (
"os"
"path/filepath"
"testing"
"golang.org/x/sys/windows"
)
const expectedWindowsRetainedHandleShareMode = windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE
// Keep this contract compile-enforced so Windows cross-test compilation catches a future
// FILE_SHARE_DELETE regression even when the tests are compiled on a non-Windows host.
var _ [windowsRetainedHandleShareMode - expectedWindowsRetainedHandleShareMode]struct{}
var _ [expectedWindowsRetainedHandleShareMode - windowsRetainedHandleShareMode]struct{}
func TestOpenWindowsComponentBlocksMutationWhileHandleIsRetained(t *testing.T) {
t.Run("parent rename", func(t *testing.T) {
parent := filepath.Join(t.TempDir(), "parent")
if err := os.Mkdir(parent, 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(parent, "secret"), []byte("secret"), 0o600); err != nil {
t.Fatal(err)
}
handle, err := openWindowsComponent(parent, true)
if err != nil {
t.Fatal(err)
}
renamed := parent + "-renamed"
if err := os.Rename(parent, renamed); err == nil {
windows.CloseHandle(handle)
t.Fatal("parent rename succeeded while its safe-I/O handle was retained")
}
if err := windows.CloseHandle(handle); err != nil {
t.Fatal(err)
}
if err := os.Rename(parent, renamed); err != nil {
t.Fatalf("parent rename after closing its safe-I/O handle: %v", err)
}
})
t.Run("final delete", func(t *testing.T) {
path := filepath.Join(t.TempDir(), "secret")
if err := os.WriteFile(path, []byte("secret"), 0o600); err != nil {
t.Fatal(err)
}
handle, err := openWindowsComponent(path, false)
if err != nil {
t.Fatal(err)
}
if err := os.Remove(path); err == nil {
windows.CloseHandle(handle)
t.Fatal("final-file deletion succeeded while its safe-I/O handle was retained")
}
if err := windows.CloseHandle(handle); err != nil {
t.Fatal(err)
}
if err := os.Remove(path); err != nil {
t.Fatalf("final-file deletion after closing its safe-I/O handle: %v", err)
}
})
}
func TestWriteCanonicalExclusiveRequiresRestrictiveMode(t *testing.T) {
if err := writeCanonicalExclusive(`C:\\tmp\\thothctl-output.yaml`, []byte("x"), 0o640); err == nil {
t.Fatal("accepted non-restrictive output mode")
}
}
func TestWriteCanonicalExclusiveCreatesProtectedOwnerOnlyDACL(t *testing.T) {
root, err := filepath.EvalSymlinks(t.TempDir())
if err != nil {
t.Fatal(err)
}
path := filepath.Join(root, "candidate.yaml")
if err := writeCanonicalExclusive(path, []byte("x"), 0o600); err != nil {
t.Fatal(err)
}
sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.DACL_SECURITY_INFORMATION)
if err != nil {
t.Fatal(err)
}
control, _, err := sd.Control()
if err != nil {
t.Fatal(err)
}
if control&windows.SE_DACL_PROTECTED == 0 {
t.Fatalf("output DACL control = %#x, want protected", control)
}
acl, _, err := sd.DACL()
if err != nil || acl == nil || acl.AceCount != 1 {
t.Fatalf("output DACL = %#v, err=%v; want one owner ACE", acl, err)
}
}