@dotenvx/dotenvx 1.75.0 → 2.0.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 +34 -1
- package/README.md +4 -114
- package/package.json +8 -7
- package/src/cli/actions/decrypt.js +35 -17
- package/src/cli/actions/doctor.js +2 -2
- package/src/cli/actions/encrypt.js +64 -85
- package/src/cli/actions/ext/genexample.js +2 -2
- package/src/cli/actions/get.js +16 -7
- package/src/cli/actions/keypair.js +55 -42
- package/src/cli/actions/login.js +4 -3
- package/src/cli/actions/normalizeArmorAliases.js +9 -0
- package/src/cli/actions/run.js +65 -22
- package/src/cli/actions/set.js +51 -37
- package/src/cli/dotenvx.js +6 -21
- package/src/lib/api/{postKeypair.js → postArmorKeyring.js} +4 -14
- package/src/lib/{helpers/envResolution → conventions}/environment.js +1 -1
- package/src/lib/conventions/filepaths.js +9 -0
- package/src/lib/conventions/keynames.js +38 -0
- package/src/lib/helpers/catchAndLog.js +4 -1
- package/src/lib/helpers/cryptography/index.js +1 -12
- package/src/lib/helpers/cryptography/mutateKeysSrc.js +5 -21
- package/src/lib/helpers/envResolution/index.js +1 -1
- package/src/lib/helpers/errors.js +13 -0
- package/src/lib/helpers/executeDynamic.js +1 -3
- package/src/lib/helpers/keypairMetadata.js +3 -3
- package/src/lib/helpers/readEnvKey.js +4 -3
- package/src/lib/main.d.ts +2 -39
- package/src/lib/main.js +82 -45
- package/src/lib/providers/armor/index.js +29 -0
- package/src/lib/providers/index.js +46 -0
- package/src/lib/providers/worker.js +6 -0
- package/src/lib/resolvers/envs.js +302 -0
- package/src/lib/resolvers/get.js +69 -0
- package/src/lib/resolvers/keypair.js +81 -0
- package/src/lib/resolvers/ls.js +41 -0
- package/src/lib/services/armorDown.js +2 -2
- package/src/lib/services/{armorKeypair.js → armorKeyring.js} +14 -51
- package/src/lib/services/armorMove.js +2 -2
- package/src/lib/services/armorPull.js +2 -2
- package/src/lib/services/armorPush.js +2 -2
- package/src/lib/services/armorUp.js +2 -2
- package/src/lib/services/genexample.js +19 -8
- package/src/lib/services/login.js +25 -0
- package/src/lib/services/prebuild.js +11 -5
- package/src/lib/services/precommit.js +11 -5
- package/src/lib/transforms/decrypt.js +83 -0
- package/src/lib/transforms/encrypt.js +179 -0
- package/src/lib/transforms/set.js +179 -0
- package/src/shared/logger.js +5 -0
- package/src/cli/actions/normalizeArmorOptions.js +0 -10
- package/src/cli/actions/rotate.js +0 -87
- package/src/lib/helpers/append.js +0 -61
- package/src/lib/helpers/cryptography/armorKeypair.js +0 -42
- package/src/lib/helpers/cryptography/armorKeypairSync.js +0 -55
- package/src/lib/helpers/cryptography/encryptValue.js +0 -7
- package/src/lib/helpers/cryptography/localKeypair.js +0 -14
- package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +0 -38
- package/src/lib/helpers/cryptography/provision.js +0 -56
- package/src/lib/helpers/cryptography/provisionSync.js +0 -51
- package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +0 -26
- package/src/lib/helpers/dotenvParse.js +0 -55
- package/src/lib/helpers/escapeDollarSigns.js +0 -5
- package/src/lib/helpers/escapeForRegex.js +0 -5
- package/src/lib/helpers/evalKeyValue.js +0 -23
- package/src/lib/helpers/isFullyEncrypted.js +0 -27
- package/src/lib/helpers/isIgnoringDotenvKeys.js +0 -19
- package/src/lib/helpers/keyResolution/index.js +0 -16
- package/src/lib/helpers/keyResolution/keyNamesForEnvFile.js +0 -24
- package/src/lib/helpers/keyResolution/keyValues.js +0 -99
- package/src/lib/helpers/keyResolution/keyValuesFromEnvSrc.js +0 -80
- package/src/lib/helpers/keyResolution/keyValuesSync.js +0 -103
- package/src/lib/helpers/keyResolution/readFileKey.js +0 -17
- package/src/lib/helpers/keyResolution/readFileKeySync.js +0 -15
- package/src/lib/helpers/keyResolution/readProcessKey.js +0 -7
- package/src/lib/helpers/localDisplayPath.js +0 -11
- package/src/lib/helpers/parse.js +0 -214
- package/src/lib/helpers/pluralize.js +0 -10
- package/src/lib/helpers/quotes.js +0 -36
- package/src/lib/helpers/replace.js +0 -82
- package/src/lib/helpers/resolveEscapeSequences.js +0 -5
- package/src/lib/services/decrypt.js +0 -157
- package/src/lib/services/encrypt.js +0 -214
- package/src/lib/services/get.js +0 -76
- package/src/lib/services/keypair.js +0 -60
- package/src/lib/services/loginPoll.js +0 -36
- package/src/lib/services/ls.js +0 -57
- package/src/lib/services/rotate.js +0 -198
- package/src/lib/services/run.js +0 -255
- package/src/lib/services/sets.js +0 -326
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
const fsx = require('./../../lib/helpers/fsx')
|
|
2
|
-
const { logger } = require('./../../shared/logger')
|
|
3
|
-
|
|
4
|
-
const Rotate = require('./../../lib/services/rotate')
|
|
5
|
-
|
|
6
|
-
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
7
|
-
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
|
-
const Session = require('../../db/session')
|
|
9
|
-
const normalizeArmorOptions = require('./normalizeArmorOptions')
|
|
10
|
-
|
|
11
|
-
async function rotate () {
|
|
12
|
-
const options = normalizeArmorOptions(this.opts())
|
|
13
|
-
const spinner = await createSpinner({ ...options, text: 'rotating', frames: ['⟳', '⤾', '⥁'] })
|
|
14
|
-
|
|
15
|
-
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
16
|
-
|
|
17
|
-
const envs = this.envs
|
|
18
|
-
const sesh = new Session()
|
|
19
|
-
const noArmor = options.armor === false || (await sesh.noArmor())
|
|
20
|
-
|
|
21
|
-
// stdout - should not have a try so that exit codes can surface to stdout
|
|
22
|
-
if (options.stdout) {
|
|
23
|
-
if (spinner) spinner.stop()
|
|
24
|
-
const {
|
|
25
|
-
processedEnvs
|
|
26
|
-
} = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, {
|
|
27
|
-
command: process.argv.slice(2)
|
|
28
|
-
}).run()
|
|
29
|
-
if (spinner) spinner.stop()
|
|
30
|
-
for (const processedEnv of processedEnvs) {
|
|
31
|
-
console.log(processedEnv.envSrc)
|
|
32
|
-
if (processedEnv.localPrivateKeyAdded) {
|
|
33
|
-
console.log('')
|
|
34
|
-
console.log(processedEnv.envKeysSrc)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
process.exit(0) // exit early
|
|
38
|
-
} else {
|
|
39
|
-
try {
|
|
40
|
-
if (spinner) spinner.stop()
|
|
41
|
-
const {
|
|
42
|
-
processedEnvs,
|
|
43
|
-
changedFilepaths,
|
|
44
|
-
unchangedFilepaths
|
|
45
|
-
} = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, {
|
|
46
|
-
command: process.argv.slice(2)
|
|
47
|
-
}).run()
|
|
48
|
-
|
|
49
|
-
for (const processedEnv of processedEnvs) {
|
|
50
|
-
logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
51
|
-
if (processedEnv.error) {
|
|
52
|
-
logger.warn(processedEnv.error.messageWithHelp)
|
|
53
|
-
} else if (processedEnv.changed) {
|
|
54
|
-
await fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
55
|
-
if (processedEnv.localPrivateKeyAdded) {
|
|
56
|
-
await fsx.writeFileX(processedEnv.envKeysFilepath, processedEnv.envKeysSrc)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
logger.verbose(`rotated ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
60
|
-
} else {
|
|
61
|
-
logger.verbose(`no change ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (spinner) spinner.stop()
|
|
66
|
-
if (changedFilepaths.length > 0) {
|
|
67
|
-
const remoteKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.remotePrivateKeyAdded)
|
|
68
|
-
|
|
69
|
-
let msg = `⟳ rotated (${changedFilepaths.join(',')})`
|
|
70
|
-
if (remoteKeyAddedEnv) {
|
|
71
|
-
msg += ' · armored ⛨'
|
|
72
|
-
}
|
|
73
|
-
logger.success(msg)
|
|
74
|
-
} else if (unchangedFilepaths.length > 0) {
|
|
75
|
-
logger.info(`○ no change (${unchangedFilepaths})`)
|
|
76
|
-
} else {
|
|
77
|
-
// do nothing - scenario when no .env files found
|
|
78
|
-
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
if (spinner) spinner.stop()
|
|
81
|
-
catchAndLog(error)
|
|
82
|
-
process.exit(1)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
module.exports = rotate
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
const quotes = require('./quotes')
|
|
2
|
-
const dotenvParse = require('./dotenvParse')
|
|
3
|
-
const escapeForRegex = require('./escapeForRegex')
|
|
4
|
-
const escapeDollarSigns = require('./escapeDollarSigns')
|
|
5
|
-
|
|
6
|
-
function append (src, key, appendValue) {
|
|
7
|
-
let output
|
|
8
|
-
let newPart = ''
|
|
9
|
-
|
|
10
|
-
const parsed = dotenvParse(src, true, true) // skip expanding \n and skip converting \r\n
|
|
11
|
-
const _quotes = quotes(src)
|
|
12
|
-
if (Object.prototype.hasOwnProperty.call(parsed, key)) {
|
|
13
|
-
const quote = _quotes[key]
|
|
14
|
-
const originalValue = parsed[key]
|
|
15
|
-
|
|
16
|
-
newPart += `${key}=${quote}${originalValue},${appendValue}${quote}`
|
|
17
|
-
|
|
18
|
-
const escapedOriginalValue = escapeForRegex(originalValue)
|
|
19
|
-
|
|
20
|
-
// conditionally enforce end of line
|
|
21
|
-
let enforceEndOfLine = ''
|
|
22
|
-
if (escapedOriginalValue === '') {
|
|
23
|
-
enforceEndOfLine = '$' // EMPTY scenario
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const currentPart = new RegExp(
|
|
27
|
-
'^' + // start of line
|
|
28
|
-
'(\\s*)?' + // spaces
|
|
29
|
-
'(export\\s+)?' + // export
|
|
30
|
-
key + // KEY
|
|
31
|
-
'\\s*=\\s*' + // spaces (KEY = value)
|
|
32
|
-
'["\'`]?' + // open quote
|
|
33
|
-
escapedOriginalValue + // escaped value
|
|
34
|
-
'["\'`]?' + // close quote
|
|
35
|
-
enforceEndOfLine
|
|
36
|
-
,
|
|
37
|
-
'gm' // (g)lobal (m)ultiline
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
const saferInput = escapeDollarSigns(newPart) // cleanse user inputted capture groups ($1, $2 etc)
|
|
41
|
-
|
|
42
|
-
// $1 preserves spaces
|
|
43
|
-
// $2 preserves export
|
|
44
|
-
output = src.replace(currentPart, `$1$2${saferInput}`)
|
|
45
|
-
} else {
|
|
46
|
-
newPart += `${key}="${appendValue}"`
|
|
47
|
-
|
|
48
|
-
// append
|
|
49
|
-
if (src.endsWith('\n')) {
|
|
50
|
-
newPart = newPart + '\n'
|
|
51
|
-
} else {
|
|
52
|
-
newPart = '\n' + newPart
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
output = src + newPart
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return output
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
module.exports = append
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const Session = require('../../../db/session')
|
|
2
|
-
const ArmorKeypair = require('../../services/armorKeypair')
|
|
3
|
-
const sanitizeCommandForMetadata = require('../sanitizeCommandForMetadata')
|
|
4
|
-
|
|
5
|
-
function metadataFromOptions (options) {
|
|
6
|
-
if (options.metadata) return options.metadata
|
|
7
|
-
if (!options.command) return undefined
|
|
8
|
-
|
|
9
|
-
return JSON.stringify({
|
|
10
|
-
command: sanitizeCommandForMetadata(options.command)
|
|
11
|
-
})
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function armorKeypair (existingPublicKey, options = {}) {
|
|
15
|
-
const sesh = new Session()
|
|
16
|
-
const token = options.token || sesh.token()
|
|
17
|
-
if (!token) {
|
|
18
|
-
return {
|
|
19
|
-
publicKey: undefined,
|
|
20
|
-
privateKey: undefined
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const json = await new ArmorKeypair(
|
|
25
|
-
options.hostname || sesh.hostname(),
|
|
26
|
-
token,
|
|
27
|
-
sesh.devicePublicKey(),
|
|
28
|
-
existingPublicKey,
|
|
29
|
-
{
|
|
30
|
-
envFile: options.envFilepath,
|
|
31
|
-
team: options.team,
|
|
32
|
-
metadata: metadataFromOptions(options)
|
|
33
|
-
}
|
|
34
|
-
).run()
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
publicKey: json.public_key,
|
|
38
|
-
privateKey: json.private_key
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
module.exports = armorKeypair
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const childProcess = require('child_process')
|
|
3
|
-
|
|
4
|
-
const EXEC_TIMEOUT = 5 * 60 * 1000
|
|
5
|
-
|
|
6
|
-
function cliPath () {
|
|
7
|
-
return path.resolve(__dirname, '../../../cli/dotenvx.js')
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function execEnv () {
|
|
11
|
-
const env = { ...process.env }
|
|
12
|
-
env._TAPJS_PROCESSINFO_COVERAGE_ = '0'
|
|
13
|
-
|
|
14
|
-
if (env.NODE_OPTIONS && env.NODE_OPTIONS.includes('@tapjs/processinfo')) {
|
|
15
|
-
const nodeOptions = env.NODE_OPTIONS
|
|
16
|
-
.split(/\s+/)
|
|
17
|
-
.filter((option) => !option.includes('@tapjs/processinfo'))
|
|
18
|
-
.join(' ')
|
|
19
|
-
.trim()
|
|
20
|
-
|
|
21
|
-
if (nodeOptions) {
|
|
22
|
-
env.NODE_OPTIONS = nodeOptions
|
|
23
|
-
} else {
|
|
24
|
-
delete env.NODE_OPTIONS
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return env
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function armorKeypairSync (_existingPublicKey, options = {}) {
|
|
32
|
-
const args = [cliPath(), 'keypair', '--format', 'json']
|
|
33
|
-
if (options.envFilepath) args.push('-f', options.envFilepath)
|
|
34
|
-
|
|
35
|
-
let keypairs = {}
|
|
36
|
-
try {
|
|
37
|
-
keypairs = JSON.parse(childProcess.execFileSync(process.execPath, args, {
|
|
38
|
-
env: execEnv(),
|
|
39
|
-
stdio: ['inherit', 'pipe', 'inherit'],
|
|
40
|
-
timeout: EXEC_TIMEOUT
|
|
41
|
-
}).toString().trim())
|
|
42
|
-
} catch (_error) {
|
|
43
|
-
keypairs = {}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const publicKeyName = Object.keys(keypairs).find((key) => key === 'DOTENV_PUBLIC_KEY' || key.startsWith('DOTENV_PUBLIC_KEY_'))
|
|
47
|
-
const privateKeyName = Object.keys(keypairs).find((key) => key === 'DOTENV_PRIVATE_KEY' || key.startsWith('DOTENV_PRIVATE_KEY_'))
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
publicKey: keypairs[publicKeyName],
|
|
51
|
-
privateKey: keypairs[privateKeyName]
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = armorKeypairSync
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
const { derive, keypair } = require('@dotenvx/primitives')
|
|
2
|
-
|
|
3
|
-
function localKeypair (existingPrivateKey) {
|
|
4
|
-
if (existingPrivateKey) {
|
|
5
|
-
return {
|
|
6
|
-
publicKey: derive(existingPrivateKey),
|
|
7
|
-
privateKey: existingPrivateKey
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
return keypair()
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
module.exports = localKeypair
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const FIRST_TIME_KEYS_SRC = [
|
|
2
|
-
'#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
|
|
3
|
-
'#/ private decryption keys. DO NOT commit to source control /',
|
|
4
|
-
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
5
|
-
'#/ ⛨ ARMORED KEYS: `dotenvx armor up` /',
|
|
6
|
-
'#/----------------------------------------------------------/'
|
|
7
|
-
].join('\n')
|
|
8
|
-
|
|
9
|
-
const path = require('path')
|
|
10
|
-
const fsx = require('./../fsx')
|
|
11
|
-
|
|
12
|
-
function mutateKeysSrcSync ({ envFilepath, keysFilepath, privateKeyName, privateKeyValue }) {
|
|
13
|
-
const filename = path.basename(envFilepath)
|
|
14
|
-
const filepath = path.resolve(envFilepath)
|
|
15
|
-
let resolvedKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
|
|
16
|
-
if (keysFilepath) {
|
|
17
|
-
resolvedKeysFilepath = path.resolve(keysFilepath)
|
|
18
|
-
}
|
|
19
|
-
const appendPrivateKey = [`# ${filename}`, `${privateKeyName}=${privateKeyValue}`, ''].join('\n')
|
|
20
|
-
|
|
21
|
-
let keysSrc = ''
|
|
22
|
-
if (fsx.existsSync(resolvedKeysFilepath)) {
|
|
23
|
-
keysSrc = fsx.readFileXSync(resolvedKeysFilepath)
|
|
24
|
-
}
|
|
25
|
-
keysSrc = keysSrc.length > 1 ? keysSrc : `${FIRST_TIME_KEYS_SRC}\n`
|
|
26
|
-
keysSrc = `${keysSrc}\n${appendPrivateKey}`
|
|
27
|
-
|
|
28
|
-
fsx.writeFileXSync(resolvedKeysFilepath, keysSrc) // TODO: don't write if armor
|
|
29
|
-
|
|
30
|
-
const envKeysFilepath = keysFilepath || path.join(path.dirname(envFilepath), path.basename(resolvedKeysFilepath))
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
keysSrc,
|
|
34
|
-
envKeysFilepath
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
module.exports = mutateKeysSrcSync
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const mutateSrc = require('./mutateSrc')
|
|
2
|
-
const mutateKeysSrc = require('./mutateKeysSrc')
|
|
3
|
-
const armorKeypair = require('./armorKeypair')
|
|
4
|
-
const localKeypair = require('./localKeypair')
|
|
5
|
-
const { keyNamesForEnvFile } = require('../keyResolution')
|
|
6
|
-
|
|
7
|
-
async function provision ({ envSrc, envFilepath, keysFilepath, noArmor, token, selectKeyStorage, command }) {
|
|
8
|
-
noArmor = noArmor !== false
|
|
9
|
-
if (!noArmor && selectKeyStorage) {
|
|
10
|
-
noArmor = await selectKeyStorage() !== 'armored'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
|
|
14
|
-
|
|
15
|
-
let publicKey
|
|
16
|
-
let privateKey
|
|
17
|
-
let keysSrc
|
|
18
|
-
let envKeysFilepath
|
|
19
|
-
let localPrivateKeyAdded = false
|
|
20
|
-
let remotePrivateKeyAdded = false
|
|
21
|
-
|
|
22
|
-
if (noArmor) {
|
|
23
|
-
const kp = localKeypair()
|
|
24
|
-
publicKey = kp.publicKey
|
|
25
|
-
privateKey = kp.privateKey
|
|
26
|
-
} else {
|
|
27
|
-
const armorOptions = { token, envFilepath, command }
|
|
28
|
-
const kp = await armorKeypair(undefined, armorOptions)
|
|
29
|
-
publicKey = kp.publicKey
|
|
30
|
-
privateKey = kp.privateKey
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
|
|
34
|
-
envSrc = mutated.envSrc
|
|
35
|
-
|
|
36
|
-
if (noArmor) {
|
|
37
|
-
const mutated = await mutateKeysSrc({ envFilepath, keysFilepath, privateKeyName, privateKeyValue: privateKey })
|
|
38
|
-
keysSrc = mutated.keysSrc
|
|
39
|
-
envKeysFilepath = mutated.envKeysFilepath
|
|
40
|
-
localPrivateKeyAdded = true
|
|
41
|
-
} else {
|
|
42
|
-
remotePrivateKeyAdded = true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
envSrc,
|
|
47
|
-
keysSrc,
|
|
48
|
-
publicKey,
|
|
49
|
-
privateKey,
|
|
50
|
-
envKeysFilepath,
|
|
51
|
-
localPrivateKeyAdded,
|
|
52
|
-
remotePrivateKeyAdded
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
module.exports = provision
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const mutateSrc = require('./mutateSrc')
|
|
2
|
-
const mutateKeysSrcSync = require('./mutateKeysSrcSync')
|
|
3
|
-
const armorKeypairSync = require('./armorKeypairSync')
|
|
4
|
-
const localKeypair = require('./localKeypair')
|
|
5
|
-
const { keyNamesForEnvFile } = require('../keyResolution')
|
|
6
|
-
|
|
7
|
-
function provisionSync ({ envSrc, envFilepath, keysFilepath, noArmor, command }) {
|
|
8
|
-
noArmor = noArmor !== false
|
|
9
|
-
const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
|
|
10
|
-
|
|
11
|
-
let publicKey
|
|
12
|
-
let privateKey
|
|
13
|
-
let keysSrc
|
|
14
|
-
let envKeysFilepath
|
|
15
|
-
let localPrivateKeyAdded = false
|
|
16
|
-
let remotePrivateKeyAdded = false
|
|
17
|
-
|
|
18
|
-
if (noArmor) {
|
|
19
|
-
const kp = localKeypair()
|
|
20
|
-
publicKey = kp.publicKey
|
|
21
|
-
privateKey = kp.privateKey
|
|
22
|
-
} else {
|
|
23
|
-
const kp = armorKeypairSync(undefined, { envFilepath, command })
|
|
24
|
-
publicKey = kp.publicKey
|
|
25
|
-
privateKey = kp.privateKey
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
|
|
29
|
-
envSrc = mutated.envSrc
|
|
30
|
-
|
|
31
|
-
if (noArmor) {
|
|
32
|
-
const mutated = mutateKeysSrcSync({ envFilepath, keysFilepath, privateKeyName, privateKeyValue: privateKey })
|
|
33
|
-
keysSrc = mutated.keysSrc
|
|
34
|
-
envKeysFilepath = mutated.envKeysFilepath
|
|
35
|
-
localPrivateKeyAdded = true
|
|
36
|
-
} else {
|
|
37
|
-
remotePrivateKeyAdded = true
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
envSrc,
|
|
42
|
-
keysSrc,
|
|
43
|
-
publicKey,
|
|
44
|
-
privateKey,
|
|
45
|
-
envKeysFilepath,
|
|
46
|
-
localPrivateKeyAdded,
|
|
47
|
-
remotePrivateKeyAdded
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = provisionSync
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const Errors = require('./../errors')
|
|
2
|
-
const mutateSrc = require('./mutateSrc')
|
|
3
|
-
const localKeypair = require('./localKeypair')
|
|
4
|
-
|
|
5
|
-
function provisionWithPrivateKey ({ envSrc, envFilepath, keysFilepath, privateKeyValue, publicKeyValue, publicKeyName }) {
|
|
6
|
-
const { publicKey, privateKey } = localKeypair(privateKeyValue) // noArmor doesn't matter here since privateKeyValue was already discovered prior (via armor and local) and passed as privateKeyValue
|
|
7
|
-
|
|
8
|
-
// if derivation doesn't match what's in the file (or preset in env)
|
|
9
|
-
if (publicKeyValue && publicKeyValue !== publicKey) {
|
|
10
|
-
throw new Errors({ publicKey, publicKeyExisting: publicKeyValue }).mispairedPrivateKey()
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// scenario when encrypting a monorepo second .env file from a prior generated -fk .env.keys file
|
|
14
|
-
if (!publicKeyValue) {
|
|
15
|
-
const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
|
|
16
|
-
envSrc = mutated.envSrc
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
envSrc,
|
|
21
|
-
publicKey,
|
|
22
|
-
privateKey
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = provisionWithPrivateKey
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// historical dotenv.parse - https://github.com/motdotla/dotenv)
|
|
2
|
-
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
|
|
3
|
-
|
|
4
|
-
function dotenvParse (src, skipExpandForDoubleQuotes = false, skipConvertingWindowsNewlines = false, collectAllValues = false) {
|
|
5
|
-
const obj = {}
|
|
6
|
-
|
|
7
|
-
// Convert buffer to string
|
|
8
|
-
let lines = src.toString()
|
|
9
|
-
|
|
10
|
-
// Convert line breaks to same format
|
|
11
|
-
if (!skipConvertingWindowsNewlines) {
|
|
12
|
-
lines = lines.replace(/\r\n?/mg, '\n')
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
let match
|
|
16
|
-
while ((match = LINE.exec(lines)) != null) {
|
|
17
|
-
const key = match[1]
|
|
18
|
-
|
|
19
|
-
// Default undefined or null to empty string
|
|
20
|
-
let value = (match[2] || '')
|
|
21
|
-
|
|
22
|
-
// Remove whitespace
|
|
23
|
-
value = value.trim()
|
|
24
|
-
|
|
25
|
-
// Check if double quoted
|
|
26
|
-
const maybeQuote = value[0]
|
|
27
|
-
|
|
28
|
-
// Remove surrounding quotes
|
|
29
|
-
value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
|
|
30
|
-
|
|
31
|
-
// Expand newlines if double quoted
|
|
32
|
-
if (maybeQuote === '"' && !skipExpandForDoubleQuotes) {
|
|
33
|
-
value = value.replace(/\\n/g, '\n') // newline
|
|
34
|
-
value = value.replace(/\\r/g, '\r') // carriage return
|
|
35
|
-
value = value.replace(/\\t/g, '\t') // tabs
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (collectAllValues) {
|
|
39
|
-
// handle scenario where user mistakenly includes plaintext duplicate in .env:
|
|
40
|
-
//
|
|
41
|
-
// # .env
|
|
42
|
-
// HELLO="World"
|
|
43
|
-
// HELLO="encrypted:1234"
|
|
44
|
-
obj[key] = obj[key] || []
|
|
45
|
-
obj[key].push(value)
|
|
46
|
-
} else {
|
|
47
|
-
// Add to object
|
|
48
|
-
obj[key] = value
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return obj
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = dotenvParse
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const { execSync } = require('child_process')
|
|
2
|
-
const chomp = require('./chomp')
|
|
3
|
-
const Errors = require('./errors')
|
|
4
|
-
|
|
5
|
-
function evalKeyValue (key, value, processEnv, runningParsed) {
|
|
6
|
-
// Match everything between the outermost $() using a regex with non-capturing groups
|
|
7
|
-
const matches = value.match(/\$\(([^)]+(?:\)[^(]*)*)\)/g) || []
|
|
8
|
-
return matches.reduce((newValue, match) => {
|
|
9
|
-
const command = match.slice(2, -1) // Extract command by removing $() wrapper
|
|
10
|
-
let result
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
result = execSync(command, { env: { ...processEnv, ...runningParsed } }).toString() // execute command (including runningParsed)
|
|
14
|
-
} catch (e) {
|
|
15
|
-
throw new Errors({ key, command, message: e.message.trim() }).commandSubstitutionFailed()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
result = chomp(result) // chomp it
|
|
19
|
-
return newValue.replace(match, result) // Replace match with result
|
|
20
|
-
}, value)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
module.exports = evalKeyValue
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const { encrypted } = require('@dotenvx/primitives')
|
|
2
|
-
const dotenvParse = require('./dotenvParse')
|
|
3
|
-
const isPublicKey = require('./cryptography/isPublicKey')
|
|
4
|
-
|
|
5
|
-
function isFullyEncrypted (src) {
|
|
6
|
-
const parsed = dotenvParse(src, false, false, true) // collect all values
|
|
7
|
-
|
|
8
|
-
for (const [key, values] of Object.entries(parsed)) {
|
|
9
|
-
// handle scenario where user mistakenly includes plaintext duplicate in .env:
|
|
10
|
-
//
|
|
11
|
-
// # .env
|
|
12
|
-
// HELLO="World"
|
|
13
|
-
// HELLO="encrypted:1234"
|
|
14
|
-
//
|
|
15
|
-
// key => [value1, ...]
|
|
16
|
-
for (const value of values) {
|
|
17
|
-
const result = encrypted(value) || isPublicKey(key)
|
|
18
|
-
if (!result) {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return true
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = isFullyEncrypted
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const fsx = require('./fsx')
|
|
2
|
-
const ignore = require('ignore')
|
|
3
|
-
|
|
4
|
-
function isIgnoringDotenvKeys () {
|
|
5
|
-
if (!fsx.existsSync('.gitignore')) {
|
|
6
|
-
return false
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const gitignore = fsx.readFileXSync('.gitignore')
|
|
10
|
-
const ig = ignore(gitignore).add(gitignore)
|
|
11
|
-
|
|
12
|
-
if (!ig.ignores('.env.keys')) {
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return true
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = isIgnoringDotenvKeys
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
keyNamesForEnvFile: require('./keyNamesForEnvFile'),
|
|
3
|
-
keyValues: require('./keyValues'),
|
|
4
|
-
keyValuesSync: require('./keyValuesSync'),
|
|
5
|
-
keyValuesFromEnvSrc: require('./keyValuesFromEnvSrc'),
|
|
6
|
-
|
|
7
|
-
// private
|
|
8
|
-
// private keys are resolved via keyValuesSync()
|
|
9
|
-
|
|
10
|
-
// other
|
|
11
|
-
readProcessKey: require('./readProcessKey'),
|
|
12
|
-
readFileKey: require('./readFileKey'),
|
|
13
|
-
readFileKeySync: require('./readFileKeySync'),
|
|
14
|
-
guessPrivateKeyFilename: require('./../guessPrivateKeyFilename'),
|
|
15
|
-
dotenvPrivateKeyNames: require('./../dotenvPrivateKeyNames')
|
|
16
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const canonicalEnvFilename = require('./../canonicalEnvFilename')
|
|
2
|
-
const environment = require('./../envResolution/environment')
|
|
3
|
-
|
|
4
|
-
function keyNamesForEnvFile (filepath) {
|
|
5
|
-
const filename = canonicalEnvFilename(filepath)
|
|
6
|
-
|
|
7
|
-
// .env
|
|
8
|
-
if (filename === '.env') {
|
|
9
|
-
return {
|
|
10
|
-
publicKeyName: 'DOTENV_PUBLIC_KEY',
|
|
11
|
-
privateKeyName: 'DOTENV_PRIVATE_KEY'
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// .env.ENVIRONMENT
|
|
16
|
-
const resolvedEnvironment = environment(filename).toUpperCase()
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
publicKeyName: `DOTENV_PUBLIC_KEY_${resolvedEnvironment}`,
|
|
20
|
-
privateKeyName: `DOTENV_PRIVATE_KEY_${resolvedEnvironment}`
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
module.exports = keyNamesForEnvFile
|