lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 90cb4a9e7a17df9cc6c2c382fa01bda7b10019df
parent b54fe27136dd33aa16375572c209ceee96d94f07
Author: Sean Enck <sean@ttypty.com>
Date:   Sat,  7 Jun 2025 16:01:03 -0400

hooks are gone, if they come back - it will be different

Diffstat:
Mcmd/lb/main_test.go | 9+--------
Dcmd/lb/tests/hooks/all.sh | 2--
Dcmd/lb/tests/hooks/test.sh | 2--
Minternal/app/help/core.go | 17-----------------
Minternal/app/help/core_test.go | 2+-
Dinternal/app/help/doc/hooks.txt | 9---------
Minternal/backend/actions.go | 62+++++++-------------------------------------------------------
Dinternal/backend/hooks.go | 83-------------------------------------------------------------------------------
Dinternal/backend/hooks_test.go | 71-----------------------------------------------------------------------
Minternal/config/core.go | 1-
Minternal/config/toml_test.go | 2+-
Minternal/config/vars.go | 20--------------------
Minternal/config/vars_test.go | 4----
13 files changed, 10 insertions(+), 274 deletions(-)

diff --git a/cmd/lb/main_test.go b/cmd/lb/main_test.go @@ -28,7 +28,6 @@ const ( reKeyKeyData = "rekeyfile" clipWait = 1 clipTries = 6 - hookDir = "hooks" ) var ( @@ -132,13 +131,7 @@ func unpackDir(dir, under string, mode os.FileMode) error { for _, d := range dirs { name := d.Name() if d.IsDir() { - if name != hookDir { - return fmt.Errorf("unexpected embedded dir: %s", name) - } - if err := unpackDir(filepath.Join(dir, name), filepath.Join(under, name), 0o755); err != nil { - return err - } - continue + return fmt.Errorf("unexpected embedded dir: %s", name) } data, err := testingFiles.ReadFile(filepath.Join(dir, name)) if err != nil { diff --git a/cmd/lb/tests/hooks/all.sh b/cmd/lb/tests/hooks/all.sh @@ -1,2 +0,0 @@ -#!/bin/sh -echo "$@" diff --git a/cmd/lb/tests/hooks/test.sh b/cmd/lb/tests/hooks/test.sh @@ -1,2 +0,0 @@ -#!/bin/sh -echo "CALLED" diff --git a/internal/app/help/core.go b/internal/app/help/core.go @@ -11,7 +11,6 @@ import ( "text/template" "git.sr.ht/~enckse/lockbox/internal/app/commands" - "git.sr.ht/~enckse/lockbox/internal/backend" "git.sr.ht/~enckse/lockbox/internal/config" "git.sr.ht/~enckse/lockbox/internal/output" ) @@ -44,17 +43,6 @@ type ( KeyFile string NoKey string } - Hooks struct { - Mode struct { - Pre string - Post string - } - Action struct { - Remove string - Insert string - Move string - } - } } ) @@ -127,11 +115,6 @@ func Usage(verbose bool, exe string) ([]string, error) { document.Config.XDG = config.ConfigXDG document.ReKey.KeyFile = setDocFlag(commands.ReKeyFlags.KeyFile) document.ReKey.NoKey = commands.ReKeyFlags.NoKey - document.Hooks.Mode.Pre = string(backend.HookPre) - document.Hooks.Mode.Post = string(backend.HookPost) - document.Hooks.Action.Insert = string(backend.InsertAction) - document.Hooks.Action.Remove = string(backend.RemoveAction) - document.Hooks.Action.Move = string(backend.MoveAction) files, err := docs.ReadDir(docDir) if err != nil { return nil, err diff --git a/internal/app/help/core_test.go b/internal/app/help/core_test.go @@ -13,7 +13,7 @@ func TestUsage(t *testing.T) { t.Errorf("invalid usage, out of date? %d", len(u)) } u, _ = help.Usage(true, "lb") - if len(u) != 102 { + if len(u) != 92 { t.Errorf("invalid verbose usage, out of date? %d", len(u)) } for _, usage := range u { diff --git a/internal/app/help/doc/hooks.txt b/internal/app/help/doc/hooks.txt @@ -1,9 +0,0 @@ -When hooks are enabled the files in the specified hook directory -are executed with the following parameters (in this order): - -- string: "{{ .Hooks.Mode.Pre }}" or "{{ .Hooks.Mode.Post }}" representing when the hook is executing. - -- string: "{{ .Hooks.Action.Move }}", "{{ .Hooks.Action.Insert }}", or "{{ - .Hooks.Action.Remove }}" indicating the user action - -- string: the path to the entry being operated on diff --git a/internal/backend/actions.go b/internal/backend/actions.go @@ -14,18 +14,7 @@ import ( ) type ( - // ActionMode represents activities performed via transactions - ActionMode string - action func(t Context) error -) - -const ( - // MoveAction represents changes via moves, like the Move command - MoveAction ActionMode = "mv" - // InsertAction represents changes via inserts, like the Insert command - InsertAction ActionMode = "insert" - // RemoveAction represents changes via deletions, like Remove or globbed remove commands - RemoveAction ActionMode = "rm" + action func(t Context) error ) func (t *Transaction) act(cb action) error { @@ -224,20 +213,10 @@ func (t *Transaction) Move(src *Entity, dst string) error { if err != nil { return err } - action := MoveAction - if dst == src.Path { - action = InsertAction - } - hook, err := NewHook(src.Path, action) - if err != nil { - return err - } - if err := hook.Run(HookPre); err != nil { - return err - } - err = t.change(func(c Context) error { + isMove := dst != src.Path + return t.change(func(c Context) error { c.removeEntity(sOffset, sTitle) - if action == MoveAction { + if isMove { c.removeEntity(dOffset, dTitle) } e := gokeepasslib.NewEntry() @@ -259,10 +238,6 @@ func (t *Transaction) Move(src *Entity, dst string) error { c.alterEntities(true, dOffset, dTitle, &e) return nil }) - if err != nil { - return err - } - return hook.Run(HookPost) } // Insert is a move to the same location @@ -284,30 +259,18 @@ func (t *Transaction) RemoveAll(entities []Entity) error { return errors.New("no entities given") } type removal struct { - parts []string title string - hook Hook + parts []string } removals := []removal{} - hasHooks := false for _, entity := range entities { offset, title, err := splitComponents(entity.Path) if err != nil { return err } - hook, err := NewHook(entity.Path, RemoveAction) - if err != nil { - return err - } - if err := hook.Run(HookPre); err != nil { - return err - } - if hook.enabled { - hasHooks = true - } - removals = append(removals, removal{parts: offset, title: title, hook: hook}) + removals = append(removals, removal{parts: offset, title: title}) } - err := t.change(func(c Context) error { + return t.change(func(c Context) error { for _, entity := range removals { if ok := c.removeEntity(entity.parts, entity.title); !ok { return errors.New("failed to remove entity") @@ -315,15 +278,4 @@ func (t *Transaction) RemoveAll(entities []Entity) error { } return nil }) - if err != nil { - return err - } - if hasHooks { - for _, entity := range removals { - if err := entity.hook.Run(HookPost); err != nil { - return err - } - } - } - return nil } diff --git a/internal/backend/hooks.go b/internal/backend/hooks.go @@ -1,83 +0,0 @@ -// Package backend handles kdbx interactions -package backend - -import ( - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "git.sr.ht/~enckse/lockbox/internal/config" - "git.sr.ht/~enckse/lockbox/internal/platform" -) - -type ( - // HookMode are hook operations the user can tie to - HookMode string - // Hook represents a runnable user-defined hook - Hook struct { - path string - mode ActionMode - scripts []string - enabled bool - } -) - -const ( - internalHookEnv = "___HOOK___CALLED___" - // HookPre are triggers BEFORE an action is performed on an entity - HookPre HookMode = "pre" - // HookPost are triggers AFTER an action is performed on an entity - HookPost HookMode = "post" -) - -// NewHook will create a new hook type -func NewHook(path string, a ActionMode) (Hook, error) { - enabled := config.EnvHooksEnabled.Get() - if !enabled || os.Getenv(internalHookEnv) != "" { - return Hook{enabled: false}, nil - } - if strings.TrimSpace(path) == "" { - return Hook{}, errors.New("empty path is not allowed for hooks") - } - dir := config.EnvHookDir.Get() - if dir == "" { - return Hook{enabled: false}, nil - } - if !platform.PathExists(dir) { - return Hook{}, errors.New("hook directory does NOT exist") - } - entries, err := os.ReadDir(dir) - if err != nil { - return Hook{}, err - } - scripts := []string{} - for _, e := range entries { - if e.IsDir() { - return Hook{}, errors.New("found subdirectory in hookdir") - } - scripts = append(scripts, filepath.Join(dir, e.Name())) - } - return Hook{path: path, mode: a, enabled: len(scripts) > 0, scripts: scripts}, nil -} - -// Run will execute any scripts configured as hooks -func (h Hook) Run(mode HookMode) error { - if !h.enabled { - return nil - } - env := os.Environ() - env = append(env, fmt.Sprintf("%s=1", internalHookEnv)) - for _, s := range h.scripts { - c := exec.Command(s, string(mode), string(h.mode), h.path) - c.Stdout = os.Stdout - c.Stderr = os.Stderr - c.Env = env - if err := c.Run(); err != nil { - return err - } - } - return nil -} diff --git a/internal/backend/hooks_test.go b/internal/backend/hooks_test.go @@ -1,71 +0,0 @@ -package backend_test - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "git.sr.ht/~enckse/lockbox/internal/backend" - "git.sr.ht/~enckse/lockbox/internal/config/store" -) - -func TestHooks(t *testing.T) { - store.Clear() - h, err := backend.NewHook("a", backend.InsertAction) - if err != nil { - t.Errorf("invalid error: %v", err) - } - if err := h.Run(backend.HookPre); err != nil { - t.Errorf("invalid error: %v", err) - } - if _, err := backend.NewHook("", backend.InsertAction); err.Error() != "empty path is not allowed for hooks" { - t.Errorf("wrong error: %v", err) - } - store.SetString("LOCKBOX_HOOKS_DIRECTORY", "is_garbage") - if _, err := backend.NewHook("b", backend.InsertAction); err.Error() != "hook directory does NOT exist" { - t.Errorf("wrong error: %v", err) - } - testPath := filepath.Join("testdata", "hooks.kdbx") - os.RemoveAll(testPath) - if err := os.MkdirAll(testPath, 0o755); err != nil { - t.Errorf("failed, mkdir: %v", err) - } - store.SetString("LOCKBOX_HOOKS_DIRECTORY", testPath) - h, err = backend.NewHook("a", backend.InsertAction) - if err != nil { - t.Errorf("invalid error: %v", err) - } - if err := h.Run(backend.HookPre); err != nil { - t.Errorf("invalid error: %v", err) - } - sub := filepath.Join(testPath, "subdir") - if err := os.MkdirAll(sub, 0o755); err != nil { - t.Errorf("failed, mkdir sub: %v", err) - } - if _, err := backend.NewHook("b", backend.InsertAction); err.Error() != "found subdirectory in hookdir" { - t.Errorf("wrong error: %v", err) - } - if err := os.RemoveAll(sub); err != nil { - t.Errorf("failed rmdir: %v", err) - } - script := filepath.Join(testPath, "testscript") - if err := os.WriteFile(script, []byte{}, 0o644); err != nil { - t.Errorf("unable to write script: %v", err) - } - h, err = backend.NewHook("a", backend.InsertAction) - if err != nil { - t.Errorf("invalid error: %v", err) - } - if err := h.Run(backend.HookPre); strings.Contains("fork/exec", err.Error()) { - t.Errorf("wrong error: %v", err) - } - store.SetBool("LOCKBOX_HOOKS_ENABLED", false) - h, err = backend.NewHook("a", backend.InsertAction) - if err != nil { - t.Errorf("invalid error: %v", err) - } - if err := h.Run(backend.HookPre); err != nil { - t.Errorf("wrong error: %v", err) - } -} diff --git a/internal/config/core.go b/internal/config/core.go @@ -22,7 +22,6 @@ const ( jsonCategory = "JSON_" credsCategory = "CREDENTIALS_" defaultCategory = "DEFAULTS_" - hookCategory = "HOOKS_" environmentPrefix = "LOCKBOX_" commandArgsExample = "[cmd args...]" fileExample = "<file>" diff --git a/internal/config/toml_test.go b/internal/config/toml_test.go @@ -251,7 +251,7 @@ func TestDefaultTOMLToLoadFile(t *testing.T) { if err := config.LoadConfigFile(file); err != nil { t.Errorf("invalid error: %v", err) } - if len(store.List()) != 27 { + if len(store.List()) != 25 { t.Errorf("invalid environment after load") } } diff --git a/internal/config/vars.go b/internal/config/vars.go @@ -69,14 +69,6 @@ var ( description: "Enable terminal colors.", }), }) - // EnvHooksEnabled indicates if hooks are enabled - EnvHooksEnabled = environmentRegister(EnvironmentBool{ - environmentDefault: newDefaultedEnvironment(true, - environmentBase{ - key: hookCategory + "ENABLED", - description: "Enable hooks", - }), - }) // EnvInteractive indicates if operating in interactive mode EnvInteractive = environmentRegister(EnvironmentBool{ environmentDefault: newDefaultedEnvironment(true, @@ -118,18 +110,6 @@ var ( flags: []stringsFlags{canExpandFlag}, }, }) - // EnvHookDir is the directory of hooks to execute - EnvHookDir = environmentRegister(EnvironmentString{ - environmentStrings: environmentStrings{ - environmentDefault: newDefaultedEnvironment("", - environmentBase{ - key: hookCategory + "DIRECTORY", - description: "The path to a directory of hooks to execute on actions against the database.", - }), - allowed: []string{"<directory>"}, - flags: []stringsFlags{canDefaultFlag, canExpandFlag}, - }, - }) // EnvClipCopy allows overriding the clipboard copy command EnvClipCopy = environmentRegister(EnvironmentArray{ environmentStrings: environmentStrings{ diff --git a/internal/config/vars_test.go b/internal/config/vars_test.go @@ -28,10 +28,6 @@ func TestColorSetting(t *testing.T) { checkYesNo("LOCKBOX_COLOR_ENABLED", t, config.EnvColorEnabled, true) } -func TestNoHook(t *testing.T) { - checkYesNo("LOCKBOX_HOOKS_ENABLED", t, config.EnvHooksEnabled, true) -} - func TestInteractiveSetting(t *testing.T) { checkYesNo("LOCKBOX_INTERACTIVE", t, config.EnvInteractive, true) }