From reto at labrat.space Sat Jun 1 12:11:34 2024 From: reto at labrat.space (Reto) Date: Sat, 1 Jun 2024 14:11:34 +0200 Subject: Resolve .gpg-id files must end in a newline to be recognized In-Reply-To: <875xuv9t3b.fsf@freakingpenguin.com> References: <875xuv9t3b.fsf@freakingpenguin.com> Message-ID: On Thu, May 30, 2024 at 12:12:40PM GMT, Richard Sent wrote: > To create subdirectories in .password-store that are encrypted > using a different gpg key, the user needs to create a .gpg-id file in > that subdirectory. No they are supposed to use the corresponding `pass init` command. That this happens to be a file on disc in the store is an implementation detail. pass(1): > init [ --path=sub-folder, -p sub-folder ] gpg-id... > [...] > --path or -p is specified, along with an argument, a specific gpg-id or set of gpg-ids > is assigned for that specific sub folder of the password store. If only one gpg-id is given, > and it is an empty string, then the current .gpg-id file for > [...] If you mess with implementation details it's on you to do it correctly ;) From richard at freakingpenguin.com Sat Jun 1 23:51:34 2024 From: richard at freakingpenguin.com (Richard Sent) Date: Sat, 01 Jun 2024 19:51:34 -0400 Subject: Resolve .gpg-id files must end in a newline to be recognized In-Reply-To: (reto@labrat.space's message of "Sat, 1 Jun 2024 14:11:34 +0200") References: <875xuv9t3b.fsf@freakingpenguin.com> Message-ID: <87wmn86x2x.fsf@freakingpenguin.com> Hi Reto! Reto writes: > No they are supposed to use the corresponding `pass init` command. > That this happens to be a file on disc in the store is an implementation detail. > pass(1): > >> init [ --path=sub-folder, -p sub-folder ] gpg-id... >> [...] >> --path or -p is specified, along with an argument, a specific gpg-id or set of gpg-ids >> is assigned for that specific sub folder of the password store. If only one gpg-id is given, >> and it is an empty string, then the current .gpg-id file for >> [...] > > > If you mess with implementation details it's on you to do it correctly ;) That's fair! My mistake for missing the mention of subfolders. :) I still believe basic error handling is appropriate here. On a related note, the manual seems to treat sub-folder, sub folder, subfolder, and sub directory interchangeably. Should we standardize on just one for consistency? > init > ...If --path or -p is specified, along with an argument, a specific > gpg-id or set of gpg-ids is assigned for that specific sub folder of > the password store. If only one gpg-id is given, and it is an empty > string, then the current .gpg-id file for the specified sub-folder > > ls subfolder > List names of passwords inside the tree at subfolder by using the > tree(1) program. This command is alternatively named list. > ~/.password-store/.gpg-id > ... If this file exists in any sub directories, passwords inside > those sub directories are encrypted using those keys. This should > be set using the init command. Hey what do you know it mentions the init command here ^ too oops. -- Take it easy, Richard Sent Making my computer weirder one commit at a time. From mike at vorburger.ch Sun Jun 2 12:24:22 2024 From: mike at vorburger.ch (Michael Vorburger) Date: Sun, 2 Jun 2024 14:24:22 +0200 Subject: SIGPIPE Message-ID: Hi, I've run into a SIGPIPE (exit code 141) with a particular password containing many special characters. Breaking up the line as follows somehow fixes the problem for me: https://github.com/vorburger/password-store/pull/2/files https://patch-diff.githubusercontent.com/raw/vorburger/password-store/pull/2.diff The same is also attached to this email, as output by "git format-patch -1". Would you like to merge this? Tx, M. _______________________ Michael Vorburger http://www.vorburger.ch -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-Fix-a-SIGPIPE-exit-code-141-bug-I-ve-run-into-with-a.patch Type: text/x-patch Size: 1126 bytes Desc: not available URL: From casselt at luis.uni-hannover.de Mon Jun 3 08:18:38 2024 From: casselt at luis.uni-hannover.de (Torsten Casselt) Date: Mon, 03 Jun 2024 10:18:38 +0200 Subject: [extern] SIGPIPE In-Reply-To: References: Message-ID: Hi Michael, the bug happens because of the combination of tail and head with a pipe. I already sent a patch in over a month ago that fixes this in one line. Could you please test if this also fixes your issue? I attached the patch again. Thanks, Torsten Am Sonntag, dem 02.06.2024 um 14:24 +0200 schrieb Michael Vorburger: > Hi, > > I've run into a SIGPIPE (exit code 141) with a particular password > containing many special characters. Breaking up the line as follows > somehow fixes the problem for me: > > https://github.com/vorburger/password-store/pull/2/files > > https://patch-diff.githubusercontent.com/raw/vorburger/password-store/pull/2.diff > > The same is also attached to this email, as output by "git format- > patch -1". > > Would you like to merge this? > > Tx, > M. > _______________________ > Michael Vorburger > http://www.vorburger.ch -------------- next part -------------- A non-text attachment was scrubbed... Name: large-file-pipefail-fix.patch Type: text/x-patch Size: 891 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: smime.p7s Type: application/pkcs7-signature Size: 7679 bytes Desc: not available URL: From matthias.gerstner at nefkom.net Wed Jun 12 13:16:42 2024 From: matthias.gerstner at nefkom.net (matthias.gerstner at nefkom.net) Date: Wed, 12 Jun 2024 15:16:42 +0200 Subject: [PATCH] Only force 'tree' color output if stdout is a terminal Message-ID: <20240612131706.11385-1-matthias.gerstner@nefkom.net> From: Matthias Gerstner Currently something like `pass ls | cat` will still produce color escape sequences. This is problematic when trying to parse the output, or otherwise storing the output in a file etc. --- src/password-store.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/password-store.sh b/src/password-store.sh index 22e818f..dcf7d47 100755 --- a/src/password-store.sh +++ b/src/password-store.sh @@ -19,6 +19,7 @@ CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-45}" GENERATED_LENGTH="${PASSWORD_STORE_GENERATED_LENGTH:-25}" CHARACTER_SET="${PASSWORD_STORE_CHARACTER_SET:-[:punct:][:alnum:]}" CHARACTER_SET_NO_SYMBOLS="${PASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS:-[:alnum:]}" +[[ -t 1 ]] && TREE_COLORIZE="-C" unset GIT_DIR GIT_WORK_TREE GIT_NAMESPACE GIT_INDEX_FILE GIT_INDEX_VERSION GIT_OBJECT_DIRECTORY GIT_COMMON_DIR export GIT_CEILING_DIRECTORIES="$PREFIX/.." @@ -402,7 +403,7 @@ cmd_show() { else echo "${path%\/}" fi - tree -N -C -l --noreport "$PREFIX/$path" 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g' # remove .gpg at end of line, but keep colors + tree -N $TREE_COLORIZE -l --noreport "$PREFIX/$path" 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g' # remove .gpg at end of line, but keep colors elif [[ -z $path ]]; then die "Error: password store is empty. Try \"pass init\"." else @@ -414,7 +415,7 @@ cmd_find() { [[ $# -eq 0 ]] && die "Usage: $PROGRAM $COMMAND pass-names..." IFS="," eval 'echo "Search Terms: $*"' local terms="*$(printf '%s*|*' "$@")" - tree -N -C -l --noreport -P "${terms%|*}" --prune --matchdirs --ignore-case "$PREFIX" 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g' + tree -N $TREE_COLORIZE -l --noreport -P "${terms%|*}" --prune --matchdirs --ignore-case "$PREFIX" 3>&- | tail -n +2 | sed -E 's/\.gpg(\x1B\[[0-9]+m)?( ->|$)/\1\2/g' } cmd_grep() { -- 2.43.2 From semseysandor at gmail.com Fri Jun 14 08:26:33 2024 From: semseysandor at gmail.com (=?UTF-8?Q?S=C3=A1ndor_Semsey?=) Date: Fri, 14 Jun 2024 10:26:33 +0200 Subject: [PATCH] Bash completion for extensions Message-ID: Bash completion for extensions doesn't work. Basically `pass.bash-completion` tries to call the extension's completion function, but it's never sourced, so the function is not defined, therefore not called. There were a few attempts to fix this bug, but none of them got merged: https://lists.zx2c4.com/pipermail/password-store/2019-January/003556.html https://lists.zx2c4.com/pipermail/password-store/2019-February/003563.html https://lists.zx2c4.com/pipermail/password-store/2019-April/003639.html https://lists.zx2c4.com/pipermail/password-store/2022-December/004681.html So, I'm submitting a new patch. It's simple and uses `_completion_loader` from Bash to auto-load completion functions (concept borrowed from git). Also I documented the (currently undocumented) PASSWORD_STORE_EXTENSION_COMMANDS environment variable. >From 9c17e2687b03bbf79359a7181c9639954a4d375b Mon Sep 17 00:00:00 2001 From: Sandor Semsey Date: Sun, 9 Jun 2024 14:43:56 +0200 Subject: [PATCH 1/2] bash completion: load extension completions dynamically diff --git a/src/completion/pass.bash-completion b/src/completion/pass.bash-completion index 2d23cbf..443b17c 100644 --- a/src/completion/pass.bash-completion +++ b/src/completion/pass.bash-completion @@ -87,7 +87,8 @@ _pass() local commands="init ls find grep show insert generate edit rm mv cp git help version ${PASSWORD_STORE_EXTENSION_COMMANDS[*]}" if [[ $COMP_CWORD -gt 1 ]]; then local lastarg="${COMP_WORDS[$COMP_CWORD-1]}" - case "${COMP_WORDS[1]}" in + local command="${COMP_WORDS[1]}" + case "${command}" in init) if [[ $lastarg == "-p" || $lastarg == "--path" ]]; then _pass_complete_folders @@ -123,18 +124,25 @@ _pass() git) COMPREPLY+=($(compgen -W "init push pull config log reflog rebase" -- ${cur})) ;; + *) + # To add completion for an extension command define a function like this: + # __password_store_extension_complete_() { + # COMPREPLY+=($(compgen -W "-o --option" -- ${cur})) + # _pass_complete_entries 1 + # } + # + # and add the command to the $PASSWORD_STORE_EXTENSION_COMMANDS array + local extension_completion_func="__password_store_extension_complete_${command}" + if [[ " ${PASSWORD_STORE_EXTENSION_COMMANDS[*]} " == *" ${command} "* ]]; then + if ! type "${extension_completion_func}" &> /dev/null && type _completion_loader &> /dev/null; then + _completion_loader "pass-${command}" + fi + if type "${extension_completion_func}" &> /dev/null; then + "${extension_completion_func}" + fi + fi + ;; esac - - # To add completion for an extension command define a function like this: - # __password_store_extension_complete_() { - # COMPREPLY+=($(compgen -W "-o --option" -- ${cur})) - # _pass_complete_entries 1 - # } - # - # and add the command to the $PASSWORD_STORE_EXTENSION_COMMANDS array - if [[ " ${PASSWORD_STORE_EXTENSION_COMMANDS[*]} " == *" ${COMP_WORDS[1]} "* ]] && type "__password_store_extension_complete_${COMP_WORDS[1]}" &> /dev/null; then - "__password_store_extension_complete_${COMP_WORDS[1]}" - fi else COMPREPLY+=($(compgen -W "${commands}" -- ${cur})) _pass_complete_entries 1 >From a98cbd4ca2793be4f87abd2ec3f35fc2d669cf06 Mon Sep 17 00:00:00 2001 From: Sandor Semsey Date: Wed, 12 Jun 2024 03:42:47 +0200 Subject: [PATCH 2/2] man: document PASSWORD_STORE_EXTENSION_COMMANDS diff --git a/man/pass.1 b/man/pass.1 index a555dcb..19a1dba 100644 --- a/man/pass.1 +++ b/man/pass.1 @@ -456,6 +456,9 @@ This environment variable must be set to "true" for extensions to be enabled. The location to look for executable extension files, by default \fIPASSWORD_STORE_DIR/.extensions\fP. .TP +.I PASSWORD_STORE_EXTENSION_COMMANDS +Array to hold commands added by extensions. +.TP .I PASSWORD_STORE_SIGNING_KEY If this environment variable is set, then all \fB.gpg-id\fP files and non-system extension files must be signed using a detached signature using the GPG key specified by the full 40 character From prajwalnadig21 at gmail.com Sun Jun 23 09:33:33 2024 From: prajwalnadig21 at gmail.com (Prajwal S N) Date: Sun, 23 Jun 2024 11:33:33 +0200 Subject: Add feature to delete password from clipmenu (clipboard manager) Message-ID: Hi everyone, I've been using pass for a while now, and also use clipmenu, a clipboard manager that integrates with dmenu. I know quite a few people with the same setup, and strongly suspect that there are a lot more out there that I do not know. I recently discovered that when pass copies the password to the clipboard, it is written into clipmenu's history, and persists there. This is understandably a security concern, since clipmenu stores the history file in plaintext in the ~/.cache directory. The fix for this is quite straightforward - we already have support for removing clipboard entries from Klipper on KDE[1], and we would have to add a line below that to do the same for clipmenu: clipdel -d "$(echo -n "$1")" Would a patch for this be accepted? Or would you have any other ideas for how we can mitigate this issue? Thanks and regards, Prajwal [1]: https://git.zx2c4.com/password-store/tree/src/password-store.sh#n193 From ac at alexeicolin.com Mon Jun 24 03:21:18 2024 From: ac at alexeicolin.com (Alexei Colin) Date: Mon, 24 Jun 2024 03:21:18 +0000 Subject: [PATCH] importer: protonpass2json: from ProtonPass JSON Message-ID: <20240624032019.15076-1-ac@alexeicolin.com> Signed-off-by: Alexei Colin --- Please accept yet another liberator script. I should have added this to pass-import extension instead of this standalone script, but didn't realize it existed until after was done. Please consider a simple edit to the website in the section "Migrating to pass": it would help to mention the pass-import extension in this section specifically, in addition to its listing in the extensions section above: To free password data from the clutches of other (bloated) password managers, various users have come up with different password store organizations that work best for them. The [pass-import] extension supports importing passwords from many such programs. Alternatively, some users have contributed standalone scripts to help with the import: One more correction to the website: "browserpass: Chrome plugin" -- browser pass supports both Firefox and Chrome. Also, it seems to be more mature compared to passff, so it is worth pointing Firefox users to it. Thank you. contrib/importers/protonpass2json2pass.py | 242 ++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100755 contrib/importers/protonpass2json2pass.py diff --git a/contrib/importers/protonpass2json2pass.py b/contrib/importers/protonpass2json2pass.py new file mode 100755 index 0000000..ad9cd21 --- /dev/null +++ b/contrib/importers/protonpass2json2pass.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 + +# Copyright 2015 David Francoeur +# Copyright 2017 Nathan Sommer +# Copyright 2024 Alexei Colin +# +# This file is licensed under the GPLv2+. Please see COPYING for more +# information. +# +# ProtonPass exports to JSON. Items are grouped into vaults. Each item is +# imported into a file at path // +# or, if username is blank, then a file at path / +# with the following content: +# +# +# +# user: +# url: +# passkeys: +# +# +# notes: +# +# Any missing fields will be omitted from the entry. +# Items that have multiple URLs have multiple 'url:' lines. +# +# The two-factor authentication token (TOTP) URI is imported in the format +# expected by pass-otp extension. Passkeys are imported as a serialized JSON +# object (a list of dictionaries); a blank line terminates the object. +# +# Notes (rather than login type items) are imported as files without a +# password (first line is empty) and with only the 'notes:' lines. +# Multiline notes start on the line following the 'notes:' header line. +# +# The vault directory name in the path can be converted to lowercase using the +# --to-lower switch. +# +# Items can be filtered by vault name or by item type by passing regular +# expressions to --include-vaults, --exclude-vaults, --include-types, +# --exclude-types. The include filters take precedence over the exclude +# filters. Use the start and end markers to match a whole word rather +# than a prefix or suffix, e.g. '^foo$'. +# +# Default usage: ./protonpass2json2pass.py input.json +# +# To see the full usage: ./protonpass2json2pass.py -h + +import argparse +import json +import os +import re +from subprocess import Popen, PIPE +import sys + + +class UsageArgParser(argparse.ArgumentParser): + """ + Custom ArgumentParser class which prints the full usage message if the + input file is not provided. + """ + def error(self, message): + print(message, file=sys.stderr) + self.print_help() + sys.exit(2) + + +def pass_import_entry(path, data): + """Import new password entry to password-store using pass insert command""" + proc = Popen(['pass', 'insert', '--multiline', path], stdin=PIPE, + stdout=PIPE) + proc.communicate(data.encode('utf8')) + proc.wait() + + +def confirmation(prompt): + """ + Ask the user for 'y' or 'n' confirmation and return a boolean indicating + the user's choice. Returns True if the user simply presses enter. + """ + + prompt = '{0} {1} '.format(prompt, '(Y/n)') + + while True: + user_input = input(prompt) + + if len(user_input) > 0: + first_char = user_input.lower()[0] + else: + first_char = 'y' + + if first_char == 'y': + return True + elif first_char == 'n': + return False + + print('Please enter y or n') + +def sanitize_path(path): + """ Replace characters that cannot be part of a file path""" + return path.replace("/", "_") + +def insert_file_contents(filename, args): + """ Read the file and insert each entry """ + + entries = [] + + with open(filename, 'rb') as fin: + jobj = json.load(fin) + vaults = jobj['vaults'] + for _, vault in vaults.items(): + vault_name = vault['name'] + if not eval_filter(vault_name, args.include_vaults, args.exclude_vaults): + continue + + vault_path = sanitize_path(vault_name) + if args.to_lower: + vault_path = vault_path.lower() + for item in vault['items']: + try: + item_path, data = parse_item(item, + args.include_types, args.exclude_types) + except: + print("Failed to parse item:", file=sys.stderr) + print(json.dumps(item, indent=4), file=sys.stderr) + raise + + if item_path: + path = os.path.join(vault_path, item_path) + entries.append((path, data)) + + if len(entries) == 0: + print('No entries to import') + return + + print('Entries to import:') + for (path, data) in entries: + print(path) + if confirmation('Proceed?'): + for (path, data) in entries: + pass_import_entry(path, data) + print(path, 'imported!') + + +def parse_item(item, include_types=None, exclude_types=None): + item_data = item['data'] + item_type = item_data['type'] + metadata = item_data['metadata'] + content = item_data['content'] + + if not eval_filter(item_type, include_types, exclude_types): + return None, None + + item_name = metadata['name'] + + fields = "" + if content: + password = content['password'] + + # pass-otp extension expects line containing just the otpauth:// URI + otp_uri = content['totpUri'] + if otp_uri: + fields += '{}\n'.format(otp_uri) + + username = content['username'] + if username: + fields += 'user: {}\n'.format(username) + + for url in content['urls']: + fields += 'url: {}\n'.format(url) + + passkeys = content['passkeys'] + if passkeys: + fields += 'passkeys:\n' + fields += json.dumps(passkeys, indent=4) + fields += '\n' + else: + password, username = "", "" + + note = metadata['note'] + if note: + fields += 'notes:' + if '\n' in note: # start multiline conent on a new line + fields += '\n' # don't use os.linesep to keep content deterministic + else: + fields += ' ' + fields += note + '\n' + + # first line must be password, will be blank for notes or if no password + data = '{}\n'.format(password) + data += fields + + path = sanitize_path(item_name) + if username: + path = os.path.join(path, sanitize_path(username)) + return path, data + + +def eval_filter(value, include_re, exclude_re): + """Returns whether item should be included based on regexp filters + + Include filter takes precedence over exclude filter.""" + if include_re: + return re.match(include_re, value) + + if exclude_re: + return not re.match(exclude_re, value) + return True + + +def main(): + description = 'Import pass entries from an exported ProtonPass JSON file.' + parser = UsageArgParser(description=description) + + parser.add_argument('--include-vaults', + help="Regular expression to filter out items by type, " + "takes precedence over exclude " + "(e.g. '^Personal$' to include only the vault named 'Personal')") + parser.add_argument('--exclude-vaults', + help="Regular expression to filter out vaults by name " + "(e.g. '^Notes' to exclude vaults that begin with 'Notes')") + + parser.add_argument('--include-types', + help="Regular expression to filter items by type, " + "takes precedence over exclude " + "(e.g. '^(login|notes)$' to include only logins and notes)") + parser.add_argument('--exclude-types', + help="Regular expression to filter out items by type " + \ + "(e.g. '^note$' to exclude all notes)") + + parser.add_argument('--to-lower', action='store_true', + help="Convert names to lower case in file paths") + parser.add_argument('input_file', help="The JSON file to read from") + + args = parser.parse_args() + + input_file = args.input_file + print("File to read:", input_file) + insert_file_contents(input_file, args) + + +if __name__ == '__main__': + main() -- 2.45.2 From mcepl at cepl.eu Mon Jun 24 11:31:06 2024 From: mcepl at cepl.eu (=?utf-8?q?Mat=C4=9Bj_Cepl?=) Date: Mon, 24 Jun 2024 13:31:06 +0200 Subject: Add feature to delete password from clipmenu (clipboard manager) In-Reply-To: References: Message-ID: On Sun Jun 23, 2024 at 11:33 AM CEST, Prajwal S N wrote: > I recently discovered that when pass copies the password to the > clipboard, it is written into clipmenu?s history, and persists > there. I think this is completely wrong approach from the beginning and it doesn?t make sense to me to work around a bad design. Credentials should never get anywhere close to clipboard in the first place. Use something like wtype and input that password directly from the password filling application. See what we do in https://git.cepl.eu/cgit/rofi/rofi-pass/tree/rofi-pass, that sounds like much better design to me (and yes, there is also a clipboard opportunity, but I just don?t use it at all). And yes, there is no way around clipboard for the output of `pass generate -c`, but that?s just unfortunate and something else than day-to-day filling of prompts on various web pages. Best, Mat?j -- http://matej.ceplovi.cz/blog/, @mcepl at floss.social GPG Finger: 3C76 A027 CA45 AD70 98B5 BC1D 7920 5802 880B C9D8 But if we find we have left our bones to bleach in these desert sands for nothing, beware the fury of the legions ? -- Centurion in a letter home from North Africa 3rd Century -------------- next part -------------- A non-text attachment was scrubbed... Name: E09FEF25D96484AC.asc Type: application/pgp-keys Size: 3102 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 216 bytes Desc: not available URL: