From 9ee413ce719d08b0c2b6a6a1992cae3626a4153a Mon Sep 17 00:00:00 2001 From: AGitBoy Date: Sat, 26 Jan 2019 18:11:31 -0500 Subject: [PATCH] Added options for console output & url filters --- README.md | 4 +- bin/cli.js | 18 +++-- package-lock.json | 148 +++++++++++++++++++++++----------------- package.json | 2 +- src/index.js | 33 ++++++--- src/schedule-cleanup.js | 11 ++- 6 files changed, 137 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index a477bb1..5bde041 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ clipboard. ## Usage ``` -$ bitwarden-dmenu --help +$ bitwarden-demu --help Usage: bitwarden-dmenu [options] The DMENU_PATH environment variable can be used to point to an alternative dmenu implementation. Defaults to 'dmenu'. @@ -20,10 +20,12 @@ Options: Defaults to 15s. --session-timeout Number of seconds after an unlock that the menu can be accessed without providing a password again. Defaults to 0s. + --stdout Prints the password & username to stdout --sync-vault-after Number of seconds allowable between last bitwarden sync and current time. Defaults to 0s. --on-error Arbitrary command to run if the program fails. The thrown error is piped to the given command. Defaults to none. + --url Url to filter by. --verbose Show extra logs useful for debugging. ``` diff --git a/bin/cli.js b/bin/cli.js index a9851da..be5e0e7 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -10,6 +10,9 @@ const scheduleCleanup = require('../src/schedule-cleanup') const cachePasswordDefault = 15 const sessionTimeoutDefault = 0 const syncVaultAfterDefault = 0 +const urlFilterDefault = null +const stdoutDefault = false + const args = minimist(process.argv.slice(2)) if (args.help) { console.log( @@ -22,11 +25,13 @@ Options: Defaults to ${cachePasswordDefault}s. --session-timeout Number of seconds after an unlock that the menu can be accessed without providing a password again. Defaults to ${sessionTimeoutDefault}s. + --stdout Prints the password & username to stdout --sync-vault-after Number of seconds allowable between last bitwarden sync and current time. Defaults to ${syncVaultAfterDefault}s. --on-error Arbitrary command to run if the program fails. The thrown error is piped to the given command. Defaults to none. - + --url Url to filter by. + --verbose Show extra logs useful for debugging. ` ) @@ -37,6 +42,9 @@ const clearClipboardAfter = args['clear-clipboard'] || cachePasswordDefault const sessionTimeout = args['session-timeout'] || sessionTimeoutDefault const syncVaultAfter = args['sync-vault-after'] || syncVaultAfterDefault const onErrorCommand = args['on-error'] +const urlFilter = args['url'] || urlFilterDefault +const stdout = args['stdout'] || stdoutDefault + console.debug = args['verbose'] ? (...msgs) => console.log(...msgs, '\n') : () => {} @@ -45,12 +53,13 @@ const oldestAllowedVaultSync = syncVaultAfter const saveSession = Boolean(sessionTimeout) const sessionFile = path.resolve(os.tmpdir(), 'bitwarden-session.txt') -menu({ saveSession, sessionFile, oldestAllowedVaultSync }) +menu({ saveSession, sessionFile, oldestAllowedVaultSync, urlFilter, stdout }) .then(() => scheduleCleanup({ lockBitwardenAfter: sessionTimeout, clearClipboardAfter, - sessionFile + sessionFile, + stdout }) ) .catch(e => { @@ -60,7 +69,8 @@ menu({ saveSession, sessionFile, oldestAllowedVaultSync }) scheduleCleanup({ lockBitwardenAfter: 0, clearClipboardAfter: 0, - sessionFile + sessionFile, + stdout }) .catch(e => { // simply log an error with cleanup diff --git a/package-lock.json b/package-lock.json index 80e093d..a7aa1be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,26 @@ { "name": "bitwarden-dmenu", - "version": "1.1.4", + "version": "1.2.4", "lockfileVersion": 1, "requires": true, "dependencies": { "@bitwarden/cli": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@bitwarden/cli/-/cli-1.3.0.tgz", - "integrity": "sha512-zXJfkSGsvk0MH+Ik150htYqmW4vEPrvuEaV4NGsHzYhoqyu4Hfk7Vf4w2soKmT3LF8Cj7eMiGnDo/G852edCBw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@bitwarden/cli/-/cli-1.7.0.tgz", + "integrity": "sha512-rynqUqyfC33dMQ21OlrOu4MQVEvtjyHw1kmu85+l0j9f2CQWEffeqVwoHF5GTtQOXev4PMyqRfSSYI6dRsseag==", "requires": { + "big-integer": "1.6.36", "chalk": "2.4.1", - "commander": "2.15.1", + "commander": "2.18.0", "form-data": "2.3.2", - "inquirer": "5.2.0", + "inquirer": "6.2.0", "lowdb": "1.0.0", - "lunr": "2.3.1", - "node-fetch": "2.1.2", - "node-forge": "0.7.1", - "papaparse": "4.3.5" + "lunr": "2.3.3", + "node-fetch": "2.2.0", + "node-forge": "0.7.6", + "papaparse": "4.6.0", + "tldjs": "2.3.1", + "zxcvbn": "4.4.2" } }, "ansi-escapes": { @@ -48,6 +51,11 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "big-integer": { + "version": "1.6.36", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", + "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==" + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -59,9 +67,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, "cli-cursor": { "version": "2.1.0", @@ -107,9 +115,9 @@ } }, "commander": { - "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==" }, "cross-spawn": { "version": "5.1.0", @@ -146,12 +154,12 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, @@ -179,9 +187,9 @@ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, "has-flag": { "version": "3.0.0", @@ -197,20 +205,20 @@ } }, "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", "requires": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.1.0", + "external-editor": "^3.0.0", "figures": "^2.0.0", - "lodash": "^4.3.0", + "lodash": "^4.17.10", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^5.5.2", + "rxjs": "^6.1.0", "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" @@ -237,9 +245,9 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lowdb": { "version": "1.0.0", @@ -263,21 +271,21 @@ } }, "lunr": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.1.tgz", - "integrity": "sha1-ETYWorYC3cEJMqe/ik5uV+v+zfI=" + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.3.tgz", + "integrity": "sha512-rlAEsgU9Bnavca2w1WJ6+6cdeHMXNyadcersyk3ZpuhgWb5HBNj8l4WwJz9PjksAhYDlpQffCVXPctOn+wCIVA==" }, "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" }, "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", "requires": { - "mime-db": "~1.36.0" + "mime-db": "~1.37.0" } }, "mimic-fn": { @@ -296,14 +304,14 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "node-fetch": { - "version": "2.1.2", - "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", - "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", + "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==" }, "node-forge": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", - "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=" + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", + "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==" }, "npm-run-path": { "version": "2.0.2", @@ -332,9 +340,9 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "papaparse": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-4.3.5.tgz", - "integrity": "sha1-ts31yub+nsYDsb5m8RSmOsZFoDY=" + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-4.6.0.tgz", + "integrity": "sha512-ylm8pmgyz9rkS3Ng/ru5tHUF3JxWwKYP0aZZWZ8eCGdSxoqgYiDUXLNQei73mUJOjHw8QNu5ZNCsLoDpkMA6sg==" }, "path-key": { "version": "2.0.1", @@ -357,6 +365,11 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -375,11 +388,11 @@ } }, "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "requires": { - "symbol-observable": "1.0.1" + "tslib": "^1.9.0" } }, "safer-buffer": { @@ -443,16 +456,19 @@ "has-flag": "^3.0.0" } }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" - }, "through": { "version": "2.3.8", "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, + "tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "requires": { + "punycode": "^1.4.1" + } + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -461,6 +477,11 @@ "os-tmpdir": "~1.0.2" } }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -473,6 +494,11 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" } } } diff --git a/package.json b/package.json index a17584e..a54b4bc 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "lint": "git ls-files '*.js' '*.json' | xargs prettier -l" }, "dependencies": { - "@bitwarden/cli": "^1.3.0", + "@bitwarden/cli": "^1.4.0", "clipboardy": "^1.2.3", "minimist": "^1.2.0" }, diff --git a/src/index.js b/src/index.js index cb8a539..f28887c 100644 --- a/src/index.js +++ b/src/index.js @@ -47,8 +47,17 @@ const syncIfNecessary = ({ session, oldestAllowedVaultSync }) => { } // get the list all password accounts in the vault -const getAccounts = ({ session }) => { - const listStr = bwRun('list', 'items', `--session=${session}`) +const getAccounts = ({ session, urlFilter }) => { + var listStr = "" + if(urlFilter) { + listStr = bwRun('list', 'items', `--url=${urlFilter}`, `--session=${session}`) + } else { + listStr = bwRun('list', 'items', `--session=${session}`) + } + // const listStr = urlFilter + // ? bwRun('list', 'items', `--url=${urlFilter}`, `--session=${session}`) + // : bwRun('list', 'items', `--session=${session}`) + const list = JSON.parse(listStr) return list } @@ -60,7 +69,7 @@ const chooseAccount = async ({ list }) => { const accountNames = loginList.map(a => `${a.name}: ${a.login.username}`) // -i allows case insensitive matching - const selected = await dmenuRun('-i')(accountNames.join('\n')) + const selected = await dmenuRun()(accountNames.join('\n')) const index = accountNames.indexOf(selected) // accountNames indexes match loginList indexes const selectedAccount = loginList[index] @@ -92,7 +101,9 @@ const chooseField = async ({ selectedAccount }) => { module.exports = async ({ saveSession, sessionFile, - oldestAllowedVaultSync + oldestAllowedVaultSync, + urlFilter, + stdout }) => { const session = await getSessionVar({ saveSession, sessionFile }) @@ -100,14 +111,18 @@ module.exports = async ({ syncIfNecessary({ session, oldestAllowedVaultSync }) // bw list - const list = getAccounts({ session }) + const list = getAccounts({ session, urlFilter }) // choose account in dmenu const selectedAccount = await chooseAccount({ list }) - // choose field to copy in dmenu - const valueToCopy = await chooseField({ selectedAccount }) + if(stdout) { + console.log(`${selectedAccount.login.username}\n${selectedAccount.login.password}`) + } else { + // choose field to copy in dmenu + const valueToCopy = await chooseField({ selectedAccount }) - // copy to clipboard - clipboardy.writeSync(valueToCopy) + // copy to clipboard + clipboardy.writeSync(valueToCopy) + } } diff --git a/src/schedule-cleanup.js b/src/schedule-cleanup.js index e37e571..4910a6d 100644 --- a/src/schedule-cleanup.js +++ b/src/schedule-cleanup.js @@ -9,7 +9,7 @@ const timeout = n => new Promise(resolve => setTimeout(resolve, n)) * */ -module.exports = ({ lockBitwardenAfter, clearClipboardAfter, sessionFile }) => { +module.exports = ({ lockBitwardenAfter, clearClipboardAfter, sessionFile, stdout }) => { console.debug('begin cleanup') return Promise.all([ timeout(lockBitwardenAfter * 1000).then(() => { @@ -21,11 +21,16 @@ module.exports = ({ lockBitwardenAfter, clearClipboardAfter, sessionFile }) => { console.debug(`${sessionFile} already removed.`) } bwRun('lock') - console.info('bitwarden is locked.') + // don't output to stdout if it is being used for reading information + if(!stdout) { + console.info('bitwarden is locked.') + } }), timeout(clearClipboardAfter * 1000).then(() => { clipboardy.writeSync('') - console.info('clipboard is cleared.') + if(!stdout) { + console.info('clipboard is cleared.') + } }) ]) }