@dotenvx/dotenvx 1.72.0 → 1.73.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/README.md +1 -1
  3. package/package.json +1 -2
  4. package/src/cli/actions/armor/down.js +37 -0
  5. package/src/cli/actions/armor/move.js +42 -0
  6. package/src/cli/actions/armor/pull.js +37 -0
  7. package/src/cli/actions/armor/push.js +37 -0
  8. package/src/cli/actions/armor/up.js +37 -0
  9. package/src/cli/actions/login.js +0 -1
  10. package/src/cli/commands/armor.js +72 -0
  11. package/src/cli/dotenvx.js +3 -0
  12. package/src/db/session.js +90 -22
  13. package/src/lib/api/getAccount.js +32 -0
  14. package/src/lib/api/postArmorDown.js +48 -0
  15. package/src/lib/api/postArmorMove.js +48 -0
  16. package/src/lib/api/postArmorPull.js +48 -0
  17. package/src/lib/api/postArmorPush.js +48 -0
  18. package/src/lib/api/postArmorUp.js +51 -0
  19. package/src/lib/api/postKeypair.js +60 -0
  20. package/src/lib/helpers/armoredKeyDisplay.js +10 -0
  21. package/src/lib/helpers/cryptography/armorKeypair.js +32 -11
  22. package/src/lib/helpers/cryptography/armorKeypairSync.js +48 -7
  23. package/src/lib/helpers/cryptography/provision.js +2 -2
  24. package/src/lib/helpers/cryptography/provisionSync.js +2 -2
  25. package/src/lib/helpers/keyResolution/index.js +1 -1
  26. package/src/lib/helpers/keyResolution/{keyNames.js → keyNamesForEnvFile.js} +2 -2
  27. package/src/lib/helpers/keyResolution/keyValues.js +2 -2
  28. package/src/lib/helpers/keyResolution/keyValuesSync.js +2 -2
  29. package/src/lib/helpers/keypairMetadata.js +77 -0
  30. package/src/lib/helpers/readEnvKey.js +31 -0
  31. package/src/lib/helpers/removeEnvKey.js +50 -0
  32. package/src/lib/helpers/sanitizeCommandForMetadata.js +64 -0
  33. package/src/lib/helpers/teamChoicesFromMeta.js +8 -0
  34. package/src/lib/helpers/upsertEnvKey.js +61 -0
  35. package/src/lib/services/armorDown.js +71 -0
  36. package/src/lib/services/armorKeypair.js +156 -0
  37. package/src/lib/services/armorMove.js +54 -0
  38. package/src/lib/services/armorPull.js +71 -0
  39. package/src/lib/services/armorPush.js +76 -0
  40. package/src/lib/services/armorUp.js +73 -0
  41. package/src/lib/services/decrypt.js +2 -2
  42. package/src/lib/services/encrypt.js +2 -2
  43. package/src/lib/services/keypair.js +5 -7
  44. package/src/lib/services/loginPoll.js +1 -0
  45. package/src/lib/services/rotate.js +2 -2
  46. package/src/lib/services/run.js +3 -3
  47. package/src/lib/services/sets.js +3 -3
  48. package/src/lib/extensions/armor.js +0 -204
