@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.
Files changed (62) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/package.json +3 -3
  3. package/src/cli/actions/encrypt.js +0 -3
  4. package/src/cli/actions/get.js +4 -2
  5. package/src/cli/actions/keypair.js +3 -2
  6. package/src/cli/actions/rotate.js +10 -5
  7. package/src/cli/actions/run.js +1 -14
  8. package/src/cli/actions/set.js +3 -2
  9. package/src/cli/commands/ext.js +0 -3
  10. package/src/cli/dotenvx.js +12 -9
  11. package/src/db/session.js +21 -0
  12. package/src/lib/extensions/ops.js +98 -0
  13. package/src/lib/helpers/buildEnvs.js +2 -15
  14. package/src/lib/helpers/{decryptKeyValue.js → cryptography/decryptKeyValue.js} +1 -1
  15. package/src/lib/helpers/cryptography/index.js +14 -0
  16. package/src/lib/helpers/{isPublicKey.js → cryptography/isPublicKey.js} +1 -1
  17. package/src/lib/helpers/{keypair.js → cryptography/localKeypair.js} +2 -2
  18. package/src/lib/helpers/cryptography/mutateKeysSrc.js +38 -0
  19. package/src/lib/helpers/cryptography/mutateSrc.js +24 -0
  20. package/src/lib/helpers/cryptography/opsKeypair.js +14 -0
  21. package/src/lib/helpers/cryptography/provision.js +47 -0
  22. package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +26 -0
  23. package/src/lib/helpers/envResolution/determine.js +46 -0
  24. package/src/lib/helpers/{guessEnvironment.js → envResolution/environment.js} +4 -6
  25. package/src/lib/helpers/envResolution/index.js +8 -0
  26. package/src/lib/helpers/errors.js +13 -0
  27. package/src/lib/helpers/findEnvFiles.js +1 -1
  28. package/src/lib/helpers/isFullyEncrypted.js +3 -3
  29. package/src/lib/helpers/keyResolution/index.js +13 -0
  30. package/src/lib/helpers/keyResolution/keyNames.js +24 -0
  31. package/src/lib/helpers/keyResolution/keyValues.js +85 -0
  32. package/src/lib/helpers/keyResolution/readFileKey.js +15 -0
  33. package/src/lib/helpers/keyResolution/readProcessKey.js +7 -0
  34. package/src/lib/helpers/parse.js +1 -1
  35. package/src/lib/helpers/prependPublicKey.js +17 -0
  36. package/src/lib/helpers/preserveShebang.js +16 -0
  37. package/src/lib/main.d.ts +19 -3
  38. package/src/lib/main.js +9 -17
  39. package/src/lib/services/decrypt.js +23 -19
  40. package/src/lib/services/encrypt.js +41 -136
  41. package/src/lib/services/get.js +3 -3
  42. package/src/lib/services/keypair.js +12 -16
  43. package/src/lib/services/prebuild.js +2 -2
  44. package/src/lib/services/precommit.js +2 -2
  45. package/src/lib/services/rotate.js +59 -42
  46. package/src/lib/services/run.js +29 -112
  47. package/src/lib/services/sets.js +40 -124
  48. package/src/lib/helpers/decrypt.js +0 -31
  49. package/src/lib/helpers/deprecationNotice.js +0 -17
  50. package/src/lib/helpers/determineEnvs.js +0 -65
  51. package/src/lib/helpers/encrypt.js +0 -29
  52. package/src/lib/helpers/findPrivateKey.js +0 -25
  53. package/src/lib/helpers/findPublicKey.js +0 -15
  54. package/src/lib/helpers/guessPrivateKeyName.js +0 -18
  55. package/src/lib/helpers/guessPublicKeyName.js +0 -18
  56. package/src/lib/helpers/parseEncryptionKeyFromDotenvKey.js +0 -19
  57. package/src/lib/helpers/parseEnvironmentFromDotenvKey.js +0 -19
  58. package/src/lib/helpers/smartDotenvPrivateKey.js +0 -89
  59. package/src/lib/helpers/smartDotenvPublicKey.js +0 -42
  60. package/src/lib/services/ops.js +0 -111
  61. /package/src/lib/helpers/{encryptValue.js → cryptography/encryptValue.js} +0 -0
  62. /package/src/lib/helpers/{isEncrypted.js → cryptography/isEncrypted.js} +0 -0
