@centient/secrets 0.9.0 → 0.11.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/README.md +424 -8
- package/dist/audit/hmac-chain.d.ts +117 -0
- package/dist/audit/hmac-chain.d.ts.map +1 -0
- package/dist/audit/hmac-chain.js +811 -0
- package/dist/audit/hmac-chain.js.map +1 -0
- package/dist/audit/ocsf.d.ts +108 -0
- package/dist/audit/ocsf.d.ts.map +1 -0
- package/dist/audit/ocsf.js +204 -0
- package/dist/audit/ocsf.js.map +1 -0
- package/dist/cli/hidden-prompt.d.ts.map +1 -1
- package/dist/cli/hidden-prompt.js +55 -10
- package/dist/cli/hidden-prompt.js.map +1 -1
- package/dist/cli/messages.d.ts +40 -8
- package/dist/cli/messages.d.ts.map +1 -1
- package/dist/cli/messages.js +34 -8
- package/dist/cli/messages.js.map +1 -1
- package/dist/cli/secrets-cli.d.ts.map +1 -1
- package/dist/cli/secrets-cli.js +191 -32
- package/dist/cli/secrets-cli.js.map +1 -1
- package/dist/client.d.ts +26 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +129 -0
- package/dist/client.js.map +1 -0
- package/dist/compatibility.d.ts +199 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +419 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/crypto/darwin-keychain.d.ts +394 -0
- package/dist/crypto/darwin-keychain.d.ts.map +1 -0
- package/dist/crypto/darwin-keychain.js +793 -0
- package/dist/crypto/darwin-keychain.js.map +1 -0
- package/dist/crypto/vault-common.d.ts +55 -12
- package/dist/crypto/vault-common.d.ts.map +1 -1
- package/dist/crypto/vault-common.js +74 -71
- package/dist/crypto/vault-common.js.map +1 -1
- package/dist/environment/EnvironmentManager.d.ts.map +1 -1
- package/dist/environment/EnvironmentManager.js +5 -10
- package/dist/environment/EnvironmentManager.js.map +1 -1
- package/dist/index.d.ts +37 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -6
- package/dist/index.js.map +1 -1
- package/dist/key-providers/keychain-provider.d.ts +30 -2
- package/dist/key-providers/keychain-provider.d.ts.map +1 -1
- package/dist/key-providers/keychain-provider.js +29 -5
- package/dist/key-providers/keychain-provider.js.map +1 -1
- package/dist/key-providers/onepassword-provider.d.ts +65 -5
- package/dist/key-providers/onepassword-provider.d.ts.map +1 -1
- package/dist/key-providers/onepassword-provider.js +159 -67
- package/dist/key-providers/onepassword-provider.js.map +1 -1
- package/dist/key-providers/op-cli.d.ts +78 -0
- package/dist/key-providers/op-cli.d.ts.map +1 -0
- package/dist/key-providers/op-cli.js +123 -0
- package/dist/key-providers/op-cli.js.map +1 -0
- package/dist/key-providers/passphrase-provider.d.ts +20 -0
- package/dist/key-providers/passphrase-provider.d.ts.map +1 -1
- package/dist/key-providers/passphrase-provider.js +49 -0
- package/dist/key-providers/passphrase-provider.js.map +1 -1
- package/dist/key-providers/probe.d.ts +46 -0
- package/dist/key-providers/probe.d.ts.map +1 -0
- package/dist/key-providers/probe.js +70 -0
- package/dist/key-providers/probe.js.map +1 -0
- package/dist/key-providers/types.d.ts +51 -1
- package/dist/key-providers/types.d.ts.map +1 -1
- package/dist/platform/agent-detect.d.ts +39 -3
- package/dist/platform/agent-detect.d.ts.map +1 -1
- package/dist/platform/agent-detect.js +55 -9
- package/dist/platform/agent-detect.js.map +1 -1
- package/dist/platform/session-context.d.ts +77 -0
- package/dist/platform/session-context.d.ts.map +1 -0
- package/dist/platform/session-context.js +83 -0
- package/dist/platform/session-context.js.map +1 -0
- package/dist/policies/audit-trail.d.ts +25 -0
- package/dist/policies/audit-trail.d.ts.map +1 -0
- package/dist/policies/audit-trail.js +31 -0
- package/dist/policies/audit-trail.js.map +1 -0
- package/dist/secret-state.d.ts +54 -0
- package/dist/secret-state.d.ts.map +1 -0
- package/dist/secret-state.js +2 -0
- package/dist/secret-state.js.map +1 -0
- package/dist/sinks/index.d.ts +9 -0
- package/dist/sinks/index.d.ts.map +1 -0
- package/dist/sinks/index.js +5 -0
- package/dist/sinks/index.js.map +1 -0
- package/dist/sinks/jsonl.d.ts +16 -0
- package/dist/sinks/jsonl.d.ts.map +1 -0
- package/dist/sinks/jsonl.js +51 -0
- package/dist/sinks/jsonl.js.map +1 -0
- package/dist/sinks/otel.d.ts +22 -0
- package/dist/sinks/otel.d.ts.map +1 -0
- package/dist/sinks/otel.js +155 -0
- package/dist/sinks/otel.js.map +1 -0
- package/dist/sinks/queue.d.ts +12 -0
- package/dist/sinks/queue.d.ts.map +1 -0
- package/dist/sinks/queue.js +89 -0
- package/dist/sinks/queue.js.map +1 -0
- package/dist/sinks/syslog.d.ts +17 -0
- package/dist/sinks/syslog.d.ts.map +1 -0
- package/dist/sinks/syslog.js +103 -0
- package/dist/sinks/syslog.js.map +1 -0
- package/dist/sinks/types.d.ts +27 -0
- package/dist/sinks/types.d.ts.map +1 -0
- package/dist/sinks/types.js +18 -0
- package/dist/sinks/types.js.map +1 -0
- package/dist/vault/policy.d.ts +64 -34
- package/dist/vault/policy.d.ts.map +1 -1
- package/dist/vault/policy.js +109 -67
- package/dist/vault/policy.js.map +1 -1
- package/dist/vault/session-vault-errors.d.ts +72 -4
- package/dist/vault/session-vault-errors.d.ts.map +1 -1
- package/dist/vault/session-vault-errors.js +95 -4
- package/dist/vault/session-vault-errors.js.map +1 -1
- package/dist/vault/session-vault.d.ts +145 -2
- package/dist/vault/session-vault.d.ts.map +1 -1
- package/dist/vault/session-vault.js +548 -72
- package/dist/vault/session-vault.js.map +1 -1
- package/dist/vault/sidecar.d.ts +9 -0
- package/dist/vault/sidecar.d.ts.map +1 -1
- package/dist/vault/sidecar.js +72 -3
- package/dist/vault/sidecar.js.map +1 -1
- package/dist/vault/types.d.ts +58 -10
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/dist/vault/vault-env.d.ts +43 -50
- package/dist/vault/vault-env.d.ts.map +1 -1
- package/dist/vault/vault-env.js +141 -62
- package/dist/vault/vault-env.js.map +1 -1
- package/dist/vault/vault-gpg.d.ts +6 -1
- package/dist/vault/vault-gpg.d.ts.map +1 -1
- package/dist/vault/vault-gpg.js +10 -8
- package/dist/vault/vault-gpg.js.map +1 -1
- package/dist/vault/vault-libsecret.d.ts +6 -1
- package/dist/vault/vault-libsecret.d.ts.map +1 -1
- package/dist/vault/vault-libsecret.js +16 -14
- package/dist/vault/vault-libsecret.js.map +1 -1
- package/dist/vault/vault-onepassword.d.ts +119 -0
- package/dist/vault/vault-onepassword.d.ts.map +1 -0
- package/dist/vault/vault-onepassword.js +271 -0
- package/dist/vault/vault-onepassword.js.map +1 -0
- package/dist/vault/vault-utils.d.ts +37 -3
- package/dist/vault/vault-utils.d.ts.map +1 -1
- package/dist/vault/vault-utils.js +65 -3
- package/dist/vault/vault-utils.js.map +1 -1
- package/dist/vault/vault-windows.d.ts +7 -2
- package/dist/vault/vault-windows.d.ts.map +1 -1
- package/dist/vault/vault-windows.js +21 -16
- package/dist/vault/vault-windows.js.map +1 -1
- package/dist/vault/vault.d.ts +120 -9
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +222 -139
- package/dist/vault/vault.js.map +1 -1
- package/package.json +8 -2
package/dist/index.js
CHANGED
|
@@ -2,28 +2,60 @@
|
|
|
2
2
|
export { encrypt, decrypt, encryptObject, decryptObject } from "./crypto/vault-common.js";
|
|
3
3
|
export { ALGORITHM, IV_LENGTH, AUTH_TAG_LENGTH, KEY_LENGTH } from "./crypto/vault-common.js";
|
|
4
4
|
// Credential vault
|
|
5
|
-
export { storeCredential, getCredential, deleteCredential, listCredentials, getActiveVaultType, isSessionValid } from "./vault/vault.js";
|
|
5
|
+
export { storeCredential, getCredential, deleteCredential, listCredentials, getActiveVaultType, isSessionValid, KeychainVault, AUTH_KEYCHAIN_SERVICE } from "./vault/vault.js";
|
|
6
|
+
export { EnvVault, ENV_VAULT_PREFIX, credentialKeyToEnvName, } from "./vault/vault-env.js";
|
|
7
|
+
export { createSecretsClient } from "./client.js";
|
|
8
|
+
export { probeBackendState, probeVaultBackendState } from "./vault/vault.js";
|
|
9
|
+
export { probeKeyProviderState, probeKeyProvider } from "./key-providers/probe.js";
|
|
10
|
+
// Compatibility floor (#214) — the provider-published list of behavioural gaps
|
|
11
|
+
// an OLD version of this package silently has, so a consumer's `doctor` can
|
|
12
|
+
// grade its own installed version. Pure, total, advisory: nothing here runs at
|
|
13
|
+
// import, nothing throws, and no vault path consults it. A graded floor, never
|
|
14
|
+
// a boot-time refusal.
|
|
15
|
+
export { SECRETS_PACKAGE_VERSION, MIN_ASSESSABLE_VERSION, COMPATIBILITY_FLOOR, assessCompatibility, assessPin, lowestVersionInRange, } from "./compatibility.js";
|
|
6
16
|
// Platform detection
|
|
7
17
|
export { isTTY, canUnicode, isHeadless, canLaunchBrowser, isCIEnvironment, isDockerContainer, isSSHSession } from "./platform/platform.js";
|
|
8
18
|
export { isAgentEnvironment } from "./platform/agent-detect.js";
|
|
19
|
+
// Session-context detection (#188) — session vs sessionless, NOT platform.
|
|
20
|
+
// A launchd system-domain daemon and an SSH login have no security session:
|
|
21
|
+
// no consent UI can render, and the keychain search list cannot be trusted.
|
|
22
|
+
export { detectSessionContext, isSessionlessContext, SESSION_CONTEXT_OVERRIDE_ENV } from "./platform/session-context.js";
|
|
23
|
+
// Hardened macOS Keychain core (#188) — the single place in this package that
|
|
24
|
+
// spawns `security`. Discriminated results throughout; explicit-target reads
|
|
25
|
+
// in a sessionless context; `-U` replace-in-place and `-T /usr/bin/security`
|
|
26
|
+
// reader preauthorization on every write.
|
|
27
|
+
export { SYSTEM_KEYCHAIN_PATH, SECURITY_BINARY, readKeychainCredential, readSystemKeychainCredential, readSearchListCredential, writeKeychainCredential, writeSystemKeychainCredential, writeDefaultKeychainCredential, deleteKeychainCredential, storedWithinReaderReach,
|
|
28
|
+
// Credential STATE (readability — runs the reader's own read, discards the
|
|
29
|
+
// value). The `probe*Entry` trio below answers PRESENCE only and must not be
|
|
30
|
+
// used as credential state.
|
|
31
|
+
probeKeychainCredentialState, probeKeychainEntry, probeSystemKeychainEntry, probeSearchListEntry, migrateLoginCredentialToSystem, keychainSearchList, privilegedWriteCommand, shellQuote, classifyKeychainRead, toSecretState, dumpKeychain, } from "./crypto/darwin-keychain.js";
|
|
9
32
|
// Environment management
|
|
10
33
|
export { getEnvironmentManager, EnvironmentManager } from "./environment/EnvironmentManager.js";
|
|
11
34
|
// CLI
|
|
12
35
|
export { runSecrets } from "./cli/secrets-cli.js";
|
|
36
|
+
// Auth CLI message catalog — public so a consuming CLI imports it instead of
|
|
37
|
+
// forking a copy that drifts (#217 C6-34). `./cli/messages.ts` states its scope.
|
|
38
|
+
export { AUTH_MESSAGES } from "./cli/messages.js";
|
|
13
39
|
// Key storage helpers (legacy — prefer key-providers for vault key access)
|
|
14
40
|
export { getKeyFromKeychain, storeKeyInKeychain, storeStringInKeychain, getStringFromKeychain, deleteFromKeychain } from "./crypto/vault-common.js";
|
|
15
41
|
export { KeychainProvider, DEFAULT_KEYCHAIN_SERVICE, DEFAULT_KEYCHAIN_ACCOUNT, } from "./key-providers/keychain-provider.js";
|
|
16
42
|
export { OnePasswordProvider } from "./key-providers/onepassword-provider.js";
|
|
43
|
+
export { detectOpCli, runOp, OpCliError, OP_PROBE_TIMEOUT_MS, OP_READ_TIMEOUT_MS, OP_WRITE_TIMEOUT_MS } from "./key-providers/op-cli.js";
|
|
44
|
+
export { OnePasswordVault, DEFAULT_OP_TAG, OP_LIST_CACHE_TTL_MS } from "./vault/vault-onepassword.js";
|
|
17
45
|
export { PassphraseProvider, DEFAULT_PASSPHRASE_KDF_PARAMS, DEFAULT_PASSPHRASE_VAULT_PATH, PASSPHRASE_KDF, PASSPHRASE_METADATA_VERSION, PASSPHRASE_SALT_LENGTH, PASSPHRASE_VERIFIER_ALGORITHM, PASSPHRASE_VERIFIER_CONTEXT, derivePassphraseKey, passphraseMetadataPathForVault, } from "./key-providers/passphrase-provider.js";
|
|
18
46
|
export { resolveKeyProvider, getProviderByType, loadConfig, saveSecretsConfig } from "./key-providers/resolve.js";
|
|
19
|
-
// Validation
|
|
20
|
-
|
|
47
|
+
// Validation — the credential-key grammar, enforced on every vault path (#168).
|
|
48
|
+
// `InvalidCredentialKeyError` is re-exported from the VaultError block below.
|
|
49
|
+
export { isValidKey, isValidKeyPrefix, assertValidKey, assertValidKeyPrefix, } from "./vault/vault-utils.js";
|
|
21
50
|
// Policy
|
|
22
|
-
export { setSecretsPolicies, getActivePolicies,
|
|
51
|
+
export { setSecretsPolicies, getActivePolicies, rejectedEventType } from "./vault/policy.js";
|
|
52
|
+
export { auditTrail } from "./policies/audit-trail.js";
|
|
53
|
+
export { OCSF_SCHEMA_VERSION, OCSF_IAM_CATEGORY_UID, OCSF_ENTITY_MANAGEMENT_CLASS_UID, toCredentialAuditEvent, } from "./audit/ocsf.js";
|
|
54
|
+
export { AuditChainError, canonicalizeAuditEvent, createHmacAuditChain, verifyHmacAuditChain, } from "./audit/hmac-chain.js";
|
|
23
55
|
// Session-backed vault (envelope encryption, single unlock per session)
|
|
24
56
|
// Recommended for long-running processes (daemons) holding N credentials;
|
|
25
57
|
// supersedes per-item `getCredential` calls for those consumers. See
|
|
26
58
|
// packages/secrets/docs/session-vault.md and issue #40 for the threat model.
|
|
27
|
-
export { openVault, VAULT_SCHEMA_VERSION, VAULT_AAD_PREFIX, DEFAULT_VAULT_PATH, DEFAULT_SIDECAR_PATH } from "./vault/session-vault.js";
|
|
28
|
-
export { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, } from "./vault/session-vault.js";
|
|
59
|
+
export { createVault, openVault, rekeyVault, VAULT_SCHEMA_VERSION, VAULT_AAD_PREFIX, DEFAULT_VAULT_PATH, DEFAULT_SIDECAR_PATH } from "./vault/session-vault.js";
|
|
60
|
+
export { VaultError, VaultUnlockError, VaultDecryptError, VaultRollbackError, VaultClosedError, VaultLockError, VaultRestoreError, InvalidCredentialKeyError, } from "./vault/session-vault.js";
|
|
29
61
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE7F,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE7F,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE/K,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AASlD,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAGnF,+EAA+E;AAC/E,4EAA4E;AAC5E,+EAA+E;AAC/E,+EAA+E;AAC/E,uBAAuB;AACvB,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAQ5B,qBAAqB;AACrB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3I,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAGzH,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,0CAA0C;AAC1C,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,4BAA4B,EAC5B,wBAAwB,EACxB,uBAAuB,EACvB,6BAA6B,EAC7B,8BAA8B,EAC9B,wBAAwB,EACxB,uBAAuB;AACvB,2EAA2E;AAC3E,6EAA6E;AAC7E,4BAA4B;AAC5B,4BAA4B,EAC5B,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,8BAA8B,EAC9B,kBAAkB,EAClB,sBAAsB,EACtB,UAAU,EACV,oBAAoB,EACpB,aAAa,EACb,YAAY,GACb,MAAM,6BAA6B,CAAC;AAUrC,yBAAyB;AACzB,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAEhG,MAAM;AACN,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,6EAA6E;AAC7E,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlD,2EAA2E;AAC3E,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAIpJ,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEzI,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEtG,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,6BAA6B,EAC7B,cAAc,EACd,2BAA2B,EAC3B,sBAAsB,EACtB,6BAA6B,EAC7B,2BAA2B,EAC3B,mBAAmB,EACnB,8BAA8B,GAC/B,MAAM,wCAAwC,CAAC;AAOhD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGlH,gFAAgF;AAChF,8EAA8E;AAC9E,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAGhC,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE7F,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,gCAAgC,EAChC,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAUzB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAc/B,wEAAwE;AACxE,0EAA0E;AAC1E,qEAAqE;AACrE,6EAA6E;AAC7E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChK,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,0BAA0B,CAAC"}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Key Provider — macOS Keychain
|
|
3
3
|
*
|
|
4
|
-
* Wraps the
|
|
5
|
-
*
|
|
4
|
+
* Wraps the getKeyFromKeychain / storeKeyInKeychain functions from
|
|
5
|
+
* vault-common.ts as a KeyProvider implementation. Those in turn delegate to
|
|
6
|
+
* the hardened `security` core in `crypto/darwin-keychain.ts` (#188), so the
|
|
7
|
+
* vault MASTER KEY inherits the same posture as credential values: explicit
|
|
8
|
+
* System-keychain reads in a sessionless context, `-U` replace-in-place
|
|
9
|
+
* writes, and `-T /usr/bin/security` reader preauthorization.
|
|
10
|
+
*
|
|
11
|
+
* Fixing only the value layer would have been half a fix — the master key
|
|
12
|
+
* would have become unreadable in exactly the contexts the values had just
|
|
13
|
+
* become readable in.
|
|
6
14
|
*
|
|
7
15
|
* Only available on macOS (darwin). Uses the `security` CLI to interact
|
|
8
16
|
* with the system Keychain, which prompts for Touch ID or system password.
|
|
9
17
|
*/
|
|
18
|
+
import type { KeychainDeps } from "../crypto/darwin-keychain.js";
|
|
19
|
+
import type { SecretState } from "../secret-state.js";
|
|
10
20
|
import type { KeyProvider, KeyProviderType } from "./types.js";
|
|
11
21
|
/**
|
|
12
22
|
* Default Keychain item coordinates. These are the historical values every
|
|
@@ -29,11 +39,18 @@ export interface KeychainProviderOptions {
|
|
|
29
39
|
service?: string;
|
|
30
40
|
/** Keychain account name. Defaults to {@link DEFAULT_KEYCHAIN_ACCOUNT}. */
|
|
31
41
|
account?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Injection seam for the `security` CLI and the platform/environment that
|
|
44
|
+
* decide the read path (#188). Omitted in production; supplied by tests so
|
|
45
|
+
* the exact argv is assertable without touching a real keychain.
|
|
46
|
+
*/
|
|
47
|
+
deps?: Partial<KeychainDeps>;
|
|
32
48
|
}
|
|
33
49
|
export declare class KeychainProvider implements KeyProvider {
|
|
34
50
|
readonly name: KeyProviderType;
|
|
35
51
|
private readonly service;
|
|
36
52
|
private readonly account;
|
|
53
|
+
private readonly deps;
|
|
37
54
|
/**
|
|
38
55
|
* @param options - Optional per-consumer Keychain item coordinates. With no
|
|
39
56
|
* options the provider targets the historical `centient-vault`/`vault-key`
|
|
@@ -45,5 +62,16 @@ export declare class KeychainProvider implements KeyProvider {
|
|
|
45
62
|
getKey(): Buffer | null;
|
|
46
63
|
storeKey(key: Buffer): boolean;
|
|
47
64
|
deleteKey(): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The five-way state of the vault master key, value-free (#188).
|
|
67
|
+
*
|
|
68
|
+
* Runs the SAME read {@link getKey} would run in this context, then discards
|
|
69
|
+
* the key and returns only the value-free {@link SecretState}. A
|
|
70
|
+
* metadata-only lookup would answer presence, not readability — and a key
|
|
71
|
+
* that is present but sits behind an ACL this context cannot satisfy is
|
|
72
|
+
* exactly the case a caller needs told apart from "no key stored". `found`
|
|
73
|
+
* here means the reader will get bytes.
|
|
74
|
+
*/
|
|
75
|
+
probeKey(): SecretState;
|
|
48
76
|
}
|
|
49
77
|
//# sourceMappingURL=keychain-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keychain-provider.d.ts","sourceRoot":"","sources":["../../src/key-providers/keychain-provider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"keychain-provider.d.ts","sourceRoot":"","sources":["../../src/key-providers/keychain-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAM/D;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AACzD,eAAO,MAAM,wBAAwB,cAAc,CAAC;AAMpD;;;;;;;;GAQG;AACH,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CAC9B;AAMD,qBAAa,gBAAiB,YAAW,WAAW;IAClD,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAc;IAE5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAE7C;;;;OAIG;gBACS,OAAO,GAAE,uBAA4B;IAMjD,wCAAwC;IACxC,MAAM,CAAC,MAAM,IAAI,OAAO;IAIxB,MAAM,IAAI,MAAM,GAAG,IAAI;IAIvB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI9B,SAAS,IAAI,OAAO;IAIpB;;;;;;;;;OASG;IACH,QAAQ,IAAI,WAAW;CAGxB"}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Key Provider — macOS Keychain
|
|
3
3
|
*
|
|
4
|
-
* Wraps the
|
|
5
|
-
*
|
|
4
|
+
* Wraps the getKeyFromKeychain / storeKeyInKeychain functions from
|
|
5
|
+
* vault-common.ts as a KeyProvider implementation. Those in turn delegate to
|
|
6
|
+
* the hardened `security` core in `crypto/darwin-keychain.ts` (#188), so the
|
|
7
|
+
* vault MASTER KEY inherits the same posture as credential values: explicit
|
|
8
|
+
* System-keychain reads in a sessionless context, `-U` replace-in-place
|
|
9
|
+
* writes, and `-T /usr/bin/security` reader preauthorization.
|
|
10
|
+
*
|
|
11
|
+
* Fixing only the value layer would have been half a fix — the master key
|
|
12
|
+
* would have become unreadable in exactly the contexts the values had just
|
|
13
|
+
* become readable in.
|
|
6
14
|
*
|
|
7
15
|
* Only available on macOS (darwin). Uses the `security` CLI to interact
|
|
8
16
|
* with the system Keychain, which prompts for Touch ID or system password.
|
|
9
17
|
*/
|
|
10
18
|
import { getKeyFromKeychain, storeKeyInKeychain, deleteFromKeychain, } from "../crypto/vault-common.js";
|
|
19
|
+
import { probeKeychainCredentialState } from "../crypto/darwin-keychain.js";
|
|
11
20
|
// =============================================================================
|
|
12
21
|
// Constants
|
|
13
22
|
// =============================================================================
|
|
@@ -25,6 +34,7 @@ export class KeychainProvider {
|
|
|
25
34
|
name = "keychain";
|
|
26
35
|
service;
|
|
27
36
|
account;
|
|
37
|
+
deps;
|
|
28
38
|
/**
|
|
29
39
|
* @param options - Optional per-consumer Keychain item coordinates. With no
|
|
30
40
|
* options the provider targets the historical `centient-vault`/`vault-key`
|
|
@@ -33,19 +43,33 @@ export class KeychainProvider {
|
|
|
33
43
|
constructor(options = {}) {
|
|
34
44
|
this.service = options.service ?? DEFAULT_KEYCHAIN_SERVICE;
|
|
35
45
|
this.account = options.account ?? DEFAULT_KEYCHAIN_ACCOUNT;
|
|
46
|
+
this.deps = options.deps ?? {};
|
|
36
47
|
}
|
|
37
48
|
/** Returns true if running on macOS. */
|
|
38
49
|
static detect() {
|
|
39
50
|
return process.platform === "darwin";
|
|
40
51
|
}
|
|
41
52
|
getKey() {
|
|
42
|
-
return getKeyFromKeychain(this.service, this.account);
|
|
53
|
+
return getKeyFromKeychain(this.service, this.account, this.deps);
|
|
43
54
|
}
|
|
44
55
|
storeKey(key) {
|
|
45
|
-
return storeKeyInKeychain(this.service, this.account, key);
|
|
56
|
+
return storeKeyInKeychain(this.service, this.account, key, this.deps);
|
|
46
57
|
}
|
|
47
58
|
deleteKey() {
|
|
48
|
-
return deleteFromKeychain(this.service, this.account);
|
|
59
|
+
return deleteFromKeychain(this.service, this.account, this.deps);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The five-way state of the vault master key, value-free (#188).
|
|
63
|
+
*
|
|
64
|
+
* Runs the SAME read {@link getKey} would run in this context, then discards
|
|
65
|
+
* the key and returns only the value-free {@link SecretState}. A
|
|
66
|
+
* metadata-only lookup would answer presence, not readability — and a key
|
|
67
|
+
* that is present but sits behind an ACL this context cannot satisfy is
|
|
68
|
+
* exactly the case a caller needs told apart from "no key stored". `found`
|
|
69
|
+
* here means the reader will get bytes.
|
|
70
|
+
*/
|
|
71
|
+
probeKey() {
|
|
72
|
+
return probeKeychainCredentialState(this.service, this.account, this.deps);
|
|
49
73
|
}
|
|
50
74
|
}
|
|
51
75
|
//# sourceMappingURL=keychain-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keychain-provider.js","sourceRoot":"","sources":["../../src/key-providers/keychain-provider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"keychain-provider.js","sourceRoot":"","sources":["../../src/key-providers/keychain-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAK5E,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,gBAAgB,CAAC;AACzD,MAAM,CAAC,MAAM,wBAAwB,GAAG,WAAW,CAAC;AA4BpD,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAoB,UAAU,CAAC;IAE3B,OAAO,CAAS;IAChB,OAAO,CAAS;IAChB,IAAI,CAAwB;IAE7C;;;;OAIG;IACH,YAAY,UAAmC,EAAE;QAC/C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,wBAAwB,CAAC;QAC3D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,wBAAwB,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,wCAAwC;IACxC,MAAM,CAAC,MAAM;QACX,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACvC,CAAC;IAED,MAAM;QACJ,OAAO,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC;IAED,SAAS;QACP,OAAO,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;;;;OASG;IACH,QAAQ;QACN,OAAO,4BAA4B,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC;CACF"}
|
|
@@ -9,9 +9,21 @@
|
|
|
9
9
|
* 3. CLI session — user ran `op signin` (OP_SESSION_* env var)
|
|
10
10
|
*
|
|
11
11
|
* The `op` CLI handles auth selection internally; this provider just
|
|
12
|
-
* shells out to `op read` / `op item create`
|
|
12
|
+
* shells out to `op read` / `op item create` through the shared `op-cli.ts`
|
|
13
|
+
* helper (ADR-004 §6), which it co-owns with the credential-value backend
|
|
14
|
+
* `OnePasswordVault`.
|
|
13
15
|
*
|
|
14
|
-
* No dependency on @1password/sdk —
|
|
16
|
+
* No dependency on @1password/sdk — `execFileSync` only.
|
|
17
|
+
*
|
|
18
|
+
* **Key material never reaches argv (#102).** `storeKey` builds the item JSON
|
|
19
|
+
* in-process and pipes it to `op item create -` via `runOp`'s `input` option —
|
|
20
|
+
* the argv-safe seam ADR-004 §4 specifies and `OnePasswordVault.store` already
|
|
21
|
+
* uses. Nothing readable in `ps` / `/proc/<pid>/cmdline` carries the key. This
|
|
22
|
+
* closes ADR-001's "Process argument visibility" negative, which had the hex
|
|
23
|
+
* key in a `password=<hex>` argv element for the life of the call.
|
|
24
|
+
*
|
|
25
|
+
* `getKey` uses `op read`, whose only argv is a non-secret `op://` reference —
|
|
26
|
+
* the value comes back on stdout, so that path was never exposed.
|
|
15
27
|
*/
|
|
16
28
|
import type { KeyProvider, KeyProviderType, OnePasswordConfig } from "./types.js";
|
|
17
29
|
export declare class OnePasswordProvider implements KeyProvider {
|
|
@@ -29,10 +41,58 @@ export declare class OnePasswordProvider implements KeyProvider {
|
|
|
29
41
|
*/
|
|
30
42
|
static detect(): boolean;
|
|
31
43
|
getKey(): Buffer | null;
|
|
44
|
+
/**
|
|
45
|
+
* Store the vault key. Overwrites any existing item.
|
|
46
|
+
*
|
|
47
|
+
* The hex travels on **stdin** inside an item template and never appears in
|
|
48
|
+
* argv (#102): the template goes to `op item create -` through `runOp`'s
|
|
49
|
+
* `input` option, the argv-safe seam ADR-004 §4 specifies. Item ids, titles
|
|
50
|
+
* and vault names are not secret and stay on the command line.
|
|
51
|
+
*
|
|
52
|
+
* ## Ordering: create first, delete the old item last
|
|
53
|
+
*
|
|
54
|
+
* `op item edit` has no stdin template form, so the overwrite cannot be an
|
|
55
|
+
* edit-in-place without putting `password=<hex>` back in argv. The write is
|
|
56
|
+
* therefore a replace — but a **non-destructive** one, sequenced so no window
|
|
57
|
+
* exists in which the master key is absent from 1Password:
|
|
58
|
+
*
|
|
59
|
+
* 1. Resolve the existing item's id (absent ⇒ plain create, no step 3).
|
|
60
|
+
* 2. Create the replacement under the **same canonical title** — `op`
|
|
61
|
+
* permits duplicate titles — and capture the new item's id.
|
|
62
|
+
* Create fails ⇒ the old item is untouched and this returns false.
|
|
63
|
+
* 3. Delete the **old** item by id. Delete fails ⇒ roll back by deleting
|
|
64
|
+
* the **new** item by id, leaving the old one in place, and return false.
|
|
65
|
+
*
|
|
66
|
+
* The key is only ever removed from 1Password after its replacement durably
|
|
67
|
+
* exists. Nothing here renames, so there is no window where a rename failure
|
|
68
|
+
* strands the item under a title `getKey` cannot read.
|
|
69
|
+
*
|
|
70
|
+
* **Residual window (double failure).** If step 3's delete fails *and* the
|
|
71
|
+
* rollback delete of the new item also fails, both items remain and this
|
|
72
|
+
* returns false. That is not a loss: no key is destroyed, and {@link getKey}
|
|
73
|
+
* does not silently pick one — `op read` on an ambiguous title fails, so the
|
|
74
|
+
* caller gets a loud failure rather than a wrong key. The operator resolves
|
|
75
|
+
* it by deleting whichever item is stale.
|
|
76
|
+
*/
|
|
32
77
|
storeKey(key: Buffer): boolean;
|
|
33
78
|
deleteKey(): boolean;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
79
|
+
/**
|
|
80
|
+
* The id of the existing key item, or null when there is none.
|
|
81
|
+
*
|
|
82
|
+
* @throws when 1Password cannot be asked, or answers with an item whose id
|
|
83
|
+
* we cannot read. Both are "unknown", not "absent" — and {@link storeKey}
|
|
84
|
+
* must not treat unknown as absent, or a transient read failure would end
|
|
85
|
+
* with two items under one title.
|
|
86
|
+
*/
|
|
87
|
+
private findItemId;
|
|
88
|
+
/**
|
|
89
|
+
* Delete an item by id or title, treating "no such item" as the desired end
|
|
90
|
+
* state.
|
|
91
|
+
*
|
|
92
|
+
* Unlike {@link deleteKey}, an auth or network failure is **rethrown** — the
|
|
93
|
+
* {@link storeKey} path needs to distinguish "the old item is gone" from
|
|
94
|
+
* "we could not remove it", because only the second case must roll back.
|
|
95
|
+
*/
|
|
96
|
+
private deleteItem;
|
|
37
97
|
}
|
|
38
98
|
//# sourceMappingURL=onepassword-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onepassword-provider.d.ts","sourceRoot":"","sources":["../../src/key-providers/onepassword-provider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"onepassword-provider.d.ts","sourceRoot":"","sources":["../../src/key-providers/onepassword-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAqBlF,qBAAa,mBAAoB,YAAW,WAAW;IACrD,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,MAAM,CAAC,EAAE,iBAAiB;IAKtC;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,IAAI,OAAO;IAIxB,MAAM,IAAI,MAAM,GAAG,IAAI;IAcvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAsD9B,SAAS,IAAI,OAAO;IAcpB;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IAsBlB;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;CAUnB"}
|
|
@@ -9,11 +9,23 @@
|
|
|
9
9
|
* 3. CLI session — user ran `op signin` (OP_SESSION_* env var)
|
|
10
10
|
*
|
|
11
11
|
* The `op` CLI handles auth selection internally; this provider just
|
|
12
|
-
* shells out to `op read` / `op item create`
|
|
12
|
+
* shells out to `op read` / `op item create` through the shared `op-cli.ts`
|
|
13
|
+
* helper (ADR-004 §6), which it co-owns with the credential-value backend
|
|
14
|
+
* `OnePasswordVault`.
|
|
13
15
|
*
|
|
14
|
-
* No dependency on @1password/sdk —
|
|
16
|
+
* No dependency on @1password/sdk — `execFileSync` only.
|
|
17
|
+
*
|
|
18
|
+
* **Key material never reaches argv (#102).** `storeKey` builds the item JSON
|
|
19
|
+
* in-process and pipes it to `op item create -` via `runOp`'s `input` option —
|
|
20
|
+
* the argv-safe seam ADR-004 §4 specifies and `OnePasswordVault.store` already
|
|
21
|
+
* uses. Nothing readable in `ps` / `/proc/<pid>/cmdline` carries the key. This
|
|
22
|
+
* closes ADR-001's "Process argument visibility" negative, which had the hex
|
|
23
|
+
* key in a `password=<hex>` argv element for the life of the call.
|
|
24
|
+
*
|
|
25
|
+
* `getKey` uses `op read`, whose only argv is a non-secret `op://` reference —
|
|
26
|
+
* the value comes back on stdout, so that path was never exposed.
|
|
15
27
|
*/
|
|
16
|
-
import {
|
|
28
|
+
import { detectOpCli, runOp, OpCliError, OP_READ_TIMEOUT_MS, OP_WRITE_TIMEOUT_MS, } from "./op-cli.js";
|
|
17
29
|
// =============================================================================
|
|
18
30
|
// Defaults
|
|
19
31
|
// =============================================================================
|
|
@@ -40,38 +52,12 @@ export class OnePasswordProvider {
|
|
|
40
52
|
* is configured (desktop app / prior sign-in).
|
|
41
53
|
*/
|
|
42
54
|
static detect() {
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
execFileSync("op", ["--version"], {
|
|
46
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
47
|
-
timeout: 5000,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
// Service account mode — no further checks needed
|
|
54
|
-
if (process.env.OP_SERVICE_ACCOUNT_TOKEN) {
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
// Interactive mode — check for configured accounts
|
|
58
|
-
try {
|
|
59
|
-
const output = execFileSync("op", ["account", "list", "--format=json"], { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 }).trim();
|
|
60
|
-
// Returns "[]" when no accounts are configured
|
|
61
|
-
return output !== "[]" && output.length > 2;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
55
|
+
return detectOpCli();
|
|
66
56
|
}
|
|
67
57
|
getKey() {
|
|
68
58
|
try {
|
|
69
59
|
const ref = `op://${this.vault}/${this.item}/${FIELD_NAME}`;
|
|
70
|
-
const result =
|
|
71
|
-
encoding: "utf8",
|
|
72
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
73
|
-
timeout: 30000,
|
|
74
|
-
}).trim();
|
|
60
|
+
const result = runOp(["read", ref], { timeoutMs: OP_WRITE_TIMEOUT_MS });
|
|
75
61
|
if (!result)
|
|
76
62
|
return null;
|
|
77
63
|
const buf = Buffer.from(result, "hex");
|
|
@@ -84,63 +70,169 @@ export class OnePasswordProvider {
|
|
|
84
70
|
return null;
|
|
85
71
|
}
|
|
86
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Store the vault key. Overwrites any existing item.
|
|
75
|
+
*
|
|
76
|
+
* The hex travels on **stdin** inside an item template and never appears in
|
|
77
|
+
* argv (#102): the template goes to `op item create -` through `runOp`'s
|
|
78
|
+
* `input` option, the argv-safe seam ADR-004 §4 specifies. Item ids, titles
|
|
79
|
+
* and vault names are not secret and stay on the command line.
|
|
80
|
+
*
|
|
81
|
+
* ## Ordering: create first, delete the old item last
|
|
82
|
+
*
|
|
83
|
+
* `op item edit` has no stdin template form, so the overwrite cannot be an
|
|
84
|
+
* edit-in-place without putting `password=<hex>` back in argv. The write is
|
|
85
|
+
* therefore a replace — but a **non-destructive** one, sequenced so no window
|
|
86
|
+
* exists in which the master key is absent from 1Password:
|
|
87
|
+
*
|
|
88
|
+
* 1. Resolve the existing item's id (absent ⇒ plain create, no step 3).
|
|
89
|
+
* 2. Create the replacement under the **same canonical title** — `op`
|
|
90
|
+
* permits duplicate titles — and capture the new item's id.
|
|
91
|
+
* Create fails ⇒ the old item is untouched and this returns false.
|
|
92
|
+
* 3. Delete the **old** item by id. Delete fails ⇒ roll back by deleting
|
|
93
|
+
* the **new** item by id, leaving the old one in place, and return false.
|
|
94
|
+
*
|
|
95
|
+
* The key is only ever removed from 1Password after its replacement durably
|
|
96
|
+
* exists. Nothing here renames, so there is no window where a rename failure
|
|
97
|
+
* strands the item under a title `getKey` cannot read.
|
|
98
|
+
*
|
|
99
|
+
* **Residual window (double failure).** If step 3's delete fails *and* the
|
|
100
|
+
* rollback delete of the new item also fails, both items remain and this
|
|
101
|
+
* returns false. That is not a loss: no key is destroyed, and {@link getKey}
|
|
102
|
+
* does not silently pick one — `op read` on an ambiguous title fails, so the
|
|
103
|
+
* caller gets a loud failure rather than a wrong key. The operator resolves
|
|
104
|
+
* it by deleting whichever item is stale.
|
|
105
|
+
*/
|
|
87
106
|
storeKey(key) {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
const item = {
|
|
108
|
+
title: this.item,
|
|
109
|
+
category: "PASSWORD",
|
|
110
|
+
vault: { name: this.vault },
|
|
111
|
+
fields: [
|
|
112
|
+
{ id: FIELD_NAME, type: "CONCEALED", value: key.toString("hex") },
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
// Step 1. What is already there? A failure to answer that is a failure to
|
|
116
|
+
// store: creating blind would risk leaving two items under one title.
|
|
117
|
+
let oldId;
|
|
96
118
|
try {
|
|
97
|
-
|
|
98
|
-
return true;
|
|
119
|
+
oldId = this.findItemId();
|
|
99
120
|
}
|
|
100
121
|
catch {
|
|
101
|
-
|
|
102
|
-
return true;
|
|
122
|
+
return false;
|
|
103
123
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// Private helpers
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
itemExists() {
|
|
124
|
+
// Step 2. Write the replacement. Until this succeeds nothing is removed.
|
|
125
|
+
let newId;
|
|
109
126
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
const created = runOp(["item", "create", "--format=json", "-"], {
|
|
128
|
+
input: JSON.stringify(item),
|
|
129
|
+
timeoutMs: OP_WRITE_TIMEOUT_MS,
|
|
130
|
+
});
|
|
131
|
+
newId = parseItemId(created);
|
|
112
132
|
}
|
|
113
133
|
catch {
|
|
114
134
|
return false;
|
|
115
135
|
}
|
|
116
|
-
|
|
117
|
-
|
|
136
|
+
if (oldId === null)
|
|
137
|
+
return true;
|
|
138
|
+
// Step 3. Retire the superseded item, by id so the duplicate title cannot
|
|
139
|
+
// make the delete ambiguous.
|
|
118
140
|
try {
|
|
119
|
-
|
|
120
|
-
"item", "create",
|
|
121
|
-
"--category", "Password",
|
|
122
|
-
"--title", this.item,
|
|
123
|
-
"--vault", this.vault,
|
|
124
|
-
`${FIELD_NAME}=${keyHex}`,
|
|
125
|
-
], { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
|
|
141
|
+
this.deleteItem(oldId);
|
|
126
142
|
return true;
|
|
127
143
|
}
|
|
128
144
|
catch {
|
|
145
|
+
// Roll back: the store did not complete, so the vault should look as it
|
|
146
|
+
// did before. Deleting the new item leaves the old key readable.
|
|
147
|
+
if (newId !== null) {
|
|
148
|
+
try {
|
|
149
|
+
this.deleteItem(newId);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Double failure — see "Residual window" above. Both items remain;
|
|
153
|
+
// getKey fails loudly on the ambiguous title rather than returning a
|
|
154
|
+
// key that may be the wrong one, and neither key is lost.
|
|
155
|
+
}
|
|
156
|
+
}
|
|
129
157
|
return false;
|
|
130
158
|
}
|
|
131
159
|
}
|
|
132
|
-
|
|
160
|
+
deleteKey() {
|
|
133
161
|
try {
|
|
134
|
-
|
|
135
|
-
"item", "edit", this.item,
|
|
136
|
-
"--vault", this.vault,
|
|
137
|
-
`${FIELD_NAME}=${keyHex}`,
|
|
138
|
-
], { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
|
|
139
|
-
return true;
|
|
162
|
+
this.deleteItem(this.item);
|
|
140
163
|
}
|
|
141
164
|
catch {
|
|
142
|
-
|
|
165
|
+
// Unchanged from before #102: this entry point reports success for any
|
|
166
|
+
// failure, not just a missing item.
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Private helpers
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
/**
|
|
174
|
+
* The id of the existing key item, or null when there is none.
|
|
175
|
+
*
|
|
176
|
+
* @throws when 1Password cannot be asked, or answers with an item whose id
|
|
177
|
+
* we cannot read. Both are "unknown", not "absent" — and {@link storeKey}
|
|
178
|
+
* must not treat unknown as absent, or a transient read failure would end
|
|
179
|
+
* with two items under one title.
|
|
180
|
+
*/
|
|
181
|
+
findItemId() {
|
|
182
|
+
let raw;
|
|
183
|
+
try {
|
|
184
|
+
raw = runOp(["item", "get", this.item, "--vault", this.vault, "--format=json"], { timeoutMs: OP_READ_TIMEOUT_MS });
|
|
143
185
|
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
188
|
+
return null;
|
|
189
|
+
throw err;
|
|
190
|
+
}
|
|
191
|
+
const id = parseItemId(raw);
|
|
192
|
+
if (id === null) {
|
|
193
|
+
throw new Error(`op item get ${this.item} returned no readable item id — refusing to ` +
|
|
194
|
+
"create a second item under the same title");
|
|
195
|
+
}
|
|
196
|
+
return id;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Delete an item by id or title, treating "no such item" as the desired end
|
|
200
|
+
* state.
|
|
201
|
+
*
|
|
202
|
+
* Unlike {@link deleteKey}, an auth or network failure is **rethrown** — the
|
|
203
|
+
* {@link storeKey} path needs to distinguish "the old item is gone" from
|
|
204
|
+
* "we could not remove it", because only the second case must roll back.
|
|
205
|
+
*/
|
|
206
|
+
deleteItem(ref) {
|
|
207
|
+
try {
|
|
208
|
+
runOp(["item", "delete", ref, "--vault", this.vault], {
|
|
209
|
+
timeoutMs: OP_WRITE_TIMEOUT_MS,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
214
|
+
return;
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// =============================================================================
|
|
220
|
+
// Helpers
|
|
221
|
+
// =============================================================================
|
|
222
|
+
/** The `id` of an item in `op … --format=json` output, or null if unreadable. */
|
|
223
|
+
function parseItemId(raw) {
|
|
224
|
+
if (raw === "")
|
|
225
|
+
return null;
|
|
226
|
+
let parsed;
|
|
227
|
+
try {
|
|
228
|
+
parsed = JSON.parse(raw);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return null;
|
|
144
232
|
}
|
|
233
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
234
|
+
return null;
|
|
235
|
+
const id = parsed.id;
|
|
236
|
+
return typeof id === "string" && id.length > 0 ? id : null;
|
|
145
237
|
}
|
|
146
238
|
//# sourceMappingURL=onepassword-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onepassword-provider.js","sourceRoot":"","sources":["../../src/key-providers/onepassword-provider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"onepassword-provider.js","sourceRoot":"","sources":["../../src/key-providers/onepassword-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,EACL,WAAW,EACX,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,gFAAgF;AAChF,WAAW;AACX,gFAAgF;AAEhF,MAAM,aAAa,GAAG,SAAS,CAAC;AAChC,MAAM,YAAY,GAAG,oBAAoB,CAAC;AAC1C,MAAM,UAAU,GAAG,UAAU,CAAC;AAE9B,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF,MAAM,OAAO,mBAAmB;IACrB,IAAI,GAAoB,WAAW,CAAC;IAC5B,KAAK,CAAS;IACd,IAAI,CAAS;IAE9B,YAAY,MAA0B;QACpC,IAAI,CAAC,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,aAAa,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,MAAM,EAAE,IAAI,IAAI,YAAY,CAAC;IAC3C,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM;QACX,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IAED,MAAM;QACJ,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC;YAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;YACxE,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YACzB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACvC,mDAAmD;YACnD,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAC;YACnC,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,QAAQ,CAAC,GAAW;QAClB,MAAM,IAAI,GAAG;YACX,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE;YAC3B,MAAM,EAAE;gBACN,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;aAClE;SACF,CAAC;QAEF,0EAA0E;QAC1E,sEAAsE;QACtE,IAAI,KAAoB,CAAC;QACzB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QAED,yEAAyE;QACzE,IAAI,KAAoB,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE;gBAC9D,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC3B,SAAS,EAAE,mBAAmB;aAC/B,CAAC,CAAC;YACH,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAEhC,0EAA0E;QAC1E,6BAA6B;QAC7B,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,iEAAiE;YACjE,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,IAAI,CAAC;oBACH,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBACzB,CAAC;gBAAC,MAAM,CAAC;oBACP,mEAAmE;oBACnE,qEAAqE;oBACrE,0DAA0D;gBAC5D,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,SAAS;QACP,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,oCAAoC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8EAA8E;IAC9E,kBAAkB;IAClB,8EAA8E;IAE9E;;;;;;;OAOG;IACK,UAAU;QAChB,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,KAAK,CACT,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,EAClE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAClC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,UAAU,IAAI,GAAG,CAAC,UAAU;gBAAE,OAAO,IAAI,CAAC;YAC7D,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,eAAe,IAAI,CAAC,IAAI,8CAA8C;gBACpE,2CAA2C,CAC9C,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;;;;;;OAOG;IACK,UAAU,CAAC,GAAW;QAC5B,IAAI,CAAC;YACH,KAAK,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE;gBACpD,SAAS,EAAE,mBAAmB;aAC/B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,UAAU,IAAI,GAAG,CAAC,UAAU;gBAAE,OAAO;YACxD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF,iFAAiF;AACjF,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/D,MAAM,EAAE,GAAI,MAA2B,CAAC,EAAE,CAAC;IAC3C,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
|