diff --git a/overlays/default.nix b/overlays/default.nix index cf1c8944..90055d11 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -79,6 +79,10 @@ in { ]; }); + wl-clipboard = addPatches prev.wl-clipboard [./wl-clipboard-secrets.diff]; + + pass = addPatches prev.pass [./pass-wlclipboard-secret.diff]; + hydra_unstable = (prev.hydra_unstable.overrideAttrs (old: { version = "2024-05-23"; diff --git a/overlays/pass-wlclipboard-secret.diff b/overlays/pass-wlclipboard-secret.diff new file mode 100644 index 00000000..0383e742 --- /dev/null +++ b/overlays/pass-wlclipboard-secret.diff @@ -0,0 +1,46 @@ +diff --git a/src/password-store.sh b/src/password-store.sh +index a0dcf2e..0723cf7 100755 +--- a/src/password-store.sh ++++ b/src/password-store.sh +@@ -156,7 +156,7 @@ check_sneaky_paths() { + + clip() { + if [[ -n $WAYLAND_DISPLAY ]]; then +- local copy_cmd=( wl-copy ) ++ local copy_cmd=( wl-copy -t text/secret ) + local paste_cmd=( wl-paste -n ) + if [[ $X_SELECTION == primary ]]; then + copy_cmd+=( --primary ) +@@ -170,31 +170,8 @@ clip() { + else + die "Error: No X11 or Wayland display detected" + fi +- local sleep_argv0="password store sleep on display $display_name" +- +- # This base64 business is because bash cannot store binary data in a shell +- # variable. Specifically, it cannot store nulls nor (non-trivally) store +- # trailing new lines. +- pkill -f "^$sleep_argv0" 2>/dev/null && sleep 0.5 +- local before="$("${paste_cmd[@]}" 2>/dev/null | $BASE64)" + echo -n "$1" | "${copy_cmd[@]}" || die "Error: Could not copy data to the clipboard" +- ( +- ( exec -a "$sleep_argv0" bash <<<"trap 'kill %1' TERM; sleep '$CLIP_TIME' & wait" ) +- local now="$("${paste_cmd[@]}" | $BASE64)" +- [[ $now != $(echo -n "$1" | $BASE64) ]] && before="$now" +- +- # It might be nice to programatically check to see if klipper exists, +- # as well as checking for other common clipboard managers. But for now, +- # this works fine -- if qdbus isn't there or if klipper isn't running, +- # this essentially becomes a no-op. +- # +- # Clipboard managers frequently write their history out in plaintext, +- # so we axe it here: +- qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory &>/dev/null +- +- echo "$before" | $BASE64 -d | "${copy_cmd[@]}" +- ) >/dev/null 2>&1 & disown +- echo "Copied $2 to clipboard. Will clear in $CLIP_TIME seconds." ++ echo "Copied $2 to clipboard." + } + + qrcode() { diff --git a/overlays/wl-clipboard-secrets.diff b/overlays/wl-clipboard-secrets.diff new file mode 100644 index 00000000..14c6b7fe --- /dev/null +++ b/overlays/wl-clipboard-secrets.diff @@ -0,0 +1,61 @@ +diff --git a/src/util/string.c b/src/util/string.c +index bfea1de..445a869 100644 +--- a/src/util/string.c ++++ b/src/util/string.c +@@ -55,6 +55,12 @@ int mime_type_is_text(const char *mime_type) { + return basic || common || special; + } + ++int mime_type_is_sensitive(const char *mime_type) { ++ /* A heuristic to detect sensitive mime types */ ++ ++ return str_has_suffix(mime_type, "secret"); ++} ++ + int str_has_prefix(const char *string, const char *prefix) { + size_t prefix_length = strlen(prefix); + return strncmp(string, prefix, prefix_length) == 0; +diff --git a/src/util/string.h b/src/util/string.h +index 1477944..08307d1 100644 +--- a/src/util/string.h ++++ b/src/util/string.h +@@ -25,6 +25,7 @@ + typedef char * const *argv_t; + + int mime_type_is_text(const char *mime_type); ++int mime_type_is_sensitive(const char *mime_type); + + int str_has_prefix(const char *string, const char *prefix); + int str_has_suffix(const char *string, const char *suffix); +diff --git a/src/wl-paste.c b/src/wl-paste.c +index dee2fad..bd68567 100644 +--- a/src/wl-paste.c ++++ b/src/wl-paste.c +@@ -53,6 +53,7 @@ struct types { + int inferred_available; + int plain_text_utf8_available; + int plain_text_available; ++ int sensitive; + const char *having_explicit_as_prefix; + const char *any_text; + const char *any; +@@ -99,6 +100,9 @@ static struct types classify_offer_types(struct offer *offer) { + ) { + types.having_explicit_as_prefix = mime_type; + } ++ if (mime_type_is_sensitive(mime_type)) { ++ types.sensitive = 1; ++ } + } + return types; + } +@@ -319,7 +323,7 @@ static void selection_callback(struct offer *offer, int primary) { + wl_display_flush(wl_display); + + close(pipefd[1]); +- rc = run_paste_command(pipefd[0], "data"); ++ rc = run_paste_command(pipefd[0], types.sensitive == 1 ? "sensitive" : "data"); + if (!rc) { + if (options.watch) { + /* Try to cope without exiting completely */ + diff --git a/pkgs/pass-wofi/pass-wofi.sh b/pkgs/pass-wofi/pass-wofi.sh index 8c51f727..75667948 100644 --- a/pkgs/pass-wofi/pass-wofi.sh +++ b/pkgs/pass-wofi/pass-wofi.sh @@ -39,20 +39,24 @@ Fill" field=$(printf "$fields" | wofi -S dmenu) || field="password" fi +secret=0 + case "${field,,}" in - "password") - value="$(pass "$selected" | head -n 1)" && [ -n "$value" ] || \ - { notify-send "Error" "No password for $selected" -i error -t 6000; exit 3; } - ;; "username") value="$username" ;; "url") value="$url" ;; + "password") + value="$(pass "$selected" | head -n 1)" && [ -n "$value" ] || \ + { notify-send "Error" "No password for $selected" -i error -t 6000; exit 3; } + secret=1 + ;; "otp") value="$(pass otp "$selected")" || \ { notify-send "Error" "No OTP for $selected" -i error -t 6000; exit 3; } + secret=1 ;; "fill") password="$(pass "$selected" | head -n 1)" && [ -n "$password" ] || \ @@ -65,13 +69,23 @@ case "${field,,}" in if otp="$(pass otp "$selected")" && [ -n "$otp" ]; then field="OTP" value="$otp" + secret=1 fi ;; *) exit 4 esac + if [ -n "$value" ]; then - wl-copy "$value" - notify-send "Copied $field:" "$value" -i edit-copy -t 4000 + if [ "$secret" = 1 ]; then + mime="text/secret" + else + mime="text/plain" + fi + wl-copy -t "$mime" "$value" + prefix="${value:0:3}" + suffix="${value:3}" + censored_value="${prefix}${suffix//?/*}" + notify-send "Copied $field:" "$censored_value" -i edit-copy -t 4000 fi