@@ -5,23 +5,31 @@ const picomatch = require('picomatch')
5
5
  const TYPE_ENV_FILE = 'envFile'
6
6
 
7
7
  const Errors = require('./../helpers/errors')
8
- const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
9
- const guessPublicKeyName = require('./../helpers/guessPublicKeyName')
10
- const encryptValue = require('./../helpers/encryptValue')
11
- const isEncrypted = require('./../helpers/isEncrypted')
12
- const dotenvParse = require('./../helpers/dotenvParse')
8
+
9
+ const {
10
+ determine
11
+ } = require('./../helpers/envResolution')
12
+
13
+ const {
14
+ keyNames,
15
+ keyValues
16
+ } = require('./../helpers/keyResolution')
17
+
18
+ const {
19
+ encryptValue,
20
+ isEncrypted,
21
+ isPublicKey,
22
+ provision,
23
+ provisionWithPrivateKey
24
+ } = require('./../helpers/cryptography')
25
+
13
26
  const replace = require('./../helpers/replace')
27
+ const dotenvParse = require('./../helpers/dotenvParse')
14
28
  const detectEncoding = require('./../helpers/detectEncoding')
15
- const determineEnvs = require('./../helpers/determineEnvs')
16
- const { findPrivateKey } = require('./../helpers/findPrivateKey')
17
- const findPublicKey = require('./../helpers/findPublicKey')
18
- const keypair = require('./../helpers/keypair')
19
- const truncate = require('./../helpers/truncate')
20
- const isPublicKey = require('./../helpers/isPublicKey')
21
29
 
