secrez 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +503 -444
- package/bin/secrez.js +50 -47
- package/coverage.report +91 -85
- package/package.json +10 -12
- package/src/Command.js +78 -57
- package/src/PreCommand.js +75 -70
- package/src/Welcome.js +144 -134
- package/src/cliConfig.js +14 -14
- package/src/commands/Alias.js +123 -100
- package/src/commands/Bash.js +10 -12
- package/src/commands/Cat.js +117 -107
- package/src/commands/Cd.js +39 -42
- package/src/commands/Chat.js +75 -63
- package/src/commands/Conf.js +123 -99
- package/src/commands/Contacts.js +189 -171
- package/src/commands/Copy.js +132 -113
- package/src/commands/Courier.js +123 -105
- package/src/commands/Ds.js +88 -76
- package/src/commands/Edit.js +122 -103
- package/src/commands/Export.js +201 -116
- package/src/commands/Find.js +115 -110
- package/src/commands/Help.js +20 -23
- package/src/commands/Import.js +296 -225
- package/src/commands/Lcat.js +36 -39
- package/src/commands/Lcd.js +38 -39
- package/src/commands/Lls.js +58 -55
- package/src/commands/Lpwd.js +20 -24
- package/src/commands/Ls.js +107 -97
- package/src/commands/Mkdir.js +35 -38
- package/src/commands/Mv.js +147 -114
- package/src/commands/Paste.js +68 -65
- package/src/commands/Pwd.js +18 -23
- package/src/commands/Quit.js +22 -24
- package/src/commands/Rm.js +78 -70
- package/src/commands/Shell.js +31 -32
- package/src/commands/Ssh.js +77 -63
- package/src/commands/Tag.js +133 -112
- package/src/commands/Totp.js +166 -136
- package/src/commands/Touch.js +169 -56
- package/src/commands/Use.js +44 -41
- package/src/commands/Ver.js +16 -18
- package/src/commands/Whoami.js +34 -37
- package/src/commands/chat/Contacts.js +41 -44
- package/src/commands/chat/Help.js +20 -23
- package/src/commands/chat/Join.js +59 -55
- package/src/commands/chat/Leave.js +16 -22
- package/src/commands/chat/Quit.js +19 -24
- package/src/commands/chat/Send.js +58 -57
- package/src/commands/chat/Show.js +60 -51
- package/src/commands/chat/Whoami.js +18 -22
- package/src/commands/index.js +20 -22
- package/src/index.js +3 -3
- package/src/prompts/ChatPrompt.js +87 -82
- package/src/prompts/ChatPromptMock.js +11 -17
- package/src/prompts/CommandPrompt.js +146 -138
- package/src/prompts/Completion.js +64 -69
- package/src/prompts/MainPrompt.js +84 -77
- package/src/prompts/MainPromptMock.js +19 -30
- package/src/prompts/MultiEditorPrompt.js +21 -22
- package/src/prompts/SigintManager.js +21 -24
- package/src/utils/AliasManager.js +16 -18
- package/src/utils/ContactManager.js +15 -17
- package/src/utils/Fido2Client.js +59 -49
- package/src/utils/HelpProto.js +130 -117
- package/src/utils/Logger.js +48 -50
- package/.eslintignore +0 -0
- package/.eslintrc +0 -33
- package/.jshintrc +0 -3
package/src/commands/Totp.js
CHANGED
@@ -1,105 +1,127 @@
|
|
1
|
-
const {authenticator} = require(
|
2
|
-
const path = require(
|
3
|
-
const fs = require(
|
4
|
-
const {execSync} = require(
|
5
|
-
const {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
const { authenticator } = require("otplib");
|
2
|
+
const path = require("path");
|
3
|
+
const fs = require("fs-extra");
|
4
|
+
const { execSync } = require("child_process");
|
5
|
+
const {
|
6
|
+
isYaml,
|
7
|
+
yamlParse,
|
8
|
+
yamlStringify,
|
9
|
+
execAsync,
|
10
|
+
TRUE,
|
11
|
+
} = require("@secrez/utils");
|
12
|
+
const { Node } = require("@secrez/fs");
|
13
|
+
const QrCode = require("qrcode-reader");
|
14
|
+
const Jimp = require("jimp");
|
11
15
|
|
16
|
+
class Totp extends require("../Command") {
|
12
17
|
setHelpAndCompletion() {
|
13
18
|
this.cliConfig.completion.totp = {
|
14
19
|
_func: this.selfCompletion(this),
|
15
|
-
_self: this
|
16
|
-
}
|
17
|
-
this.cliConfig.completion.help.totp = true
|
20
|
+
_self: this,
|
21
|
+
};
|
22
|
+
this.cliConfig.completion.help.totp = true;
|
18
23
|
this.optionDefinitions = [
|
19
24
|
{
|
20
|
-
name:
|
21
|
-
alias:
|
22
|
-
type: Boolean
|
25
|
+
name: "help",
|
26
|
+
alias: "h",
|
27
|
+
type: Boolean,
|
23
28
|
},
|
24
29
|
{
|
25
|
-
name:
|
26
|
-
completionType:
|
27
|
-
alias:
|
30
|
+
name: "path",
|
31
|
+
completionType: "file",
|
32
|
+
alias: "p",
|
28
33
|
defaultOption: true,
|
29
|
-
type: String
|
34
|
+
type: String,
|
30
35
|
},
|
31
36
|
{
|
32
|
-
name:
|
33
|
-
alias:
|
34
|
-
type: Number
|
37
|
+
name: "duration",
|
38
|
+
alias: "d",
|
39
|
+
type: Number,
|
35
40
|
},
|
36
41
|
{
|
37
|
-
name:
|
38
|
-
type: Boolean
|
42
|
+
name: "no-beep",
|
43
|
+
type: Boolean,
|
39
44
|
},
|
40
45
|
{
|
41
|
-
name:
|
42
|
-
alias:
|
43
|
-
type: Boolean
|
46
|
+
name: "from-clipboard",
|
47
|
+
alias: "c",
|
48
|
+
type: Boolean,
|
44
49
|
},
|
45
50
|
{
|
46
|
-
name:
|
47
|
-
alias:
|
48
|
-
type: String
|
51
|
+
name: "from-image",
|
52
|
+
alias: "i",
|
53
|
+
type: String,
|
49
54
|
},
|
50
55
|
{
|
51
|
-
name:
|
52
|
-
alias:
|
53
|
-
type: String
|
56
|
+
name: "set",
|
57
|
+
alias: "s",
|
58
|
+
type: String,
|
54
59
|
},
|
55
60
|
{
|
56
|
-
name:
|
57
|
-
type: Boolean
|
61
|
+
name: "force",
|
62
|
+
type: Boolean,
|
58
63
|
},
|
59
64
|
{
|
60
|
-
name:
|
61
|
-
type: String
|
62
|
-
}
|
63
|
-
]
|
65
|
+
name: "test",
|
66
|
+
type: String,
|
67
|
+
},
|
68
|
+
];
|
64
69
|
this.defaults = {
|
65
|
-
duration: 8
|
66
|
-
}
|
70
|
+
duration: 8,
|
71
|
+
};
|
67
72
|
}
|
68
73
|
|
69
74
|
help() {
|
70
75
|
return {
|
71
|
-
description: [
|
72
|
-
'Generate a TOTP code if a totp field exists in the card.'
|
73
|
-
],
|
76
|
+
description: ["Generate a TOTP code if a totp field exists in the card."],
|
74
77
|
examples: [
|
75
|
-
[
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
[
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
[
|
79
|
+
"totp coinbase.yml",
|
80
|
+
"prints a totp code and copies it to the clipboard for 5 seconds",
|
81
|
+
],
|
82
|
+
[
|
83
|
+
'totp coinbase.yml -s "9syh 34rd ge6s hey3 u874"',
|
84
|
+
"set up a totp code, if not set yet",
|
85
|
+
],
|
86
|
+
[
|
87
|
+
"totp github.com.yml -s USyehAA35TSE --force",
|
88
|
+
"update an existing totp code",
|
89
|
+
],
|
90
|
+
["totp coinbase.yml -d 2", "keeps it in the clipboard for 2 seconds"],
|
91
|
+
[
|
92
|
+
"totp github.yml --from-clipboard",
|
93
|
+
'get a secret from a qr code copied in the clipboard and add a field "totp" with the secret in "github.yml"',
|
94
|
+
],
|
95
|
+
[
|
96
|
+
"totp github.yml --from-image qrcode.png",
|
97
|
+
"get a secret from the image",
|
98
|
+
],
|
99
|
+
['totp --test "IHSG UY6T WTGS"', "tests a secret"],
|
100
|
+
],
|
101
|
+
};
|
84
102
|
}
|
85
103
|
|
86
104
|
async isImagePasteSupported() {
|
87
105
|
/* istanbul ignore if */
|
88
106
|
if (TRUE()) {
|
89
|
-
let result
|
107
|
+
let result;
|
90
108
|
switch (process.platform) {
|
91
|
-
case
|
92
|
-
result = await execAsync(
|
109
|
+
case "darwin":
|
110
|
+
result = await execAsync("which", __dirname, ["pngpaste"]);
|
93
111
|
if (!result.message || result.code === 1) {
|
94
|
-
throw new Error(
|
112
|
+
throw new Error(
|
113
|
+
'pngpaste is required. Run "brew install pngpaste" in another terminal to install it'
|
114
|
+
);
|
95
115
|
}
|
96
|
-
break
|
97
|
-
case
|
98
|
-
throw new Error(
|
116
|
+
break;
|
117
|
+
case "win32":
|
118
|
+
throw new Error("Operation not supported on Windows");
|
99
119
|
default:
|
100
|
-
result = await execAsync(
|
120
|
+
result = await execAsync("which", __dirname, ["xclip"]);
|
101
121
|
if (!result.message || result.code === 1) {
|
102
|
-
throw new Error(
|
122
|
+
throw new Error(
|
123
|
+
'xclip is required. On Debian/Ubuntu you can install it with "sudo apt install xclip"'
|
124
|
+
);
|
103
125
|
}
|
104
126
|
}
|
105
127
|
}
|
@@ -108,155 +130,163 @@ class Totp extends require('../Command') {
|
|
108
130
|
async readFromClipboard(options) {
|
109
131
|
/* istanbul ignore if */
|
110
132
|
if (TRUE()) {
|
111
|
-
await this.isImagePasteSupported()
|
112
|
-
let p = path.resolve(this.secrez.config.tmpPath,
|
113
|
-
let result
|
133
|
+
await this.isImagePasteSupported();
|
134
|
+
let p = path.resolve(this.secrez.config.tmpPath, "image.png");
|
135
|
+
let result;
|
114
136
|
switch (process.platform) {
|
115
|
-
case
|
116
|
-
result = await execAsync(
|
137
|
+
case "darwin":
|
138
|
+
result = await execAsync("pngpaste", __dirname, [p]);
|
117
139
|
if (result.error) {
|
118
140
|
if (/target image\/png not available/.test(result.error)) {
|
119
|
-
throw new Error(
|
141
|
+
throw new Error("The clipboard does not contain an image");
|
120
142
|
}
|
121
|
-
throw new Error(result.error)
|
143
|
+
throw new Error(result.error);
|
122
144
|
}
|
123
|
-
break
|
145
|
+
break;
|
124
146
|
default:
|
125
147
|
try {
|
126
|
-
result = execSync(
|
148
|
+
result = execSync(
|
149
|
+
`xclip -selection clipboard -t image/png -o > ${p}`
|
150
|
+
).toString();
|
127
151
|
} catch (e) {
|
128
|
-
throw new Error(
|
152
|
+
throw new Error("Wrong content in the clipboard");
|
129
153
|
}
|
130
154
|
}
|
131
|
-
return p
|
155
|
+
return p;
|
132
156
|
}
|
133
157
|
}
|
134
158
|
|
135
159
|
async readFromImage(options) {
|
136
|
-
let p = this.externalFs.getNormalizedPath(options.fromImage)
|
137
|
-
const buffer = await fs.readFile(p)
|
160
|
+
let p = this.externalFs.getNormalizedPath(options.fromImage);
|
161
|
+
const buffer = await fs.readFile(p);
|
138
162
|
return new Promise((resolve, reject) => {
|
139
163
|
Jimp.read(buffer, (err, image) => {
|
140
164
|
if (err) {
|
141
|
-
reject(err.message)
|
165
|
+
reject(err.message);
|
142
166
|
}
|
143
|
-
const qr = new QrCode()
|
167
|
+
const qr = new QrCode();
|
144
168
|
qr.callback = (err, value) => {
|
145
169
|
/* istanbul ignore if */
|
146
170
|
if (err) {
|
147
|
-
reject(err.message)
|
171
|
+
reject(err.message);
|
148
172
|
}
|
149
|
-
resolve(value.result)
|
150
|
-
}
|
173
|
+
resolve(value.result);
|
174
|
+
};
|
151
175
|
try {
|
152
|
-
qr.decode(image.bitmap)
|
176
|
+
qr.decode(image.bitmap);
|
153
177
|
} catch (e) {
|
154
|
-
reject(e.message)
|
178
|
+
reject(e.message);
|
155
179
|
}
|
156
|
-
})
|
157
|
-
})
|
180
|
+
});
|
181
|
+
});
|
158
182
|
}
|
159
183
|
|
160
184
|
async totp(options = {}) {
|
161
|
-
let secret = options.set
|
162
|
-
let originalPath = options.path
|
185
|
+
let secret = options.set;
|
186
|
+
let originalPath = options.path;
|
163
187
|
if (options.test) {
|
164
|
-
const token = authenticator.generate(options.test.replace(/\s/g,
|
165
|
-
return token
|
188
|
+
const token = authenticator.generate(options.test.replace(/\s/g, ""));
|
189
|
+
return token;
|
166
190
|
}
|
167
191
|
if (options.fromImage || options.fromClipboard) {
|
168
192
|
/* istanbul ignore if */
|
169
193
|
if (options.fromClipboard) {
|
170
|
-
options.fromImage = await this.readFromClipboard(options)
|
194
|
+
options.fromImage = await this.readFromClipboard(options);
|
171
195
|
}
|
172
196
|
try {
|
173
|
-
let result = await this.readFromImage(options)
|
174
|
-
secret = result.split(
|
197
|
+
let result = await this.readFromImage(options);
|
198
|
+
secret = result.split("secret=")[1].split("&")[0];
|
175
199
|
} catch (e) {
|
176
|
-
throw new Error(
|
200
|
+
throw new Error("The file does not look like a valid 2FA QR code");
|
177
201
|
}
|
178
202
|
}
|
179
|
-
let err =
|
180
|
-
let currentIndex = this.internalFs.treeIndex
|
181
|
-
let data = await this.internalFs.getTreeIndexAndPath(options.path)
|
203
|
+
let err = "The file is not a card with a totp field";
|
204
|
+
let currentIndex = this.internalFs.treeIndex;
|
205
|
+
let data = await this.internalFs.getTreeIndexAndPath(options.path);
|
182
206
|
/* istanbul ignore if */
|
183
207
|
if (currentIndex !== data.index) {
|
184
|
-
await this.internalFs.mountTree(data.index, true)
|
208
|
+
await this.internalFs.mountTree(data.index, true);
|
185
209
|
}
|
186
210
|
if (secret && !originalPath) {
|
187
|
-
return `The secret in the QR Code is "${secret}"
|
211
|
+
return `The secret in the QR Code is "${secret}"`;
|
188
212
|
}
|
189
|
-
options.path = data.path
|
190
|
-
let tree = data.tree
|
191
|
-
let p = tree.getNormalizedPath(options.path)
|
192
|
-
let node = tree.root.getChildFromPath(p)
|
213
|
+
options.path = data.path;
|
214
|
+
let tree = data.tree;
|
215
|
+
let p = tree.getNormalizedPath(options.path);
|
216
|
+
let node = tree.root.getChildFromPath(p);
|
193
217
|
if (Node.isFile(node)) {
|
194
|
-
let entry = (
|
195
|
-
|
196
|
-
|
197
|
-
|
218
|
+
let entry = (
|
219
|
+
await this.prompt.commands.cat.cat({
|
220
|
+
path: p,
|
221
|
+
unformatted: true,
|
222
|
+
})
|
223
|
+
)[0];
|
198
224
|
if (Node.isText(entry)) {
|
199
|
-
let {content} = entry
|
225
|
+
let { content } = entry;
|
200
226
|
if (isYaml(p) && !options.allFile) {
|
201
|
-
let parsed
|
227
|
+
let parsed;
|
202
228
|
try {
|
203
|
-
parsed = yamlParse(content)
|
229
|
+
parsed = yamlParse(content);
|
204
230
|
} catch (e) {
|
205
|
-
throw new Error(
|
231
|
+
throw new Error("The yml is malformed");
|
206
232
|
}
|
207
233
|
if (secret) {
|
208
234
|
if (parsed.totp && !options.force) {
|
209
|
-
throw new Error(
|
235
|
+
throw new Error(
|
236
|
+
'A totp already set. Use the "--force" option to override it'
|
237
|
+
);
|
210
238
|
}
|
211
|
-
parsed.totp = secret
|
212
|
-
let entry = node.getEntry()
|
213
|
-
entry.set(
|
214
|
-
await this.internalFs.tree.update(node, entry)
|
239
|
+
parsed.totp = secret;
|
240
|
+
let entry = node.getEntry();
|
241
|
+
entry.set("content", yamlStringify(parsed));
|
242
|
+
await this.internalFs.tree.update(node, entry);
|
215
243
|
return [
|
216
|
-
|
217
|
-
`Try it, running "totp ${node.getPath()}"
|
218
|
-
].join(
|
244
|
+
"A totp field has been successfully created.",
|
245
|
+
`Try it, running "totp ${node.getPath()}"`,
|
246
|
+
].join("\n");
|
219
247
|
} else {
|
220
|
-
let totp = parsed.totp
|
248
|
+
let totp = parsed.totp;
|
221
249
|
if (totp) {
|
222
|
-
totp = totp.replace(/\s/g,
|
223
|
-
const token = authenticator.generate(totp)
|
250
|
+
totp = totp.replace(/\s/g, "");
|
251
|
+
const token = authenticator.generate(totp);
|
224
252
|
this.prompt.commands.copy.copy({
|
225
253
|
thisString: token,
|
226
254
|
duration: [options.duration || this.defaults.duration],
|
227
|
-
noBeep: options.noBeep
|
228
|
-
})
|
229
|
-
return token
|
255
|
+
noBeep: options.noBeep,
|
256
|
+
});
|
257
|
+
return token;
|
230
258
|
}
|
231
259
|
}
|
232
260
|
}
|
233
261
|
}
|
234
262
|
}
|
235
|
-
throw new Error(err)
|
263
|
+
throw new Error(err);
|
236
264
|
}
|
237
265
|
|
238
266
|
async exec(options = {}) {
|
239
267
|
if (options.help) {
|
240
|
-
return this.showHelp()
|
268
|
+
return this.showHelp();
|
241
269
|
}
|
242
270
|
try {
|
243
|
-
this.validate(options)
|
244
|
-
let token = await this.totp(options)
|
271
|
+
this.validate(options);
|
272
|
+
let token = await this.totp(options);
|
245
273
|
if (options.fromImage || options.fromClipboard) {
|
246
|
-
this.Logger.grey(token)
|
274
|
+
this.Logger.grey(token);
|
247
275
|
} else {
|
248
|
-
this.Logger.grey(
|
276
|
+
this.Logger.grey("TOTP token: " + this.chalk.bold.black(token));
|
249
277
|
if (!options.test) {
|
250
|
-
this.Logger.grey(
|
278
|
+
this.Logger.grey(
|
279
|
+
`It will stay in the clipboard for ${
|
280
|
+
options.duration || this.defaults.duration
|
281
|
+
} seconds`
|
282
|
+
);
|
251
283
|
}
|
252
284
|
}
|
253
285
|
} catch (e) {
|
254
|
-
this.Logger.red(e.message)
|
286
|
+
this.Logger.red(e.message);
|
255
287
|
}
|
256
|
-
await this.prompt.run()
|
288
|
+
await this.prompt.run();
|
257
289
|
}
|
258
290
|
}
|
259
291
|
|
260
|
-
module.exports = Totp
|
261
|
-
|
262
|
-
|
292
|
+
module.exports = Totp;
|