@@ -0,0 +1,48 @@
1
+ const { http } = require('../helpers/http')
2
+ const buildApiError = require('../helpers/buildApiError')
3
+ const packageJson = require('../helpers/packageJson')
4
+ const normalizeToken = require('../helpers/normalizeToken')
5
+
6
+ class PostArmorPull {
7
+ constructor (hostname, token, devicePublicKey, publicKey, team) {
8
+ this.hostname = hostname
9
+ this.token = token
10
+ this.devicePublicKey = devicePublicKey
11
+ this.publicKey = publicKey
12
+ this.team = team
13
+ }
14
+
15
+ async run () {
16
+ const token = normalizeToken(this.token)
17
+ const devicePublicKey = this.devicePublicKey
18
+ const publicKey = this.publicKey
19
+ const team = this.team
20
+ const url = `${this.hostname}/api/armor/pull`
21
+
22
+ const body = {
23
+ device_public_key: devicePublicKey,
24
+ cli_version: packageJson.version,
25
+ public_key: publicKey,
26
+ team
27
+ }
28
+
29
+ const resp = await http(url, {
30
+ method: 'POST',
31
+ headers: {
32
+ Authorization: `Bearer ${token}`,
33
+ 'Content-Type': 'application/json'
34
+ },
35
+ body: JSON.stringify(body)
36
+ })
37
+
38
+ const json = await resp.body.json()
39
+
40
+ if (resp.statusCode >= 400) {
41
+ throw buildApiError(resp.statusCode, json)
42
+ }
43
+
44
+ return json
45
+ }
46
+ }
47
+
48
+ module.exports = PostArmorPull
@@ -0,0 +1,48 @@
1
+ const { http } = require('../helpers/http')
2
+ const buildApiError = require('../helpers/buildApiError')
3
+ const packageJson = require('../helpers/packageJson')
4
+ const normalizeToken = require('../helpers/normalizeToken')
5
+
6
+ class PostArmorPush {
7
+ constructor (hostname, token, devicePublicKey, privateKey, team) {
8
+ this.hostname = hostname
9
+ this.token = token
10
+ this.devicePublicKey = devicePublicKey
11
+ this.privateKey = privateKey
12
+ this.team = team
13
+ }
14
+
15
+ async run () {
16
+ const token = normalizeToken(this.token)
17
+ const devicePublicKey = this.devicePublicKey
18
+ const privateKey = this.privateKey
19
+ const team = this.team
20
+ const url = `${this.hostname}/api/armor/push`
21
+
22
+ const body = {
23
+ device_public_key: devicePublicKey,
24
+ cli_version: packageJson.version,
25
+ private_key: privateKey,
26
+ team
27
+ }
28
+
29
+ const resp = await http(url, {
30
+ method: 'POST',
31
+ headers: {
32
+ Authorization: `Bearer ${token}`,
33
+ 'Content-Type': 'application/json'
34
+ },
35
+ body: JSON.stringify(body)
36
+ })
37
+
38
+ const json = await resp.body.json()
39
+
40
+ if (resp.statusCode >= 400) {
41
+ throw buildApiError(resp.statusCode, json)
42
+ }
43
+
44
+ return json
45
+ }
46
+ }
47
+
48
+ module.exports = PostArmorPush
@@ -0,0 +1,51 @@
1
+ const { http } = require('../helpers/http')
2
+ const buildApiError = require('../helpers/buildApiError')
3
+ const packageJson = require('../helpers/packageJson')
4
+ const normalizeToken = require('../helpers/normalizeToken')
5
+
6
+ class PostArmorUp {
7
+ constructor (hostname, token, devicePublicKey, publicKey, privateKey, team) {
8
+ this.hostname = hostname
9
+ this.token = token
10
+ this.devicePublicKey = devicePublicKey
11
+ this.publicKey = publicKey
12
+ this.privateKey = privateKey
13
+ this.team = team
14
+ }
15
+
16
+ async run () {
17
+ const token = normalizeToken(this.token)
18
+ const devicePublicKey = this.devicePublicKey
19
+ const publicKey = this.publicKey
20
+ const privateKey = this.privateKey
21
+ const team = this.team
22
+ const url = `${this.hostname}/api/armor/up`
23
+
24
+ const body = {
25
+ device_public_key: devicePublicKey,
26
+ cli_version: packageJson.version,
27
+ public_key: publicKey,
28
+ private_key: privateKey,
29
+ team
30
+ }
31
+
32
+ const resp = await http(url, {
33
+ method: 'POST',
34
+ headers: {
35
+ Authorization: `Bearer ${token}`,
36
+ 'Content-Type': 'application/json'
37
+ },
38
+ body: JSON.stringify(body)
39
+ })
40
+
41
+ const json = await resp.body.json()
42
+
43
+ if (resp.statusCode >= 400) {
44
+ throw buildApiError(resp.statusCode, json)
45
+ }
46
+
47
+ return json
48
+ }
49
+ }
50
+
51
+ module.exports = PostArmorUp
@@ -0,0 +1,60 @@
1
+ const { http } = require('../helpers/http')
2
+ const buildApiError = require('../helpers/buildApiError')
3
+ const packageJson = require('../helpers/packageJson')
4
+ const normalizeToken = require('../helpers/normalizeToken')
5
+
6
+ class PostKeypair {
7
+ constructor (hostname, token, devicePublicKey, publicKey, team, metadata, grantToken) {
8
+ this.hostname = hostname || 'https://armor.dotenvx.com'
9
+ this.token = token
10
+ this.devicePublicKey = devicePublicKey
11
+ this.publicKey = publicKey
12
+ this.team = team
13
+ this.metadata = metadata
14
+ this.grantToken = grantToken
15
+ }
16
+
17
+ async run () {
18
+ const token = normalizeToken(this.token)
19
+ const url = `${this.hostname}/api/keypair`
20
+ const body = {
21
+ device_public_key: this.devicePublicKey,
22
+ cli_version: packageJson.version
23
+ }
24
+
25
+ if (this.publicKey) {
26
+ body.public_key = this.publicKey
27
+ }
28
+
29
+ if (this.team) {
30
+ body.team = this.team
31
+ }
32
+
33
+ if (this.metadata && Object.keys(this.metadata).length > 0) {
34
+ body.metadata = this.metadata
35
+ }
36
+
37
+ if (this.grantToken) {
38
+ body.grant_token = this.grantToken
39
+ }
40
+
41
+ const resp = await http(url, {
42
+ method: 'POST',
43
+ headers: {
44
+ Authorization: `Bearer ${token}`,
45
+ 'Content-Type': 'application/json'
46
+ },
47
+ body: JSON.stringify(body)
48
+ })
49
+
50
+ const json = await resp.body.json()
51
+
52
+ if (resp.statusCode >= 400) {
53
+ throw buildApiError(resp.statusCode, json)
54
+ }
55
+
56
+ return json
57
+ }
58
+ }
59
+
60
+ module.exports = PostKeypair
@@ -0,0 +1,10 @@
1
+ function armoredKeyDisplay (publicKey) {
2
+ if (!publicKey) return ''
3
+
4
+ const prefix = String(publicKey).slice(0, 6).toUpperCase()
5
+ if (prefix.length <= 3) return prefix
6
+
7
+ return `${prefix.slice(0, 3)} ${prefix.slice(3)}`
8
+ }
9
+
10
+ module.exports = armoredKeyDisplay
@@ -1,20 +1,41 @@
1
- const Armor = require('../../extensions/armor')
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
+ }
2
13
 
