@dotenvx/dotenvx 2.7.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/package.json +1 -1
- package/src/cli/actions/settings/device.js +23 -0
- package/src/cli/actions/settings/hostname.js +20 -0
- package/src/cli/actions/settings/off.js +14 -0
- package/src/cli/actions/settings/on.js +14 -0
- package/src/cli/actions/settings/path.js +20 -0
- package/src/cli/actions/settings/token.js +23 -0
- package/src/cli/actions/settings/username.js +20 -0
- package/src/cli/commands/armor.js +3 -0
- package/src/cli/commands/lock.js +6 -0
- package/src/cli/commands/native.js +6 -0
- package/src/cli/commands/settings.js +62 -0
- package/src/db/session.js +60 -7
- package/src/lib/helpers/linuxSecretService.js +13 -11
- package/src/lib/helpers/macosKeychain.js +26 -0
- package/src/lib/helpers/windowsCredentialManager.js +11 -9
- package/src/lib/providers/native/index.js +42 -14
- package/src/lib/services/keychainDown.js +3 -35
- package/src/lib/services/keychainPull.js +2 -20
- package/src/lib/services/keychainPush.js +3 -39
- package/src/lib/services/keychainUp.js +3 -39
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.7.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.7.1...main)
|
|
6
|
+
|
|
7
|
+
## [2.7.1](https://github.com/dotenvx/dotenvx/compare/v2.7.0...v2.7.1) (2026-07-13)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* Security Improvement: `dotenvx armor login` attempts to set the `DOTENVX_ARMOR_TOKEN` in your native OS secret store going forward. For current Armor users run `dotenvx armor logout` and then `dotenvx armor login` to make use of this immediately. ([#886](https://github.com/dotenvx/dotenvx/pull/886))
|
|
6
12
|
|
|
7
13
|
## [2.7.0](https://github.com/dotenvx/dotenvx/compare/v2.6.0...v2.7.0) (2026-07-13)
|
|
8
14
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { logger } = require('../../../shared/logger')
|
|
2
|
+
const Session = require('../../../db/session')
|
|
3
|
+
const mask = require('../../../lib/helpers/mask')
|
|
4
|
+
|
|
5
|
+
function device () {
|
|
6
|
+
const options = this.opts()
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const value = new Session().devicePublicKey()
|
|
10
|
+
if (value && value.length > 1) {
|
|
11
|
+
console.log(options.unmask ? value : mask(value, 6))
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
logger.error('missing device. Try generating one with [dotenvx armor login].')
|
|
16
|
+
process.exit(1)
|
|
17
|
+
} catch (error) {
|
|
18
|
+
logger.error(error.message)
|
|
19
|
+
process.exit(1)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = device
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { logger } = require('../../../shared/logger')
|
|
2
|
+
const Session = require('../../../db/session')
|
|
3
|
+
|
|
4
|
+
function hostname () {
|
|
5
|
+
try {
|
|
6
|
+
const value = new Session().hostname()
|
|
7
|
+
if (value && value.length > 1) {
|
|
8
|
+
console.log(value)
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
logger.error('missing hostname. Try running [dotenvx armor login].')
|
|
13
|
+
process.exit(1)
|
|
14
|
+
} catch (error) {
|
|
15
|
+
logger.error(error.message)
|
|
16
|
+
process.exit(1)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = hostname
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const { logger } = require('../../../shared/logger')
|
|
2
|
+
const Session = require('../../../db/session')
|
|
3
|
+
|
|
4
|
+
function off () {
|
|
5
|
+
try {
|
|
6
|
+
new Session().turnOff()
|
|
7
|
+
logger.success('✔ armor: off')
|
|
8
|
+
} catch (error) {
|
|
9
|
+
logger.error(error.message)
|
|
10
|
+
process.exit(1)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = off
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const { logger } = require('../../../shared/logger')
|
|
2
|
+
const Session = require('../../../db/session')
|
|
3
|
+
|
|
4
|
+
function on () {
|
|
5
|
+
try {
|
|
6
|
+
new Session().turnOn()
|
|
7
|
+
logger.success('✔ armor: on')
|
|
8
|
+
} catch (error) {
|
|
9
|
+
logger.error(error.message)
|
|
10
|
+
process.exit(1)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = on
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { logger } = require('../../../shared/logger')
|
|
2
|
+
const Session = require('../../../db/session')
|
|
3
|
+
|
|
4
|
+
function path () {
|
|
5
|
+
try {
|
|
6
|
+
const value = new Session().path()
|
|
7
|
+
if (value && value.length > 1) {
|
|
8
|
+
console.log(value)
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
logger.error('missing path. Try running [dotenvx armor login].')
|
|
13
|
+
process.exit(1)
|
|
14
|
+
} catch (error) {
|
|
15
|
+
logger.error(error.message)
|
|
16
|
+
process.exit(1)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = path
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { logger } = require('../../../shared/logger')
|
|
2
|
+
const Session = require('../../../db/session')
|
|
3
|
+
const mask = require('../../../lib/helpers/mask')
|
|
4
|
+
|
|
5
|
+
function token () {
|
|
6
|
+
const options = this.opts()
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const value = new Session().token()
|
|
10
|
+
if (value && value.length > 1) {
|
|
11
|
+
console.log(options.unmask ? value : mask(value))
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
logger.error('missing token. Try generating one with [dotenvx armor login].')
|
|
16
|
+
process.exit(1)
|
|
17
|
+
} catch (error) {
|
|
18
|
+
logger.error(error.message)
|
|
19
|
+
process.exit(1)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = token
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { logger } = require('../../../shared/logger')
|
|
2
|
+
const Session = require('../../../db/session')
|
|
3
|
+
|
|
4
|
+
function username () {
|
|
5
|
+
try {
|
|
6
|
+
const value = new Session().username()
|
|
7
|
+
if (value) {
|
|
8
|
+
console.log(value)
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
logger.error('login required. Try running [dotenvx armor login].')
|
|
13
|
+
process.exit(1)
|
|
14
|
+
} catch (error) {
|
|
15
|
+
logger.error(error.message)
|
|
16
|
+
process.exit(1)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = username
|
package/src/cli/commands/lock.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
function configureLockCommand (lock) {
|
|
2
|
+
lock.hook('preAction', async () => {
|
|
3
|
+
const Session = require('./../../db/session')
|
|
4
|
+
const sesh = new Session()
|
|
5
|
+
await sesh.notifyUpdate()
|
|
6
|
+
})
|
|
7
|
+
|
|
2
8
|
lock
|
|
3
9
|
.description('lock private keys with a local passphrase')
|
|
4
10
|
.action(function () {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
function configureNativeCommand (native) {
|
|
2
|
+
native.hook('preAction', async () => {
|
|
3
|
+
const Session = require('./../../db/session')
|
|
4
|
+
const sesh = new Session()
|
|
5
|
+
await sesh.notifyUpdate()
|
|
6
|
+
})
|
|
7
|
+
|
|
2
8
|
native
|
|
3
9
|
.description('move private keys into your OS secret store')
|
|
4
10
|
.action(function () {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function configureSettingsCommand (settings) {
|
|
2
|
+
settings
|
|
3
|
+
.description('settings')
|
|
4
|
+
.action(function () {
|
|
5
|
+
this.help()
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
settings
|
|
9
|
+
.command('username')
|
|
10
|
+
.description('print your username')
|
|
11
|
+
.action(function (...args) {
|
|
12
|
+
return require('./../actions/settings/username').apply(this, args)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
settings
|
|
16
|
+
.command('token')
|
|
17
|
+
.description('print your access token (--unmask)')
|
|
18
|
+
.option('--unmask', 'unmask access token')
|
|
19
|
+
.action(function (...args) {
|
|
20
|
+
return require('./../actions/settings/token').apply(this, args)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
settings
|
|
24
|
+
.command('device')
|
|
25
|
+
.description('print your device pubkey (--unmask)')
|
|
26
|
+
.option('--unmask', 'unmask device pubkey')
|
|
27
|
+
.action(function (...args) {
|
|
28
|
+
return require('./../actions/settings/device').apply(this, args)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
settings
|
|
32
|
+
.command('hostname')
|
|
33
|
+
.description('print hostname')
|
|
34
|
+
.action(function (...args) {
|
|
35
|
+
return require('./../actions/settings/hostname').apply(this, args)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
settings
|
|
39
|
+
.command('path')
|
|
40
|
+
.description('print path to settings file')
|
|
41
|
+
.action(function (...args) {
|
|
42
|
+
return require('./../actions/settings/path').apply(this, args)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
settings
|
|
46
|
+
.command('on')
|
|
47
|
+
.description('turn armor on')
|
|
48
|
+
.action(function (...args) {
|
|
49
|
+
return require('./../actions/settings/on').apply(this, args)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
settings
|
|
53
|
+
.command('off')
|
|
54
|
+
.description('turn armor off')
|
|
55
|
+
.action(function (...args) {
|
|
56
|
+
return require('./../actions/settings/off').apply(this, args)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return settings
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = configureSettingsCommand
|
package/src/db/session.js
CHANGED
|
@@ -5,6 +5,7 @@ const { Conf, dotenv, envPaths } = require('@dotenvx/tooling')
|
|
|
5
5
|
const jsonToEnv = require('./../lib/helpers/jsonToEnv')
|
|
6
6
|
const packageJson = require('./../lib/helpers/packageJson')
|
|
7
7
|
const { http } = require('./../lib/helpers/http')
|
|
8
|
+
const nativeProvider = require('./../lib/providers/native')
|
|
8
9
|
const { logger } = require('./../shared/logger')
|
|
9
10
|
|
|
10
11
|
const HOURS_24 = 60 * 60 * 24 * 1000
|
|
@@ -14,7 +15,8 @@ const ARMOR = {
|
|
|
14
15
|
HOSTNAME: 'DOTENVX_ARMOR_HOSTNAME',
|
|
15
16
|
USER: 'DOTENVX_ARMOR_USER',
|
|
16
17
|
USERNAME: 'DOTENVX_ARMOR_USERNAME',
|
|
17
|
-
TOKEN: 'DOTENVX_ARMOR_TOKEN'
|
|
18
|
+
TOKEN: 'DOTENVX_ARMOR_TOKEN',
|
|
19
|
+
ON: 'DOTENVX_ARMOR_ON'
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
const DOTENVX = {
|
|
@@ -100,7 +102,7 @@ class Session {
|
|
|
100
102
|
|
|
101
103
|
status () {
|
|
102
104
|
// if logged in
|
|
103
|
-
if (this.username() && this.token()) {
|
|
105
|
+
if (this.username() && this.token() && this.on()) {
|
|
104
106
|
return 'on'
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -133,7 +135,15 @@ class Session {
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
token () {
|
|
136
|
-
return this.
|
|
138
|
+
return this.getFromSecretStore(ARMOR.TOKEN) || undefined
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
on () {
|
|
142
|
+
return (this.readSetting('ON') || 'true') === 'true'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
off () {
|
|
146
|
+
return (this.readSetting('ON') || 'true') === 'false'
|
|
137
147
|
}
|
|
138
148
|
|
|
139
149
|
devicePublicKey () {
|
|
@@ -187,7 +197,7 @@ class Session {
|
|
|
187
197
|
store.set(DOTENVX.VERSION_LAST_CHECK, now)
|
|
188
198
|
|
|
189
199
|
if (versionGreaterThan(remote, packageJson.version)) {
|
|
190
|
-
console.error('⛆ update available [
|
|
200
|
+
console.error('⛆ update available [curl -sfS https://dotenvx.sh | sh]')
|
|
191
201
|
}
|
|
192
202
|
} catch (error) {
|
|
193
203
|
logger.debug(error.message)
|
|
@@ -222,6 +232,47 @@ class Session {
|
|
|
222
232
|
//
|
|
223
233
|
// Set/Delete
|
|
224
234
|
//
|
|
235
|
+
getFromSecretStore (key) {
|
|
236
|
+
try {
|
|
237
|
+
const value = nativeProvider.get(key)
|
|
238
|
+
if (value) return value
|
|
239
|
+
} catch {}
|
|
240
|
+
|
|
241
|
+
const store = this.openStore()
|
|
242
|
+
if (!store) return undefined
|
|
243
|
+
|
|
244
|
+
return store.get(key)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
setInSecretStore (key, value) {
|
|
248
|
+
try {
|
|
249
|
+
nativeProvider.set(key, value)
|
|
250
|
+
return value
|
|
251
|
+
} catch {
|
|
252
|
+
this.createStore().set(key, value)
|
|
253
|
+
return value
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
deleteFromSecretStore (key) {
|
|
258
|
+
try {
|
|
259
|
+
nativeProvider.delete(key)
|
|
260
|
+
} catch {}
|
|
261
|
+
|
|
262
|
+
const store = this.openStore()
|
|
263
|
+
if (store) store.delete(key)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
turnOn () {
|
|
267
|
+
this.createStore().set(ARMOR.ON, 'true')
|
|
268
|
+
return 'true'
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
turnOff () {
|
|
272
|
+
this.createStore().set(ARMOR.ON, 'false')
|
|
273
|
+
return 'false'
|
|
274
|
+
}
|
|
275
|
+
|
|
225
276
|
login (hostname, id, username, accessToken) {
|
|
226
277
|
if (!hostname) {
|
|
227
278
|
throw new Error('DOTENVX_ARMOR_HOSTNAME not set. Run [dotenvx armor login]')
|
|
@@ -242,8 +293,9 @@ class Session {
|
|
|
242
293
|
const store = this.createStore()
|
|
243
294
|
store.set(ARMOR.USER, id)
|
|
244
295
|
store.set(ARMOR.USERNAME, username)
|
|
245
|
-
|
|
296
|
+
this.setInSecretStore(ARMOR.TOKEN, accessToken)
|
|
246
297
|
store.set(ARMOR.HOSTNAME, hostname)
|
|
298
|
+
store.set(ARMOR.ON, 'true')
|
|
247
299
|
|
|
248
300
|
return accessToken
|
|
249
301
|
}
|
|
@@ -261,13 +313,14 @@ class Session {
|
|
|
261
313
|
throw new Error('DOTENVX_ARMOR_TOKEN not set. Run [dotenvx armor login]')
|
|
262
314
|
}
|
|
263
315
|
|
|
316
|
+
this.deleteFromSecretStore(ARMOR.TOKEN)
|
|
317
|
+
|
|
264
318
|
const store = this.openStore()
|
|
265
319
|
if (!store) return true
|
|
266
|
-
|
|
267
320
|
store.delete(ARMOR.USER)
|
|
268
321
|
store.delete(ARMOR.USERNAME)
|
|
269
|
-
store.delete(ARMOR.TOKEN)
|
|
270
322
|
store.delete(ARMOR.HOSTNAME)
|
|
323
|
+
store.delete(ARMOR.ON)
|
|
271
324
|
store.delete(DOTENVX.VERSION)
|
|
272
325
|
store.delete(DOTENVX.VERSION_LAST_CHECK)
|
|
273
326
|
return true
|
|
@@ -7,7 +7,7 @@ function attributes (publicKey) {
|
|
|
7
7
|
return ['service', SERVICE, 'public-key', publicKey]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
function
|
|
10
|
+
function get (publicKey) {
|
|
11
11
|
try {
|
|
12
12
|
return execFileSync(SECRET_TOOL_BIN, ['lookup', ...attributes(publicKey)], {
|
|
13
13
|
encoding: 'utf8',
|
|
@@ -18,7 +18,7 @@ function findGenericPassword (publicKey) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function
|
|
21
|
+
function set (publicKey, privateKey, label) {
|
|
22
22
|
try {
|
|
23
23
|
execFileSync(SECRET_TOOL_BIN, ['store', `--label=${label}`, ...attributes(publicKey)], {
|
|
24
24
|
input: privateKey,
|
|
@@ -30,14 +30,16 @@ function addGenericPassword (publicKey, label, privateKey) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
module.exports = {
|
|
34
|
+
get,
|
|
35
|
+
set,
|
|
36
|
+
delete (publicKey) {
|
|
37
|
+
try {
|
|
38
|
+
execFileSync(SECRET_TOOL_BIN, ['clear', ...attributes(publicKey)], {
|
|
39
|
+
stdio: ['ignore', 'ignore', 'pipe']
|
|
40
|
+
})
|
|
41
|
+
} catch {
|
|
42
|
+
throw new Error('failed to delete private key from Linux Secret Service')
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
|
-
|
|
43
|
-
module.exports = { findGenericPassword, addGenericPassword, deleteGenericPassword }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { execFileSync } = require('child_process')
|
|
2
|
+
|
|
3
|
+
const SECURITY_BIN = '/usr/bin/security'
|
|
4
|
+
const SERVICE = 'dotenvx'
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
get (key) {
|
|
8
|
+
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', key, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
set (key, value, label) {
|
|
12
|
+
try {
|
|
13
|
+
execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', key, '-l', label, '-w', value], { stdio: 'ignore' })
|
|
14
|
+
} catch {
|
|
15
|
+
throw new Error('failed to save private key to macOS Keychain')
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
delete (key) {
|
|
20
|
+
try {
|
|
21
|
+
execFileSync(SECURITY_BIN, ['delete-generic-password', '-s', SERVICE, '-a', key], { stdio: 'ignore' })
|
|
22
|
+
} catch {
|
|
23
|
+
throw new Error('failed to delete private key from macOS Keychain')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -134,7 +134,7 @@ function run (payload) {
|
|
|
134
134
|
})
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
function
|
|
137
|
+
function get (publicKey) {
|
|
138
138
|
try {
|
|
139
139
|
return run({ action: 'read', target: target(publicKey) }).trim() || null
|
|
140
140
|
} catch {
|
|
@@ -142,7 +142,7 @@ function findGenericPassword (publicKey) {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
function
|
|
145
|
+
function set (publicKey, privateKey) {
|
|
146
146
|
try {
|
|
147
147
|
run({ action: 'write', target: target(publicKey), username: publicKey, secret: privateKey })
|
|
148
148
|
} catch {
|
|
@@ -150,12 +150,14 @@ function addGenericPassword (publicKey, privateKey) {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
module.exports = {
|
|
154
|
+
get,
|
|
155
|
+
set,
|
|
156
|
+
delete (publicKey) {
|
|
157
|
+
try {
|
|
158
|
+
run({ action: 'delete', target: target(publicKey) })
|
|
159
|
+
} catch {
|
|
160
|
+
throw new Error('failed to delete private key from Windows Credential Manager')
|
|
161
|
+
}
|
|
158
162
|
}
|
|
159
163
|
}
|
|
160
|
-
|
|
161
|
-
module.exports = { findGenericPassword, addGenericPassword, deleteGenericPassword }
|
|
@@ -1,27 +1,52 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
1
|
+
const macosKeychain = require('../../helpers/macosKeychain')
|
|
3
2
|
const windowsCredentialManager = require('../../helpers/windowsCredentialManager')
|
|
4
3
|
const linuxSecretService = require('../../helpers/linuxSecretService')
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function get (key) {
|
|
6
|
+
if (process.platform === 'win32') {
|
|
7
|
+
return windowsCredentialManager.get(key)
|
|
8
|
+
}
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
if (process.platform === 'linux') {
|
|
11
|
+
return linuxSecretService.get(key)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return macosKeychain.get(key)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function set (key, value, label = key) {
|
|
18
|
+
if (process.platform === 'win32') {
|
|
19
|
+
windowsCredentialManager.set(key, value, label)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (process.platform === 'linux') {
|
|
24
|
+
linuxSecretService.set(key, value, label)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
macosKeychain.set(key, value, label)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
index.delete = function (key) {
|
|
32
|
+
if (process.platform === 'win32') {
|
|
33
|
+
windowsCredentialManager.delete(key)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (process.platform === 'linux') {
|
|
38
|
+
linuxSecretService.delete(key)
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
macosKeychain.delete(key)
|
|
11
43
|
}
|
|
12
44
|
|
|
13
45
|
function index (publicKeyHex) {
|
|
14
46
|
if (!['darwin', 'linux', 'win32'].includes(process.platform)) return {}
|
|
15
47
|
|
|
16
48
|
try {
|
|
17
|
-
|
|
18
|
-
if (process.platform === 'win32') {
|
|
19
|
-
privateKeyHex = windowsCredentialManager.findGenericPassword(publicKeyHex)
|
|
20
|
-
} else if (process.platform === 'linux') {
|
|
21
|
-
privateKeyHex = linuxSecretService.findGenericPassword(publicKeyHex)
|
|
22
|
-
} else {
|
|
23
|
-
privateKeyHex = findMacosPrivateKey(publicKeyHex)
|
|
24
|
-
}
|
|
49
|
+
const privateKeyHex = get(publicKeyHex)
|
|
25
50
|
|
|
26
51
|
if (!privateKeyHex) return {}
|
|
27
52
|
|
|
@@ -31,4 +56,7 @@ function index (publicKeyHex) {
|
|
|
31
56
|
}
|
|
32
57
|
}
|
|
33
58
|
|
|
59
|
+
index.set = set
|
|
60
|
+
index.get = get
|
|
61
|
+
|
|
34
62
|
module.exports = index
|
|
@@ -1,40 +1,8 @@
|
|
|
1
|
-
const { execFileSync } = require('child_process')
|
|
2
|
-
|
|
3
1
|
const keynames = require('../conventions/keynames')
|
|
4
2
|
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
3
|
const upsertEnvKey = require('../helpers/upsertEnvKey')
|
|
6
4
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
7
|
-
const
|
|
8
|
-
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
9
|
-
|
|
10
|
-
const SECURITY_BIN = '/usr/bin/security'
|
|
11
|
-
const SERVICE = 'dotenvx'
|
|
12
|
-
|
|
13
|
-
function findGenericPassword (publicKey) {
|
|
14
|
-
if (process.platform === 'win32') {
|
|
15
|
-
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (process.platform === 'linux') {
|
|
19
|
-
return linuxSecretService.findGenericPassword(publicKey)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function deleteGenericPassword (publicKey) {
|
|
26
|
-
if (process.platform === 'win32') {
|
|
27
|
-
windowsCredentialManager.deleteGenericPassword(publicKey)
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (process.platform === 'linux') {
|
|
32
|
-
linuxSecretService.deleteGenericPassword(publicKey)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
execFileSync(SECURITY_BIN, ['delete-generic-password', '-s', SERVICE, '-a', publicKey], { stdio: 'ignore' })
|
|
37
|
-
}
|
|
5
|
+
const nativeProvider = require('../providers/native')
|
|
38
6
|
|
|
39
7
|
class KeychainDown {
|
|
40
8
|
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
@@ -55,7 +23,7 @@ class KeychainDown {
|
|
|
55
23
|
let privateKey
|
|
56
24
|
|
|
57
25
|
try {
|
|
58
|
-
privateKey =
|
|
26
|
+
privateKey = nativeProvider.get(publicKey)
|
|
59
27
|
if (!privateKey) {
|
|
60
28
|
const secretStore = process.platform === 'win32'
|
|
61
29
|
? 'Windows Credential Manager'
|
|
@@ -78,7 +46,7 @@ class KeychainDown {
|
|
|
78
46
|
}
|
|
79
47
|
|
|
80
48
|
upsertEnvKey(privateKeyName, privateKey, envKeysFile)
|
|
81
|
-
|
|
49
|
+
nativeProvider.delete(publicKey)
|
|
82
50
|
|
|
83
51
|
return {
|
|
84
52
|
changed: true,
|
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
const { execFileSync } = require('child_process')
|
|
2
|
-
|
|
3
1
|
const keynames = require('../conventions/keynames')
|
|
4
2
|
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
3
|
const upsertEnvKey = require('../helpers/upsertEnvKey')
|
|
6
4
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
7
|
-
const
|
|
8
|
-
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
9
|
-
|
|
10
|
-
const SECURITY_BIN = '/usr/bin/security'
|
|
11
|
-
const SERVICE = 'dotenvx'
|
|
12
|
-
|
|
13
|
-
function findGenericPassword (publicKey) {
|
|
14
|
-
if (process.platform === 'win32') {
|
|
15
|
-
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (process.platform === 'linux') {
|
|
19
|
-
return linuxSecretService.findGenericPassword(publicKey)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
23
|
-
}
|
|
5
|
+
const nativeProvider = require('../providers/native')
|
|
24
6
|
|
|
25
7
|
class KeychainPull {
|
|
26
8
|
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
@@ -41,7 +23,7 @@ class KeychainPull {
|
|
|
41
23
|
let privateKey
|
|
42
24
|
|
|
43
25
|
try {
|
|
44
|
-
privateKey =
|
|
26
|
+
privateKey = nativeProvider.get(publicKey)
|
|
45
27
|
if (!privateKey) throw new Error('not found')
|
|
46
28
|
} catch {
|
|
47
29
|
const secretStore = process.platform === 'win32'
|
|
@@ -1,43 +1,7 @@
|
|
|
1
|
-
const { execFileSync } = require('child_process')
|
|
2
|
-
|
|
3
1
|
const keynames = require('../conventions/keynames')
|
|
4
2
|
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
3
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
6
|
-
const
|
|
7
|
-
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
8
|
-
|
|
9
|
-
const SECURITY_BIN = '/usr/bin/security'
|
|
10
|
-
const SERVICE = 'dotenvx'
|
|
11
|
-
|
|
12
|
-
function addGenericPassword (publicKey, label, privateKey) {
|
|
13
|
-
if (process.platform === 'win32') {
|
|
14
|
-
windowsCredentialManager.addGenericPassword(publicKey, privateKey)
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (process.platform === 'linux') {
|
|
19
|
-
linuxSecretService.addGenericPassword(publicKey, label, privateKey)
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', publicKey, '-l', label, '-w', privateKey], { stdio: 'ignore' })
|
|
25
|
-
} catch {
|
|
26
|
-
throw new Error('failed to save private key to macOS Keychain')
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function findGenericPassword (publicKey) {
|
|
31
|
-
if (process.platform === 'win32') {
|
|
32
|
-
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (process.platform === 'linux') {
|
|
36
|
-
return linuxSecretService.findGenericPassword(publicKey)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
40
|
-
}
|
|
4
|
+
const nativeProvider = require('../providers/native')
|
|
41
5
|
|
|
42
6
|
class KeychainPush {
|
|
43
7
|
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
@@ -59,7 +23,7 @@ class KeychainPush {
|
|
|
59
23
|
const label = `dotenvx (${armoredKeyDisplay(publicKey)})`
|
|
60
24
|
|
|
61
25
|
try {
|
|
62
|
-
const existingPrivateKey =
|
|
26
|
+
const existingPrivateKey = nativeProvider.get(publicKey)
|
|
63
27
|
|
|
64
28
|
if (existingPrivateKey === privateKey) {
|
|
65
29
|
return {
|
|
@@ -72,7 +36,7 @@ class KeychainPush {
|
|
|
72
36
|
}
|
|
73
37
|
} catch {}
|
|
74
38
|
|
|
75
|
-
|
|
39
|
+
nativeProvider.set(publicKey, privateKey, label)
|
|
76
40
|
|
|
77
41
|
return {
|
|
78
42
|
changed: true,
|
|
@@ -1,44 +1,8 @@
|
|
|
1
|
-
const { execFileSync } = require('child_process')
|
|
2
|
-
|
|
3
1
|
const keynames = require('../conventions/keynames')
|
|
4
2
|
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
3
|
const removeEnvKey = require('../helpers/removeEnvKey')
|
|
6
4
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
7
|
-
const
|
|
8
|
-
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
9
|
-
|
|
10
|
-
const SECURITY_BIN = '/usr/bin/security'
|
|
11
|
-
const SERVICE = 'dotenvx'
|
|
12
|
-
|
|
13
|
-
function addGenericPassword (publicKey, label, privateKey) {
|
|
14
|
-
if (process.platform === 'win32') {
|
|
15
|
-
windowsCredentialManager.addGenericPassword(publicKey, privateKey)
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (process.platform === 'linux') {
|
|
20
|
-
linuxSecretService.addGenericPassword(publicKey, label, privateKey)
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', publicKey, '-l', label, '-w', privateKey], { stdio: 'ignore' })
|
|
26
|
-
} catch {
|
|
27
|
-
throw new Error('failed to save private key to macOS Keychain')
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function findGenericPassword (publicKey) {
|
|
32
|
-
if (process.platform === 'win32') {
|
|
33
|
-
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (process.platform === 'linux') {
|
|
37
|
-
return linuxSecretService.findGenericPassword(publicKey)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
41
|
-
}
|
|
5
|
+
const nativeProvider = require('../providers/native')
|
|
42
6
|
|
|
43
7
|
class KeychainUp {
|
|
44
8
|
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
@@ -60,7 +24,7 @@ class KeychainUp {
|
|
|
60
24
|
let existingPrivateKey
|
|
61
25
|
|
|
62
26
|
try {
|
|
63
|
-
existingPrivateKey =
|
|
27
|
+
existingPrivateKey = nativeProvider.get(publicKey)
|
|
64
28
|
} catch {}
|
|
65
29
|
|
|
66
30
|
const privateKey = readEnvKey(privateKeyName, envKeysFile, { strict: !existingPrivateKey })
|
|
@@ -89,7 +53,7 @@ class KeychainUp {
|
|
|
89
53
|
}
|
|
90
54
|
}
|
|
91
55
|
|
|
92
|
-
|
|
56
|
+
nativeProvider.set(publicKey, privateKey, label)
|
|
93
57
|
removeEnvKey(privateKeyName, envKeysFile)
|
|
94
58
|
|
|
95
59
|
return {
|