@dotenvx/dotenvx 2.2.1 → 2.3.0
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/CHANGELOG.md +21 -1
- package/README.md +99 -67
- package/package.json +2 -2
- package/src/cli/actions/decrypt.js +2 -3
- package/src/cli/actions/encrypt.js +2 -3
- package/src/cli/actions/get.js +2 -3
- package/src/cli/actions/keychain/up.js +2 -2
- package/src/cli/actions/keypair.js +2 -3
- package/src/cli/actions/lock/down.js +74 -0
- package/src/cli/actions/lock/up.js +80 -0
- package/src/cli/actions/run.js +2 -3
- package/src/cli/actions/set.js +2 -3
- package/src/cli/commands/armor.js +19 -1
- package/src/cli/commands/lock.js +27 -0
- package/src/cli/commands/{keychain.js → native.js} +13 -13
- package/src/cli/dotenvx.js +16 -20
- package/src/db/session.js +7 -7
- package/src/lib/helpers/errors.js +13 -0
- package/src/lib/helpers/executeDynamic.js +1 -19
- package/src/lib/main.d.ts +9 -33
- package/src/lib/main.js +3 -3
- package/src/lib/providers/index.js +1 -1
- package/src/lib/services/keychainPull.js +1 -1
- package/src/lib/services/lockDown.js +157 -0
- package/src/lib/services/lockUp.js +150 -0
- package/src/cli/actions/normalizeArmorAliases.js +0 -9
|
@@ -3,7 +3,7 @@ const Session = require('./../../db/session')
|
|
|
3
3
|
|
|
4
4
|
function configureArmorCommand (armor) {
|
|
5
5
|
armor
|
|
6
|
-
.description('move private keys
|
|
6
|
+
.description('move private keys into Dotenvx Armor [www.dotenvx.com/armor]')
|
|
7
7
|
.allowUnknownOption()
|
|
8
8
|
.argument('[command]', 'dotenvx-armor command')
|
|
9
9
|
.argument('[args...]', 'dotenvx-armor command arguments')
|
|
@@ -66,6 +66,24 @@ function configureArmorCommand (armor) {
|
|
|
66
66
|
.option('--token <token>', 'set token')
|
|
67
67
|
.action(moveAction)
|
|
68
68
|
|
|
69
|
+
// dotenvx armor login
|
|
70
|
+
const loginAction = require('./../actions/login')
|
|
71
|
+
armor
|
|
72
|
+
.command('login')
|
|
73
|
+
.description('log in to Dotenvx Armor')
|
|
74
|
+
.allowUnknownOption()
|
|
75
|
+
.option('--hostname <hostname>', 'set Armor ⛨ hostname')
|
|
76
|
+
.action(loginAction)
|
|
77
|
+
|
|
78
|
+
// dotenvx armor logout
|
|
79
|
+
const logoutAction = require('./../actions/logout')
|
|
80
|
+
armor
|
|
81
|
+
.command('logout')
|
|
82
|
+
.description('log out of Dotenvx Armor')
|
|
83
|
+
.allowUnknownOption()
|
|
84
|
+
.option('--hostname <hostname>', 'set Armor ⛨ hostname')
|
|
85
|
+
.action(logoutAction)
|
|
86
|
+
|
|
69
87
|
return armor
|
|
70
88
|
}
|
|
71
89
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function configureLockCommand (lock) {
|
|
2
|
+
lock
|
|
3
|
+
.description('lock private keys with a local passphrase')
|
|
4
|
+
.action(function () {
|
|
5
|
+
this.help()
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const upAction = require('./../actions/lock/up')
|
|
9
|
+
lock
|
|
10
|
+
.command('up')
|
|
11
|
+
.description('lock key in .env.keys')
|
|
12
|
+
.option('-f, --env-file <path>', 'path to your env file')
|
|
13
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
14
|
+
.action(upAction)
|
|
15
|
+
|
|
16
|
+
const downAction = require('./../actions/lock/down')
|
|
17
|
+
lock
|
|
18
|
+
.command('down')
|
|
19
|
+
.description('unlock key in .env.keys')
|
|
20
|
+
.option('-f, --env-file <path>', 'path to your env file')
|
|
21
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
22
|
+
.action(downAction)
|
|
23
|
+
|
|
24
|
+
return lock
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = configureLockCommand
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
.description('
|
|
1
|
+
function configureNativeCommand (native) {
|
|
2
|
+
native
|
|
3
|
+
.description('move private keys into your OS secret store (macOS Keychain supported)')
|
|
4
4
|
.action(function () {
|
|
5
5
|
this.help()
|
|
6
6
|
})
|
|
7
7
|
|
|
8
8
|
const upAction = require('./../actions/keychain/up')
|
|
9
|
-
|
|
9
|
+
native
|
|
10
10
|
.command('up')
|
|
11
|
-
.description('store key in
|
|
11
|
+
.description('store key in OS secret store')
|
|
12
12
|
.option('-f, --env-file <path>', 'path to your env file')
|
|
13
13
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
14
14
|
.action(upAction)
|
|
15
15
|
|
|
16
16
|
const downAction = require('./../actions/keychain/down')
|
|
17
|
-
|
|
17
|
+
native
|
|
18
18
|
.command('down')
|
|
19
|
-
.description('move key from
|
|
19
|
+
.description('move key from OS secret store to .env.keys')
|
|
20
20
|
.option('-f, --env-file <path>', 'path to your env file')
|
|
21
21
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
22
22
|
.action(downAction)
|
|
23
23
|
|
|
24
24
|
const pushAction = require('./../actions/keychain/push')
|
|
25
|
-
|
|
25
|
+
native
|
|
26
26
|
.command('push')
|
|
27
|
-
.description('push key to
|
|
27
|
+
.description('push key to OS secret store from .env.keys')
|
|
28
28
|
.option('-f, --env-file <path>', 'path to your env file')
|
|
29
29
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
30
30
|
.action(pushAction)
|
|
31
31
|
|
|
32
32
|
const pullAction = require('./../actions/keychain/pull')
|
|
33
|
-
|
|
33
|
+
native
|
|
34
34
|
.command('pull')
|
|
35
|
-
.description('pull key from
|
|
35
|
+
.description('pull key from OS secret store into .env.keys')
|
|
36
36
|
.option('-f, --env-file <path>', 'path to your env file')
|
|
37
37
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
38
38
|
.action(pullAction)
|
|
39
39
|
|
|
40
|
-
return
|
|
40
|
+
return native
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
module.exports =
|
|
43
|
+
module.exports = configureNativeCommand
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/* c8 ignore start */
|
|
4
|
-
const { Command
|
|
4
|
+
const { Command } = require('commander')
|
|
5
5
|
const program = new Command()
|
|
6
6
|
|
|
7
7
|
const { setLogLevel, logger } = require('../shared/logger')
|
|
@@ -73,8 +73,7 @@ program.command('run')
|
|
|
73
73
|
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
74
74
|
.option('--token <token>', 'set Armor ⛨ token')
|
|
75
75
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
76
|
-
.option('--no-
|
|
77
|
-
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
76
|
+
.option('--no-native', 'disable OS secret store features')
|
|
78
77
|
.action(function (...args) {
|
|
79
78
|
this.envs = envs
|
|
80
79
|
return require('./actions/run').apply(this, args)
|
|
@@ -97,8 +96,7 @@ program.command('get')
|
|
|
97
96
|
.option('--pp', 'pretty print output (alias)')
|
|
98
97
|
.option('--format <type>', 'format of the output (json, shell, colon, eval)', 'json')
|
|
99
98
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
100
|
-
.option('--no-
|
|
101
|
-
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
99
|
+
.option('--no-native', 'disable OS secret store features')
|
|
102
100
|
.action(function (...args) {
|
|
103
101
|
this.envs = envs
|
|
104
102
|
return require('./actions/get').apply(this, args)
|
|
@@ -118,8 +116,7 @@ program.command('set')
|
|
|
118
116
|
.option('-p, --plain', 'store value as plain text', false)
|
|
119
117
|
.option('--no-create', 'do not create .env file(s) when missing')
|
|
120
118
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
121
|
-
.option('--no-
|
|
122
|
-
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
119
|
+
.option('--no-native', 'disable OS secret store features')
|
|
123
120
|
.action(function (...args) {
|
|
124
121
|
this.envs = envs
|
|
125
122
|
return require('./actions/set').apply(this, args)
|
|
@@ -136,8 +133,7 @@ program.command('encrypt')
|
|
|
136
133
|
.option('--token <token>', 'set Armor ⛨ token')
|
|
137
134
|
.option('--no-create', 'do not create .env file(s) when missing')
|
|
138
135
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
139
|
-
.option('--no-
|
|
140
|
-
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
136
|
+
.option('--no-native', 'disable OS secret store features')
|
|
141
137
|
.action(function (...args) {
|
|
142
138
|
this.envs = envs
|
|
143
139
|
return require('./actions/encrypt').apply(this, args)
|
|
@@ -151,8 +147,7 @@ program.command('decrypt')
|
|
|
151
147
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
152
148
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
153
149
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
154
|
-
.option('--no-
|
|
155
|
-
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
150
|
+
.option('--no-native', 'disable OS secret store features')
|
|
156
151
|
.option('--stdout', 'send to stdout')
|
|
157
152
|
.action(function (...args) {
|
|
158
153
|
this.envs = envs
|
|
@@ -167,8 +162,7 @@ program.command('keypair')
|
|
|
167
162
|
.option('-f, --env-file <path>', 'path(s) to your env file(s)')
|
|
168
163
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
169
164
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
170
|
-
.option('--no-
|
|
171
|
-
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
165
|
+
.option('--no-native', 'disable OS secret store features')
|
|
172
166
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
173
167
|
.option('--pp', 'pretty print output (alias)')
|
|
174
168
|
.option('--format <type>', 'format of the output (json, shell, colon)', 'json')
|
|
@@ -231,7 +225,7 @@ program.command('doctor', { hidden: true })
|
|
|
231
225
|
return require('./actions/doctor').apply(this, args)
|
|
232
226
|
})
|
|
233
227
|
|
|
234
|
-
// dotenvx login
|
|
228
|
+
// dotenvx login (compatibility alias for dotenvx armor login)
|
|
235
229
|
program.command('login', { hidden: true })
|
|
236
230
|
.description('log in to move keys off-device, share with your team, and audit access')
|
|
237
231
|
.allowUnknownOption()
|
|
@@ -240,7 +234,7 @@ program.command('login', { hidden: true })
|
|
|
240
234
|
return require('./actions/login').apply(this, args)
|
|
241
235
|
})
|
|
242
236
|
|
|
243
|
-
// dotenvx logout
|
|
237
|
+
// dotenvx logout (compatibility alias for dotenvx armor logout)
|
|
244
238
|
program.command('logout', { hidden: true })
|
|
245
239
|
.description('log out of connected security features')
|
|
246
240
|
.allowUnknownOption()
|
|
@@ -268,13 +262,15 @@ program.command('help [command]')
|
|
|
268
262
|
// dotenvx armor
|
|
269
263
|
program.addHelpText('after', ' ')
|
|
270
264
|
program.addHelpText('after', 'Professional Security: ')
|
|
271
|
-
program.addHelpText('after', '
|
|
272
|
-
program.addHelpText('after', '
|
|
273
|
-
program.addHelpText('after', ' keychain ⌥ move private keys into macOS Keychain')
|
|
265
|
+
program.addHelpText('after', ' lock ⊡ lock private keys with a local passphrase')
|
|
266
|
+
program.addHelpText('after', ' native ⌥ move private keys into your OS secret store (macOS Keychain supported)')
|
|
274
267
|
program.addHelpText('after', ' armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]')
|
|
275
268
|
|
|
276
|
-
// dotenvx
|
|
277
|
-
require('./commands/
|
|
269
|
+
// dotenvx native
|
|
270
|
+
require('./commands/native')(program.command('native', { hidden: true }))
|
|
271
|
+
|
|
272
|
+
// dotenvx lock
|
|
273
|
+
require('./commands/lock')(program.command('lock', { hidden: true }))
|
|
278
274
|
|
|
279
275
|
// dotenvx armor
|
|
280
276
|
require('./commands/armor')(program.command('armor', { hidden: true }))
|
package/src/db/session.js
CHANGED
|
@@ -226,19 +226,19 @@ class Session {
|
|
|
226
226
|
//
|
|
227
227
|
login (hostname, id, username, accessToken) {
|
|
228
228
|
if (!hostname) {
|
|
229
|
-
throw new Error('DOTENVX_ARMOR_HOSTNAME not set. Run [dotenvx login]')
|
|
229
|
+
throw new Error('DOTENVX_ARMOR_HOSTNAME not set. Run [dotenvx armor login]')
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
if (!id) {
|
|
233
|
-
throw new Error('DOTENVX_ARMOR_USER not set. Run [dotenvx login]')
|
|
233
|
+
throw new Error('DOTENVX_ARMOR_USER not set. Run [dotenvx armor login]')
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
if (!username) {
|
|
237
|
-
throw new Error('DOTENVX_ARMOR_USERNAME not set. Run [dotenvx login]')
|
|
237
|
+
throw new Error('DOTENVX_ARMOR_USERNAME not set. Run [dotenvx armor login]')
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
if (!accessToken) {
|
|
241
|
-
throw new Error('DOTENVX_ARMOR_TOKEN not set. Run [dotenvx login]')
|
|
241
|
+
throw new Error('DOTENVX_ARMOR_TOKEN not set. Run [dotenvx armor login]')
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
const store = this.createStore()
|
|
@@ -252,15 +252,15 @@ class Session {
|
|
|
252
252
|
|
|
253
253
|
logout (hostname, id, accessToken) {
|
|
254
254
|
if (!hostname) {
|
|
255
|
-
throw new Error('DOTENVX_ARMOR_HOSTNAME not set. Run [dotenvx login]')
|
|
255
|
+
throw new Error('DOTENVX_ARMOR_HOSTNAME not set. Run [dotenvx armor login]')
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
if (!id) {
|
|
259
|
-
throw new Error('DOTENVX_ARMOR_USER not set. Run [dotenvx login]')
|
|
259
|
+
throw new Error('DOTENVX_ARMOR_USER not set. Run [dotenvx armor login]')
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
if (!accessToken) {
|
|
263
|
-
throw new Error('DOTENVX_ARMOR_TOKEN not set. Run [dotenvx login]')
|
|
263
|
+
throw new Error('DOTENVX_ARMOR_TOKEN not set. Run [dotenvx armor login]')
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
const store = this.openStore()
|
|
@@ -7,6 +7,7 @@ const ISSUE_BY_CODE = {
|
|
|
7
7
|
DANGEROUS_DEPENDENCY_HOIST: 'https://github.com/dotenvx/dotenvx/issues/622',
|
|
8
8
|
INVALID_COLOR: 'must be 256 colors',
|
|
9
9
|
INVALID_CONVENTION: 'https://github.com/dotenvx/dotenvx/issues/761',
|
|
10
|
+
INVALID_PASSPHRASE: 'try again with the correct passphrase',
|
|
10
11
|
INVALID_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/465',
|
|
11
12
|
INVALID_PUBLIC_KEY: 'https://github.com/dotenvx/dotenvx/issues/756',
|
|
12
13
|
MALFORMED_ENCRYPTED_DATA: 'https://github.com/dotenvx/dotenvx/issues/467',
|
|
@@ -157,6 +158,18 @@ class Errors {
|
|
|
157
158
|
return e
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
invalidPassphrase () {
|
|
162
|
+
const code = 'INVALID_PASSPHRASE'
|
|
163
|
+
const message = `[${code}] could not unlock ${this.privateKeyName || 'DOTENV_PRIVATE_KEY'} using passphrase`
|
|
164
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
165
|
+
|
|
166
|
+
const e = new Error(message)
|
|
167
|
+
e.code = code
|
|
168
|
+
e.help = help
|
|
169
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
170
|
+
return e
|
|
171
|
+
}
|
|
172
|
+
|
|
160
173
|
malformedEncryptedData () {
|
|
161
174
|
const code = 'MALFORMED_ENCRYPTED_DATA'
|
|
162
175
|
const message = `[${code}] could not decrypt ${this.key} because encrypted data appears malformed`
|
|
@@ -42,20 +42,6 @@ function armorBanner () {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function dynamicAttempts (command, forwardedArgs) {
|
|
45
|
-
if (command === 'vlt') {
|
|
46
|
-
return [
|
|
47
|
-
['dotenvx-armor', forwardedArgs],
|
|
48
|
-
['dotenvx-ops', forwardedArgs]
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (command === 'ops') {
|
|
53
|
-
return [
|
|
54
|
-
['dotenvx-armor', forwardedArgs],
|
|
55
|
-
['dotenvx-ops', forwardedArgs]
|
|
56
|
-
]
|
|
57
|
-
}
|
|
58
|
-
|
|
59
45
|
return [[`dotenvx-${command}`, forwardedArgs]]
|
|
60
46
|
}
|
|
61
47
|
|
|
@@ -85,11 +71,7 @@ function executeDynamic (program, command, rawArgs) {
|
|
|
85
71
|
}
|
|
86
72
|
|
|
87
73
|
if (result.error) {
|
|
88
|
-
if (command === '
|
|
89
|
-
console.log(armorBanner())
|
|
90
|
-
} else if (command === 'ops') {
|
|
91
|
-
console.log(armorBanner())
|
|
92
|
-
} else if (command === 'armor') {
|
|
74
|
+
if (command === 'armor') {
|
|
93
75
|
console.log(armorBanner())
|
|
94
76
|
} else {
|
|
95
77
|
logger.info(`error: unknown command '${command}'`)
|
package/src/lib/main.d.ts
CHANGED
|
@@ -180,20 +180,12 @@ export interface DotenvConfigOptions {
|
|
|
180
180
|
noArmor?: boolean;
|
|
181
181
|
|
|
182
182
|
/**
|
|
183
|
-
* Turn off
|
|
183
|
+
* Turn off OS secret store features.
|
|
184
184
|
*
|
|
185
185
|
* @default false
|
|
186
|
-
* @example require('@dotenvx/dotenvx').config({
|
|
186
|
+
* @example require('@dotenvx/dotenvx').config({ noNative: true })
|
|
187
187
|
*/
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Turn off Dotenvx Armor features. Alias for `noArmor`.
|
|
192
|
-
*
|
|
193
|
-
* @default false
|
|
194
|
-
* @example require('@dotenvx/dotenvx').config({ noOps: true })
|
|
195
|
-
*/
|
|
196
|
-
noOps?: boolean;
|
|
188
|
+
noNative?: boolean;
|
|
197
189
|
|
|
198
190
|
}
|
|
199
191
|
|
|
@@ -271,20 +263,12 @@ export interface SetOptions {
|
|
|
271
263
|
noArmor?: boolean;
|
|
272
264
|
|
|
273
265
|
/**
|
|
274
|
-
* Turn off
|
|
266
|
+
* Turn off OS secret store features.
|
|
275
267
|
*
|
|
276
268
|
* @default false
|
|
277
|
-
* @example require('@dotenvx/dotenvx').set(key, value, {
|
|
269
|
+
* @example require('@dotenvx/dotenvx').set(key, value, { noNative: true })
|
|
278
270
|
*/
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Turn off Dotenvx Armor features. Alias for `noArmor`.
|
|
283
|
-
*
|
|
284
|
-
* @default false
|
|
285
|
-
* @example require('@dotenvx/dotenvx').set(key, value, { noOps: true })
|
|
286
|
-
*/
|
|
287
|
-
noOps?: boolean;
|
|
271
|
+
noNative?: boolean;
|
|
288
272
|
|
|
289
273
|
}
|
|
290
274
|
|
|
@@ -364,20 +348,12 @@ export interface GetOptions {
|
|
|
364
348
|
noArmor?: boolean;
|
|
365
349
|
|
|
366
350
|
/**
|
|
367
|
-
* Turn off
|
|
368
|
-
*
|
|
369
|
-
* @default false
|
|
370
|
-
* @example require('@dotenvx/dotenvx').get('KEY', { noKeychain: true })
|
|
371
|
-
*/
|
|
372
|
-
noKeychain?: boolean;
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Turn off Dotenvx Armor features. Alias for `noArmor`.
|
|
351
|
+
* Turn off OS secret store features.
|
|
376
352
|
*
|
|
377
353
|
* @default false
|
|
378
|
-
* @example require('@dotenvx/dotenvx').get('KEY', {
|
|
354
|
+
* @example require('@dotenvx/dotenvx').get('KEY', { noNative: true })
|
|
379
355
|
*/
|
|
380
|
-
|
|
356
|
+
noNative?: boolean;
|
|
381
357
|
|
|
382
358
|
}
|
|
383
359
|
|
package/src/lib/main.js
CHANGED
|
@@ -380,11 +380,11 @@ const ls = function (directory, envFile, excludeEnvFile) {
|
|
|
380
380
|
|
|
381
381
|
function resolveNoArmor (options = {}) {
|
|
382
382
|
const sesh = new Session()
|
|
383
|
-
return options.noArmor === true ||
|
|
383
|
+
return options.noArmor === true || (!options.token && sesh.noArmorSync())
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
function resolveNoKeychain (options = {}) {
|
|
387
|
-
return options.
|
|
387
|
+
return options.noNative === true || options.native === false
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
module.exports = {
|
|
@@ -395,7 +395,7 @@ module.exports = {
|
|
|
395
395
|
set,
|
|
396
396
|
get,
|
|
397
397
|
ls,
|
|
398
|
-
// expose for libs depending on @dotenvx/dotenvx
|
|
398
|
+
// expose for libs depending on @dotenvx/dotenvx
|
|
399
399
|
setLogLevel,
|
|
400
400
|
logger,
|
|
401
401
|
getColor,
|
|
@@ -46,7 +46,7 @@ function armorProviderForOptions (options) {
|
|
|
46
46
|
function useKeychain (options) {
|
|
47
47
|
if (process.platform !== 'darwin') return false
|
|
48
48
|
if (process.env.CI) return false
|
|
49
|
-
return options.
|
|
49
|
+
return options.noNative !== true && options.native !== false && options.noKeychain !== true
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function useArmor (options, noArmor) {
|
|
@@ -29,7 +29,7 @@ class KeychainPull {
|
|
|
29
29
|
try {
|
|
30
30
|
privateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
31
31
|
} catch {
|
|
32
|
-
throw new Error(`[NOT_FOUND] private key not found in macOS Keychain (${armoredKeyDisplay(publicKey)}). fix: [dotenvx
|
|
32
|
+
throw new Error(`[NOT_FOUND] private key not found in macOS Keychain (${armoredKeyDisplay(publicKey)}). fix: [dotenvx native up]`)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const result = upsertEnvKey(privateKeyName, privateKey, envKeysFile)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const { derive, publickeys, scan } = require('@dotenvx/primitives')
|
|
3
|
+
|
|
4
|
+
const Errors = require('../helpers/errors')
|
|
5
|
+
const upsertEnvKey = require('../helpers/upsertEnvKey')
|
|
6
|
+
|
|
7
|
+
function latestValues (parsed) {
|
|
8
|
+
const result = {}
|
|
9
|
+
|
|
10
|
+
for (const [key, values] of Object.entries(parsed)) {
|
|
11
|
+
result[key] = values[values.length - 1]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return result
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function lockedCandidates (keysSrc) {
|
|
18
|
+
const { parsed } = scan(keysSrc)
|
|
19
|
+
const values = latestValues(parsed)
|
|
20
|
+
const candidates = []
|
|
21
|
+
|
|
22
|
+
for (const [privateKeyName, privateKey] of Object.entries(values)) {
|
|
23
|
+
if (!privateKeyName.startsWith('DOTENV_PRIVATE_KEY')) continue
|
|
24
|
+
if (!privateKey.startsWith('locked:')) continue
|
|
25
|
+
|
|
26
|
+
const parts = privateKey.split(':')
|
|
27
|
+
if (parts.length < 3) continue
|
|
28
|
+
|
|
29
|
+
candidates.push({
|
|
30
|
+
privateKeyName,
|
|
31
|
+
lockedPrivateKeyValue: privateKey,
|
|
32
|
+
publicKeyValue: parts[1]
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return candidates
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function privateKeyring (keysSrc) {
|
|
40
|
+
const { parsed } = scan(keysSrc)
|
|
41
|
+
const values = latestValues(parsed)
|
|
42
|
+
const keyring = {}
|
|
43
|
+
|
|
44
|
+
for (const [privateKeyName, privateKey] of Object.entries(values)) {
|
|
45
|
+
if (!privateKeyName.startsWith('DOTENV_PRIVATE_KEY')) continue
|
|
46
|
+
if (privateKey.startsWith('locked:')) continue
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const publicKey = derive(privateKey)
|
|
50
|
+
keyring[publicKey] = { privateKeyName, privateKeyValue: privateKey }
|
|
51
|
+
} catch {}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return keyring
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class LockDown {
|
|
58
|
+
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
59
|
+
this.envFile = envFile
|
|
60
|
+
this.envKeysFile = envKeysFile
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
lockedCandidates () {
|
|
64
|
+
const keysSrc = fs.readFileSync(this.envKeysFile, 'utf8')
|
|
65
|
+
const candidates = lockedCandidates(keysSrc)
|
|
66
|
+
|
|
67
|
+
if (candidates.length < 1) {
|
|
68
|
+
throw new Errors({ key: 'DOTENV_PRIVATE_KEY' }).missingKey()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return candidates
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
plan () {
|
|
75
|
+
const envSrc = fs.readFileSync(this.envFile, 'utf8')
|
|
76
|
+
const keysSrc = fs.readFileSync(this.envKeysFile, 'utf8')
|
|
77
|
+
const publicKeyValues = publickeys(envSrc)
|
|
78
|
+
const lockedByPublicKey = {}
|
|
79
|
+
const plaintextByPublicKey = privateKeyring(keysSrc)
|
|
80
|
+
const locked = []
|
|
81
|
+
const alreadyUnlocked = []
|
|
82
|
+
|
|
83
|
+
for (const lockedPrivateKey of lockedCandidates(keysSrc)) {
|
|
84
|
+
lockedByPublicKey[lockedPrivateKey.publicKeyValue] = lockedPrivateKey
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const publicKey of publicKeyValues) {
|
|
88
|
+
const lockedPrivateKey = lockedByPublicKey[publicKey]
|
|
89
|
+
if (lockedPrivateKey) {
|
|
90
|
+
locked.push(lockedPrivateKey)
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const privateKey = plaintextByPublicKey[publicKey]
|
|
95
|
+
if (privateKey) {
|
|
96
|
+
alreadyUnlocked.push({
|
|
97
|
+
changed: false,
|
|
98
|
+
privateKeyName: privateKey.privateKeyName,
|
|
99
|
+
privateKeyValue: privateKey.privateKeyValue,
|
|
100
|
+
lockedPrivateKeyValue: undefined,
|
|
101
|
+
publicKeyValue: publicKey
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (locked.length < 1 && alreadyUnlocked.length < 1) {
|
|
107
|
+
throw new Errors({ key: 'DOTENV_PRIVATE_KEY' }).missingKey()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { locked, alreadyUnlocked }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
run (unlockPrivateKey) {
|
|
114
|
+
const { locked, alreadyUnlocked } = this.plan()
|
|
115
|
+
const results = [...alreadyUnlocked]
|
|
116
|
+
|
|
117
|
+
if (locked.length > 0 && !unlockPrivateKey) {
|
|
118
|
+
throw new Error('missing unlock private key function')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const lockedPrivateKey of locked) {
|
|
122
|
+
let privateKeyValue
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
privateKeyValue = unlockPrivateKey(lockedPrivateKey.lockedPrivateKeyValue)
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new Errors({ privateKeyName: lockedPrivateKey.privateKeyName }).invalidPassphrase()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const publicKeyValue = derive(privateKeyValue)
|
|
131
|
+
|
|
132
|
+
if (publicKeyValue !== lockedPrivateKey.publicKeyValue) continue
|
|
133
|
+
|
|
134
|
+
const result = upsertEnvKey(lockedPrivateKey.privateKeyName, privateKeyValue, this.envKeysFile)
|
|
135
|
+
|
|
136
|
+
results.push({
|
|
137
|
+
changed: result.changed,
|
|
138
|
+
privateKeyName: lockedPrivateKey.privateKeyName,
|
|
139
|
+
privateKeyValue,
|
|
140
|
+
lockedPrivateKeyValue: lockedPrivateKey.lockedPrivateKeyValue,
|
|
141
|
+
publicKeyValue
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (results.length < 1) {
|
|
146
|
+
throw new Errors({ key: 'DOTENV_PRIVATE_KEY' }).missingKey()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
changed: results.some(result => result.changed),
|
|
151
|
+
results
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = LockDown
|
|
157
|
+
module.exports.lockedCandidates = lockedCandidates
|