@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.
- package/CHANGELOG.md +8 -1
- package/README.md +40 -8
- package/package.json +1 -1
- package/src/cli/actions/define.js +17 -0
- package/src/cli/actions/ext/precommit.js +3 -0
- package/src/cli/actions/ext/precommitClean.js +21 -0
- package/src/cli/actions/get.js +2 -1
- package/src/cli/actions/init.js +3 -0
- package/src/cli/actions/lock/down.js +13 -32
- package/src/cli/actions/lock/up.js +13 -30
- package/src/cli/actions/run.js +2 -1
- package/src/cli/commands/custody.js +2 -2
- package/src/cli/commands/ext.js +3 -1
- package/src/cli/commands/fileOptions.js +22 -0
- package/src/cli/dotenvx.js +38 -21
- package/src/lib/custodians/index.js +73 -0
- package/src/lib/{helpers/bitwardenCustody.js → custodians/local/bitwarden.js} +12 -7
- package/src/lib/custodians/local/file.js +18 -0
- package/src/lib/custodians/local/native/backend.js +62 -0
- package/src/lib/custodians/local/native/index.js +28 -0
- package/src/lib/{helpers/storeNativePrivateKey.js → custodians/local/native/store.js} +3 -3
- package/src/lib/{helpers/onePasswordCustody.js → custodians/local/onepassword.js} +9 -5
- package/src/lib/custodians/lock.js +80 -0
- package/src/lib/{providers/armor/index.js → custodians/managed/armor/get.js} +6 -6
- package/src/lib/custodians/managed/armor/index.js +24 -0
- package/src/lib/custodians/managed/armor/store.js +37 -0
- package/src/lib/helpers/installPrecommitFilter.js +50 -0
- package/src/lib/helpers/installPrecommitHook.js +2 -2
- package/src/lib/helpers/lockedValue.js +24 -0
- package/src/lib/helpers/macosKeychain.js +18 -2
- package/src/lib/helpers/matchesStoredKey.js +16 -0
- package/src/lib/helpers/normalizeDotenvConfigPath.js +1 -1
- package/src/lib/helpers/parseWithDecryptor.js +3 -0
- package/src/lib/helpers/prompts.js +18 -0
- package/src/lib/helpers/resolveLockPassword.js +5 -0
- package/src/lib/helpers/selectKeyStorage.js +9 -21
- package/src/lib/helpers/unlockedValue.js +26 -0
- package/src/lib/helpers/withLockedKeys.js +41 -0
- package/src/lib/main.d.ts +6 -0
- package/src/lib/main.js +4 -1
- package/src/lib/providers/index.js +9 -66
- package/src/lib/providers/native/index.js +2 -62
- package/src/lib/providers/provider-worker.js +1 -1
- package/src/lib/resolvers/envs.js +14 -5
- package/src/lib/resolvers/get.js +1 -0
- package/src/lib/services/custodyTransfer.js +2 -2
- package/src/lib/services/init.js +49 -0
- package/src/lib/services/precommit.js +12 -1
- package/src/lib/services/validate.js +1 -0
- package/src/lib/transforms/encrypt.js +6 -49
- 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
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
110
|
-
if (
|
|
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
|
-
|
|
2
|
-
|
|
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')
|
|
@@ -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,
|
package/src/lib/resolvers/get.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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) {
|