@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
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault backend — 1Password credential storage (ADR-004).
|
|
3
|
+
*
|
|
4
|
+
* Stores credential **values** in a dedicated 1Password vault via the `op` CLI.
|
|
5
|
+
* This is a different layer from ADR-001's `OnePasswordProvider`, which stores
|
|
6
|
+
* the vault *encryption key*: an operator may keep the key in the Keychain and
|
|
7
|
+
* the credentials in 1Password, or the reverse. The two carry separate config
|
|
8
|
+
* blocks for exactly that reason (ADR-004 §1).
|
|
9
|
+
*
|
|
10
|
+
* ## Two properties this backend exists to hold
|
|
11
|
+
*
|
|
12
|
+
* 1. **Never auto-selected.** `op` being installed must not silently route
|
|
13
|
+
* credentials into someone's personal vault — surprising secret placement is
|
|
14
|
+
* a security defect (P15/P2). Selection is explicit opt-in, and
|
|
15
|
+
* {@link OnePasswordVault.detect} refuses without it.
|
|
16
|
+
* 2. **Secret values never touch argv.** Writes build the item JSON in-process
|
|
17
|
+
* and pipe it to `op item create -`, so nothing readable in `ps` ever carries
|
|
18
|
+
* a secret. This is the pattern issue #102 retrofitted to the key provider,
|
|
19
|
+
* which now writes through the same `op item create -` stdin seam. Its
|
|
20
|
+
* replace ordering differs — create first, delete the superseded item last —
|
|
21
|
+
* because losing the vault *master key* between two calls would be
|
|
22
|
+
* unrecoverable, where a credential value can be re-fetched from its source.
|
|
23
|
+
*/
|
|
24
|
+
import { assertValidKey } from "./vault-utils.js";
|
|
25
|
+
import { detectOpCli, runOp, OpCliError, OP_READ_TIMEOUT_MS, OP_WRITE_TIMEOUT_MS, } from "../key-providers/op-cli.js";
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Constants
|
|
28
|
+
// =============================================================================
|
|
29
|
+
/** Default tag applied to every item this backend writes. */
|
|
30
|
+
export const DEFAULT_OP_TAG = "centient";
|
|
31
|
+
/** The 1Password field a credential value lives in. */
|
|
32
|
+
const FIELD_NAME = "password";
|
|
33
|
+
/**
|
|
34
|
+
* List-cache TTL, mirroring `KEYCHAIN_LIST_CACHE_TTL_MS` (ADR-004 §8).
|
|
35
|
+
*
|
|
36
|
+
* Only key **names** are cached, never values: a cached value would both extend
|
|
37
|
+
* how long plaintext sits in the heap and serve a rotated or revoked credential
|
|
38
|
+
* until expiry. 1Password stays the source of truth for values.
|
|
39
|
+
*/
|
|
40
|
+
export const OP_LIST_CACHE_TTL_MS = 5_000;
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// Implementation
|
|
43
|
+
// =============================================================================
|
|
44
|
+
export class OnePasswordVault {
|
|
45
|
+
name = "1password";
|
|
46
|
+
vault;
|
|
47
|
+
tag;
|
|
48
|
+
now;
|
|
49
|
+
listCache = null;
|
|
50
|
+
warned = false;
|
|
51
|
+
/**
|
|
52
|
+
* @param config - must carry a `vault`; there is deliberately no default
|
|
53
|
+
* (ADR-004 §1 — guessing a vault name risks writing credentials somewhere
|
|
54
|
+
* the operator did not intend).
|
|
55
|
+
* @throws {Error} when `vault` is missing or blank.
|
|
56
|
+
*/
|
|
57
|
+
constructor(config) {
|
|
58
|
+
const vault = config.vault?.trim();
|
|
59
|
+
if (!vault) {
|
|
60
|
+
throw new Error('secrets.onePasswordBackend.vault is required when backend is "1password" ' +
|
|
61
|
+
"(or set CENTIENT_OP_VAULT). There is no default vault — see ADR-004 §1.");
|
|
62
|
+
}
|
|
63
|
+
this.vault = vault;
|
|
64
|
+
this.tag = config.tag?.trim() || DEFAULT_OP_TAG;
|
|
65
|
+
this.now = config.now ?? Date.now;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether this backend may be used.
|
|
69
|
+
*
|
|
70
|
+
* Requires **both** an explicit opt-in and a usable `op`. The opt-in half is
|
|
71
|
+
* what keeps 1Password out of the auto-cascade: without it this returns false
|
|
72
|
+
* even on a machine where `op` is installed and signed in.
|
|
73
|
+
*
|
|
74
|
+
* @param optedIn - true when config or env explicitly selected this backend.
|
|
75
|
+
*/
|
|
76
|
+
static detect(optedIn) {
|
|
77
|
+
if (!optedIn)
|
|
78
|
+
return false;
|
|
79
|
+
return detectOpCli();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Write a credential. The value travels on stdin and never appears in argv.
|
|
83
|
+
*
|
|
84
|
+
* Update is modeled as replace (delete-then-create) so create and update share
|
|
85
|
+
* one argv-safe path rather than needing a separate `op item edit` form.
|
|
86
|
+
*/
|
|
87
|
+
store(key, value) {
|
|
88
|
+
assertValidKey(key, "write");
|
|
89
|
+
const item = {
|
|
90
|
+
title: key,
|
|
91
|
+
category: "PASSWORD",
|
|
92
|
+
vault: { name: this.vault },
|
|
93
|
+
tags: [this.tag],
|
|
94
|
+
fields: [{ id: FIELD_NAME, type: "CONCEALED", value }],
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
// Replace semantics: drop any existing item first so `create` is always
|
|
98
|
+
// the write path. delete() is idempotent, so a missing item is fine.
|
|
99
|
+
this.deleteItem(key);
|
|
100
|
+
runOp(["item", "create", "--format=json", "-"], {
|
|
101
|
+
input: JSON.stringify(item),
|
|
102
|
+
timeoutMs: OP_WRITE_TIMEOUT_MS,
|
|
103
|
+
});
|
|
104
|
+
this.invalidateListCache();
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
this.warnOnce("store", key, err);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Read a credential value. Returns null when absent or on failure.
|
|
114
|
+
*
|
|
115
|
+
* @throws {InvalidCredentialKeyError} for a key this backend could never
|
|
116
|
+
* have written — building an `op://` reference from it is exactly the
|
|
117
|
+
* misparse the key grammar exists to prevent, and `null` would report the
|
|
118
|
+
* impossible key as a merely absent one.
|
|
119
|
+
*/
|
|
120
|
+
retrieve(key) {
|
|
121
|
+
assertValidKey(key, "read");
|
|
122
|
+
try {
|
|
123
|
+
const value = runOp(["read", `op://${this.vault}/${key}/${FIELD_NAME}`], { timeoutMs: OP_WRITE_TIMEOUT_MS });
|
|
124
|
+
return value === "" ? null : value;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
this.warnOnce("retrieve", key, err);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Delete a credential. Idempotent — a missing item is success.
|
|
133
|
+
*
|
|
134
|
+
* @throws {InvalidCredentialKeyError} — deliberately NOT folded into the
|
|
135
|
+
* idempotent-success path. "Missing" and "impossible" are different
|
|
136
|
+
* answers: such a key was never storable here, so reporting success would
|
|
137
|
+
* assert something about a key this backend does not accept.
|
|
138
|
+
*/
|
|
139
|
+
delete(key) {
|
|
140
|
+
assertValidKey(key, "delete");
|
|
141
|
+
try {
|
|
142
|
+
this.deleteItem(key);
|
|
143
|
+
this.invalidateListCache();
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
this.warnOnce("delete", key, err);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Enumerate credential keys, optionally prefix-filtered.
|
|
153
|
+
*
|
|
154
|
+
* Per the {@link VaultBackend} contract this returns `[]` for a genuinely
|
|
155
|
+
* empty vault but **throws** on a transient failure, so a caller can tell
|
|
156
|
+
* "nothing stored" from "1Password did not answer" and retry the latter.
|
|
157
|
+
* The `--tags` filter keeps unrelated items in a shared vault out of the
|
|
158
|
+
* result; no secret values appear in `op item list` output.
|
|
159
|
+
*/
|
|
160
|
+
async listKeys(prefix) {
|
|
161
|
+
const cached = this.readListCache();
|
|
162
|
+
const keys = cached ?? this.fetchKeys();
|
|
163
|
+
if (cached === null) {
|
|
164
|
+
this.listCache = {
|
|
165
|
+
keys,
|
|
166
|
+
expiresAt: this.now() + OP_LIST_CACHE_TTL_MS,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return prefix === undefined ? [...keys] : keys.filter((k) => k.startsWith(prefix));
|
|
170
|
+
}
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// Private helpers
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
fetchKeys() {
|
|
175
|
+
let raw;
|
|
176
|
+
try {
|
|
177
|
+
raw = runOp(["item", "list", "--vault", this.vault, "--tags", this.tag, "--format=json"], { timeoutMs: OP_READ_TIMEOUT_MS });
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
// A missing/empty vault is an empty enumeration; anything else is
|
|
181
|
+
// transient and must surface so the caller can retry (contract, §7).
|
|
182
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
183
|
+
return [];
|
|
184
|
+
this.warnOnce("listKeys", null, err);
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
if (raw === "")
|
|
188
|
+
return [];
|
|
189
|
+
let parsed;
|
|
190
|
+
try {
|
|
191
|
+
parsed = JSON.parse(raw);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
this.warnOnce("listKeys", null, err);
|
|
195
|
+
throw new Error(`op item list returned unparseable JSON: ${String(err)}`);
|
|
196
|
+
}
|
|
197
|
+
if (!Array.isArray(parsed))
|
|
198
|
+
return [];
|
|
199
|
+
return parsed
|
|
200
|
+
.map((item) => typeof item === "object" && item !== null
|
|
201
|
+
? item.title
|
|
202
|
+
: undefined)
|
|
203
|
+
.filter((title) => typeof title === "string" && title.length > 0);
|
|
204
|
+
}
|
|
205
|
+
// Key validation lives in `assertValidKey` (vault-utils), not in a private
|
|
206
|
+
// `acceptKey` here.
|
|
207
|
+
//
|
|
208
|
+
// #167 had to enforce locally and quietly. This was the first backend where
|
|
209
|
+
// the `op://<vault>/<key>/password` reference made the invariant
|
|
210
|
+
// load-bearing — a key containing `/` stores fine (a 1Password title is just
|
|
211
|
+
// a string) and then re-parses on read into a different item and field — but
|
|
212
|
+
// the shared path enforced nothing, so a loud refusal here would have been
|
|
213
|
+
// this backend alone diverging from the others. The compromise was a
|
|
214
|
+
// warn-once that returned false/null.
|
|
215
|
+
//
|
|
216
|
+
// #168 removed the reason for it. `vault.ts` now rejects a non-conforming
|
|
217
|
+
// key before any backend is reached, and every backend asserts the same
|
|
218
|
+
// grammar with the same typed error. Keeping the warn-once as well would
|
|
219
|
+
// leave two layers enforcing one rule with two different outcomes — a throw
|
|
220
|
+
// from the cascade, a quiet `false`/`null` here — which is the divergence
|
|
221
|
+
// #168 exists to remove. The stderr warning is redundant too:
|
|
222
|
+
// `InvalidCredentialKeyError` names the key, the operation and the reason,
|
|
223
|
+
// and unlike a warning it cannot be missed.
|
|
224
|
+
deleteItem(key) {
|
|
225
|
+
try {
|
|
226
|
+
runOp(["item", "delete", key, "--vault", this.vault], {
|
|
227
|
+
timeoutMs: OP_WRITE_TIMEOUT_MS,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
// Idempotent: the item not being there is the desired end state.
|
|
232
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
233
|
+
return;
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
readListCache() {
|
|
238
|
+
if (this.listCache === null)
|
|
239
|
+
return null;
|
|
240
|
+
if (this.now() >= this.listCache.expiresAt) {
|
|
241
|
+
this.listCache = null;
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
return this.listCache.keys;
|
|
245
|
+
}
|
|
246
|
+
invalidateListCache() {
|
|
247
|
+
this.listCache = null;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Surface an unexpected `op` failure once per instance (ADR-004 §7, the
|
|
251
|
+
* libsecret lesson from #121).
|
|
252
|
+
*
|
|
253
|
+
* A not-found is normal and stays quiet. Anything else means we are
|
|
254
|
+
* authenticated but the call still failed — a misconfiguration the operator
|
|
255
|
+
* needs to see, since the non-throwing backend contract would otherwise
|
|
256
|
+
* swallow it entirely. Once per instance keeps a hot loop from flooding
|
|
257
|
+
* stderr while still making the first occurrence visible.
|
|
258
|
+
*/
|
|
259
|
+
warnOnce(op, key, err) {
|
|
260
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
261
|
+
return;
|
|
262
|
+
if (this.warned)
|
|
263
|
+
return;
|
|
264
|
+
this.warned = true;
|
|
265
|
+
const target = key === null ? `vault ${this.vault}` : `${this.vault}/${key}`;
|
|
266
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
267
|
+
process.stderr.write(`[secrets] WARNING: 1Password backend ${op} failed on ${target}: ${detail}\n` +
|
|
268
|
+
`[secrets] Further 1Password warnings from this instance are suppressed.\n`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=vault-onepassword.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-onepassword.js","sourceRoot":"","sources":["../../src/vault/vault-onepassword.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EACL,WAAW,EACX,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AAEpC,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF,6DAA6D;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;AAEzC,uDAAuD;AACvD,MAAM,UAAU,GAAG,UAAU,CAAC;AAE9B;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAe1C,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAG,WAAW,CAAC;IAEX,KAAK,CAAS;IACd,GAAG,CAAS;IACZ,GAAG,CAAe;IAE3B,SAAS,GAAiD,IAAI,CAAC;IAC/D,MAAM,GAAG,KAAK,CAAC;IAEvB;;;;;OAKG;IACH,YAAY,MAA+B;QACzC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,2EAA2E;gBACzE,yEAAyE,CAC5E,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,cAAc,CAAC;QAChD,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,CAAC,OAAgB;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3B,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAW,EAAE,KAAa;QAC9B,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAE7B,MAAM,IAAI,GAAG;YACX,KAAK,EAAE,GAAG;YACV,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE;YAC3B,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;YAChB,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;SACvD,CAAC;QAEF,IAAI,CAAC;YACH,wEAAwE;YACxE,qEAAqE;YACrE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACrB,KAAK,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE;gBAC9C,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC3B,SAAS,EAAE,mBAAmB;aAC/B,CAAC,CAAC;YACH,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,GAAW;QAClB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAE5B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,KAAK,CACjB,CAAC,MAAM,EAAE,QAAQ,IAAI,CAAC,KAAK,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC,EACnD,EAAE,SAAS,EAAE,mBAAmB,EAAE,CACnC,CAAC;YACF,OAAO,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,GAAW;QAChB,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAE9B,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACrB,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAe;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACxC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,SAAS,GAAG;gBACf,IAAI;gBACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,oBAAoB;aAC7C,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,8EAA8E;IAC9E,kBAAkB;IAClB,8EAA8E;IAEtE,SAAS;QACf,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,KAAK,CACT,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAC5E,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAClC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,kEAAkE;YAClE,qEAAqE;YACrE,IAAI,GAAG,YAAY,UAAU,IAAI,GAAG,CAAC,UAAU;gBAAE,OAAO,EAAE,CAAC;YAC3D,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACrC,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,IAAI,GAAG,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC1B,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,2CAA2C,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QAEtC,OAAO,MAAM;aACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;YACvC,CAAC,CAAE,IAA4B,CAAC,KAAK;YACrC,CAAC,CAAC,SAAS,CACd;aACA,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvF,CAAC;IAED,2EAA2E;IAC3E,oBAAoB;IACpB,EAAE;IACF,4EAA4E;IAC5E,iEAAiE;IACjE,6EAA6E;IAC7E,6EAA6E;IAC7E,2EAA2E;IAC3E,qEAAqE;IACrE,sCAAsC;IACtC,EAAE;IACF,0EAA0E;IAC1E,wEAAwE;IACxE,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,8DAA8D;IAC9D,2EAA2E;IAC3E,4CAA4C;IAEpC,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,iEAAiE;YACjE,IAAI,GAAG,YAAY,UAAU,IAAI,GAAG,CAAC,UAAU;gBAAE,OAAO;YACxD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACzC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC;YAC3C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;IAEO,mBAAmB;QACzB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAED;;;;;;;;;OASG;IACK,QAAQ,CAAC,EAAU,EAAE,GAAkB,EAAE,GAAY;QAC3D,IAAI,GAAG,YAAY,UAAU,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO;QACxD,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC;QAC7E,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wCAAwC,EAAE,cAAc,MAAM,KAAK,MAAM,IAAI;YAC3E,2EAA2E,CAC9E,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
* Auth Vault — Shared Utilities
|
|
3
3
|
*
|
|
4
4
|
* Shared validation and helper functions used across all vault backends.
|
|
5
|
+
*
|
|
6
|
+
* The key grammar is a **package-wide invariant**, not a per-backend
|
|
7
|
+
* preference: `vault.ts` enforces it once at the cascade before any backend
|
|
8
|
+
* is dispatched to (#168), and each backend re-asserts it so that a directly
|
|
9
|
+
* constructed backend cannot bypass the rule. Several backends place keys in
|
|
10
|
+
* subprocess argv or an `op://` reference path; the grammar keeps that shared
|
|
11
|
+
* namespace portable and is defense in depth, never the subprocess injection
|
|
12
|
+
* boundary.
|
|
5
13
|
*/
|
|
6
14
|
/**
|
|
7
15
|
* Returns true if the given credential key name is valid.
|
|
@@ -14,9 +22,35 @@
|
|
|
14
22
|
* Hyphens and dots are both permitted as namespace separators so callers
|
|
15
23
|
* can choose whichever convention reads best (`soma-anthropic-token1`,
|
|
16
24
|
* `soma.anthropic.token1`). Underscores, uppercase, whitespace, and
|
|
17
|
-
* shell metacharacters are deliberately rejected
|
|
18
|
-
*
|
|
19
|
-
*
|
|
25
|
+
* shell metacharacters are deliberately rejected to keep the cross-backend
|
|
26
|
+
* namespace portable. Subprocess callers must still use structural argv/stdin
|
|
27
|
+
* separation rather than treating this grammar as an injection boundary.
|
|
20
28
|
*/
|
|
21
29
|
export declare function isValidKey(key: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Returns true if `prefix` could be the leading substring of a valid key.
|
|
32
|
+
*
|
|
33
|
+
* The empty string is accepted: `listCredentials("")` selects everything,
|
|
34
|
+
* exactly as omitting the argument does.
|
|
35
|
+
*
|
|
36
|
+
* The length bound is one shorter for a prefix ending in a separator. A key
|
|
37
|
+
* must end alphanumeric and is capped at 64, so a 64-character prefix ending
|
|
38
|
+
* in `.` or `-` could only ever be extended into a 65-character key — no valid
|
|
39
|
+
* key starts with it. Accepting it would dispatch an unsatisfiable filter to
|
|
40
|
+
* the backend and return an empty list, which reads as "no such credentials"
|
|
41
|
+
* rather than the `InvalidCredentialKeyError` every other malformed prefix
|
|
42
|
+
* raises — the same not-found/malformed collapse this module exists to end.
|
|
43
|
+
*/
|
|
44
|
+
export declare function isValidKeyPrefix(prefix: string): boolean;
|
|
45
|
+
/** Operations a key can be rejected for — mirrors `SecretsOperation["type"]`. */
|
|
46
|
+
export type KeyOperation = "read" | "write" | "delete" | "enumerate";
|
|
47
|
+
/**
|
|
48
|
+
* Throw `InvalidCredentialKeyError` unless `key` matches the key grammar.
|
|
49
|
+
*
|
|
50
|
+
* Backends call this instead of returning `false`/`null`, so a malformed key
|
|
51
|
+
* is never mistaken for a failed write or an absent credential (#168, P2).
|
|
52
|
+
*/
|
|
53
|
+
export declare function assertValidKey(key: string, operation: KeyOperation): void;
|
|
54
|
+
/** Throw `InvalidCredentialKeyError` unless `prefix` is a valid key prefix. */
|
|
55
|
+
export declare function assertValidKeyPrefix(prefix: string, operation: KeyOperation): void;
|
|
22
56
|
//# sourceMappingURL=vault-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-utils.d.ts","sourceRoot":"","sources":["../../src/vault/vault-utils.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vault-utils.d.ts","sourceRoot":"","sources":["../../src/vault/vault-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAcH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAgBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAMxD;AAED,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,GAAG,IAAI,CAGzE;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,GAAG,IAAI,CAGlF"}
|
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
* Auth Vault — Shared Utilities
|
|
3
3
|
*
|
|
4
4
|
* Shared validation and helper functions used across all vault backends.
|
|
5
|
+
*
|
|
6
|
+
* The key grammar is a **package-wide invariant**, not a per-backend
|
|
7
|
+
* preference: `vault.ts` enforces it once at the cascade before any backend
|
|
8
|
+
* is dispatched to (#168), and each backend re-asserts it so that a directly
|
|
9
|
+
* constructed backend cannot bypass the rule. Several backends place keys in
|
|
10
|
+
* subprocess argv or an `op://` reference path; the grammar keeps that shared
|
|
11
|
+
* namespace portable and is defense in depth, never the subprocess injection
|
|
12
|
+
* boundary.
|
|
5
13
|
*/
|
|
14
|
+
import { InvalidCredentialKeyError } from "./session-vault-errors.js";
|
|
6
15
|
/**
|
|
7
16
|
* Allowed key name pattern — lowercase alphanumeric plus hyphen and dot,
|
|
8
17
|
* 2-64 characters. The inner class permits hyphens and dots so that
|
|
@@ -23,11 +32,64 @@ const VALID_KEY_RE = /^[a-z0-9][a-z0-9.-]*[a-z0-9]$/;
|
|
|
23
32
|
* Hyphens and dots are both permitted as namespace separators so callers
|
|
24
33
|
* can choose whichever convention reads best (`soma-anthropic-token1`,
|
|
25
34
|
* `soma.anthropic.token1`). Underscores, uppercase, whitespace, and
|
|
26
|
-
* shell metacharacters are deliberately rejected
|
|
27
|
-
*
|
|
28
|
-
*
|
|
35
|
+
* shell metacharacters are deliberately rejected to keep the cross-backend
|
|
36
|
+
* namespace portable. Subprocess callers must still use structural argv/stdin
|
|
37
|
+
* separation rather than treating this grammar as an injection boundary.
|
|
29
38
|
*/
|
|
30
39
|
export function isValidKey(key) {
|
|
31
40
|
return VALID_KEY_RE.test(key) && key.length <= 64;
|
|
32
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Allowed key-*prefix* pattern — every proper prefix of a valid key, plus the
|
|
44
|
+
* valid keys themselves.
|
|
45
|
+
*
|
|
46
|
+
* `listCredentials(prefix)` filters by string prefix, so the natural way to
|
|
47
|
+
* scope an enumeration to a namespace is to pass the separator too:
|
|
48
|
+
* `listCredentials("soma.anthropic.")`. That is not a valid *key* (it ends in
|
|
49
|
+
* a separator), so validating a prefix with `isValidKey` would reject the
|
|
50
|
+
* documented usage. A prefix therefore relaxes exactly one rule — the trailing
|
|
51
|
+
* character may be a separator — and keeps the rest, since a prefix reaches
|
|
52
|
+
* the same subprocess argv / `op://` positions a key does.
|
|
53
|
+
*/
|
|
54
|
+
const VALID_KEY_PREFIX_RE = /^[a-z0-9][a-z0-9.-]*$/;
|
|
55
|
+
/**
|
|
56
|
+
* Returns true if `prefix` could be the leading substring of a valid key.
|
|
57
|
+
*
|
|
58
|
+
* The empty string is accepted: `listCredentials("")` selects everything,
|
|
59
|
+
* exactly as omitting the argument does.
|
|
60
|
+
*
|
|
61
|
+
* The length bound is one shorter for a prefix ending in a separator. A key
|
|
62
|
+
* must end alphanumeric and is capped at 64, so a 64-character prefix ending
|
|
63
|
+
* in `.` or `-` could only ever be extended into a 65-character key — no valid
|
|
64
|
+
* key starts with it. Accepting it would dispatch an unsatisfiable filter to
|
|
65
|
+
* the backend and return an empty list, which reads as "no such credentials"
|
|
66
|
+
* rather than the `InvalidCredentialKeyError` every other malformed prefix
|
|
67
|
+
* raises — the same not-found/malformed collapse this module exists to end.
|
|
68
|
+
*/
|
|
69
|
+
export function isValidKeyPrefix(prefix) {
|
|
70
|
+
if (prefix === "")
|
|
71
|
+
return true;
|
|
72
|
+
if (!VALID_KEY_PREFIX_RE.test(prefix))
|
|
73
|
+
return false;
|
|
74
|
+
// A trailing separator needs at least one more alphanumeric to become a key.
|
|
75
|
+
const max = /[.-]$/.test(prefix) ? 63 : 64;
|
|
76
|
+
return prefix.length <= max;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Throw `InvalidCredentialKeyError` unless `key` matches the key grammar.
|
|
80
|
+
*
|
|
81
|
+
* Backends call this instead of returning `false`/`null`, so a malformed key
|
|
82
|
+
* is never mistaken for a failed write or an absent credential (#168, P2).
|
|
83
|
+
*/
|
|
84
|
+
export function assertValidKey(key, operation) {
|
|
85
|
+
if (isValidKey(key))
|
|
86
|
+
return;
|
|
87
|
+
throw new InvalidCredentialKeyError(key, operation, "key");
|
|
88
|
+
}
|
|
89
|
+
/** Throw `InvalidCredentialKeyError` unless `prefix` is a valid key prefix. */
|
|
90
|
+
export function assertValidKeyPrefix(prefix, operation) {
|
|
91
|
+
if (isValidKeyPrefix(prefix))
|
|
92
|
+
return;
|
|
93
|
+
throw new InvalidCredentialKeyError(prefix, operation, "prefix");
|
|
94
|
+
}
|
|
33
95
|
//# sourceMappingURL=vault-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-utils.js","sourceRoot":"","sources":["../../src/vault/vault-utils.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vault-utils.js","sourceRoot":"","sources":["../../src/vault/vault-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE;;;;;;;GAOG;AACH,MAAM,YAAY,GAAG,+BAA+B,CAAC;AAErD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,6EAA6E;IAC7E,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3C,OAAO,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC;AAC9B,CAAC;AAKD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,SAAuB;IACjE,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO;IAC5B,MAAM,IAAI,yBAAyB,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,SAAuB;IAC1E,IAAI,gBAAgB,CAAC,MAAM,CAAC;QAAE,OAAO;IACrC,MAAM,IAAI,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
* Storage: credentials are stored in the Windows Credential Manager under the
|
|
13
13
|
* resource name "centient", with the logical key as the username.
|
|
14
14
|
*
|
|
15
|
-
* Error handling:
|
|
15
|
+
* Error handling: a storage failure returns false/null — this backend never
|
|
16
|
+
* throws for that. A credential key that violates the shared key grammar is
|
|
17
|
+
* NOT a storage failure and throws `InvalidCredentialKeyError` (#168): such a
|
|
18
|
+
* key is unaddressable here, and reporting it as a failed write or an absent
|
|
19
|
+
* credential would be indistinguishable from the ordinary miss.
|
|
16
20
|
*/
|
|
17
21
|
import type { VaultBackend } from "./types.js";
|
|
18
22
|
/**
|
|
@@ -29,6 +33,7 @@ import type { VaultBackend } from "./types.js";
|
|
|
29
33
|
* }
|
|
30
34
|
*/
|
|
31
35
|
export declare class WindowsVault implements VaultBackend {
|
|
36
|
+
readonly name = "windows";
|
|
32
37
|
/**
|
|
33
38
|
* Returns true if running inside a WSL environment where powershell.exe is
|
|
34
39
|
* accessible.
|
|
@@ -49,7 +54,7 @@ export declare class WindowsVault implements VaultBackend {
|
|
|
49
54
|
* as the username and `value` as the password.
|
|
50
55
|
*
|
|
51
56
|
* @param key - Logical key name (e.g. 'auth-token', 'refresh-token')
|
|
52
|
-
* @param value - The credential value to store
|
|
57
|
+
* @param value - The credential value to store (UTF-8/base64 over stdin)
|
|
53
58
|
* @returns true on success, false if storage fails
|
|
54
59
|
*/
|
|
55
60
|
store(key: string, value: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-windows.d.ts","sourceRoot":"","sources":["../../src/vault/vault-windows.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vault-windows.d.ts","sourceRoot":"","sources":["../../src/vault/vault-windows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgD/C;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAa,YAAW,YAAY;IAC/C,QAAQ,CAAC,IAAI,aAAa;IAE1B;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,IAAI,OAAO;IAoBxB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAWpC;;;;;;;;;OASG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAqB1C;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAoBpC;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAsB5B;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CA2BnD"}
|
|
@@ -12,11 +12,15 @@
|
|
|
12
12
|
* Storage: credentials are stored in the Windows Credential Manager under the
|
|
13
13
|
* resource name "centient", with the logical key as the username.
|
|
14
14
|
*
|
|
15
|
-
* Error handling:
|
|
15
|
+
* Error handling: a storage failure returns false/null — this backend never
|
|
16
|
+
* throws for that. A credential key that violates the shared key grammar is
|
|
17
|
+
* NOT a storage failure and throws `InvalidCredentialKeyError` (#168): such a
|
|
18
|
+
* key is unaddressable here, and reporting it as a failed write or an absent
|
|
19
|
+
* credential would be indistinguishable from the ordinary miss.
|
|
16
20
|
*/
|
|
17
21
|
import { spawnSync } from "child_process";
|
|
18
22
|
import { readFileSync } from "fs";
|
|
19
|
-
import {
|
|
23
|
+
import { assertValidKey } from "./vault-utils.js";
|
|
20
24
|
// =============================================================================
|
|
21
25
|
// Constants
|
|
22
26
|
// =============================================================================
|
|
@@ -36,12 +40,13 @@ function escapePsValue(value) {
|
|
|
36
40
|
* Executes a PowerShell command via powershell.exe with no profile and
|
|
37
41
|
* non-interactive mode. Returns stdout trimmed, or null on error.
|
|
38
42
|
*
|
|
39
|
-
* Uses spawnSync to bypass the shell
|
|
40
|
-
*
|
|
43
|
+
* Uses spawnSync to bypass the OS shell. Credential values are UTF-8/base64
|
|
44
|
+
* encoded on stdin and decoded by a fixed PowerShell fragment, so arbitrary
|
|
45
|
+
* value bytes never enter the interpreter command string.
|
|
41
46
|
*/
|
|
42
|
-
function runPowershell(command) {
|
|
47
|
+
function runPowershell(command, input) {
|
|
43
48
|
try {
|
|
44
|
-
const result = spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], { encoding: "utf8" });
|
|
49
|
+
const result = spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], { encoding: "utf8", input });
|
|
45
50
|
if (result.error || result.status !== 0)
|
|
46
51
|
return null;
|
|
47
52
|
return result.stdout.trim();
|
|
@@ -67,6 +72,7 @@ function runPowershell(command) {
|
|
|
67
72
|
* }
|
|
68
73
|
*/
|
|
69
74
|
export class WindowsVault {
|
|
75
|
+
name = "windows";
|
|
70
76
|
/**
|
|
71
77
|
* Returns true if running inside a WSL environment where powershell.exe is
|
|
72
78
|
* accessible.
|
|
@@ -114,23 +120,24 @@ export class WindowsVault {
|
|
|
114
120
|
* as the username and `value` as the password.
|
|
115
121
|
*
|
|
116
122
|
* @param key - Logical key name (e.g. 'auth-token', 'refresh-token')
|
|
117
|
-
* @param value - The credential value to store
|
|
123
|
+
* @param value - The credential value to store (UTF-8/base64 over stdin)
|
|
118
124
|
* @returns true on success, false if storage fails
|
|
119
125
|
*/
|
|
120
126
|
store(key, value) {
|
|
121
|
-
|
|
122
|
-
return false;
|
|
127
|
+
assertValidKey(key, "write");
|
|
123
128
|
const escapedKey = escapePsValue(key);
|
|
124
|
-
const
|
|
129
|
+
const encodedValue = Buffer.from(value, "utf8").toString("base64");
|
|
125
130
|
const command = [
|
|
126
131
|
"& {",
|
|
127
132
|
"[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime];",
|
|
133
|
+
"$passwordBytes = [System.Convert]::FromBase64String([Console]::In.ReadToEnd());",
|
|
134
|
+
"$password = [System.Text.Encoding]::UTF8.GetString($passwordBytes);",
|
|
128
135
|
"$vault = New-Object Windows.Security.Credentials.PasswordVault;",
|
|
129
|
-
`$cred = New-Object Windows.Security.Credentials.PasswordCredential('${escapePsValue(RESOURCE_NAME)}', '${escapedKey}',
|
|
136
|
+
`$cred = New-Object Windows.Security.Credentials.PasswordCredential('${escapePsValue(RESOURCE_NAME)}', '${escapedKey}', $password);`,
|
|
130
137
|
"$vault.Add($cred)",
|
|
131
138
|
"}",
|
|
132
139
|
].join(" ");
|
|
133
|
-
const result = runPowershell(command);
|
|
140
|
+
const result = runPowershell(command, encodedValue);
|
|
134
141
|
return result !== null;
|
|
135
142
|
}
|
|
136
143
|
/**
|
|
@@ -140,8 +147,7 @@ export class WindowsVault {
|
|
|
140
147
|
* @returns The stored password, or null if not found / retrieval fails
|
|
141
148
|
*/
|
|
142
149
|
retrieve(key) {
|
|
143
|
-
|
|
144
|
-
return null;
|
|
150
|
+
assertValidKey(key, "read");
|
|
145
151
|
const escapedKey = escapePsValue(key);
|
|
146
152
|
const command = [
|
|
147
153
|
"& {",
|
|
@@ -164,8 +170,7 @@ export class WindowsVault {
|
|
|
164
170
|
* @returns true on success (including "not found"), false on unexpected error
|
|
165
171
|
*/
|
|
166
172
|
delete(key) {
|
|
167
|
-
|
|
168
|
-
return false;
|
|
173
|
+
assertValidKey(key, "delete");
|
|
169
174
|
const escapedKey = escapePsValue(key);
|
|
170
175
|
const command = [
|
|
171
176
|
"& {",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-windows.js","sourceRoot":"","sources":["../../src/vault/vault-windows.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vault-windows.js","sourceRoot":"","sources":["../../src/vault/vault-windows.ts"],"names":[],"mappings":"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"}
|