feat(cli): TTY-aware return + OSC52 clipboard with terminal gating
This commit is contained in:
parent
06f4b87af1
commit
81122f8607
2 changed files with 74 additions and 0 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -144,6 +145,43 @@ func bwGet(run cmdRunner, env []string, field, name string) (string, error) {
|
|||
return run("bw", bwGetArgs(field, name), env)
|
||||
}
|
||||
|
||||
func returnMode(isTTY bool) string {
|
||||
if isTTY {
|
||||
return "clipboard"
|
||||
}
|
||||
return "stdout"
|
||||
}
|
||||
|
||||
// stdoutIsTTY reports whether stdout is a character device (a terminal).
|
||||
func stdoutIsTTY() bool {
|
||||
fi, err := os.Stdout.Stat()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.Mode()&os.ModeCharDevice != 0
|
||||
}
|
||||
|
||||
// osc52 returns the OSC 52 escape that makes the local terminal copy payload to
|
||||
// the system clipboard (works over SSH; no X11). osc52clear copies empty.
|
||||
func osc52(payload string) string {
|
||||
return "\x1b]52;c;" + base64.StdEncoding.EncodeToString([]byte(payload)) + "\a"
|
||||
}
|
||||
func osc52clear() string { return "\x1b]52;c;\a" }
|
||||
|
||||
// terminalAllowed gates OSC 52: only terminals known to honor clipboard writes,
|
||||
// else we'd dump the secret's base64 into scrollback on unsupported terminals.
|
||||
func terminalAllowed(term, termProgram string) bool {
|
||||
t := strings.ToLower(term)
|
||||
p := strings.ToLower(termProgram)
|
||||
for _, ok := range []string{"kitty", "alacritty", "foot", "wezterm", "ghostty", "tmux", "screen"} {
|
||||
if strings.Contains(t, ok) || strings.Contains(p, ok) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// xterm proper supports it only when the program is a known-good emulator.
|
||||
return false
|
||||
}
|
||||
|
||||
func vaultSetup(args []string) error { return fmt.Errorf("not implemented") }
|
||||
func vaultStatus(args []string) error { return fmt.Errorf("not implemented") }
|
||||
func vaultList(args []string) error { return fmt.Errorf("not implemented") }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -140,3 +141,38 @@ func TestBwUnlockReturnsSession(t *testing.T) {
|
|||
t.Fatalf("unlock argv = %v", last)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReturnMode(t *testing.T) {
|
||||
if returnMode(true) != "clipboard" || returnMode(false) != "stdout" {
|
||||
t.Fatal("returnMode wrong")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOSC52Encode(t *testing.T) {
|
||||
got := osc52("secret")
|
||||
want := "\x1b]52;c;" + base64.StdEncoding.EncodeToString([]byte("secret")) + "\a"
|
||||
if got != want {
|
||||
t.Fatalf("osc52 = %q want %q", got, want)
|
||||
}
|
||||
if osc52clear() != "\x1b]52;c;\a" {
|
||||
t.Fatalf("osc52clear wrong: %q", osc52clear())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminalAllowed(t *testing.T) {
|
||||
allow := []struct{ term, prog string }{
|
||||
{"xterm-kitty", ""}, {"alacritty", ""}, {"foot", ""}, {"tmux-256color", ""},
|
||||
{"screen-256color", ""}, {"xterm-256color", "WezTerm"}, {"xterm-256color", "ghostty"},
|
||||
}
|
||||
for _, c := range allow {
|
||||
if !terminalAllowed(c.term, c.prog) {
|
||||
t.Errorf("terminalAllowed(%q,%q) = false, want true", c.term, c.prog)
|
||||
}
|
||||
}
|
||||
deny := []struct{ term, prog string }{{"dumb", ""}, {"", ""}, {"vt100", ""}}
|
||||
for _, c := range deny {
|
||||
if terminalAllowed(c.term, c.prog) {
|
||||
t.Errorf("terminalAllowed(%q,%q) = true, want false", c.term, c.prog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue