-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathcli.js
More file actions
executable file
·133 lines (113 loc) · 4.74 KB
/
cli.js
File metadata and controls
executable file
·133 lines (113 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env node
const os = require('os')
const path = require('path')
const { exec } = require('child_process')
const minimist = require('minimist')
const packageJson = require('../package.json')
const menu = require('../src')
const obfuscateState = require('../src/util/obfuscate')
const { scheduleCleanup } = require('../src/schedule-cleanup')
const { CancelError, CommandError } = require('../src/util/error')
const BW_LIST_ARGS_DEFAULT = ''
const CACHE_PASSWORD_DEFAULT = 15
const DMENU_ARGS_DEFAULT = ''
const DMENU_PSWD_ARGS_DEFAULT = ''
const SESSION_TIMEOUT_DEFAULT = 0
const SYNC_VAULT_AFTER_DEFAULT = 0
const STDOUT_DEFAULT = false
const args = minimist(process.argv.slice(2))
if (args.help) {
console.log(
`bitwarden-dmenu v${packageJson.version}
Usage: bitwarden-dmenu [options]
The DMENU_PATH environment variable can be used to point to an alternative dmenu implementation. Defaults to 'dmenu'.
Options:
--bw-list-args Arbitrary arguments to pass to bitwarden's 'list' command
Defaults to nothing.
--clear-clipboard Number of seconds to keep selected field in the clipboard.
Defaults to ${CACHE_PASSWORD_DEFAULT}s.
--dmenu-args Sets arbitrary arguments to pass to dmenu
Defaults to nothing.
--dmenu-pswd-args Sets arbitrary arguments to pass to the dmenu password prompt
Defaults to nothing.
--session-timeout Number of seconds after an unlock that the menu can be accessed
without providing a password again. Defaults to ${SESSION_TIMEOUT_DEFAULT}s.
--stdout Prints the password and username to stdout
--sync-vault-after Number of seconds allowable between last bitwarden sync and
current time. Defaults to ${SYNC_VAULT_AFTER_DEFAULT}s.
--on-error Arbitrary command to run if the program fails. The thrown error
is piped to the given command. Defaults to none.
--stdout Prints the password and username to stdout
--debug Show extra logs useful for debugging.
--debug-unsafe Show debug logs WITHOUT obfuscating your sensitive info. Do not share!
`
)
process.exit()
}
const bwListArgs = args['bw-list-args'] || BW_LIST_ARGS_DEFAULT
const dmenuArgs = args['dmenu-args'] || DMENU_ARGS_DEFAULT
const dmenuPswdArgs = args['dmenu-pswd-args'] || DMENU_PSWD_ARGS_DEFAULT
const unsafeDebug = args['debug-unsafe']
const sessionTimeout = args['session-timeout'] || SESSION_TIMEOUT_DEFAULT
const syncVaultAfter = args['sync-vault-after'] || SYNC_VAULT_AFTER_DEFAULT
const onErrorCommand = args['on-error']
const stdout = args['stdout'] || STDOUT_DEFAULT
// prevent clipboard clearing from locking up process when printing to stdout
const clearClipboardAfter = stdout ? 0 : args['clear-clipboard'] || CACHE_PASSWORD_DEFAULT
console.debug =
args['debug'] || args['debug-unsafe'] ? (...msgs) => console.log(...msgs, '\n') : () => {}
console.info = args['stdout'] ? () => {} : console.info
if (args['debug-unsafe']) obfuscateState.turnOff()
const oldestAllowedVaultSync = syncVaultAfter
const saveSession = Boolean(sessionTimeout)
const sessionFile = path.resolve(os.tmpdir(), 'bitwarden-session.txt')
const dmenuArgsParsed = dmenuArgs ? dmenuArgs.split(/\s+/) : []
const dmenuPswdArgsParsed = ['-p', 'Password:', '-nf', 'black', '-nb', 'black'].concat(
dmenuPswdArgs ? dmenuPswdArgs.split(/\s+/) : []
)
const pipeErrorToCommand = (command, e) =>
new Promise((resolve, reject) => {
const errorCommand = exec(onErrorCommand)
console.log(e)
if (e instanceof CommandError) errorCommand.stdin.write(`'${e.errorMessage}'`)
else errorCommand.stdin.write(`'${e.toString()}'`)
errorCommand.stdin.end()
errorCommand.on('close', status => {
if (status === 0) resolve()
else reject()
})
})
async function runBitwardenDmenu() {
try {
await menu({
bwListArgs,
dmenuArgs: dmenuArgsParsed,
dmenuPswdArgs: dmenuPswdArgsParsed,
saveSession,
sessionFile,
stdout,
oldestAllowedVaultSync,
unsafeDebug
})
return { lockBitwardenAfter: sessionTimeout, clearClipboardAfter }
} catch (e) {
if (e instanceof CancelError) {
console.debug('cancelled bitwarden-dmenu early.')
return { lockBitwardenAfter: sessionTimeout, clearClipboardAfter: null }
} else if (onErrorCommand) await pipeErrorToCommand(onErrorCommand, e)
else {
console.error('an error occurred:')
console.error(e)
}
return { lockBitwardenAfter: 0, clearClipboardAfter: 0 }
}
}
;(async () => {
const { lockBitwardenAfter, clearClipboardAfter } = await runBitwardenDmenu()
await scheduleCleanup({
lockBitwardenAfter,
clearClipboardAfter,
sessionFile,
stdout
})
})()