lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 12a28cec60fb45fd70b6233c50b5afc015318da3
parent e16ccb77d092c455a30cfe40247bc9db47fc5a07
Author: Sean Enck <sean@ttypty.com>
Date:   Sun,  9 Oct 2022 10:29:06 -0400

allow override clipboard commands

Diffstat:
Minternal/inputs/env.go | 7++++++-
Minternal/platform/clipboard.go | 58+++++++++++++++++++++++++++++++++++++++++++++-------------
Minternal/platform/clipboard_test.go | 36+++++++++++++++++++++++++++++-------
3 files changed, 80 insertions(+), 21 deletions(-)

diff --git a/internal/inputs/env.go b/internal/inputs/env.go @@ -21,6 +21,7 @@ const ( interactiveEnv = prefixKey + "INTERACTIVE" readOnlyEnv = prefixKey + "READONLY" fieldTOTPEnv = prefixKey + "TOTP" + clipBaseEnv = prefixKey + "CLIP_" formatTOTPEnv = fieldTOTPEnv + "_FORMAT" keyModeEnv = prefixKey + "KEYMODE" keyEnv = prefixKey + "KEY" @@ -31,9 +32,13 @@ const ( // StoreEnv is the location of the filesystem store that lb is operating on. StoreEnv = prefixKey + "STORE" // ClipMaxEnv is the max time a value should be stored in the clipboard. - ClipMaxEnv = prefixKey + "CLIPMAX" + ClipMaxEnv = clipBaseEnv + "MAX" // ColorBetweenEnv is a comma-delimited list of times to color totp outputs (e.g. 0:5,30:35 which is the default). ColorBetweenEnv = fieldTOTPEnv + "_BETWEEN" + // ClipPasteEnv allows overriding the clipboard paste command + ClipPasteEnv = clipBaseEnv + "PASTE" + // ClipCopyEnv allows overriding the clipboard copy command + ClipCopyEnv = clipBaseEnv + "COPY" ) // EnvOrDefault will get the environment value OR default if env is not set. diff --git a/internal/platform/clipboard.go b/internal/platform/clipboard.go @@ -7,8 +7,10 @@ import ( "os" "os/exec" "strconv" + "strings" "github.com/enckse/lockbox/internal/inputs" + "github.com/google/shlex" ) const ( @@ -24,6 +26,30 @@ type ( } ) +func newClipboard(copying, pasting []string) (Clipboard, error) { + max := maxTime + useMax := os.Getenv(inputs.ClipMaxEnv) + if useMax != "" { + i, err := strconv.Atoi(useMax) + if err != nil { + return Clipboard{}, err + } + if i < 1 { + return Clipboard{}, errors.New("clipboard max time must be greater than 0") + } + max = i + } + return Clipboard{copying: copying, pasting: pasting, MaxTime: max}, nil +} + +func overrideCommand(v string) ([]string, error) { + value := inputs.EnvOrDefault(v, "") + if strings.TrimSpace(value) == "" { + return nil, nil + } + return shlex.Split(value) +} + // NewClipboard will retrieve the commands to use for clipboard operations. func NewClipboard() (Clipboard, error) { noClip, err := inputs.IsNoClipEnabled() @@ -33,22 +59,22 @@ func NewClipboard() (Clipboard, error) { if noClip { return Clipboard{}, errors.New("clipboard is off") } - sys, err := NewPlatform() + overridePaste, err := overrideCommand(inputs.ClipPasteEnv) if err != nil { return Clipboard{}, err } - max := maxTime - useMax := os.Getenv(inputs.ClipMaxEnv) - if useMax != "" { - i, err := strconv.Atoi(useMax) - if err != nil { - return Clipboard{}, err - } - if i < 1 { - return Clipboard{}, errors.New("clipboard max time must be greater than 0") - } - max = i + overrideCopy, err := overrideCommand(inputs.ClipCopyEnv) + if err != nil { + return Clipboard{}, err + } + if overrideCopy != nil && overridePaste != nil { + return newClipboard(overrideCopy, overridePaste) + } + sys, err := NewPlatform() + if err != nil { + return Clipboard{}, err } + var copying []string var pasting []string switch sys { @@ -67,7 +93,13 @@ func NewClipboard() (Clipboard, error) { default: return Clipboard{}, errors.New("clipboard is unavailable") } - return Clipboard{copying: copying, pasting: pasting, MaxTime: max}, nil + if overridePaste != nil { + pasting = overridePaste + } + if overrideCopy != nil { + copying = overrideCopy + } + return newClipboard(copying, pasting) } // CopyTo will copy to clipboard, if non-empty will clear later. diff --git a/internal/platform/clipboard_test.go b/internal/platform/clipboard_test.go @@ -9,7 +9,7 @@ import ( ) func TestNoClipboard(t *testing.T) { - os.Setenv("LOCKBOX_CLIPMAX", "") + os.Setenv("LOCKBOX_CLIP_MAX", "") os.Setenv("LOCKBOX_NOCLIP", "yes") _, err := platform.NewClipboard() if err == nil || err.Error() != "clipboard is off" { @@ -20,7 +20,7 @@ func TestNoClipboard(t *testing.T) { func TestMaxTime(t *testing.T) { os.Setenv("LOCKBOX_NOCLIP", "no") os.Setenv("LOCKBOX_PLATFORM", string(platform.LinuxWayland)) - os.Setenv("LOCKBOX_CLIPMAX", "") + os.Setenv("LOCKBOX_CLIP_MAX", "") c, err := platform.NewClipboard() if err != nil { t.Errorf("invalid clipboard: %v", err) @@ -28,7 +28,7 @@ func TestMaxTime(t *testing.T) { if c.MaxTime != 45 { t.Error("invalid default") } - os.Setenv("LOCKBOX_CLIPMAX", "1") + os.Setenv("LOCKBOX_CLIP_MAX", "1") c, err = platform.NewClipboard() if err != nil { t.Errorf("invalid clipboard: %v", err) @@ -36,12 +36,12 @@ func TestMaxTime(t *testing.T) { if c.MaxTime != 1 { t.Error("invalid default") } - os.Setenv("LOCKBOX_CLIPMAX", "-1") + os.Setenv("LOCKBOX_CLIP_MAX", "-1") _, err = platform.NewClipboard() if err == nil || err.Error() != "clipboard max time must be greater than 0" { t.Errorf("invalid max time error: %v", err) } - os.Setenv("LOCKBOX_CLIPMAX", "$&(+") + os.Setenv("LOCKBOX_CLIP_MAX", "$&(+") _, err = platform.NewClipboard() if err == nil || err.Error() != "strconv.Atoi: parsing \"$&(+\": invalid syntax" { t.Errorf("invalid max time error: %v", err) @@ -50,7 +50,7 @@ func TestMaxTime(t *testing.T) { func TestClipboardInstances(t *testing.T) { os.Setenv("LOCKBOX_NOCLIP", "no") - os.Setenv("LOCKBOX_CLIPMAX", "") + os.Setenv("LOCKBOX_CLIP_MAX", "") for _, item := range []platform.System{platform.MacOS, platform.LinuxWayland, platform.LinuxX, platform.WindowsLinux} { os.Setenv("LOCKBOX_PLATFORM", string(item)) _, err := platform.NewClipboard() @@ -60,7 +60,8 @@ func TestClipboardInstances(t *testing.T) { } } -func TestArgs(t *testing.T) { +func TestArgsOverride(t *testing.T) { + os.Setenv("LOCKBOX_CLIP_PASTE", "abc xyz 111") os.Setenv("LOCKBOX_PLATFORM", string(platform.WindowsLinux)) c, _ := platform.NewClipboard() cmd, args := c.Args(true) @@ -68,6 +69,27 @@ func TestArgs(t *testing.T) { t.Error("invalid parse") } cmd, args = c.Args(false) + if cmd != "abc" || len(args) != 2 || args[0] != "xyz" || args[1] != "111" { + t.Error("invalid parse") + } + os.Setenv("LOCKBOX_CLIP_COPY", "zzz lll 123") + c, _ = platform.NewClipboard() + cmd, args = c.Args(true) + if cmd != "zzz" || len(args) != 2 || args[0] != "lll" || args[1] != "123" { + t.Error("invalid parse") + } + cmd, args = c.Args(false) + if cmd != "abc" || len(args) != 2 || args[0] != "xyz" || args[1] != "111" { + t.Error("invalid parse") + } + os.Setenv("LOCKBOX_CLIP_PASTE", "") + os.Setenv("LOCKBOX_CLIP_COPY", "") + c, _ = platform.NewClipboard() + cmd, args = c.Args(true) + if cmd != "clip.exe" || len(args) != 0 { + t.Error("invalid parse") + } + cmd, args = c.Args(false) if cmd != "powershell.exe" || len(args) != 2 || args[0] != "-command" || args[1] != "Get-Clipboard" { fmt.Println(args) t.Error("invalid parse")