3
14
  async function armorKeypair (existingPublicKey, options = {}) {
4
- const keypairOptions = {
5
- token: options.token,
6
- envFilepath: options.envFilepath,
7
- command: options.command
15
+ const sesh = new Session()
16
+ const token = options.token || sesh.token()
17
+ if (!token) {
18
+ return {
19
+ publicKey: undefined,
20
+ privateKey: undefined
21
+ }
8
22
  }
9
23
 
10
- const kp = await new Armor().keypair(existingPublicKey, keypairOptions)
11
-
12
- const publicKey = kp.public_key
13
- const privateKey = kp.private_key
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()
14
35
 
15
36
  return {
16
- publicKey,
17
- privateKey
37
+ publicKey: json.public_key,
38
+ privateKey: json.private_key
18
39
  }
19
40
  }
20
41
 
@@ -1,13 +1,54 @@
1
- const Armor = require('../../extensions/armor')
1
+ const path = require('path')
2
+ const childProcess = require('child_process')
2
3
 
3
- function armorKeypairSync (existingPublicKey, options = {}) {
4
- const kp = new Armor().keypairSync(existingPublicKey, options)
5
- const publicKey = kp.public_key
6
- const privateKey = kp.private_key
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_'))
7
48
 
8
49
  return {
9
- publicKey,
10
- privateKey
50
+ publicKey: keypairs[publicKeyName],
51
+ privateKey: keypairs[privateKeyName]
11
52
  }
12
53
  }
13
54
 
@@ -2,7 +2,7 @@ const mutateSrc = require('./mutateSrc')
2
2
  const mutateKeysSrc = require('./mutateKeysSrc')
3
3
  const armorKeypair = require('./armorKeypair')
4
4
  const localKeypair = require('./localKeypair')
5
- const { keyNames } = require('../keyResolution')
5
+ const { keyNamesForEnvFile } = require('../keyResolution')
6
6
 
7
7
  async function provision ({ envSrc, envFilepath, keysFilepath, noArmor, token, selectKeyStorage, command }) {
8
8
  noArmor = noArmor !== false
@@ -10,7 +10,7 @@ async function provision ({ envSrc, envFilepath, keysFilepath, noArmor, token, s
10
10
  noArmor = await selectKeyStorage() !== 'armored'
11
11
  }
12
12
 
13
- const { publicKeyName, privateKeyName } = keyNames(envFilepath)
13
+ const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
14
14
 
15
15
  let publicKey
16
16
  let privateKey
@@ -2,11 +2,11 @@ const mutateSrc = require('./mutateSrc')
2
2
  const mutateKeysSrcSync = require('./mutateKeysSrcSync')
3
3
  const armorKeypairSync = require('./armorKeypairSync')
4
4
  const localKeypair = require('./localKeypair')
5
- const { keyNames } = require('../keyResolution')
5
+ const { keyNamesForEnvFile } = require('../keyResolution')
6
6
 
7
7
  function provisionSync ({ envSrc, envFilepath, keysFilepath, noArmor, command }) {
8
8
  noArmor = noArmor !== false
9
- const { publicKeyName, privateKeyName } = keyNames(envFilepath)
9
+ const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
10
10
 
11
11
  let publicKey
12
12
  let privateKey
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- keyNames: require('./keyNames'),
2
+ keyNamesForEnvFile: require('./keyNamesForEnvFile'),
3
3
  keyValues: require('./keyValues'),
4
4
  keyValuesSync: require('./keyValuesSync'),
5
5
  keyValuesFromEnvSrc: require('./keyValuesFromEnvSrc'),
@@ -1,7 +1,7 @@
1
1
  const canonicalEnvFilename = require('./../canonicalEnvFilename')
2
2
  const environment = require('./../envResolution/environment')
3
3
 
4
- function keyNames (filepath) {
4
+ function keyNamesForEnvFile (filepath) {
5
5
  const filename = canonicalEnvFilename(filepath)
6
6
 
7
7
  // .env
@@ -21,4 +21,4 @@ function keyNames (filepath) {
21
21
  }
22
22
  }
23
23
 
24
- module.exports = keyNames
24
+ module.exports = keyNamesForEnvFile
@@ -2,7 +2,7 @@ const path = require('path')
2
2
 
3
3
  const fsx = require('./../fsx')
4
4
  const dotenvParse = require('./../dotenvParse')
5
- const keyNames = require('./keyNames')
5
+ const keyNamesForEnvFile = require('./keyNamesForEnvFile')
6
6
  const readProcessKey = require('./readProcessKey')
7
7
  const readFileKey = require('./readFileKey')
8
8
  const armorKeypair = require('../cryptography/armorKeypair')
@@ -35,7 +35,7 @@ async function invertForPrivateKeyName (filepath) {
35
35
  async function keyValues (filepath, opts = {}) {
36
36
  let keysFilepath = opts.keysFilepath || null
37
37
  const noArmor = opts.noArmor === true
38
- const names = keyNames(filepath)
38
+ const names = keyNamesForEnvFile(filepath)
39
39
  const publicKeyName = names.publicKeyName // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
40
40
  let privateKeyName = names.privateKeyName // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
41
41
 
@@ -2,7 +2,7 @@ const path = require('path')
2
2
 
3
3
  const fsx = require('./../fsx')
4
4
  const dotenvParse = require('./../dotenvParse')
5
- const keyNames = require('./keyNames')
5
+ const keyNamesForEnvFile = require('./keyNamesForEnvFile')
6
6
  const readProcessKey = require('./readProcessKey')
7
7
  const readFileKeySync = require('./readFileKeySync')
8
8
  const armorKeypairSync = require('../cryptography/armorKeypairSync')
@@ -35,7 +35,7 @@ function invertForPrivateKeyName (filepath) {
35
35
  function keyValuesSync (filepath, opts = {}) {
36
36
  let keysFilepath = opts.keysFilepath || null
37
37
  const noArmor = opts.noArmor === true
38
- const names = keyNames(filepath)
38
+ const names = keyNamesForEnvFile(filepath)
39
39
  const publicKeyName = names.publicKeyName // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
40
40
  let privateKeyName = names.privateKeyName // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
41
41
 
@@ -0,0 +1,77 @@
1
+ const path = require('path')
2
+ const fs = require('fs')
3
+ const execa = require('execa')
4
+ const dotenvParse = require('./dotenvParse')
5
+ const canonicalEnvFilename = require('./canonicalEnvFilename')
6
+ const environment = require('./envResolution/environment')
7
+ const sanitizeCommandForMetadata = require('./sanitizeCommandForMetadata')
8
+
9
+ function compact (object) {
10
+ return Object.entries(object).reduce((acc, [key, value]) => {
11
+ if (value !== undefined && value !== null && value !== '') {
12
+ acc[key] = value
13
+ }
14
+
15
+ return acc
16
+ }, {})
17
+ }
18
+
19
+ function commandFromMetadata (metadata) {
20
+ try {
21
+ const parsed = JSON.parse(metadata)
22
+ return parsed && parsed.command ? sanitizeCommandForMetadata(parsed.command) : parsed && parsed.command
23
+ } catch (_error) {
24
+ return null
25
+ }
26
+ }
27
+
28
+ function gitRoot () {
29
+ try {
30
+ return execa.sync('git', ['rev-parse', '--show-toplevel']).stdout.toString().trim()
31
+ } catch (_error) {
32
+ return null
33
+ }
34
+ }
35
+
36
+ function normalizeFilepath (envFile) {
37
+ const root = gitRoot()
38
+ const filepath = path.resolve(envFile)
39
+
40
+ if (root) {
41
+ return path.relative(root, filepath).replace(/\\/g, '/')
42
+ }
43
+
44
+ return path.relative(process.cwd(), filepath).replace(/\\/g, '/')
45
+ }
46
+
47
+ function projectName () {
48
+ return path.basename(gitRoot() || process.cwd())
49
+ }
50
+
51
+ function envKeys (envFile) {
52
+ let src
53
+ try {
54
+ src = fs.readFileSync(envFile)
55
+ } catch (_error) {
56
+ return null
57
+ }
58
+
59
+ const parsed = dotenvParse(src)
60
+
61
+ if (!parsed || Object.keys(parsed).length === 0) return null
62
+
63
+ return Object.keys(parsed)
64
+ }
65
+
66
+ function keypairMetadata (envFile = '.env', metadata = undefined) {
67
+ return compact({
68
+ filepath: normalizeFilepath(envFile),
69
+ filename: canonicalEnvFilename(envFile),
70
+ environment: environment(envFile),
71
+ project_name: projectName(),
72
+ keys: envKeys(envFile),
73
+ command: commandFromMetadata(metadata)
74
+ })
75
+ }
76
+
77
+ module.exports = keypairMetadata
@@ -0,0 +1,31 @@
1
+ const fs = require('fs')
2
+ const dotenvParse = require('./dotenvParse')
3
+ const Errors = require('./errors')
4
+
5
+ function ignored (error, options) {
6
+ return (options.ignore || []).includes(error.code)
7
+ }
8
+
9
+ function readEnvKey (key, filepath, options = {}) {
10
+ let src
11
+ try {
12
+ src = fs.readFileSync(filepath)
13
+ } catch (_error) {
14
+ const error = new Errors({ envFilepath: filepath }).missingEnvFile()
15
+ if (ignored(error, options)) return undefined
16
+ if (options.strict) throw error
17
+ return undefined
18
+ }
19
+
20
+ const parsed = dotenvParse(src)
21
+ const value = parsed[key]
22
+ if (value === undefined) {
23
+ const error = new Errors({ key }).missingKey()
24
+ if (ignored(error, options)) return undefined
25
+ if (options.strict) throw error
26
+ }
27
+
28
+ return value
29
+ }
30
+
31
+ module.exports = readEnvKey
@@ -0,0 +1,50 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+
4
+ function escapeForRegex (value) {
5
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
6
+ }
7
+
8
+ function removeEnvKey (key, keysFilepath = '.env.keys') {
9
+ const resolvedKeysFilepath = path.resolve(keysFilepath)
10
+
11
+ if (!fs.existsSync(resolvedKeysFilepath)) {
12
+ return {
13
+ changed: false,
14
+ key,
15
+ filepath: keysFilepath
16
+ }
17
+ }
18
+
19
+ const src = fs.readFileSync(resolvedKeysFilepath, 'utf8')
20
+ const eol = src.includes('\r\n') ? '\r\n' : '\n'
21
+ const keyPattern = new RegExp(`^\\s*(?:export\\s+)?${escapeForRegex(key)}\\s*=`)
22
+ const envKeyPattern = /^\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*\s*=/
23
+ const lines = src.length > 0 ? src.split(/\r?\n/) : []
24
+
25
+ while (lines.length > 0 && lines[lines.length - 1] === '') {
26
+ lines.pop()
27
+ }
28
+
29
+ const nextLines = lines.filter((line) => !keyPattern.test(line))
30
+ const changed = nextLines.length !== lines.length
31
+
32
+ if (changed) {
33
+ const hasRemainingKeys = nextLines.some((line) => envKeyPattern.test(line))
34
+
35
+ if (hasRemainingKeys) {
36
+ const nextSrc = `${nextLines.join(eol)}${eol}`
37
+ fs.writeFileSync(resolvedKeysFilepath, nextSrc, 'utf8')
38
+ } else {
39
+ fs.rmSync(resolvedKeysFilepath, { force: true })
40
+ }
41
+ }
42
+
43
+ return {
44
+ changed,
45
+ key,
46
+ filepath: keysFilepath
47
+ }
48
+ }
49
+
50
+ module.exports = removeEnvKey
@@ -0,0 +1,64 @@
1
+ function commandParts (command) {
2
+ if (Array.isArray(command)) {
3
+ return command.map(arg => `${arg}`)
4
+ }
5
+
6
+ const parts = []
7
+ const src = `${command}`
8
+ const regex = /"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|(\S+)/g
9
+ let match
10
+
11
+ while ((match = regex.exec(src)) !== null) {
12
+ parts.push(match[1] || match[2] || match[3])
13
+ }
14
+
15
+ return parts
16
+ }
17
+
18
+ function flagName (part) {
19
+ if (!part.startsWith('--')) return null
20
+ return part.slice(2).split('=')[0].toLowerCase()
21
+ }
22
+
23
+ function isSecretFlag (part) {
24
+ const name = flagName(part)
25
+ if (!name) return false
26
+ if (name === 'pass' || name === 'password' || name === 'passphrase' || name === 'pwd') return true
27
+
28
+ return name.includes('token') ||
29
+ name.includes('password') ||
30
+ name.includes('secret') ||
31
+ name.includes('api-key') ||
32
+ name.includes('apikey') ||
33
+ name.includes('private-key')
34
+ }
35
+
36
+ function sanitizeCommandForMetadata (command) {
37
+ const parts = commandParts(command)
38
+ const sanitized = []
39
+
40
+ for (let i = 0; i < parts.length; i++) {
41
+ const part = parts[i]
42
+
43
+ if (isSecretFlag(part) && part.includes('=')) {
44
+ const name = part.split('=')[0]
45
+ sanitized.push(`${name}=[REDACTED]`)
46
+ continue
47
+ }
48
+
49
+ if (isSecretFlag(part)) {
50
+ sanitized.push(part)
51
+ if (i + 1 < parts.length) {
52
+ sanitized.push('[REDACTED]')
53
+ i++
54
+ }
55
+ continue
56
+ }
57
+
58
+ sanitized.push(part)
59
+ }
60
+
61
+ return sanitized.join(' ')
62
+ }
63
+
64
+ module.exports = sanitizeCommandForMetadata
@@ -0,0 +1,8 @@
1
+ function teamChoicesFromMeta (meta) {
2
+ return meta.organizations.map(org => ({
3
+ name: org.provider_slug,
4
+ value: org.provider_slug
5
+ }))
6
+ }
7
+
8
+ module.exports = teamChoicesFromMeta