@dotenvx/dotenvx 2.28.0 → 2.28.1

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +40 -8
  3. package/package.json +1 -1
  4. package/src/cli/actions/define.js +17 -0
  5. package/src/cli/actions/ext/precommit.js +3 -0
  6. package/src/cli/actions/ext/precommitClean.js +21 -0
  7. package/src/cli/actions/get.js +2 -1
  8. package/src/cli/actions/init.js +3 -0
  9. package/src/cli/actions/lock/down.js +13 -32
  10. package/src/cli/actions/lock/up.js +13 -30
  11. package/src/cli/actions/run.js +2 -1
  12. package/src/cli/commands/custody.js +2 -2
  13. package/src/cli/commands/ext.js +3 -1
  14. package/src/cli/commands/fileOptions.js +22 -0
  15. package/src/cli/dotenvx.js +38 -21
  16. package/src/lib/custodians/index.js +73 -0
  17. package/src/lib/{helpers/bitwardenCustody.js → custodians/local/bitwarden.js} +12 -7
  18. package/src/lib/custodians/local/file.js +18 -0
  19. package/src/lib/custodians/local/native/backend.js +62 -0
  20. package/src/lib/custodians/local/native/index.js +28 -0
  21. package/src/lib/{helpers/storeNativePrivateKey.js → custodians/local/native/store.js} +3 -3
  22. package/src/lib/{helpers/onePasswordCustody.js → custodians/local/onepassword.js} +9 -5
  23. package/src/lib/custodians/lock.js +80 -0
  24. package/src/lib/{providers/armor/index.js → custodians/managed/armor/get.js} +6 -6
  25. package/src/lib/custodians/managed/armor/index.js +24 -0
  26. package/src/lib/custodians/managed/armor/store.js +37 -0
  27. package/src/lib/helpers/installPrecommitFilter.js +50 -0
  28. package/src/lib/helpers/installPrecommitHook.js +2 -2
  29. package/src/lib/helpers/lockedValue.js +24 -0
  30. package/src/lib/helpers/macosKeychain.js +18 -2
  31. package/src/lib/helpers/matchesStoredKey.js +16 -0
  32. package/src/lib/helpers/normalizeDotenvConfigPath.js +1 -1
  33. package/src/lib/helpers/parseWithDecryptor.js +3 -0
  34. package/src/lib/helpers/prompts.js +18 -0
  35. package/src/lib/helpers/resolveLockPassword.js +5 -0
  36. package/src/lib/helpers/selectKeyStorage.js +9 -21
  37. package/src/lib/helpers/unlockedValue.js +26 -0
  38. package/src/lib/helpers/withLockedKeys.js +41 -0
  39. package/src/lib/main.d.ts +6 -0
  40. package/src/lib/main.js +4 -1
  41. package/src/lib/providers/index.js +9 -66
  42. package/src/lib/providers/native/index.js +2 -62
  43. package/src/lib/providers/provider-worker.js +1 -1
  44. package/src/lib/resolvers/envs.js +14 -5
  45. package/src/lib/resolvers/get.js +1 -0
  46. package/src/lib/services/custodyTransfer.js +2 -2
  47. package/src/lib/services/init.js +49 -0
  48. package/src/lib/services/precommit.js +12 -1
  49. package/src/lib/services/validate.js +1 -0
  50. package/src/lib/transforms/encrypt.js +6 -49
  51. package/src/lib/transforms/set.js +6 -49
package/src/lib/main.d.ts CHANGED
@@ -50,6 +50,9 @@ export function parse<T extends DotenvParseOutput = DotenvParseOutput>(
50
50
  ): T;
51
51
 
