@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.
@@ -3,7 +3,7 @@ const Session = require('./../../db/session')
3
3
 
4
4
  function configureArmorCommand (armor) {
5
5
  armor
6
- .description('move private keys off-device')
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 configureKeychainCommand (keychain) {
2
- keychain
3
- .description('store private keys in macOS Keychain')
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
- keychain
9
+ native
10
10
  .command('up')
11
- .description('store key in macOS Keychain')
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
- keychain
17
+ native
18
18
  .command('down')
19
- .description('move key from macOS Keychain to .env.keys')
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
- keychain
25
+ native
26
26
  .command('push')
27
- .description('push key to macOS Keychain from .env.keys')
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
- keychain
33
+ native
34
34
  .command('pull')
35
- .description('pull key from macOS Keychain into .env.keys')
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 keychain
40
+ return native
41
41
  }
42
42
 
43
- module.exports = configureKeychainCommand
43
+ module.exports = configureNativeCommand
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /* c8 ignore start */
4
- const { Command, Option } = require('commander')
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-keychain', 'disable macOS Keychain features')
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-keychain', 'disable macOS Keychain features')
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-keychain', 'disable macOS Keychain features')
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-keychain', 'disable macOS Keychain features')
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-keychain', 'disable macOS Keychain features')
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-keychain', 'disable macOS Keychain features')
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', ' login log in to move keys off-device, share with your team, and audit access')
272
- program.addHelpText('after', ' logout log out of connected security features')
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 keychain
277
- require('./commands/keychain')(program.command('keychain', { hidden: true }))
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 === 'vlt') {
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 macOS Keychain features.
183
+ * Turn off OS secret store features.
184
184
  *
185
185
  * @default false
186
- * @example require('@dotenvx/dotenvx').config({ noKeychain: true })
186
+ * @example require('@dotenvx/dotenvx').config({ noNative: true })
187
187
  */
188
- noKeychain?: boolean;
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 macOS Keychain features.
266
+ * Turn off OS secret store features.
275
267
  *
276
268
  * @default false
277
- * @example require('@dotenvx/dotenvx').set(key, value, { noKeychain: true })
269
+ * @example require('@dotenvx/dotenvx').set(key, value, { noNative: true })
278
270
  */
279
- noKeychain?: boolean;
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 macOS Keychain features.
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', { noOps: true })
354
+ * @example require('@dotenvx/dotenvx').get('KEY', { noNative: true })
379
355
  */
380
- noOps?: boolean;
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 || options.noOps === true || (!options.token && sesh.noArmorSync())
383
+ return options.noArmor === true || (!options.token && sesh.noArmorSync())
384
384
  }
385
385
 
386
386
  function resolveNoKeychain (options = {}) {
387
- return options.noKeychain === true || options.keychain === false
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 - like dotenvx-ops
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.noKeychain !== true && options.keychain !== false
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 keychain up]`)
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