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.
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
//go:build linux || darwin
|
|
|
|
package plugins
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"syscall"
|
|
)
|
|
|
|
func createPrivateTransactionDirectory(path string) error {
|
|
if err := os.Mkdir(path, 0700); 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) {
|
|
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)
|
|
}
|
|
return verifyPrivateTransactionDirectory(path)
|
|
}
|
|
|
|
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 owner-only: %s", path)
|
|
}
|
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
|
if !ok || stat.Uid != uint32(os.Geteuid()) {
|
|
return fmt.Errorf("transaction namespace owner does not match effective user: %s", path)
|
|
}
|
|
if err := hardenPrivateTransactionDirectory(path); err != nil {
|
|
return err
|
|
}
|
|
info, err = os.Lstat(path)
|
|
if err != nil || info.Mode()&os.ModeSymlink != 0 || !info.IsDir() ||
|
|
info.Mode().Perm() != 0700 {
|
|
return errors.Join(fmt.Errorf("transaction namespace is not owner-only: %s", path), err)
|
|
}
|
|
stat, ok = info.Sys().(*syscall.Stat_t)
|
|
if !ok || stat.Uid != uint32(os.Geteuid()) {
|
|
return fmt.Errorf("transaction namespace owner does not match effective user: %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)
|
|
}
|
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
|
if !ok || stat.Uid != uint32(os.Geteuid()) {
|
|
return fmt.Errorf("transaction namespace parent owner does not match effective user: %s", path)
|
|
}
|
|
if info.Mode().Perm()&0022 != 0 {
|
|
return fmt.Errorf("transaction namespace parent is writable by another user: %s", path)
|
|
}
|
|
return nil
|
|
}
|