22
30
  class Encrypt {
23
- constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, opsOn = true) {
24
- this.envs = determineEnvs(envs, process.env)
31
+ constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, opsOn = false) {
32
+ this.envs = determine(envs, process.env)
25
33
  this.key = key
26
34
  this.excludeKey = excludeKey
27
35
  this.envKeysFilepath = envKeysFilepath
@@ -62,105 +70,36 @@ class Encrypt {
62
70
  row.keys = []
63
71
  row.type = TYPE_ENV_FILE
64
72
 
65
- const filename = path.basename(envFilepath)
66
73
  const filepath = path.resolve(envFilepath)
67
74
  row.filepath = filepath
68
75
  row.envFilepath = envFilepath
69
76
 
70
77
  try {
71
- const encoding = this._detectEncoding(filepath)
78
+ const encoding = detectEncoding(filepath)
72
79
  let envSrc = fsx.readFileX(filepath, { encoding })
73
80
  const envParsed = dotenvParse(envSrc)
74
81
 
75
82
  let publicKey
76
83
  let privateKey
77
84
 
78
- const publicKeyName = guessPublicKeyName(envFilepath)
79
- const privateKeyName = guessPrivateKeyName(envFilepath)
80
- const existingPublicKey = findPublicKey(envFilepath)
81
- const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath, this.opsOn, existingPublicKey)
82
-
83
- let envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
84
- if (this.envKeysFilepath) {
85
- envKeysFilepath = path.resolve(this.envKeysFilepath)
86
- }
87
- const relativeFilepath = path.relative(path.dirname(filepath), envKeysFilepath)
88
-
89
- if (existingPrivateKey) {
90
- // throw new Error('implement for remote Ops existingPrivateKey')
91
- const kp = keypair(existingPrivateKey)
92
- publicKey = kp.publicKey
93
- privateKey = kp.privateKey
94
-
95
- // if derivation doesn't match what's in the file (or preset in env)
96
- if (existingPublicKey && existingPublicKey !== publicKey) {
97
- const error = new Error(`derived public key (${truncate(publicKey)}) does not match the existing public key (${truncate(existingPublicKey)})`)
98
- error.code = 'INVALID_DOTENV_PRIVATE_KEY'
99
- error.help = `debug info: ${privateKeyName}=${truncate(existingPrivateKey)} (derived ${publicKeyName}=${truncate(publicKey)} vs existing ${publicKeyName}=${truncate(existingPublicKey)})`
100
- throw error
101
- }
102
-
103
- // typical scenario when encrypting a monorepo second .env file from a prior generated -fk .env.keys file
104
- if (!existingPublicKey) {
105
- const ps = this._preserveShebang(envSrc)
106
- const firstLinePreserved = ps.firstLinePreserved
107
- envSrc = ps.envSrc
108
-
109
- const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
110
-
111
- envSrc = `${firstLinePreserved}${prependPublicKey}\n${envSrc}`
112
- }
113
- } else if (existingPublicKey) {
114
- // throw new Error('implement for remote Ops existingPrivateKey')
115
- publicKey = existingPublicKey
116
- } else {
117
- // .env.keys
118
- let keysSrc = ''
119
-
120
- if (fsx.existsSync(envKeysFilepath)) {
121
- keysSrc = fsx.readFileX(envKeysFilepath)
122
- }
123
-
124
- const ps = this._preserveShebang(envSrc)
125
- const firstLinePreserved = ps.firstLinePreserved
126
- envSrc = ps.envSrc
127
-
128
- // TODO: instead get this from API
129
- const kp = keypair() // generates a fresh keypair in memory
130
- publicKey = kp.publicKey
131
- privateKey = kp.privateKey
132
- // Ops hook point (first-time key generation):
133
- // if Ops is installed and opsOff is not set, send privateKey/privateKeyName/envFilepath
134
- // to your Ops service before persisting or immediately after writing below.
135
-
136
- const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
137
-
138
- // privateKey
139
- const firstTimeKeysSrc = [
140
- '#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
141
- '#/ private decryption keys. DO NOT commit to source control /',
142
- '#/ [how it works](https://dotenvx.com/encryption) /',
143
- // '#/ backup with: `dotenvx ops backup` /',
144
- '#/----------------------------------------------------------/'
145
- ].join('\n')
146
- const appendPrivateKey = [
147
- `# ${filename}`,
148
- `${privateKeyName}=${privateKey}`,
149
- ''
150
- ].join('\n')
151
-
152
- envSrc = `${firstLinePreserved}${prependPublicKey}\n${envSrc}`
153
- keysSrc = keysSrc.length > 1 ? keysSrc : `${firstTimeKeysSrc}\n`
154
- keysSrc = `${keysSrc}\n${appendPrivateKey}`
155
-
156
- // write to .env.keys
157
- fsx.writeFileX(envKeysFilepath, keysSrc)
158
- // Ops hook point (after persistence):
159
- // if Ops is installed and opsOff is not set, trigger backup/registration now that
160
- // .env.keys has been written and row.privateKeyAdded will be true for callers.
161
-
162
- row.privateKeyAdded = true
163
- row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), path.basename(envKeysFilepath))
85
+ const { publicKeyName, privateKeyName } = keyNames(envFilepath)
86
+ const { publicKeyValue, privateKeyValue } = keyValues(envFilepath, { keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
87
+
88
+ // first pass - provision
89
+ if (!privateKeyValue && !publicKeyValue) {
90
+ const prov = provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
91
+ envSrc = prov.envSrc
92
+ publicKey = prov.publicKey
93
+ privateKey = prov.privateKey
94
+ row.privateKeyAdded = prov.privateKeyAdded
95
+ row.envKeysFilepath = prov.envKeysFilepath
96
+ } else if (privateKeyValue) {
97
+ const prov = provisionWithPrivateKey({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, privateKeyValue, publicKeyValue, publicKeyName })
98
+ publicKey = prov.publicKey
99
+ privateKey = prov.privateKey
100
+ envSrc = prov.envSrc
101
+ } else if (publicKeyValue) {
102
+ publicKey = publicKeyValue
164
103
  }
165
104
 
166
105
  row.publicKey = publicKey
@@ -179,7 +118,7 @@ class Encrypt {
179
118
  continue
180
119
  }
181
120
 
182
- const encrypted = isEncrypted(value) || isPublicKey(key, value)
121
+ const encrypted = isEncrypted(value) || isPublicKey(key)
183
122
  if (!encrypted) {
184
123
  row.keys.push(key) // track key(s)
185
124
 
@@ -224,40 +163,6 @@ class Encrypt {
224
163
 
225
164
  return this.excludeKey
226
165
  }
227
-
228
- _detectEncoding (filepath) {
229
- return detectEncoding(filepath)
230
- }
231
-
232
- _prependPublicKey (publicKeyName, publicKey, filename, relativeFilepath = '') {
233
- const comment = relativeFilepath === '.env.keys' ? '' : ` # ${relativeFilepath}`
234
-
235
- return [
236
- '#/-------------------[DOTENV_PUBLIC_KEY]--------------------/',
237
- '#/ public-key encryption for .env files /',
238
- '#/ [how it works](https://dotenvx.com/encryption) /',
239
- '#/----------------------------------------------------------/',
240
- `${publicKeyName}="${publicKey}"${comment}`,
241
- '',
242
- `# ${filename}`
243
- ].join('\n')
244
- }
245
-
246
- _preserveShebang (envSrc) {
247
- // preserve shebang
248
- const [firstLine, ...remainingLines] = envSrc.split('\n')
249
- let firstLinePreserved = ''
250
-
251
- if (firstLine.startsWith('#!')) {
252
- firstLinePreserved = firstLine + '\n'
253
- envSrc = remainingLines.join('\n')
254
- }
255
-
256
- return {
257
- firstLinePreserved,
258
- envSrc
259
- }
260
- }
261
166
  }
262
167
 
263
168
  module.exports = Encrypt
@@ -2,18 +2,18 @@ const Run = require('./run')
2
2
  const Errors = require('./../helpers/errors')
3
3
 
4
4
  class Get {
5
- constructor (key, envs = [], overload = false, DOTENV_KEY = '', all = false, envKeysFilepath = null) {
5
+ constructor (key, envs = [], overload = false, all = false, envKeysFilepath = null, opsOn = true) {
6
6
  this.key = key
7
7
  this.envs = envs
8
8
  this.overload = overload
9
- this.DOTENV_KEY = DOTENV_KEY
10
9
  this.all = all
11
10
  this.envKeysFilepath = envKeysFilepath
11
+ this.opsOn = opsOn
12
12
  }
13
13
 
14
14
  run () {
15
15
  const processEnv = { ...process.env }
16
- const { processedEnvs } = new Run(this.envs, this.overload, this.DOTENV_KEY, processEnv, this.envKeysFilepath).run()
16
+ const { processedEnvs } = new Run(this.envs, this.overload, processEnv, this.envKeysFilepath, this.opsOn).run()
17
17
 
18
18
  const errors = []
19
19
  for (const processedEnv of processedEnvs) {
@@ -1,35 +1,31 @@
1
- const guessPublicKeyName = require('./../helpers/guessPublicKeyName')
2
- const smartDotenvPublicKey = require('./../helpers/smartDotenvPublicKey')
3
- const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
4
- const smartDotenvPrivateKey = require('./../helpers/smartDotenvPrivateKey')
1
+ const {
2
+ keyNames,
3
+ keyValues
4
+ } = require('./../helpers/keyResolution')
5
5
 
6
6
  class Keypair {
7
- constructor (envFile = '.env', envKeysFilepath = null) {
7
+ constructor (envFile = '.env', envKeysFilepath = null, opsOn = false) {
8
8
  this.envFile = envFile
9
9
  this.envKeysFilepath = envKeysFilepath
10
+ this.opsOn = opsOn
10
11
  }
11
12
 
12
13
  run () {
13
14
  const out = {}
14
15
 
15
- const envFilepaths = this._envFilepaths()
16
- for (const envFilepath of envFilepaths) {
17
- // public key
18
- const publicKeyName = guessPublicKeyName(envFilepath)
19
- const publicKeyValue = smartDotenvPublicKey(envFilepath)
20
- out[publicKeyName] = publicKeyValue
21
-
22
- // private key
23
- const privateKeyName = guessPrivateKeyName(envFilepath)
24
- const privateKeyValue = smartDotenvPrivateKey(envFilepath, this.envKeysFilepath)
16
+ const filepaths = this._filepaths()
17
+ for (const filepath of filepaths) {
18
+ const { publicKeyName, privateKeyName } = keyNames(filepath)
19
+ const { publicKeyValue, privateKeyValue } = keyValues(filepath, { keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
25
20
 
21
+ out[publicKeyName] = publicKeyValue
26
22
  out[privateKeyName] = privateKeyValue
27
23
  }
28
24
 
29
25
  return out
30
26
  }
31
27
 
32
- _envFilepaths () {
28
+ _filepaths () {
33
29
  if (!Array.isArray(this.envFile)) {
34
30
  return [this.envFile]
35
31
  }
@@ -41,13 +41,13 @@ class Prebuild {
41
41
 
42
42
  // check if that file is being ignored
43
43
  if (ig.ignores(file)) {
44
- if (file === '.env.example' || file === '.env.vault' || file === '.env.x') {
44
+ if (file === '.env.example' || file === '.env.x') {
45
45
  const warning = new Error(`[dotenvx@${packageJson.version}][prebuild] ${file} (currently ignored but should not be)`)
46
46
  warning.help = `[dotenvx@${packageJson.version}][prebuild] ⮕ run [dotenvx ext gitignore --pattern !${file}]`
47
47
  warnings.push(warning)
48
48
  }
49
49
  } else {
50
- if (file !== '.env.example' && file !== '.env.vault' && file !== '.env.x') {
50
+ if (file !== '.env.example' && file !== '.env.x') {
51
51
  const src = fsx.readFileX(file)
52
52
  const encrypted = isFullyEncrypted(src)
53
53
 
@@ -57,13 +57,13 @@ class Precommit {
57
57
  if (this._isFileToBeCommitted(file)) {
58
58
  // check if that file is being ignored
59
59
  if (ig.ignores(file)) {
60
- if (file === '.env.example' || file === '.env.vault' || file === '.env.x') {
60
+ if (file === '.env.example' || file === '.env.x') {
61
61
  const warning = new Error(`[dotenvx@${packageJson.version}][precommit] ${file} (currently ignored but should not be)`)
62
62
  warning.help = `[dotenvx@${packageJson.version}][precommit] ⮕ run [dotenvx ext gitignore --pattern !${file}]`
63
63
  warnings.push(warning)
64
64
  }
65
65
  } else {
66
- if (file !== '.env.example' && file !== '.env.vault' && file !== '.env.x') {
66
+ if (file !== '.env.example' && file !== '.env.x') {
67
67
  const src = fsx.readFileX(file)
68
68
  const encrypted = isFullyEncrypted(src)
69
69
 
@@ -5,26 +5,36 @@ const picomatch = require('picomatch')
5
5
  const TYPE_ENV_FILE = 'envFile'
6
6
 
7
7
  const Errors = require('./../helpers/errors')
8
- const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
9
- const guessPublicKeyName = require('./../helpers/guessPublicKeyName')
10
- const encryptValue = require('./../helpers/encryptValue')
11
- const isEncrypted = require('./../helpers/isEncrypted')
12
- const dotenvParse = require('./../helpers/dotenvParse')
13
- const replace = require('./../helpers/replace')
8
+
9
+ const {
10
+ determine
11
+ } = require('./../helpers/envResolution')
12
+
13
+ const {
14
+ keyNames,
15
+ keyValues
16
+ } = require('./../helpers/keyResolution')
17
+
18
+ const {
19
+ opsKeypair,
20
+ localKeypair,
21
+ encryptValue,
22
+ decryptKeyValue,
23
+ isEncrypted
24
+ } = require('./../helpers/cryptography')
25
+
14
26
  const append = require('./../helpers/append')
27
+ const replace = require('./../helpers/replace')
28
+ const dotenvParse = require('./../helpers/dotenvParse')
15
29
  const detectEncoding = require('./../helpers/detectEncoding')
16
- const determineEnvs = require('./../helpers/determineEnvs')
17
- const { findPrivateKey } = require('./../helpers/findPrivateKey')
18
- const findPublicKey = require('./../helpers/findPublicKey')
19
- const decryptKeyValue = require('./../helpers/decryptKeyValue')
20
- const keypair = require('./../helpers/keypair')
21
30
 
22
31
  class Rotate {
23
- constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null) {
24
- this.envs = determineEnvs(envs, process.env)
32
+ constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, opsOn = false) {
33
+ this.envs = determine(envs, process.env)
25
34
  this.key = key
26
35
  this.excludeKey = excludeKey
27
36
  this.envKeysFilepath = envKeysFilepath
37
+ this.opsOn = opsOn
28
38
 
29
39
  this.processedEnvs = []
30
40
  this.changedFilepaths = new Set()
@@ -68,33 +78,44 @@ class Rotate {
68
78
  row.envFilepath = envFilepath
69
79
 
70
80
  try {
71
- const encoding = this._detectEncoding(filepath)
81
+ const encoding = detectEncoding(filepath)
72
82
  let envSrc = fsx.readFileX(filepath, { encoding })
73
83
  const envParsed = dotenvParse(envSrc)
74
84
 
75
- const publicKeyName = guessPublicKeyName(envFilepath)
76
- const privateKeyName = guessPrivateKeyName(envFilepath)
77
- const existingPublicKey = findPublicKey(envFilepath)
78
- const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath, false, existingPublicKey)
85
+ const { publicKeyName, privateKeyName } = keyNames(envFilepath)
86
+ const { privateKeyValue } = keyValues(envFilepath, { keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
79
87
 
80
- let envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
81
- if (this.envKeysFilepath) {
82
- envKeysFilepath = path.resolve(this.envKeysFilepath)
83
- }
84
- const keysEncoding = this._detectEncoding(envKeysFilepath)
88
+ let newPublicKey
89
+ let newPrivateKey
90
+ let envKeysFilepath
91
+ let envKeysSrc
85
92
 
86
- row.envKeysFilepath = envKeysFilepath
87
- this.envKeysSources[envKeysFilepath] ||= fsx.readFileX(envKeysFilepath, { encoding: keysEncoding })
88
- let envKeysSrc = this.envKeysSources[envKeysFilepath]
93
+ if (this.opsOn) {
94
+ const kp = opsKeypair()
95
+ newPublicKey = kp.publicKey
96
+ newPrivateKey = kp.privateKey
89
97
 
90
- // new keypair
91
- const nkp = keypair() // generates a fresh keypair in memory
92
- const newPublicKey = nkp.publicKey
93
- const newPrivateKey = nkp.privateKey
98
+ row.privateKeyAdded = false // TODO: change to localPrivateKeyAdded
99
+ } else {
100
+ envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
101
+ if (this.envKeysFilepath) {
102
+ envKeysFilepath = path.resolve(this.envKeysFilepath)
103
+ }
104
+ row.envKeysFilepath = envKeysFilepath
105
+ this.envKeysSources[envKeysFilepath] ||= fsx.readFileX(envKeysFilepath, { encoding: detectEncoding(envKeysFilepath) })
106
+ envKeysSrc = this.envKeysSources[envKeysFilepath]
107
+
108
+ const kp = localKeypair()
109
+ newPublicKey = kp.publicKey
110
+ newPrivateKey = kp.privateKey
111
+
112
+ row.privateKeyAdded = true
113
+ }
94
114
 
95
115
  // .env
96
116
  envSrc = replace(envSrc, publicKeyName, newPublicKey) // replace publicKey
97
117
  row.changed = true // track change
118
+
98
119
  for (const [key, value] of Object.entries(envParsed)) { // re-encrypt each individual key
99
120
  // key excluded - don't re-encrypt it
100
121
  if (this.exclude(key)) {
@@ -109,22 +130,22 @@ class Rotate {
109
130
  if (isEncrypted(value)) { // only re-encrypt those already encrypted
110
131
  row.keys.push(key) // track key(s)
111
132
 
112
- const decryptedValue = decryptKeyValue(key, value, privateKeyName, existingPrivateKey) // get decrypted value
113
-
133
+ const decryptedValue = decryptKeyValue(key, value, privateKeyName, privateKeyValue) // get decrypted value
114
134
  const encryptedValue = encryptValue(decryptedValue, newPublicKey) // encrypt with the new publicKey
115
135
 
116
136
  envSrc = replace(envSrc, key, encryptedValue)
117
137
  }
118
138
  }
119
139
  row.envSrc = envSrc
120
-
121
- // .env.keys - TODO: for dotenvx pro .env.keys file does not exist
122
- row.privateKeyAdded = true
123
140
  row.privateKeyName = privateKeyName
124
141
  row.privateKey = newPrivateKey
125
- envKeysSrc = append(envKeysSrc, privateKeyName, newPrivateKey) // append privateKey
126
- this.envKeysSources[envKeysFilepath] = envKeysSrc
127
- row.envKeysSrc = envKeysSrc
142
+
143
+ if (!this.opsOn) {
144
+ // keys src only for ops
145
+ envKeysSrc = append(envKeysSrc, privateKeyName, newPrivateKey) // append privateKey
146
+ this.envKeysSources[envKeysFilepath] = envKeysSrc
147
+ row.envKeysSrc = envKeysSrc
148
+ }
128
149
 
129
150
  this.changedFilepaths.add(envFilepath)
130
151
  } catch (e) {
@@ -153,10 +174,6 @@ class Rotate {
153
174
 
154
175
  return this.excludeKey
155
176
  }
156
-
157
- _detectEncoding (filepath) {
158
- return detectEncoding(filepath)
159
- }
160
177
  }
161
178
 
162
179
  module.exports = Rotate
@@ -3,24 +3,24 @@ const path = require('path')
3
3
 
4
4
  const TYPE_ENV = 'env'
5
5
  const TYPE_ENV_FILE = 'envFile'
6
- const TYPE_ENV_VAULT_FILE = 'envVaultFile'
7
6
 
8
- const decrypt = require('./../helpers/decrypt')
9
7
  const Parse = require('./../helpers/parse')
10
8
  const Errors = require('./../helpers/errors')
11
- const dotenvParse = require('./../helpers/dotenvParse')
12
- const parseEnvironmentFromDotenvKey = require('./../helpers/parseEnvironmentFromDotenvKey')
13
9
  const detectEncoding = require('./../helpers/detectEncoding')
14
- const { findPrivateKey } = require('./../helpers/findPrivateKey')
15
- const findPublicKey = require('./../helpers/findPublicKey')
16
- const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
17
- const determineEnvs = require('./../helpers/determineEnvs')
10
+
11
+ const {
12
+ keyNames,
13
+ keyValues
14
+ } = require('./../helpers/keyResolution')
15
+
16
+ const {
17
+ determine
18
+ } = require('./../helpers/envResolution')
18
19
 
19
20
  class Run {
20
- constructor (envs = [], overload = false, DOTENV_KEY = '', processEnv = process.env, envKeysFilepath = null, opsOn = true) {
21
- this.envs = determineEnvs(envs, processEnv, DOTENV_KEY)
21
+ constructor (envs = [], overload = false, processEnv = process.env, envKeysFilepath = null, opsOn = false) {
22
+ this.envs = determine(envs, processEnv)
22
23
  this.overload = overload
23
- this.DOTENV_KEY = DOTENV_KEY
24
24
  this.processEnv = processEnv
25
25
  this.envKeysFilepath = envKeysFilepath
26
26
  this.opsOn = opsOn
@@ -35,16 +35,13 @@ class Run {
35
35
  run () {
36
36
  // example
37
37
  // envs [
38
- // { type: 'envVaultFile', value: '.env.vault' },
39
38
  // { type: 'env', value: 'HELLO=one' },
40
39
  // { type: 'envFile', value: '.env' },
41
40
  // { type: 'env', value: 'HELLO=three' }
42
41
  // ]
43
42
 
44
43
  for (const env of this.envs) {
45
- if (env.type === TYPE_ENV_VAULT_FILE) { // deprecate someday - for deprecated .env.vault files
46
- this._injectEnvVaultFile(env.value)
47
- } else if (env.type === TYPE_ENV_FILE) {
44
+ if (env.type === TYPE_ENV_FILE) {
48
45
  this._injectEnvFile(env.value)
49
46
  } else if (env.type === TYPE_ENV) {
50
47
  this._injectEnv(env.value)
@@ -67,7 +64,13 @@ class Run {
67
64
  row.string = env
68
65
 
69
66
  try {
70
- const { parsed, errors, injected, preExisted } = new Parse(env, null, this.processEnv, this.overload).run()
67
+ const {
68
+ parsed,
69
+ errors,
70
+ injected,
71
+ preExisted
72
+ } = new Parse(env, null, this.processEnv, this.overload).run()
73
+
71
74
  row.parsed = parsed
72
75
  row.errors = errors
73
76
  row.injected = injected
@@ -98,13 +101,18 @@ class Run {
98
101
  const src = fsx.readFileX(filepath, { encoding })
99
102
  this.readableFilepaths.add(envFilepath)
100
103
 
101
- const publicKey = findPublicKey(envFilepath)
102
- const privateKey = findPrivateKey(envFilepath, this.envKeysFilepath, this.opsOn, publicKey)
103
- const privateKeyName = guessPrivateKeyName(envFilepath)
104
- const { parsed, errors, injected, preExisted } = new Parse(src, privateKey, this.processEnv, this.overload, privateKeyName).run()
104
+ const { privateKeyName } = keyNames(filepath)
105
+ const { privateKeyValue } = keyValues(filepath, { keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
106
+
107
+ const {
108
+ parsed,
109
+ errors,
110
+ injected,
111
+ preExisted
112
+ } = new Parse(src, privateKeyValue, this.processEnv, this.overload, privateKeyName).run()
105
113
 
106
114
  row.privateKeyName = privateKeyName
107
- row.privateKey = privateKey
115
+ row.privateKey = privateKeyValue
108
116
  row.src = src
109
117
  row.parsed = parsed
110
118
  row.errors = errors
@@ -127,102 +135,11 @@ class Run {
127
135
  this.processedEnvs.push(row)
128
136
  }
129
137
 
130
- _injectEnvVaultFile (envVaultFilepath) {
131
- const row = {}
132
- row.type = TYPE_ENV_VAULT_FILE
133
- row.filepath = envVaultFilepath
134
-
135
- const filepath = path.resolve(envVaultFilepath)
136
- this.readableFilepaths.add(envVaultFilepath)
137
-
138
- if (!fsx.existsSync(filepath)) {
139
- const code = 'MISSING_ENV_VAULT_FILE'
140
- const message = `you set DOTENV_KEY but your .env.vault file is missing: ${filepath}`
141
- const error = new Error(message)
142
- error.code = code
143
- throw error
144
- }
145
-
146
- if (this.DOTENV_KEY.length < 1) {
147
- const code = 'MISSING_DOTENV_KEY'
148
- const message = `your DOTENV_KEY appears to be blank: '${this.DOTENV_KEY}'`
149
- const error = new Error(message)
150
- error.code = code
151
- throw error
152
- }
153
-
154
- let decrypted
155
- const dotenvKeys = this._dotenvKeys()
156
- const parsedVault = this._parsedVault(filepath)
157
- for (let i = 0; i < dotenvKeys.length; i++) {
158
- try {
159
- const dotenvKey = dotenvKeys[i].trim() // dotenv://key_1234@...?environment=prod
160
-
161
- decrypted = this._decrypted(dotenvKey, parsedVault)
162
-
163
- break
164
- } catch (error) {
165
- // last key
166
- if (i + 1 >= dotenvKeys.length) {
167
- throw error
168
- }
169
- // try next key
170
- }
171
- }
172
-
173
- try {
174
- // parse this. it's the equivalent of the .env file
175
- const { parsed, errors, injected, preExisted } = new Parse(decrypted, null, this.processEnv, this.overload).run()
176
- row.parsed = parsed
177
- row.errors = errors
178
- row.injected = injected
179
- row.preExisted = preExisted
180
-
181
- this.inject(row.parsed) // inject
182
-
183
- for (const key of Object.keys(injected)) {
184
- this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
185
- }
186
- } catch (e) {
187
- row.errors = [e]
188
- }
189
-
190
- this.processedEnvs.push(row)
191
- }
192
-
193
138
  inject (parsed) {
194
139
  for (const key of Object.keys(parsed)) {
195
140
  this.processEnv[key] = parsed[key] // inject to process.env
196
141
  }
197
142
  }
198
-
199
- // handle scenario for comma separated keys - for use with key rotation
200
- // example: DOTENV_KEY="dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenvx.com/vault/.env.vault?environment=prod"
201
- _dotenvKeys () {
202
- return this.DOTENV_KEY.split(',')
203
- }
204
-
205
- // { "DOTENV_VAULT_DEVELOPMENT": "<ciphertext>" }
206
- _parsedVault (filepath) {
207
- const src = fsx.readFileX(filepath)
208
- return dotenvParse(src)
209
- }
210
-
211
- _decrypted (dotenvKey, parsedVault) {
212
- const environment = parseEnvironmentFromDotenvKey(dotenvKey)
213
-
214
- // DOTENV_KEY_PRODUCTION
215
- const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`
216
- const ciphertext = parsedVault[environmentKey]
217
- if (!ciphertext) {
218
- const error = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: cannot locate environment ${environmentKey} in your .env.vault file`)
219
- error.code = 'NOT_FOUND_DOTENV_ENVIRONMENT'
220
-
221
- throw error
222
- }
223
-
224
- return decrypt(ciphertext, dotenvKey)
225
- }
226
143
  }
227
144
 
228
145
  module.exports = Run