52
52
  export interface DotenvConfigOptions {
53
+ /** Password to unlock private keys in memory. Defaults to DOTENVX_LOCK_PASSWORD. */
54
+ lockPassword?: string;
55
+
53
56
  /**
54
57
  * Specify explicit env sources. When set, `path` and `convention` are ignored.
55
58
  *
@@ -339,6 +342,9 @@ export function set(
339
342
  ): Promise<SetOutput>;
340
343
 
341
344
  export interface GetOptions {
345
+ /** Password to unlock private keys in memory. Defaults to DOTENVX_LOCK_PASSWORD. */
346
+ lockPassword?: string;
347
+
342
348
  /**
343
349
  * Specify a custom path if your file containing environment variables is located elsewhere.
344
350
  * Can also be an array of strings, specifying multiple paths.
package/src/lib/main.js CHANGED
@@ -92,6 +92,7 @@ const config = function (options = {}) {
92
92
  noNative,
93
93
  no1Password: options.no1Password,
94
94
  noBitwarden: options.noBitwarden,
95
+ lockPassword: options.lockPassword,
95
96
  noSpinner: options.noSpinner,
96
97
  token: options.token
97
98
  })
@@ -259,6 +260,7 @@ const set = async function (key, value, options = {}) {
259
260
  noNative,
260
261
  no1Password: options.no1Password,
261
262
  noBitwarden: options.noBitwarden,
263
+ lockPassword: options.lockPassword,
262
264
  noCreate,
263
265
  encrypt
264
266
  })
@@ -341,7 +343,8 @@ const get = async function (key, options = {}) {
341
343
  noArmor,
342
344
  noNative,
343
345
  no1Password: options.no1Password,
344
- noBitwarden: options.noBitwarden
346
+ noBitwarden: options.noBitwarden,
347
+ lockPassword: options.lockPassword
345
348
  })
346
349
 
347
350
  if (options.mask !== undefined) {
@@ -1,15 +1,4 @@
1
- const Session = require('./../../db/session')
2
-
3
- const armorProvider = require('./armor/index')
4
- const nativeProvider = require('./native/index')
5
- const onePasswordCustody = require('../helpers/onePasswordCustody')
6
- const bitwardenCustody = require('../helpers/bitwardenCustody')
7
-
8
- function syncArmorProvider (publicKeyHex) {
9
- const { createSyncFn } = require('@dotenvx/tooling')
10
- const runProviderSync = createSyncFn(require.resolve('./provider-worker.js'))
11
- return runProviderSync(publicKeyHex)
12
- }
1
+ const custodians = require('../custodians')
13
2
 
14
3
  function hasKey (keyring, publicKeyHex) {
15
4
  return keyring && keyring[publicKeyHex]
@@ -37,32 +26,6 @@ function composeProvidersSync (providerFns) {
37
26
  }
38
27
  }
39
28
 
40
- function armorProviderForOptions (options) {
41
- return (publicKeyHex) => armorProvider(publicKeyHex, {
42
- onStatus: options.onStatus,
43
- token: options.token,
44
- command: options.command
45
- })
46
- }
47
-
48
- function useNative (options) {
49
- if (!['darwin', 'linux', 'win32'].includes(process.platform)) return false
50
- if (process.env.CI) return false
51
- return options.noNative !== true && options.native !== false && process.env.DOTENVX_NO_NATIVE !== 'true'
52
- }
53
-
54
- function useOnePassword (options) {
55
- return options.no1Password !== true && process.env.DOTENVX_NO_1PASSWORD !== 'true' && onePasswordCustody.configured()
56
- }
57
-
58
- function useBitwarden (options) {
59
- return options.noBitwarden !== true && process.env.DOTENVX_NO_BITWARDEN !== 'true' && bitwardenCustody.configured()
60
- }
61
-
62
- function useArmor (options, noArmor) {
63
- return options.noArmor !== true && options.armor !== false && !noArmor
64
- }
65
-
66
29
  function providerFrom (providerFns, compose) {
67
30
  if (providerFns.length === 0) return null
68
31
  if (providerFns.length === 1) return providerFns[0]
@@ -75,21 +38,11 @@ async function providers (options = {}) {
75
38
  return options.provider
76
39
  }
77
40
 
78
- const providerFns = []
79
-
80
- if (useNative(options)) {
81
- providerFns.push(nativeProvider)
82
- }
83
-
84
- if (useOnePassword(options)) providerFns.push(onePasswordCustody.get)
85
- if (useBitwarden(options)) providerFns.push(bitwardenCustody.get)
41
+ const providerFns = custodians.providers(options)
86
42
 
87
- if (options.noArmor !== true && options.armor !== false) {
88
- const sesh = new Session()
89
- const noArmor = !options.token && await sesh.noArmor()
90
- if (useArmor(options, noArmor)) {
91
- providerFns.push(armorProviderForOptions(options))
92
- }
43
+ const armor = custodians.get('armored')
44
+ if (armor.enabled(options) && await armor.configured(options)) {
45
+ providerFns.push(publicKey => armor.get(publicKey, options))
93
46
  }
94
47
 
95
48
  return providerFrom(providerFns, composeProviders)
@@ -100,21 +53,11 @@ providers.sync = function providersSync (options = {}) {
100
53
  return options.provider
101
54
  }
102
55
 
103
- const providerFns = []
104
-
105
- if (useNative(options)) {
106
- providerFns.push(nativeProvider)
107
- }
56
+ const providerFns = custodians.providers(options, true)
108
57
 
109
- if (useOnePassword(options)) providerFns.push(onePasswordCustody.getSync)
110
- if (useBitwarden(options)) providerFns.push(bitwardenCustody.getSync)
111
-
112
- if (options.noArmor !== true && options.armor !== false) {
113
- const sesh = new Session()
114
- const noArmor = !options.token && sesh.noArmorSync()
115
- if (useArmor(options, noArmor)) {
116
- providerFns.push(syncArmorProvider)
117
- }
58
+ const armor = custodians.get('armored')
59
+ if (armor.enabled(options) && armor.configuredSync(options)) {
60
+ providerFns.push(armor.getSync)
118
61
  }
119
62
 
120
63
  return providerFrom(providerFns, composeProvidersSync)
@@ -1,62 +1,2 @@
1
- const macosKeychain = require('../../helpers/macosKeychain')
2
- const windowsCredentialManager = require('../../helpers/windowsCredentialManager')
3
- const linuxSecretService = require('../../helpers/linuxSecretService')
4
-
5
- function get (key) {
6
- if (process.platform === 'win32') {
7
- return windowsCredentialManager.get(key)
8
- }
9
-
10
- if (process.platform === 'linux') {
11
- return linuxSecretService.get(key)
12
- }
13
-
14
- return macosKeychain.get(key)
15
- }
16
-
17
- function set (key, value, label = key) {
18
- if (process.platform === 'win32') {
19
- windowsCredentialManager.set(key, value, label)
20
- return
21
- }
22
-
23
- if (process.platform === 'linux') {
24
- linuxSecretService.set(key, value, label)
25
- return
26
- }
27
-
28
- macosKeychain.set(key, value, label)
29
- }
30
-
31
- index.delete = function (key) {
32
- if (process.platform === 'win32') {
33
- windowsCredentialManager.delete(key)
34
- return
35
- }
36
-
37
- if (process.platform === 'linux') {
38
- linuxSecretService.delete(key)
39
- return
40
- }
41
-
42
- macosKeychain.delete(key)
43
- }
44
-
45
- function index (publicKeyHex) {
46
- if (!['darwin', 'linux', 'win32'].includes(process.platform)) return {}
47
-
48
- try {
49
- const privateKeyHex = get(publicKeyHex)
50
-
51
- if (!privateKeyHex) return {}
52
-
53
- return { [publicKeyHex]: privateKeyHex }
54
- } catch {
55
- return {}
56
- }
57
- }
58
-
59
- index.set = set
60
- index.get = get
61
-
62
- module.exports = index
1
+ // Compatibility adapter for session credentials and native transfer commands.
2
+ module.exports = require('../../custodians/local/native/backend')
@@ -1,5 +1,5 @@
1
1
  const { runAsWorker } = require('@dotenvx/tooling')
2
- const provider = require('./armor/index')
2
+ const provider = require('../custodians/managed/armor').get
3
3
 
4
4
  runAsWorker(async (publicKeyHex) => {
5
5
  return provider(publicKeyHex)
@@ -54,8 +54,9 @@ function inject (processEnv, parsed) {
54
54
  }
55
55
  }
56
56
 
57
- function buildParseOptions ({ processEnv, overload, envKeysFilepath, provider, decryptor, proxyCredentials, proxyRules }) {
57
+ function buildParseOptions ({ lockPassword, processEnv, overload, envKeysFilepath, provider, decryptor, proxyCredentials, proxyRules }) {
58
58
  const options = {
59
+ lockPassword,
59
60
  processEnv,
60
61
  overload,
61
62
  proxyCredentials,
@@ -76,7 +77,7 @@ function buildParseOptions ({ processEnv, overload, envKeysFilepath, provider, d
76
77
  return options
77
78
  }
78
79
 
79
- async function injectEnv ({ env, overload, processEnv, envKeysFilepath, provider, decryptor, no1Password, noBitwarden, onStatus, proxyCredentials, proxyRules }) {
80
+ async function injectEnv ({ env, lockPassword, overload, processEnv, envKeysFilepath, provider, decryptor, no1Password, noBitwarden, onStatus, proxyCredentials, proxyRules }) {
80
81
  const row = {}
81
82
  row.type = TYPE_ENV
82
83
  row.string = env.value
@@ -90,6 +91,7 @@ async function injectEnv ({ env, overload, processEnv, envKeysFilepath, provider
90
91
  const parseOptions = buildParseOptions({
91
92
  processEnv: parseProcessEnv,
92
93
  overload,
94
+ lockPassword,
93
95
  envKeysFilepath,
94
96
  provider,
95
97
  decryptor,
@@ -131,7 +133,7 @@ async function injectEnv ({ env, overload, processEnv, envKeysFilepath, provider
131
133
  return row
132
134
  }
133
135
 
134
- function injectEnvSync ({ env, overload, processEnv, envKeysFilepath, provider, decryptor, no1Password, noBitwarden }) {
136
+ function injectEnvSync ({ env, lockPassword, overload, processEnv, envKeysFilepath, provider, decryptor, no1Password, noBitwarden }) {
135
137
  const row = {}
136
138
  row.type = TYPE_ENV
137
139
  row.string = env.value
@@ -145,6 +147,7 @@ function injectEnvSync ({ env, overload, processEnv, envKeysFilepath, provider,
145
147
  const parseOptions = buildParseOptions({
146
148
  processEnv: parseProcessEnv,
147
149
  overload,
150
+ lockPassword,
148
151
  envKeysFilepath,
149
152
  provider,
150
153
  decryptor
@@ -183,7 +186,7 @@ function injectEnvSync ({ env, overload, processEnv, envKeysFilepath, provider,
183
186
  return row
184
187
  }
185
188
 
186
- async function injectEnvFile ({ env, overload, processEnv, envKeysFilepath, provider, decryptor, readableFilepaths, no1Password, noBitwarden, onStatus, proxyCredentials, proxyRules }) {
189
+ async function injectEnvFile ({ env, lockPassword, overload, processEnv, envKeysFilepath, provider, decryptor, readableFilepaths, no1Password, noBitwarden, onStatus, proxyCredentials, proxyRules }) {
187
190
  const row = {}
188
191
  row.type = TYPE_ENV_FILE
189
192
  row.filepath = env.value
@@ -199,6 +202,7 @@ async function injectEnvFile ({ env, overload, processEnv, envKeysFilepath, prov
199
202
  const parseOptions = buildParseOptions({
200
203
  processEnv,
201
204
  overload,
205
+ lockPassword,
202
206
  envKeysFilepath: fk,
203
207
  provider,
204
208
  decryptor,
@@ -245,7 +249,7 @@ async function injectEnvFile ({ env, overload, processEnv, envKeysFilepath, prov
245
249
  return row
246
250
  }
247
251
 
248
- function injectEnvFileSync ({ env, overload, processEnv, envKeysFilepath, provider, decryptor, readableFilepaths, no1Password, noBitwarden }) {
252
+ function injectEnvFileSync ({ env, lockPassword, overload, processEnv, envKeysFilepath, provider, decryptor, readableFilepaths, no1Password, noBitwarden }) {
249
253
  const row = {}
250
254
  row.type = TYPE_ENV_FILE
251
255
  row.filepath = env.value
@@ -261,6 +265,7 @@ function injectEnvFileSync ({ env, overload, processEnv, envKeysFilepath, provid
261
265
  const parseOptions = buildParseOptions({
262
266
  processEnv,
263
267
  overload,
268
+ lockPassword,
264
269
  envKeysFilepath: fk,
265
270
  provider,
266
271
  decryptor
@@ -318,6 +323,7 @@ async function envs (options = {}) {
318
323
  processedEnvs.push(await injectEnvFile({
319
324
  env,
320
325
  overload: options.overload,
326
+ lockPassword: options.lockPassword,
321
327
  processEnv,
322
328
  envKeysFilepath,
323
329
  provider,
@@ -333,6 +339,7 @@ async function envs (options = {}) {
333
339
  processedEnvs.push(await injectEnv({
334
340
  env,
335
341
  overload: options.overload,
342
+ lockPassword: options.lockPassword,
336
343
  processEnv,
337
344
  envKeysFilepath,
338
345
  provider,
@@ -367,6 +374,7 @@ function envsSync (options = {}) {
367
374
  processedEnvs.push(injectEnvFileSync({
368
375
  env,
369
376
  overload: options.overload,
377
+ lockPassword: options.lockPassword,
370
378
  processEnv,
371
379
  envKeysFilepath,
372
380
  provider,
@@ -379,6 +387,7 @@ function envsSync (options = {}) {
379
387
  processedEnvs.push(injectEnvSync({
380
388
  env,
381
389
  overload: options.overload,
390
+ lockPassword: options.lockPassword,
382
391
  processEnv,
383
392
  envKeysFilepath,
384
393
  provider,
@@ -53,6 +53,7 @@ function buildOptions (options, processEnv) {
53
53
  noNative: options.noNative,
54
54
  no1Password: options.no1Password,
55
55
  noBitwarden: options.noBitwarden,
56
+ lockPassword: options.lockPassword,
56
57
  onStatus: options.onStatus
57
58
  }
58
59
  }
@@ -1,4 +1,4 @@
1
- const { derive } = require('@dotenvx/primitives')
1
+ const matchesStoredKey = require('../helpers/matchesStoredKey')
2
2
  const keynames = require('../conventions/keynames')
3
3
  const readEnvKey = require('../helpers/readEnvKey')
4
4
  const removeEnvKey = require('../helpers/removeEnvKey')
@@ -6,7 +6,7 @@ const upsertEnvKey = require('../helpers/upsertEnvKey')
6
6
 
7
7
  function verify (publicKey, privateKey) {
8
8
  try {
9
- if (derive(privateKey) === publicKey) return
9
+ if (matchesStoredKey(publicKey, privateKey)) return
10
10
  } catch {}
11
11
  throw new Error('private key does not match the .env public key')
12
12
  }
@@ -0,0 +1,49 @@
1
+ const fs = require('node:fs')
2
+ const path = require('node:path')
3
+ const { scan, encrypted } = require('@dotenvx/primitives')
4
+
5
+ module.exports = function init ({ directory = process.cwd(), envFile } = {}) {
6
+ const target = path.resolve(directory, 'Envfile')
7
+ // lstat also preserves dangling symlinks. Never overwrite an existing Envfile.
8
+ try {
9
+ fs.lstatSync(target)
10
+ return { created: false }
11
+ } catch (error) {
12
+ if (error.code !== 'ENOENT') throw error
13
+ }
14
+
15
+ const sources = []
16
+ const names = new Set()
17
+ let requireEncryption = false
18
+ for (const candidate of envFile ? [envFile] : ['.env.example', '.env']) {
19
+ let src
20
+ try {
21
+ src = fs.readFileSync(path.resolve(directory, candidate), 'utf8')
22
+ } catch (error) {
23
+ if (envFile || error.code !== 'ENOENT') throw error
24
+ continue
25
+ }
26
+ sources.push(candidate)
27
+ // scan preserves every assignment as an array, without decrypting or expanding.
28
+ for (const [key, values] of Object.entries(scan(src).parsed)) {
29
+ if (/^DOTENV_(?:PUBLIC|PRIVATE)_KEY(?:_|$)/.test(key)) continue
30
+ names.add(key)
31
+ if (values.some(value => encrypted(value))) requireEncryption = true
32
+ }
33
+ }
34
+
35
+ const keys = [...names]
36
+ const invalid = keys.filter(key => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
37
+ if (invalid.length) throw new Error(`Unsupported Envfile variable names: ${invalid.join(', ')}`)
38
+
39
+ const declarations = keys.map(key => `env "${key}"`).join('\n')
40
+ const content = `encrypted ${requireEncryption}\n` +
41
+ (declarations ? `\n${declarations}\n` : '')
42
+ try {
43
+ fs.writeFileSync(target, content, { flag: 'wx' })
44
+ } catch (error) {
45
+ if (error.code === 'EEXIST') return { created: false }
46
+ throw error
47
+ }
48
+ return { created: true, source: sources.join(', '), count: keys.length }
49
+ }
@@ -17,10 +17,14 @@ class Precommit {
17
17
  this.directory = directory
18
18
  // options
19
19
  this.install = options.install
20
+ this.global = options.global
20
21
  this.excludeEnvFile = ['test/**', 'tests/**', 'spec/**', 'specs/**', 'pytest/**', 'test_suite/**']
21
22
  }
22
23
 
23
24
  run () {
25
+ if (this.global && !this.install) {
26
+ throw new Error('--global requires --install')
27
+ }
24
28
  if (this.install) {
25
29
  const {
26
30
  successMessage
@@ -136,7 +140,14 @@ class Precommit {
136
140
  }
137
141
 
138
142
  _installPrecommitHook () {
139
- return new InstallPrecommitHook().run()
143
+ if (this.global) {
144
+ const attributesPath = require('../helpers/installPrecommitFilter')(true)
145
+ return { successMessage: `▣ dotenvx required clean filter installed globally [${attributesPath}]` }
146
+ }
147
+ const hookPath = require('../helpers/installPrecommitFilter')()
148
+ const result = new InstallPrecommitHook(hookPath).run()
149
+ result.successMessage += '\n▣ dotenvx required clean filter installed (blocks staging plaintext .env files)'
150
+ return result
140
151
  }
141
152
  }
142
153
 
@@ -31,6 +31,7 @@ module.exports = async function validate ({ envs = [], options = {}, processEnv
31
31
  noNative: options.native === false || options.noNative === true,
32
32
  no1Password: options['1password'] === false || options.no1Password === true,
33
33
  noBitwarden: options.bitwarden === false || options.noBitwarden === true,
34
+ lockPassword: options.lockPassword,
34
35
  token: options.token,
35
36
  command,
36
37
  onStatus
@@ -8,18 +8,11 @@ const SAMPLE_ENV_KIT = require('../helpers/kits/sample')
8
8
  const Errors = require('../helpers/errors')
9
9
  const { determine } = require('./../helpers/envResolution')
10
10
  const detectEncoding = require('./../helpers/detectEncoding')
11
- const { isDotenvPublicKey, isPlainKey, mutateSrc, mutateKeysSrc } = require('../helpers/cryptography')
11
+ const { isDotenvPublicKey, isPlainKey, mutateSrc } = require('../helpers/cryptography')
12
12
  const keynames = require('../conventions/keynames')
13
- const PostArmorUp = require('../api/postArmorUp')
14
- const prompts = require('../helpers/prompts')
15
- const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
16
- const isTeamRequiredError = require('../helpers/isTeamRequiredError')
17
- const Session = require('../../db/session')
18
13
 
19
14
  const selectKeyStorage = require('../helpers/selectKeyStorage')
20
- const storeNativePrivateKey = require('../helpers/storeNativePrivateKey')
21
- const onePasswordCustody = require('../helpers/onePasswordCustody')
22
- const bitwardenCustody = require('../helpers/bitwardenCustody')
15
+ const custodians = require('../custodians')
23
16
 
24
17
  async function encryptTransform (options = {}) {
25
18
  const envs = options.envs || []
@@ -27,6 +20,7 @@ async function encryptTransform (options = {}) {
27
20
  const ek = options.ek
28
21
  const fk = options.fk || '.env.keys'
29
22
  let storage
23
+ const custodyContext = {}
30
24
  const noCreate = options.noCreate
31
25
 
32
26
  const processedEnvs = []
@@ -87,46 +81,9 @@ async function encryptTransform (options = {}) {
87
81
 
88
82
  const comment = path.basename(envFilepath)
89
83
 
90
- if (storage === 'bitwarden') {
91
- await bitwardenCustody.set(publicKey, privateKey)
92
- } else if (storage === 'onepassword') {
93
- await onePasswordCustody.set(publicKey, privateKey)
94
- } else if (storage === 'native' && storeNativePrivateKey(publicKey, privateKey, fk)) {
95
- row.nativePrivateKeyAdded = true
96
- } else if (storage !== 'armored') {
97
- const mutated = mutateKeysSrc({ keysSrc, privateKeyName, privateKeyValue: privateKey, comment })
98
- keysSrc = mutated.keysSrc
99
- } else {
100
- const sesh = new Session()
101
- const hostname = sesh.hostname()
102
- const token = sesh.token()
103
- const devicePublicKey = sesh.devicePublicKey()
104
-
105
- try {
106
- await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey, undefined).run()
107
- } catch (error) {
108
- if (!isTeamRequiredError(error)) {
109
- throw error
110
- }
111
-
112
- const choices = teamChoicesFromMeta(error.meta)
113
-
114
- let team = choices[0].value
115
- if (choices.length > 1) {
116
- team = await prompts.select({
117
- message: 'Select team',
118
- choices
119
- }, {
120
- input: process.stdin,
121
- output: process.stderr
122
- })
123
- }
124
-
125
- await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey, team).run()
126
- }
127
-
128
- // don't set keysSrc (in armor)
129
- }
84
+ const stored = await custodians.store(storage, publicKey, privateKey, Object.assign(custodyContext, { keysSrc, privateKeyName, comment, keysFilepath: fk }))
85
+ if (Object.prototype.hasOwnProperty.call(stored, 'keysSrc')) keysSrc = stored.keysSrc
86
+ if (stored.nativePrivateKeyAdded) row.nativePrivateKeyAdded = true
130
87
  }
131
88
 
132
89
  const { parsed } = scan(row.envSrc, { ik, ek })
@@ -7,19 +7,12 @@ const TYPE_ENV_FILE = 'envFile'
7
7
  const getResolver = require('./../resolvers/get')
8
8
  const { determine } = require('./../helpers/envResolution')
9
9
  const detectEncoding = require('./../helpers/detectEncoding')
10
- const { isPlainKey, mutateSrc, mutateKeysSrc } = require('../helpers/cryptography')
10
+ const { isPlainKey, mutateSrc } = require('../helpers/cryptography')
11
11
  const keynames = require('../conventions/keynames')
12
12
  const Errors = require('../helpers/errors')
13
- const PostArmorUp = require('../api/postArmorUp')
14
- const prompts = require('../helpers/prompts')
15
- const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
16
- const isTeamRequiredError = require('../helpers/isTeamRequiredError')
17
- const Session = require('../../db/session')
18
13
 
19
14
  const selectKeyStorage = require('../helpers/selectKeyStorage')
20
- const storeNativePrivateKey = require('../helpers/storeNativePrivateKey')
21
- const onePasswordCustody = require('../helpers/onePasswordCustody')
22
- const bitwardenCustody = require('../helpers/bitwardenCustody')
15
+ const custodians = require('../custodians')
23
16
 
24
17
  async function setTransform (options = {}) {
25
18
  const envs = options.envs || []
@@ -28,6 +21,7 @@ async function setTransform (options = {}) {
28
21
  const fk = options.fk || '.env.keys'
29
22
  const noArmor = options.noArmor
30
23
  let storage
24
+ const custodyContext = {}
31
25
  const noNative = options.noNative
32
26
  const noCreate = options.noCreate
33
27
  const noEncrypt = !options.encrypt || isPlainKey(key)
@@ -91,46 +85,9 @@ async function setTransform (options = {}) {
91
85
 
92
86
  const comment = path.basename(envFilepath)
93
87
 
94
- if (storage === 'bitwarden') {
95
- await bitwardenCustody.set(publicKey, privateKey)
96
- } else if (storage === 'onepassword') {
97
- await onePasswordCustody.set(publicKey, privateKey)
98
- } else if (storage === 'native' && storeNativePrivateKey(publicKey, privateKey, fk)) {
99
- row.nativePrivateKeyAdded = true
100
- } else if (storage !== 'armored') {
101
- const mutated = mutateKeysSrc({ keysSrc, privateKeyName, privateKeyValue: privateKey, comment })
102
- keysSrc = mutated.keysSrc
103
- } else {
104
- const sesh = new Session()
105
- const hostname = sesh.hostname()
106
- const token = sesh.token()
107
- const devicePublicKey = sesh.devicePublicKey()
108
-
109
- try {
110
- await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey, undefined).run()
111
- } catch (error) {
112
- if (!isTeamRequiredError(error)) {
113
- throw error
114
- }
115
-
116
- const choices = teamChoicesFromMeta(error.meta)
117
-
118
- let team = choices[0].value
119
- if (choices.length > 1) {
120
- team = await prompts.select({
121
- message: 'Select team',
122
- choices
123
- }, {
124
- input: process.stdin,
125
- output: process.stderr
126
- })
127
- }
128
-
129
- await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey, team).run()
130
- }
131
-
132
- // don't set keysSrc (in armor)
133
- }
88
+ const stored = await custodians.store(storage, publicKey, privateKey, Object.assign(custodyContext, { keysSrc, privateKeyName, comment, keysFilepath: fk }))
89
+ if (Object.prototype.hasOwnProperty.call(stored, 'keysSrc')) keysSrc = stored.keysSrc
90
+ if (stored.nativePrivateKeyAdded) row.nativePrivateKeyAdded = true
134
91
  }
135
92
 
136
93
  if (noEncrypt) {