Files
Elydora-Go-SDK/cmd/elydora/plugins/transaction_private_windows.go
T
ZacharyZhang-NY eb41b991bf feat(go): durable transaction engine and Kiro IDE 1.0 adapter
Split the monolithic transaction into focused modules: append-only
crash-recoverable journal under ~/.elydora/transactions with owner
tokens and per-transaction namespaces, staged atomic publication
(stage .pending, fsync, rename, fsync), platform-specific atomic
replacement (MoveFileEx, renameat2, renamex_np), private-directory
hardening per OS, lock-guarded recovery, and workspace cleanup that
removes only transaction-created empty directories.

Add the Kiro IDE 1.0 plugin: strict v1 hook schema with one enabled
PreToolUse guard and PostToolUse audit pair bound to the active
workspace, agent-to-workspace identity binding in runtime config, and
exact migration of the legacy elydora-audit.kiro.hook contract.

Gates: go test ./... and -race pass, go vet clean, govulncheck reports
zero reachable vulnerabilities, git diff --check clean.
2026-07-21 19:02:57 -04:00

229 lines
6.8 KiB
Go

//go:build windows
package plugins
import (
"errors"
"fmt"
"os"
"unsafe"
"golang.org/x/sys/windows"
)
const transactionFileAllAccess windows.ACCESS_MASK = 0x1f01ff
const transactionParentMutationAccess windows.ACCESS_MASK = windows.FILE_WRITE_DATA |
windows.FILE_APPEND_DATA |
windows.DELETE |
windows.WRITE_DAC |
windows.WRITE_OWNER |
windows.GENERIC_WRITE |
windows.GENERIC_ALL |
0x00000040 // FILE_DELETE_CHILD
func createPrivateTransactionDirectory(path string) error {
descriptor, _, err := privateTransactionSecurityDescriptor()
if err != nil {
return err
}
pointer, err := windows.UTF16PtrFromString(path)
if err != nil {
return err
}
attributes := windows.SecurityAttributes{
Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
SecurityDescriptor: descriptor,
}
if err := windows.CreateDirectory(pointer, &attributes); err != nil {
return err
}
return verifyPrivateTransactionDirectory(path)
}
func ensurePrivateTransactionDirectory(path string) error {
info, err := os.Lstat(path)
if errors.Is(err, os.ErrNotExist) {
if createErr := createPrivateTransactionDirectory(path); createErr == nil {
return nil
} else if !errors.Is(createErr, os.ErrExist) &&
!errors.Is(createErr, windows.ERROR_ALREADY_EXISTS) {
return createErr
}
info, err = os.Lstat(path)
}
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return fmt.Errorf("transaction namespace is not a physical directory: %s", path)
}
descriptor, user, err := privateTransactionSecurityDescriptor()
if err != nil {
return err
}
dacl, _, err := descriptor.DACL()
if err != nil {
return err
}
if err := windows.SetNamedSecurityInfo(
path,
windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|
windows.DACL_SECURITY_INFORMATION|
windows.PROTECTED_DACL_SECURITY_INFORMATION,
user,
nil,
dacl,
nil,
); err != nil {
return fmt.Errorf("protect transaction namespace %s: %w", path, err)
}
return verifyPrivateTransactionDirectory(path)
}
func privateTransactionSecurityDescriptor() (*windows.SECURITY_DESCRIPTOR, *windows.SID, error) {
user, err := windows.GetCurrentProcessToken().GetTokenUser()
if err != nil {
return nil, nil, err
}
sid, err := user.User.Sid.Copy()
if err != nil {
return nil, nil, err
}
sidText := sid.String()
if sidText == "" {
return nil, nil, fmt.Errorf("resolve current Windows user SID")
}
descriptor, err := windows.SecurityDescriptorFromString(
"O:" + sidText + "D:P(A;OICI;FA;;;" + sidText + ")",
)
return descriptor, sid, err
}
func verifyPrivateTransactionDirectory(path string) error {
info, err := os.Lstat(path)
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return fmt.Errorf("transaction namespace is not a physical directory: %s", path)
}
user, err := windows.GetCurrentProcessToken().GetTokenUser()
if err != nil {
return err
}
descriptor, err := windows.GetNamedSecurityInfo(
path,
windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
)
if err != nil {
return fmt.Errorf("inspect transaction namespace DACL at %s: %w", path, err)
}
owner, _, err := descriptor.Owner()
if err != nil || owner == nil || !owner.Equals(user.User.Sid) {
return errors.Join(fmt.Errorf("transaction namespace owner changed: %s", path), err)
}
control, _, err := descriptor.Control()
if err != nil || control&windows.SE_DACL_PROTECTED == 0 {
return errors.Join(fmt.Errorf("transaction namespace DACL inherits access: %s", path), err)
}
dacl, _, err := descriptor.DACL()
if err != nil || dacl == nil || dacl.AceCount != 1 {
return errors.Join(fmt.Errorf("transaction namespace DACL is not owner-only: %s", path), err)
}
var ace *windows.ACCESS_ALLOWED_ACE
if err := windows.GetAce(dacl, 0, &ace); err != nil || ace == nil {
return errors.Join(fmt.Errorf("inspect transaction namespace owner ACE: %s", path), err)
}
aceSID := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
if ace.Header.AceType != windows.ACCESS_ALLOWED_ACE_TYPE ||
ace.Header.AceFlags != windows.OBJECT_INHERIT_ACE|windows.CONTAINER_INHERIT_ACE ||
ace.Mask != transactionFileAllAccess || !aceSID.Equals(user.User.Sid) {
return fmt.Errorf("transaction namespace DACL is not owner-only: %s", path)
}
return nil
}
func verifyTransactionNamespaceParent(path string) error {
info, err := os.Lstat(path)
if err != nil || info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return errors.Join(fmt.Errorf("transaction namespace parent is not a physical directory: %s", path), err)
}
user, err := windows.GetCurrentProcessToken().GetTokenUser()
if err != nil {
return err
}
descriptor, err := windows.GetNamedSecurityInfo(
path,
windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
)
if err != nil {
return fmt.Errorf("inspect transaction namespace parent owner at %s: %w", path, err)
}
owner, _, err := descriptor.Owner()
if err != nil || owner == nil || !owner.Equals(user.User.Sid) {
return errors.Join(
fmt.Errorf("transaction namespace parent owner does not match current user: %s", path),
err,
)
}
if err := verifyTransactionNamespaceParentDACL(path, descriptor, user.User.Sid); err != nil {
return err
}
return nil
}
func verifyTransactionNamespaceParentDACL(
path string,
descriptor *windows.SECURITY_DESCRIPTOR,
currentUser *windows.SID,
) error {
dacl, _, err := descriptor.DACL()
if err != nil || dacl == nil {
return errors.Join(fmt.Errorf("transaction namespace parent has no restrictive DACL: %s", path), err)
}
system, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
if err != nil {
return err
}
administrators, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
if err != nil {
return err
}
for index := uint32(0); index < uint32(dacl.AceCount); index++ {
var ace *windows.ACCESS_ALLOWED_ACE
if err := windows.GetAce(dacl, index, &ace); err != nil || ace == nil {
return errors.Join(fmt.Errorf("inspect transaction namespace parent ACE at %s", path), err)
}
if ace.Header.AceType == windows.ACCESS_DENIED_ACE_TYPE {
continue
}
if ace.Header.AceType != windows.ACCESS_ALLOWED_ACE_TYPE {
return fmt.Errorf(
"transaction namespace parent has unsupported DACL ACE type %d: %s",
ace.Header.AceType,
path,
)
}
if ace.Header.AceFlags&windows.INHERIT_ONLY_ACE != 0 ||
ace.Mask&transactionParentMutationAccess == 0 {
continue
}
sid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
if !sid.IsValid() {
return fmt.Errorf("transaction namespace parent has invalid DACL SID: %s", path)
}
if !sid.Equals(currentUser) && !sid.Equals(system) && !sid.Equals(administrators) {
return fmt.Errorf(
"transaction namespace parent grants mutation access to untrusted SID %s: %s",
sid.String(),
path,
)
}
}
return nil
}