@dotenvx/dotenvx 1.55.0 → 1.56.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 +18 -1
- package/package.json +3 -3
- package/src/cli/actions/encrypt.js +0 -3
- package/src/cli/actions/get.js +4 -2
- package/src/cli/actions/keypair.js +3 -2
- package/src/cli/actions/rotate.js +10 -5
- package/src/cli/actions/run.js +1 -14
- package/src/cli/actions/set.js +3 -2
- package/src/cli/commands/ext.js +0 -3
- package/src/cli/dotenvx.js +12 -9
- package/src/db/session.js +21 -0
- package/src/lib/extensions/ops.js +98 -0
- package/src/lib/helpers/buildEnvs.js +2 -15
- package/src/lib/helpers/{decryptKeyValue.js → cryptography/decryptKeyValue.js} +1 -1
- package/src/lib/helpers/cryptography/index.js +14 -0
- package/src/lib/helpers/{isPublicKey.js → cryptography/isPublicKey.js} +1 -1
- package/src/lib/helpers/{keypair.js → cryptography/localKeypair.js} +2 -2
- package/src/lib/helpers/cryptography/mutateKeysSrc.js +38 -0
- package/src/lib/helpers/cryptography/mutateSrc.js +24 -0
- package/src/lib/helpers/cryptography/opsKeypair.js +14 -0
- package/src/lib/helpers/cryptography/provision.js +47 -0
- package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +26 -0
- package/src/lib/helpers/envResolution/determine.js +46 -0
- package/src/lib/helpers/{guessEnvironment.js → envResolution/environment.js} +4 -6
- package/src/lib/helpers/envResolution/index.js +8 -0
- package/src/lib/helpers/errors.js +13 -0
- package/src/lib/helpers/findEnvFiles.js +1 -1
- package/src/lib/helpers/isFullyEncrypted.js +3 -3
- package/src/lib/helpers/keyResolution/index.js +13 -0
- package/src/lib/helpers/keyResolution/keyNames.js +24 -0
- package/src/lib/helpers/keyResolution/keyValues.js +85 -0
- package/src/lib/helpers/keyResolution/readFileKey.js +15 -0
- package/src/lib/helpers/keyResolution/readProcessKey.js +7 -0
- package/src/lib/helpers/parse.js +1 -1
- package/src/lib/helpers/prependPublicKey.js +17 -0
- package/src/lib/helpers/preserveShebang.js +16 -0
- package/src/lib/main.d.ts +19 -3
- package/src/lib/main.js +9 -17
- package/src/lib/services/decrypt.js +23 -19
- package/src/lib/services/encrypt.js +41 -136
- package/src/lib/services/get.js +3 -3
- package/src/lib/services/keypair.js +12 -16
- package/src/lib/services/prebuild.js +2 -2
- package/src/lib/services/precommit.js +2 -2
- package/src/lib/services/rotate.js +59 -42
- package/src/lib/services/run.js +29 -112
- package/src/lib/services/sets.js +40 -124
- package/src/lib/helpers/decrypt.js +0 -31
- package/src/lib/helpers/deprecationNotice.js +0 -17
- package/src/lib/helpers/determineEnvs.js +0 -65
- package/src/lib/helpers/encrypt.js +0 -29
- package/src/lib/helpers/findPrivateKey.js +0 -25
- package/src/lib/helpers/findPublicKey.js +0 -15
- package/src/lib/helpers/guessPrivateKeyName.js +0 -18
- package/src/lib/helpers/guessPublicKeyName.js +0 -18
- package/src/lib/helpers/parseEncryptionKeyFromDotenvKey.js +0 -19
- package/src/lib/helpers/parseEnvironmentFromDotenvKey.js +0 -19
- package/src/lib/helpers/smartDotenvPrivateKey.js +0 -89
- package/src/lib/helpers/smartDotenvPublicKey.js +0 -42
- package/src/lib/services/ops.js +0 -111
- /package/src/lib/helpers/{encryptValue.js → cryptography/encryptValue.js} +0 -0
- /package/src/lib/helpers/{isEncrypted.js → cryptography/isEncrypted.js} +0 -0
package/src/lib/services/sets.js
CHANGED
|
@@ -4,27 +4,36 @@ const path = require('path')
|
|
|
4
4
|
const TYPE_ENV_FILE = 'envFile'
|
|
5
5
|
|
|
6
6
|
const Errors = require('./../helpers/errors')
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
determine
|
|
10
|
+
} = require('./../helpers/envResolution')
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
keyNames,
|
|
14
|
+
keyValues
|
|
15
|
+
} = require('./../helpers/keyResolution')
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
encryptValue,
|
|
19
|
+
decryptKeyValue,
|
|
20
|
+
isEncrypted,
|
|
21
|
+
provision,
|
|
22
|
+
provisionWithPrivateKey
|
|
23
|
+
} = require('./../helpers/cryptography')
|
|
24
|
+
|
|
11
25
|
const replace = require('./../helpers/replace')
|
|
12
26
|
const dotenvParse = require('./../helpers/dotenvParse')
|
|
13
27
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
14
|
-
const determineEnvs = require('./../helpers/determineEnvs')
|
|
15
|
-
const { findPrivateKey } = require('./../helpers/findPrivateKey')
|
|
16
|
-
const findPublicKey = require('./../helpers/findPublicKey')
|
|
17
|
-
const keypair = require('./../helpers/keypair')
|
|
18
|
-
const truncate = require('./../helpers/truncate')
|
|
19
|
-
const isEncrypted = require('./../helpers/isEncrypted')
|
|
20
28
|
|
|
21
29
|
class Sets {
|
|
22
|
-
constructor (key, value, envs = [], encrypt = true, envKeysFilepath = null) {
|
|
23
|
-
this.envs =
|
|
30
|
+
constructor (key, value, envs = [], encrypt = true, envKeysFilepath = null, opsOn = false) {
|
|
31
|
+
this.envs = determine(envs, process.env)
|
|
24
32
|
this.key = key
|
|
25
33
|
this.value = value
|
|
26
34
|
this.encrypt = encrypt
|
|
27
35
|
this.envKeysFilepath = envKeysFilepath
|
|
36
|
+
this.opsOn = opsOn
|
|
28
37
|
|
|
29
38
|
this.processedEnvs = []
|
|
30
39
|
this.changedFilepaths = new Set()
|
|
@@ -57,14 +66,13 @@ class Sets {
|
|
|
57
66
|
row.value = this.value || null
|
|
58
67
|
row.type = TYPE_ENV_FILE
|
|
59
68
|
|
|
60
|
-
const filename = path.basename(envFilepath)
|
|
61
69
|
const filepath = path.resolve(envFilepath)
|
|
62
70
|
row.filepath = filepath
|
|
63
71
|
row.envFilepath = envFilepath
|
|
64
72
|
row.changed = false
|
|
65
73
|
|
|
66
74
|
try {
|
|
67
|
-
const encoding =
|
|
75
|
+
const encoding = detectEncoding(filepath)
|
|
68
76
|
let envSrc = fsx.readFileX(filepath, { encoding })
|
|
69
77
|
const envParsed = dotenvParse(envSrc)
|
|
70
78
|
row.originalValue = envParsed[row.key] || null
|
|
@@ -75,86 +83,28 @@ class Sets {
|
|
|
75
83
|
let publicKey
|
|
76
84
|
let privateKey
|
|
77
85
|
|
|
78
|
-
const publicKeyName =
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
const
|
|
91
|
-
publicKey =
|
|
92
|
-
privateKey =
|
|
86
|
+
const { publicKeyName, privateKeyName } = keyNames(filepath)
|
|
87
|
+
const { publicKeyValue, privateKeyValue } = keyValues(filepath, { keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
|
|
88
|
+
|
|
89
|
+
// first pass - provision
|
|
90
|
+
if (!privateKeyValue && !publicKeyValue) {
|
|
91
|
+
const prov = provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
|
|
92
|
+
envSrc = prov.envSrc
|
|
93
|
+
publicKey = prov.publicKey
|
|
94
|
+
privateKey = prov.privateKey
|
|
95
|
+
row.privateKeyAdded = prov.privateKeyAdded
|
|
96
|
+
row.envKeysFilepath = prov.envKeysFilepath
|
|
97
|
+
} else if (privateKeyValue) {
|
|
98
|
+
const prov = provisionWithPrivateKey({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, privateKeyValue, publicKeyValue, publicKeyName })
|
|
99
|
+
publicKey = prov.publicKey
|
|
100
|
+
privateKey = prov.privateKey
|
|
101
|
+
envSrc = prov.envSrc
|
|
93
102
|
|
|
94
103
|
if (row.originalValue) {
|
|
95
104
|
row.originalValue = decryptKeyValue(row.key, row.originalValue, privateKeyName, privateKey)
|
|
96
105
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (existingPublicKey && existingPublicKey !== publicKey) {
|
|
100
|
-
const error = new Error(`derived public key (${truncate(publicKey)}) does not match the existing public key (${truncate(existingPublicKey)})`)
|
|
101
|
-
error.code = 'INVALID_DOTENV_PRIVATE_KEY'
|
|
102
|
-
error.help = `debug info: ${privateKeyName}=${truncate(existingPrivateKey)} (derived ${publicKeyName}=${truncate(publicKey)} vs existing ${publicKeyName}=${truncate(existingPublicKey)})`
|
|
103
|
-
throw error
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// typical scenario when encrypting a monorepo second .env file from a prior generated -fk .env.keys file
|
|
107
|
-
if (!existingPublicKey) {
|
|
108
|
-
const ps = this._preserveShebang(envSrc)
|
|
109
|
-
const firstLinePreserved = ps.firstLinePreserved
|
|
110
|
-
envSrc = ps.envSrc
|
|
111
|
-
|
|
112
|
-
const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
|
|
113
|
-
|
|
114
|
-
envSrc = `${firstLinePreserved}${prependPublicKey}\n${envSrc}`
|
|
115
|
-
}
|
|
116
|
-
} else if (existingPublicKey) {
|
|
117
|
-
publicKey = existingPublicKey
|
|
118
|
-
} else {
|
|
119
|
-
// .env.keys
|
|
120
|
-
let keysSrc = ''
|
|
121
|
-
if (fsx.existsSync(envKeysFilepath)) {
|
|
122
|
-
keysSrc = fsx.readFileX(envKeysFilepath)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const ps = this._preserveShebang(envSrc)
|
|
126
|
-
const firstLinePreserved = ps.firstLinePreserved
|
|
127
|
-
envSrc = ps.envSrc
|
|
128
|
-
|
|
129
|
-
const kp = keypair() // generates a fresh keypair in memory
|
|
130
|
-
publicKey = kp.publicKey
|
|
131
|
-
privateKey = kp.privateKey
|
|
132
|
-
|
|
133
|
-
const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
|
|
134
|
-
|
|
135
|
-
// privateKey
|
|
136
|
-
const firstTimeKeysSrc = [
|
|
137
|
-
'#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
|
|
138
|
-
'#/ private decryption keys. DO NOT commit to source control /',
|
|
139
|
-
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
140
|
-
// '#/ backup with: `dotenvx ops backup` /',
|
|
141
|
-
'#/----------------------------------------------------------/'
|
|
142
|
-
].join('\n')
|
|
143
|
-
const appendPrivateKey = [
|
|
144
|
-
`# ${filename}`,
|
|
145
|
-
`${privateKeyName}=${privateKey}`,
|
|
146
|
-
''
|
|
147
|
-
].join('\n')
|
|
148
|
-
|
|
149
|
-
envSrc = `${firstLinePreserved}${prependPublicKey}\n${envSrc}`
|
|
150
|
-
keysSrc = keysSrc.length > 1 ? keysSrc : `${firstTimeKeysSrc}\n`
|
|
151
|
-
keysSrc = `${keysSrc}\n${appendPrivateKey}`
|
|
152
|
-
|
|
153
|
-
// write to .env.keys
|
|
154
|
-
fsx.writeFileX(envKeysFilepath, keysSrc)
|
|
155
|
-
|
|
156
|
-
row.privateKeyAdded = true
|
|
157
|
-
row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), path.basename(envKeysFilepath))
|
|
106
|
+
} else if (publicKeyValue) {
|
|
107
|
+
publicKey = publicKeyValue
|
|
158
108
|
}
|
|
159
109
|
|
|
160
110
|
row.publicKey = publicKey
|
|
@@ -184,40 +134,6 @@ class Sets {
|
|
|
184
134
|
|
|
185
135
|
this.processedEnvs.push(row)
|
|
186
136
|
}
|
|
187
|
-
|
|
188
|
-
_detectEncoding (filepath) {
|
|
189
|
-
return detectEncoding(filepath)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
_prependPublicKey (publicKeyName, publicKey, filename, relativeFilepath = '.env.keys') {
|
|
193
|
-
const comment = relativeFilepath === '.env.keys' ? '' : ` # -fk ${relativeFilepath}`
|
|
194
|
-
|
|
195
|
-
return [
|
|
196
|
-
'#/-------------------[DOTENV_PUBLIC_KEY]--------------------/',
|
|
197
|
-
'#/ public-key encryption for .env files /',
|
|
198
|
-
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
199
|
-
'#/----------------------------------------------------------/',
|
|
200
|
-
`${publicKeyName}="${publicKey}"${comment}`,
|
|
201
|
-
'',
|
|
202
|
-
`# ${filename}`
|
|
203
|
-
].join('\n')
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
_preserveShebang (envSrc) {
|
|
207
|
-
// preserve shebang
|
|
208
|
-
const [firstLine, ...remainingLines] = envSrc.split('\n')
|
|
209
|
-
let firstLinePreserved = ''
|
|
210
|
-
|
|
211
|
-
if (firstLine.startsWith('#!')) {
|
|
212
|
-
firstLinePreserved = firstLine + '\n'
|
|
213
|
-
envSrc = remainingLines.join('\n')
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
firstLinePreserved,
|
|
218
|
-
envSrc
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
137
|
}
|
|
222
138
|
|
|
223
139
|
module.exports = Sets
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const dotenv = require('dotenv')
|
|
2
|
-
|
|
3
|
-
const parseEncryptionKeyFromDotenvKey = require('./parseEncryptionKeyFromDotenvKey')
|
|
4
|
-
|
|
5
|
-
function decrypt (ciphertext, dotenvKey) {
|
|
6
|
-
const key = parseEncryptionKeyFromDotenvKey(dotenvKey)
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
return dotenv.decrypt(ciphertext, key)
|
|
10
|
-
} catch (e) {
|
|
11
|
-
if (e.code === 'DECRYPTION_FAILED') {
|
|
12
|
-
const error = new Error('[DECRYPTION_FAILED] Unable to decrypt .env.vault with DOTENV_KEY.')
|
|
13
|
-
error.code = 'DECRYPTION_FAILED'
|
|
14
|
-
error.help = '[DECRYPTION_FAILED] Run with debug flag [dotenvx run --debug -- yourcommand] or manually run [echo $DOTENV_KEY] to compare it to the one in .env.keys.'
|
|
15
|
-
error.debug = `[DECRYPTION_FAILED] DOTENV_KEY is ${dotenvKey}`
|
|
16
|
-
throw error
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (e.code === 'ERR_CRYPTO_INVALID_AUTH_TAG') {
|
|
20
|
-
const error = new Error('[INVALID_CIPHERTEXT] Unable to decrypt what appears to be invalid ciphertext.')
|
|
21
|
-
error.code = 'INVALID_CIPHERTEXT'
|
|
22
|
-
error.help = '[INVALID_CIPHERTEXT] Run with debug flag [dotenvx run --debug -- yourcommand] or manually check .env.vault.'
|
|
23
|
-
error.debug = `[INVALID_CIPHERTEXT] ciphertext is '${ciphertext}'`
|
|
24
|
-
throw error
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
throw e
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = decrypt
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const { logger } = require('./../../shared/logger')
|
|
2
|
-
|
|
3
|
-
class DeprecationNotice {
|
|
4
|
-
constructor (options = {}) {
|
|
5
|
-
this.DOTENV_KEY = options.DOTENV_KEY || process.env.DOTENV_KEY
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
dotenvKey () {
|
|
9
|
-
if (this.DOTENV_KEY) {
|
|
10
|
-
logger.warn('[DEPRECATION NOTICE] Setting DOTENV_KEY with .env.vault is deprecated.')
|
|
11
|
-
logger.warn('[DEPRECATION NOTICE] Run [dotenvx ext vault migrate] for instructions on converting your .env.vault file to encrypted .env files (using public key encryption algorithm secp256k1)')
|
|
12
|
-
logger.warn('[DEPRECATION NOTICE] Read more at [https://github.com/dotenvx/dotenvx/blob/main/CHANGELOG.md#0380]')
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = DeprecationNotice
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const dotenvPrivateKeyNames = require('./dotenvPrivateKeyNames')
|
|
2
|
-
const guessPrivateKeyFilename = require('./guessPrivateKeyFilename')
|
|
3
|
-
|
|
4
|
-
const TYPE_ENV_FILE = 'envFile'
|
|
5
|
-
const TYPE_ENV_VAULT_FILE = 'envVaultFile'
|
|
6
|
-
const DEFAULT_ENVS = [{ type: TYPE_ENV_FILE, value: '.env' }]
|
|
7
|
-
const DEFAULT_ENV_VAULTS = [{ type: TYPE_ENV_VAULT_FILE, value: '.env.vault' }]
|
|
8
|
-
|
|
9
|
-
function determineEnvsFromDotenvPrivateKey (privateKeyNames) {
|
|
10
|
-
const envs = []
|
|
11
|
-
|
|
12
|
-
for (const privateKeyName of privateKeyNames) {
|
|
13
|
-
const filename = guessPrivateKeyFilename(privateKeyName)
|
|
14
|
-
envs.push({ type: TYPE_ENV_FILE, value: filename })
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return envs
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function determineEnvs (envs = [], processEnv, DOTENV_KEY = '') {
|
|
21
|
-
const privateKeyNames = dotenvPrivateKeyNames(processEnv)
|
|
22
|
-
if (!envs || envs.length <= 0) {
|
|
23
|
-
// if process.env.DOTENV_PRIVATE_KEY or process.env.DOTENV_PRIVATE_KEY_${environment} is set, assume inline encryption methodology
|
|
24
|
-
if (privateKeyNames.length > 0) {
|
|
25
|
-
return determineEnvsFromDotenvPrivateKey(privateKeyNames)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (DOTENV_KEY.length > 0) {
|
|
29
|
-
// if DOTENV_KEY is set then default to look for .env.vault file
|
|
30
|
-
return DEFAULT_ENV_VAULTS
|
|
31
|
-
} else {
|
|
32
|
-
return DEFAULT_ENVS // default to .env file expectation
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
let fileAlreadySpecified = false // can be .env or .env.vault type
|
|
36
|
-
|
|
37
|
-
for (const env of envs) {
|
|
38
|
-
// if DOTENV_KEY set then we are checking if a .env.vault file is already specified
|
|
39
|
-
if (DOTENV_KEY.length > 0 && env.type === TYPE_ENV_VAULT_FILE) {
|
|
40
|
-
fileAlreadySpecified = true
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// if DOTENV_KEY not set then we are checking if a .env file is already specified
|
|
44
|
-
if (DOTENV_KEY.length <= 0 && env.type === TYPE_ENV_FILE) {
|
|
45
|
-
fileAlreadySpecified = true
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// return early since envs array objects already contain 1 .env.vault or .env file
|
|
50
|
-
if (fileAlreadySpecified) {
|
|
51
|
-
return envs
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// no .env.vault or .env file specified as a flag so we assume either .env.vault (if dotenv key is set) or a .env file
|
|
55
|
-
if (DOTENV_KEY.length > 0) {
|
|
56
|
-
// if DOTENV_KEY is set then default to look for .env.vault file
|
|
57
|
-
return [...DEFAULT_ENV_VAULTS, ...envs]
|
|
58
|
-
} else {
|
|
59
|
-
// if no DOTENV_KEY then default to look for .env file
|
|
60
|
-
return [...DEFAULT_ENVS, ...envs]
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = determineEnvs
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const crypto = require('crypto')
|
|
2
|
-
|
|
3
|
-
const parseEncryptionKeyFromDotenvKey = require('./parseEncryptionKeyFromDotenvKey')
|
|
4
|
-
|
|
5
|
-
const NONCE_BYTES = 12
|
|
6
|
-
|
|
7
|
-
function encrypt (raw, dotenvKey) {
|
|
8
|
-
const key = parseEncryptionKeyFromDotenvKey(dotenvKey)
|
|
9
|
-
|
|
10
|
-
// set up nonce
|
|
11
|
-
const nonce = crypto.randomBytes(NONCE_BYTES)
|
|
12
|
-
|
|
13
|
-
// set up cipher
|
|
14
|
-
const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce)
|
|
15
|
-
|
|
16
|
-
// generate ciphertext
|
|
17
|
-
let ciphertext = ''
|
|
18
|
-
ciphertext += cipher.update(raw, 'utf8', 'hex')
|
|
19
|
-
ciphertext += cipher.final('hex')
|
|
20
|
-
ciphertext += cipher.getAuthTag().toString('hex')
|
|
21
|
-
|
|
22
|
-
// prepend nonce
|
|
23
|
-
ciphertext = nonce.toString('hex') + ciphertext
|
|
24
|
-
|
|
25
|
-
// base64 encode output
|
|
26
|
-
return Buffer.from(ciphertext, 'hex').toString('base64')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = encrypt
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// helpers
|
|
2
|
-
const guessPrivateKeyName = require('./guessPrivateKeyName')
|
|
3
|
-
const findPublicKey = require('./findPublicKey')
|
|
4
|
-
|
|
5
|
-
// services
|
|
6
|
-
const Keypair = require('./../services/keypair')
|
|
7
|
-
const Ops = require('./../services/ops')
|
|
8
|
-
|
|
9
|
-
function findPrivateKey (envFilepath, envKeysFilepath = null, opsOn = false, publicKey = null) {
|
|
10
|
-
// use path/to/.env.${environment} to generate privateKeyName
|
|
11
|
-
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
12
|
-
|
|
13
|
-
if (opsOn) {
|
|
14
|
-
const resolvedPublicKey = publicKey || findPublicKey(envFilepath)
|
|
15
|
-
const opsPrivateKey = new Ops().keypair(resolvedPublicKey)
|
|
16
|
-
if (opsPrivateKey) {
|
|
17
|
-
return opsPrivateKey
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const keypairs = new Keypair(envFilepath, envKeysFilepath).run()
|
|
22
|
-
return keypairs[privateKeyName]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports = { findPrivateKey }
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// helpers
|
|
2
|
-
const guessPublicKeyName = require('./guessPublicKeyName')
|
|
3
|
-
|
|
4
|
-
// services
|
|
5
|
-
const Keypair = require('./../services/keypair')
|
|
6
|
-
|
|
7
|
-
function findPublicKey (envFilepath) {
|
|
8
|
-
const publicKeyName = guessPublicKeyName(envFilepath)
|
|
9
|
-
|
|
10
|
-
const keypairs = new Keypair(envFilepath).run()
|
|
11
|
-
|
|
12
|
-
return keypairs[publicKeyName]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
module.exports = findPublicKey
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const guessEnvironment = require('./guessEnvironment')
|
|
3
|
-
|
|
4
|
-
function guessPrivateKeyName (filepath) {
|
|
5
|
-
const filename = path.basename(filepath).toLowerCase()
|
|
6
|
-
|
|
7
|
-
// .env
|
|
8
|
-
if (filename === '.env') {
|
|
9
|
-
return 'DOTENV_PRIVATE_KEY'
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// .env.ENVIRONMENT
|
|
13
|
-
const environment = guessEnvironment(filename)
|
|
14
|
-
|
|
15
|
-
return `DOTENV_PRIVATE_KEY_${environment.toUpperCase()}`
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = guessPrivateKeyName
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const guessEnvironment = require('./guessEnvironment')
|
|
3
|
-
|
|
4
|
-
function guessPublicKeyName (filepath) {
|
|
5
|
-
const filename = path.basename(filepath).toLowerCase()
|
|
6
|
-
|
|
7
|
-
// .env
|
|
8
|
-
if (filename === '.env') {
|
|
9
|
-
return 'DOTENV_PUBLIC_KEY'
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// .env.ENVIRONMENT
|
|
13
|
-
const environment = guessEnvironment(filename)
|
|
14
|
-
|
|
15
|
-
return `DOTENV_PUBLIC_KEY_${environment.toUpperCase()}`
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = guessPublicKeyName
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
function parseEncryptionKeyFromDotenvKey (dotenvKey) {
|
|
2
|
-
// Parse DOTENV_KEY. Format is a URI
|
|
3
|
-
let uri
|
|
4
|
-
try {
|
|
5
|
-
uri = new URL(dotenvKey)
|
|
6
|
-
} catch (e) {
|
|
7
|
-
throw new Error('INVALID_DOTENV_KEY: Incomplete format. It should be a dotenv uri. (dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development)')
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Get decrypt key
|
|
11
|
-
const key = uri.password
|
|
12
|
-
if (!key) {
|
|
13
|
-
throw new Error('INVALID_DOTENV_KEY: Missing key part')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return Buffer.from(key.slice(-64), 'hex')
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = parseEncryptionKeyFromDotenvKey
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
function parseEnvironmentFromDotenvKey (dotenvKey) {
|
|
2
|
-
// Parse DOTENV_KEY. Format is a URI
|
|
3
|
-
let uri
|
|
4
|
-
try {
|
|
5
|
-
uri = new URL(dotenvKey)
|
|
6
|
-
} catch (e) {
|
|
7
|
-
throw new Error(`INVALID_DOTENV_KEY: ${e.message}`)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Get environment
|
|
11
|
-
const environment = uri.searchParams.get('environment')
|
|
12
|
-
if (!environment) {
|
|
13
|
-
throw new Error('INVALID_DOTENV_KEY: Missing environment part')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return environment
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = parseEnvironmentFromDotenvKey
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
const fsx = require('./fsx')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
|
|
4
|
-
const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
|
|
5
|
-
const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
|
|
6
|
-
|
|
7
|
-
const dotenvParse = require('./dotenvParse')
|
|
8
|
-
const guessPrivateKeyName = require('./guessPrivateKeyName')
|
|
9
|
-
|
|
10
|
-
function searchProcessEnv (privateKeyName) {
|
|
11
|
-
if (process.env[privateKeyName] && process.env[privateKeyName].length > 0) {
|
|
12
|
-
return process.env[privateKeyName]
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function searchKeysFile (privateKeyName, envFilepath, envKeysFilepath = null) {
|
|
17
|
-
let keysFilepath = path.resolve(path.dirname(envFilepath), '.env.keys') // typical scenario
|
|
18
|
-
if (envKeysFilepath) { // user specified -fk flag
|
|
19
|
-
keysFilepath = path.resolve(envKeysFilepath)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (fsx.existsSync(keysFilepath)) {
|
|
23
|
-
const keysSrc = fsx.readFileX(keysFilepath)
|
|
24
|
-
const keysParsed = dotenvParse(keysSrc)
|
|
25
|
-
|
|
26
|
-
if (keysParsed[privateKeyName] && keysParsed[privateKeyName].length > 0) {
|
|
27
|
-
return keysParsed[privateKeyName]
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function invertForPrivateKeyName (envFilepath) {
|
|
33
|
-
if (!fsx.existsSync(envFilepath)) {
|
|
34
|
-
return null
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const envSrc = fsx.readFileX(envFilepath)
|
|
38
|
-
const envParsed = dotenvParse(envSrc)
|
|
39
|
-
|
|
40
|
-
let publicKeyName
|
|
41
|
-
for (const keyName of Object.keys(envParsed)) {
|
|
42
|
-
if (keyName === PUBLIC_KEY_SCHEMA || keyName.startsWith(PUBLIC_KEY_SCHEMA)) {
|
|
43
|
-
publicKeyName = keyName // find DOTENV_PUBLIC_KEY* in filename
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (publicKeyName) {
|
|
48
|
-
return publicKeyName.replace(PUBLIC_KEY_SCHEMA, PRIVATE_KEY_SCHEMA) // return inverted (DOTENV_PUBLIC_KEY* -> DOTENV_PRIVATE_KEY*) if found
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return null
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function smartDotenvPrivateKey (envFilepath, envKeysFilepath = null) {
|
|
55
|
-
let privateKey = null
|
|
56
|
-
let privateKeyName = guessPrivateKeyName(envFilepath) // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
|
|
57
|
-
|
|
58
|
-
// 1. attempt process.env first
|
|
59
|
-
privateKey = searchProcessEnv(privateKeyName)
|
|
60
|
-
if (privateKey) {
|
|
61
|
-
return privateKey
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 2. attempt .env.keys second (path/to/.env.keys)
|
|
65
|
-
privateKey = searchKeysFile(privateKeyName, envFilepath, envKeysFilepath)
|
|
66
|
-
if (privateKey) {
|
|
67
|
-
return privateKey
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 3. attempt inverting `DOTENV_PUBLIC_KEY*` name inside file (unlocks custom filenames not matching .env.${ENVIRONMENT} pattern)
|
|
71
|
-
privateKeyName = invertForPrivateKeyName(envFilepath)
|
|
72
|
-
if (privateKeyName) {
|
|
73
|
-
// 3.1 attempt process.env first
|
|
74
|
-
privateKey = searchProcessEnv(privateKeyName)
|
|
75
|
-
if (privateKey) {
|
|
76
|
-
return privateKey
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// 3.2. attempt .env.keys second (path/to/.env.keys)
|
|
80
|
-
privateKey = searchKeysFile(privateKeyName, envFilepath, envKeysFilepath)
|
|
81
|
-
if (privateKey) {
|
|
82
|
-
return privateKey
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return null
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
module.exports = smartDotenvPrivateKey
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const fsx = require('./fsx')
|
|
2
|
-
const dotenvParse = require('./dotenvParse')
|
|
3
|
-
|
|
4
|
-
const guessPublicKeyName = require('./guessPublicKeyName')
|
|
5
|
-
|
|
6
|
-
function searchProcessEnv (publicKeyName) {
|
|
7
|
-
if (process.env[publicKeyName] && process.env[publicKeyName].length > 0) {
|
|
8
|
-
return process.env[publicKeyName]
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function searchEnvFile (publicKeyName, envFilepath) {
|
|
13
|
-
if (fsx.existsSync(envFilepath)) {
|
|
14
|
-
const keysSrc = fsx.readFileX(envFilepath)
|
|
15
|
-
const keysParsed = dotenvParse(keysSrc)
|
|
16
|
-
|
|
17
|
-
if (keysParsed[publicKeyName] && keysParsed[publicKeyName].length > 0) {
|
|
18
|
-
return keysParsed[publicKeyName]
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function smartDotenvPublicKey (envFilepath) {
|
|
24
|
-
let publicKey = null
|
|
25
|
-
const publicKeyName = guessPublicKeyName(envFilepath) // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
|
|
26
|
-
|
|
27
|
-
// 1. attempt process.env first
|
|
28
|
-
publicKey = searchProcessEnv(publicKeyName)
|
|
29
|
-
if (publicKey) {
|
|
30
|
-
return publicKey
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// 2. attempt .env.keys second (path/to/.env.keys)
|
|
34
|
-
publicKey = searchEnvFile(publicKeyName, envFilepath)
|
|
35
|
-
if (publicKey) {
|
|
36
|
-
return publicKey
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
module.exports = smartDotenvPublicKey
|