@dotenvx/dotenvx 2.5.0 → 2.6.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 +7 -1
- package/README.md +4 -2
- package/package.json +1 -1
- package/src/cli/commands/native.js +1 -1
- package/src/cli/dotenvx.js +1 -1
- package/src/lib/helpers/linuxSecretService.js +43 -0
- package/src/lib/providers/index.js +1 -1
- package/src/lib/providers/native/index.js +10 -4
- package/src/lib/services/keychainDown.js +13 -1
- package/src/lib/services/keychainPull.js +8 -1
- package/src/lib/services/keychainPush.js +10 -0
- package/src/lib/services/keychainUp.js +10 -0
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.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.6.0...main)
|
|
6
|
+
|
|
7
|
+
## [2.6.0](https://github.com/dotenvx/dotenvx/compare/v2.5.0...v2.6.0) (2026-07-11)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Support `native up|down|pull|push` for windows ([#834](https://github.com/dotenvx/dotenvx/pull/884))
|
|
6
12
|
|
|
7
13
|
## [2.5.0](https://github.com/dotenvx/dotenvx/compare/v2.4.2...v2.5.0) (2026-07-11)
|
|
8
14
|
|
package/README.md
CHANGED
|
@@ -2484,7 +2484,9 @@ $ dotenvx lock down
|
|
|
2484
2484
|
|
|
2485
2485
|
Move private keys into your OS secret store.
|
|
2486
2486
|
|
|
2487
|
-
Native commands support macOS Keychain and Windows Credential Manager.
|
|
2487
|
+
Native commands support macOS Keychain, Linux Secret Service, and Windows Credential Manager.
|
|
2488
|
+
|
|
2489
|
+
On Linux, `secret-tool` and an available Secret Service are required.
|
|
2488
2490
|
|
|
2489
2491
|
</details>
|
|
2490
2492
|
<details><summary>`native up`</summary><br>
|
|
@@ -2646,7 +2648,7 @@ Commands:
|
|
|
2646
2648
|
|
|
2647
2649
|
Professional Security:
|
|
2648
2650
|
lock ⊡ lock private keys with a local passphrase
|
|
2649
|
-
native ⌥ move private keys into your OS secret store
|
|
2651
|
+
native ⌥ move private keys into your OS secret store
|
|
2650
2652
|
armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]
|
|
2651
2653
|
```
|
|
2652
2654
|
|
package/package.json
CHANGED
package/src/cli/dotenvx.js
CHANGED
|
@@ -260,7 +260,7 @@ program.command('help [command]')
|
|
|
260
260
|
program.addHelpText('after', ' ')
|
|
261
261
|
program.addHelpText('after', 'Professional Security: ')
|
|
262
262
|
program.addHelpText('after', ' lock ⊡ lock private keys with a local passphrase')
|
|
263
|
-
program.addHelpText('after', ' native ⌥ move private keys into your OS secret store
|
|
263
|
+
program.addHelpText('after', ' native ⌥ move private keys into your OS secret store')
|
|
264
264
|
program.addHelpText('after', ' armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]')
|
|
265
265
|
|
|
266
266
|
// dotenvx native
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { execFileSync } = require('child_process')
|
|
2
|
+
|
|
3
|
+
const SECRET_TOOL_BIN = 'secret-tool'
|
|
4
|
+
const SERVICE = 'dotenvx'
|
|
5
|
+
|
|
6
|
+
function attributes (publicKey) {
|
|
7
|
+
return ['service', SERVICE, 'public-key', publicKey]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function findGenericPassword (publicKey) {
|
|
11
|
+
try {
|
|
12
|
+
return execFileSync(SECRET_TOOL_BIN, ['lookup', ...attributes(publicKey)], {
|
|
13
|
+
encoding: 'utf8',
|
|
14
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
15
|
+
}).trim() || null
|
|
16
|
+
} catch {
|
|
17
|
+
throw new Error('failed to read private key from Linux Secret Service')
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function addGenericPassword (publicKey, label, privateKey) {
|
|
22
|
+
try {
|
|
23
|
+
execFileSync(SECRET_TOOL_BIN, ['store', `--label=${label}`, ...attributes(publicKey)], {
|
|
24
|
+
input: privateKey,
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
stdio: ['pipe', 'ignore', 'pipe']
|
|
27
|
+
})
|
|
28
|
+
} catch {
|
|
29
|
+
throw new Error('failed to save private key to Linux Secret Service')
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function deleteGenericPassword (publicKey) {
|
|
34
|
+
try {
|
|
35
|
+
execFileSync(SECRET_TOOL_BIN, ['clear', ...attributes(publicKey)], {
|
|
36
|
+
stdio: ['ignore', 'ignore', 'pipe']
|
|
37
|
+
})
|
|
38
|
+
} catch {
|
|
39
|
+
throw new Error('failed to delete private key from Linux Secret Service')
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { findGenericPassword, addGenericPassword, deleteGenericPassword }
|
|
@@ -44,7 +44,7 @@ function armorProviderForOptions (options) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function useNative (options) {
|
|
47
|
-
if (
|
|
47
|
+
if (!['darwin', 'linux', 'win32'].includes(process.platform)) return false
|
|
48
48
|
if (process.env.CI) return false
|
|
49
49
|
return options.noNative !== true && options.native !== false && options.noKeychain !== true
|
|
50
50
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { execFileSync } = require('child_process')
|
|
2
2
|
|
|
3
3
|
const windowsCredentialManager = require('../../helpers/windowsCredentialManager')
|
|
4
|
+
const linuxSecretService = require('../../helpers/linuxSecretService')
|
|
4
5
|
|
|
5
6
|
const SECURITY_BIN = '/usr/bin/security'
|
|
6
7
|
const SERVICE = 'dotenvx'
|
|
@@ -10,12 +11,17 @@ function findMacosPrivateKey (publicKeyHex) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
function index (publicKeyHex) {
|
|
13
|
-
if (
|
|
14
|
+
if (!['darwin', 'linux', 'win32'].includes(process.platform)) return {}
|
|
14
15
|
|
|
15
16
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
let privateKeyHex
|
|
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
|
+
}
|
|
19
25
|
|
|
20
26
|
if (!privateKeyHex) return {}
|
|
21
27
|
|
|
@@ -5,6 +5,7 @@ const readEnvKey = require('../helpers/readEnvKey')
|
|
|
5
5
|
const upsertEnvKey = require('../helpers/upsertEnvKey')
|
|
6
6
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
7
7
|
const windowsCredentialManager = require('../helpers/windowsCredentialManager')
|
|
8
|
+
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
8
9
|
|
|
9
10
|
const SECURITY_BIN = '/usr/bin/security'
|
|
10
11
|
const SERVICE = 'dotenvx'
|
|
@@ -14,6 +15,10 @@ function findGenericPassword (publicKey) {
|
|
|
14
15
|
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
if (process.platform === 'linux') {
|
|
19
|
+
return linuxSecretService.findGenericPassword(publicKey)
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
18
23
|
}
|
|
19
24
|
|
|
@@ -23,6 +28,11 @@ function deleteGenericPassword (publicKey) {
|
|
|
23
28
|
return
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
if (process.platform === 'linux') {
|
|
32
|
+
linuxSecretService.deleteGenericPassword(publicKey)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
execFileSync(SECURITY_BIN, ['delete-generic-password', '-s', SERVICE, '-a', publicKey], { stdio: 'ignore' })
|
|
27
37
|
}
|
|
28
38
|
|
|
@@ -47,7 +57,9 @@ class KeychainDown {
|
|
|
47
57
|
try {
|
|
48
58
|
privateKey = findGenericPassword(publicKey)
|
|
49
59
|
if (!privateKey) {
|
|
50
|
-
const secretStore = process.platform === 'win32'
|
|
60
|
+
const secretStore = process.platform === 'win32'
|
|
61
|
+
? 'Windows Credential Manager'
|
|
62
|
+
: process.platform === 'linux' ? 'Linux Secret Service' : 'macOS Keychain'
|
|
51
63
|
throw new Error(`[NOT_FOUND] private key not found in ${secretStore} (${armoredKeyDisplay(publicKey)}). fix: [dotenvx native up]`)
|
|
52
64
|
}
|
|
53
65
|
} catch (error) {
|
|
@@ -5,6 +5,7 @@ const readEnvKey = require('../helpers/readEnvKey')
|
|
|
5
5
|
const upsertEnvKey = require('../helpers/upsertEnvKey')
|
|
6
6
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
7
7
|
const windowsCredentialManager = require('../helpers/windowsCredentialManager')
|
|
8
|
+
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
8
9
|
|
|
9
10
|
const SECURITY_BIN = '/usr/bin/security'
|
|
10
11
|
const SERVICE = 'dotenvx'
|
|
@@ -14,6 +15,10 @@ function findGenericPassword (publicKey) {
|
|
|
14
15
|
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
if (process.platform === 'linux') {
|
|
19
|
+
return linuxSecretService.findGenericPassword(publicKey)
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
18
23
|
}
|
|
19
24
|
|
|
@@ -39,7 +44,9 @@ class KeychainPull {
|
|
|
39
44
|
privateKey = findGenericPassword(publicKey)
|
|
40
45
|
if (!privateKey) throw new Error('not found')
|
|
41
46
|
} catch {
|
|
42
|
-
const secretStore = process.platform === 'win32'
|
|
47
|
+
const secretStore = process.platform === 'win32'
|
|
48
|
+
? 'Windows Credential Manager'
|
|
49
|
+
: process.platform === 'linux' ? 'Linux Secret Service' : 'macOS Keychain'
|
|
43
50
|
throw new Error(`[NOT_FOUND] private key not found in ${secretStore} (${armoredKeyDisplay(publicKey)}). fix: [dotenvx native up]`)
|
|
44
51
|
}
|
|
45
52
|
|
|
@@ -4,6 +4,7 @@ const keynames = require('../conventions/keynames')
|
|
|
4
4
|
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
5
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
6
6
|
const windowsCredentialManager = require('../helpers/windowsCredentialManager')
|
|
7
|
+
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
7
8
|
|
|
8
9
|
const SECURITY_BIN = '/usr/bin/security'
|
|
9
10
|
const SERVICE = 'dotenvx'
|
|
@@ -14,6 +15,11 @@ function addGenericPassword (publicKey, label, privateKey) {
|
|
|
14
15
|
return
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
if (process.platform === 'linux') {
|
|
19
|
+
linuxSecretService.addGenericPassword(publicKey, label, privateKey)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
try {
|
|
18
24
|
execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', publicKey, '-l', label, '-w', privateKey], { stdio: 'ignore' })
|
|
19
25
|
} catch {
|
|
@@ -26,6 +32,10 @@ function findGenericPassword (publicKey) {
|
|
|
26
32
|
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
27
33
|
}
|
|
28
34
|
|
|
35
|
+
if (process.platform === 'linux') {
|
|
36
|
+
return linuxSecretService.findGenericPassword(publicKey)
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
30
40
|
}
|
|
31
41
|
|
|
@@ -5,6 +5,7 @@ const readEnvKey = require('../helpers/readEnvKey')
|
|
|
5
5
|
const removeEnvKey = require('../helpers/removeEnvKey')
|
|
6
6
|
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
7
7
|
const windowsCredentialManager = require('../helpers/windowsCredentialManager')
|
|
8
|
+
const linuxSecretService = require('../helpers/linuxSecretService')
|
|
8
9
|
|
|
9
10
|
const SECURITY_BIN = '/usr/bin/security'
|
|
10
11
|
const SERVICE = 'dotenvx'
|
|
@@ -15,6 +16,11 @@ function addGenericPassword (publicKey, label, privateKey) {
|
|
|
15
16
|
return
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
if (process.platform === 'linux') {
|
|
20
|
+
linuxSecretService.addGenericPassword(publicKey, label, privateKey)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
try {
|
|
19
25
|
execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', publicKey, '-l', label, '-w', privateKey], { stdio: 'ignore' })
|
|
20
26
|
} catch {
|
|
@@ -27,6 +33,10 @@ function findGenericPassword (publicKey) {
|
|
|
27
33
|
return windowsCredentialManager.findGenericPassword(publicKey)
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
if (process.platform === 'linux') {
|
|
37
|
+
return linuxSecretService.findGenericPassword(publicKey)
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
return execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
31
41
|
}
|
|
32
42
|
|