@centient/secrets 0.8.0 → 0.10.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 +70 -2
- 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/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 +21 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -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/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/vault/policy.d.ts +35 -4
- package/dist/vault/policy.d.ts.map +1 -1
- package/dist/vault/policy.js +65 -6
- 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 +102 -2
- package/dist/vault/session-vault.d.ts.map +1 -1
- package/dist/vault/session-vault.js +236 -38
- package/dist/vault/session-vault.js.map +1 -1
- package/dist/vault/types.d.ts +42 -1
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/dist/vault/vault-env.d.ts +14 -0
- package/dist/vault/vault-env.d.ts.map +1 -1
- package/dist/vault/vault-env.js +15 -0
- package/dist/vault/vault-env.js.map +1 -1
- package/dist/vault/vault-gpg.d.ts +5 -1
- package/dist/vault/vault-gpg.d.ts.map +1 -1
- package/dist/vault/vault-gpg.js +9 -8
- package/dist/vault/vault-gpg.js.map +1 -1
- package/dist/vault/vault-libsecret.d.ts +24 -2
- package/dist/vault/vault-libsecret.d.ts.map +1 -1
- package/dist/vault/vault-libsecret.js +54 -10
- package/dist/vault/vault-libsecret.js.map +1 -1
- package/dist/vault/vault-onepassword.d.ts +118 -0
- package/dist/vault/vault-onepassword.d.ts.map +1 -0
- package/dist/vault/vault-onepassword.js +270 -0
- package/dist/vault/vault-onepassword.js.map +1 -0
- package/dist/vault/vault-utils.d.ts +33 -0
- package/dist/vault/vault-utils.d.ts.map +1 -1
- package/dist/vault/vault-utils.js +61 -0
- package/dist/vault/vault-utils.js.map +1 -1
- package/dist/vault/vault-windows.d.ts +5 -1
- package/dist/vault/vault-windows.d.ts.map +1 -1
- package/dist/vault/vault-windows.js +9 -8
- package/dist/vault/vault-windows.js.map +1 -1
- package/dist/vault/vault.d.ts +116 -4
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +288 -22
- package/dist/vault/vault.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,270 @@
|
|
|
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
|
+
vault;
|
|
46
|
+
tag;
|
|
47
|
+
now;
|
|
48
|
+
listCache = null;
|
|
49
|
+
warned = false;
|
|
50
|
+
/**
|
|
51
|
+
* @param config - must carry a `vault`; there is deliberately no default
|
|
52
|
+
* (ADR-004 §1 — guessing a vault name risks writing credentials somewhere
|
|
53
|
+
* the operator did not intend).
|
|
54
|
+
* @throws {Error} when `vault` is missing or blank.
|
|
55
|
+
*/
|
|
56
|
+
constructor(config) {
|
|
57
|
+
const vault = config.vault?.trim();
|
|
58
|
+
if (!vault) {
|
|
59
|
+
throw new Error('secrets.onePasswordBackend.vault is required when backend is "1password" ' +
|
|
60
|
+
"(or set CENTIENT_OP_VAULT). There is no default vault — see ADR-004 §1.");
|
|
61
|
+
}
|
|
62
|
+
this.vault = vault;
|
|
63
|
+
this.tag = config.tag?.trim() || DEFAULT_OP_TAG;
|
|
64
|
+
this.now = config.now ?? Date.now;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Whether this backend may be used.
|
|
68
|
+
*
|
|
69
|
+
* Requires **both** an explicit opt-in and a usable `op`. The opt-in half is
|
|
70
|
+
* what keeps 1Password out of the auto-cascade: without it this returns false
|
|
71
|
+
* even on a machine where `op` is installed and signed in.
|
|
72
|
+
*
|
|
73
|
+
* @param optedIn - true when config or env explicitly selected this backend.
|
|
74
|
+
*/
|
|
75
|
+
static detect(optedIn) {
|
|
76
|
+
if (!optedIn)
|
|
77
|
+
return false;
|
|
78
|
+
return detectOpCli();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Write a credential. The value travels on stdin and never appears in argv.
|
|
82
|
+
*
|
|
83
|
+
* Update is modeled as replace (delete-then-create) so create and update share
|
|
84
|
+
* one argv-safe path rather than needing a separate `op item edit` form.
|
|
85
|
+
*/
|
|
86
|
+
store(key, value) {
|
|
87
|
+
assertValidKey(key, "write");
|
|
88
|
+
const item = {
|
|
89
|
+
title: key,
|
|
90
|
+
category: "PASSWORD",
|
|
91
|
+
vault: { name: this.vault },
|
|
92
|
+
tags: [this.tag],
|
|
93
|
+
fields: [{ id: FIELD_NAME, type: "CONCEALED", value }],
|
|
94
|
+
};
|
|
95
|
+
try {
|
|
96
|
+
// Replace semantics: drop any existing item first so `create` is always
|
|
97
|
+
// the write path. delete() is idempotent, so a missing item is fine.
|
|
98
|
+
this.deleteItem(key);
|
|
99
|
+
runOp(["item", "create", "--format=json", "-"], {
|
|
100
|
+
input: JSON.stringify(item),
|
|
101
|
+
timeoutMs: OP_WRITE_TIMEOUT_MS,
|
|
102
|
+
});
|
|
103
|
+
this.invalidateListCache();
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
this.warnOnce("store", key, err);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Read a credential value. Returns null when absent or on failure.
|
|
113
|
+
*
|
|
114
|
+
* @throws {InvalidCredentialKeyError} for a key this backend could never
|
|
115
|
+
* have written — building an `op://` reference from it is exactly the
|
|
116
|
+
* misparse the key grammar exists to prevent, and `null` would report the
|
|
117
|
+
* impossible key as a merely absent one.
|
|
118
|
+
*/
|
|
119
|
+
retrieve(key) {
|
|
120
|
+
assertValidKey(key, "read");
|
|
121
|
+
try {
|
|
122
|
+
const value = runOp(["read", `op://${this.vault}/${key}/${FIELD_NAME}`], { timeoutMs: OP_WRITE_TIMEOUT_MS });
|
|
123
|
+
return value === "" ? null : value;
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
this.warnOnce("retrieve", key, err);
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Delete a credential. Idempotent — a missing item is success.
|
|
132
|
+
*
|
|
133
|
+
* @throws {InvalidCredentialKeyError} — deliberately NOT folded into the
|
|
134
|
+
* idempotent-success path. "Missing" and "impossible" are different
|
|
135
|
+
* answers: such a key was never storable here, so reporting success would
|
|
136
|
+
* assert something about a key this backend does not accept.
|
|
137
|
+
*/
|
|
138
|
+
delete(key) {
|
|
139
|
+
assertValidKey(key, "delete");
|
|
140
|
+
try {
|
|
141
|
+
this.deleteItem(key);
|
|
142
|
+
this.invalidateListCache();
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
this.warnOnce("delete", key, err);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Enumerate credential keys, optionally prefix-filtered.
|
|
152
|
+
*
|
|
153
|
+
* Per the {@link VaultBackend} contract this returns `[]` for a genuinely
|
|
154
|
+
* empty vault but **throws** on a transient failure, so a caller can tell
|
|
155
|
+
* "nothing stored" from "1Password did not answer" and retry the latter.
|
|
156
|
+
* The `--tags` filter keeps unrelated items in a shared vault out of the
|
|
157
|
+
* result; no secret values appear in `op item list` output.
|
|
158
|
+
*/
|
|
159
|
+
async listKeys(prefix) {
|
|
160
|
+
const cached = this.readListCache();
|
|
161
|
+
const keys = cached ?? this.fetchKeys();
|
|
162
|
+
if (cached === null) {
|
|
163
|
+
this.listCache = {
|
|
164
|
+
keys,
|
|
165
|
+
expiresAt: this.now() + OP_LIST_CACHE_TTL_MS,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return prefix === undefined ? [...keys] : keys.filter((k) => k.startsWith(prefix));
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Private helpers
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
fetchKeys() {
|
|
174
|
+
let raw;
|
|
175
|
+
try {
|
|
176
|
+
raw = runOp(["item", "list", "--vault", this.vault, "--tags", this.tag, "--format=json"], { timeoutMs: OP_READ_TIMEOUT_MS });
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
// A missing/empty vault is an empty enumeration; anything else is
|
|
180
|
+
// transient and must surface so the caller can retry (contract, §7).
|
|
181
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
182
|
+
return [];
|
|
183
|
+
this.warnOnce("listKeys", null, err);
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
if (raw === "")
|
|
187
|
+
return [];
|
|
188
|
+
let parsed;
|
|
189
|
+
try {
|
|
190
|
+
parsed = JSON.parse(raw);
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
this.warnOnce("listKeys", null, err);
|
|
194
|
+
throw new Error(`op item list returned unparseable JSON: ${String(err)}`);
|
|
195
|
+
}
|
|
196
|
+
if (!Array.isArray(parsed))
|
|
197
|
+
return [];
|
|
198
|
+
return parsed
|
|
199
|
+
.map((item) => typeof item === "object" && item !== null
|
|
200
|
+
? item.title
|
|
201
|
+
: undefined)
|
|
202
|
+
.filter((title) => typeof title === "string" && title.length > 0);
|
|
203
|
+
}
|
|
204
|
+
// Key validation lives in `assertValidKey` (vault-utils), not in a private
|
|
205
|
+
// `acceptKey` here.
|
|
206
|
+
//
|
|
207
|
+
// #167 had to enforce locally and quietly. This was the first backend where
|
|
208
|
+
// the `op://<vault>/<key>/password` reference made the invariant
|
|
209
|
+
// load-bearing — a key containing `/` stores fine (a 1Password title is just
|
|
210
|
+
// a string) and then re-parses on read into a different item and field — but
|
|
211
|
+
// the shared path enforced nothing, so a loud refusal here would have been
|
|
212
|
+
// this backend alone diverging from the others. The compromise was a
|
|
213
|
+
// warn-once that returned false/null.
|
|
214
|
+
//
|
|
215
|
+
// #168 removed the reason for it. `vault.ts` now rejects a non-conforming
|
|
216
|
+
// key before any backend is reached, and every backend asserts the same
|
|
217
|
+
// grammar with the same typed error. Keeping the warn-once as well would
|
|
218
|
+
// leave two layers enforcing one rule with two different outcomes — a throw
|
|
219
|
+
// from the cascade, a quiet `false`/`null` here — which is the divergence
|
|
220
|
+
// #168 exists to remove. The stderr warning is redundant too:
|
|
221
|
+
// `InvalidCredentialKeyError` names the key, the operation and the reason,
|
|
222
|
+
// and unlike a warning it cannot be missed.
|
|
223
|
+
deleteItem(key) {
|
|
224
|
+
try {
|
|
225
|
+
runOp(["item", "delete", key, "--vault", this.vault], {
|
|
226
|
+
timeoutMs: OP_WRITE_TIMEOUT_MS,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
// Idempotent: the item not being there is the desired end state.
|
|
231
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
232
|
+
return;
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
readListCache() {
|
|
237
|
+
if (this.listCache === null)
|
|
238
|
+
return null;
|
|
239
|
+
if (this.now() >= this.listCache.expiresAt) {
|
|
240
|
+
this.listCache = null;
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return this.listCache.keys;
|
|
244
|
+
}
|
|
245
|
+
invalidateListCache() {
|
|
246
|
+
this.listCache = null;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Surface an unexpected `op` failure once per instance (ADR-004 §7, the
|
|
250
|
+
* libsecret lesson from #121).
|
|
251
|
+
*
|
|
252
|
+
* A not-found is normal and stays quiet. Anything else means we are
|
|
253
|
+
* authenticated but the call still failed — a misconfiguration the operator
|
|
254
|
+
* needs to see, since the non-throwing backend contract would otherwise
|
|
255
|
+
* swallow it entirely. Once per instance keeps a hot loop from flooding
|
|
256
|
+
* stderr while still making the first occurrence visible.
|
|
257
|
+
*/
|
|
258
|
+
warnOnce(op, key, err) {
|
|
259
|
+
if (err instanceof OpCliError && err.isNotFound)
|
|
260
|
+
return;
|
|
261
|
+
if (this.warned)
|
|
262
|
+
return;
|
|
263
|
+
this.warned = true;
|
|
264
|
+
const target = key === null ? `vault ${this.vault}` : `${this.vault}/${key}`;
|
|
265
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
266
|
+
process.stderr.write(`[secrets] WARNING: 1Password backend ${op} failed on ${target}: ${detail}\n` +
|
|
267
|
+
`[secrets] Further 1Password warnings from this instance are suppressed.\n`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=vault-onepassword.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-onepassword.js","sourceRoot":"","sources":["../../src/vault/vault-onepassword.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,13 @@
|
|
|
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 of them interpolate
|
|
10
|
+
* the key into a shell string or an `op://` reference path, where the grammar
|
|
11
|
+
* is also the injection guard.
|
|
5
12
|
*/
|
|
6
13
|
/**
|
|
7
14
|
* Returns true if the given credential key name is valid.
|
|
@@ -19,4 +26,30 @@
|
|
|
19
26
|
* escaping.
|
|
20
27
|
*/
|
|
21
28
|
export declare function isValidKey(key: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Returns true if `prefix` could be the leading substring of a valid key.
|
|
31
|
+
*
|
|
32
|
+
* The empty string is accepted: `listCredentials("")` selects everything,
|
|
33
|
+
* exactly as omitting the argument does.
|
|
34
|
+
*
|
|
35
|
+
* The length bound is one shorter for a prefix ending in a separator. A key
|
|
36
|
+
* must end alphanumeric and is capped at 64, so a 64-character prefix ending
|
|
37
|
+
* in `.` or `-` could only ever be extended into a 65-character key — no valid
|
|
38
|
+
* key starts with it. Accepting it would dispatch an unsatisfiable filter to
|
|
39
|
+
* the backend and return an empty list, which reads as "no such credentials"
|
|
40
|
+
* rather than the `InvalidCredentialKeyError` every other malformed prefix
|
|
41
|
+
* raises — the same not-found/malformed collapse this module exists to end.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isValidKeyPrefix(prefix: string): boolean;
|
|
44
|
+
/** Operations a key can be rejected for — mirrors `SecretsOperation["type"]`. */
|
|
45
|
+
export type KeyOperation = "read" | "write" | "delete" | "enumerate";
|
|
46
|
+
/**
|
|
47
|
+
* Throw `InvalidCredentialKeyError` unless `key` matches the key grammar.
|
|
48
|
+
*
|
|
49
|
+
* Backends call this instead of returning `false`/`null`, so a malformed key
|
|
50
|
+
* is never mistaken for a failed write or an absent credential (#168, P2).
|
|
51
|
+
*/
|
|
52
|
+
export declare function assertValidKey(key: string, operation: KeyOperation): void;
|
|
53
|
+
/** Throw `InvalidCredentialKeyError` unless `prefix` is a valid key prefix. */
|
|
54
|
+
export declare function assertValidKeyPrefix(prefix: string, operation: KeyOperation): void;
|
|
22
55
|
//# 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;;;;;;;;;;;GAWG;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,15 @@
|
|
|
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 of them interpolate
|
|
10
|
+
* the key into a shell string or an `op://` reference path, where the grammar
|
|
11
|
+
* is also the injection guard.
|
|
5
12
|
*/
|
|
13
|
+
import { InvalidCredentialKeyError } from "./session-vault-errors.js";
|
|
6
14
|
/**
|
|
7
15
|
* Allowed key name pattern — lowercase alphanumeric plus hyphen and dot,
|
|
8
16
|
* 2-64 characters. The inner class permits hyphens and dots so that
|
|
@@ -30,4 +38,57 @@ const VALID_KEY_RE = /^[a-z0-9][a-z0-9.-]*[a-z0-9]$/;
|
|
|
30
38
|
export function isValidKey(key) {
|
|
31
39
|
return VALID_KEY_RE.test(key) && key.length <= 64;
|
|
32
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Allowed key-*prefix* pattern — every proper prefix of a valid key, plus the
|
|
43
|
+
* valid keys themselves.
|
|
44
|
+
*
|
|
45
|
+
* `listCredentials(prefix)` filters by string prefix, so the natural way to
|
|
46
|
+
* scope an enumeration to a namespace is to pass the separator too:
|
|
47
|
+
* `listCredentials("soma.anthropic.")`. That is not a valid *key* (it ends in
|
|
48
|
+
* a separator), so validating a prefix with `isValidKey` would reject the
|
|
49
|
+
* documented usage. A prefix therefore relaxes exactly one rule — the trailing
|
|
50
|
+
* character may be a separator — and keeps the rest, since a prefix reaches
|
|
51
|
+
* the same argv / shell / `op://` positions a key does.
|
|
52
|
+
*/
|
|
53
|
+
const VALID_KEY_PREFIX_RE = /^[a-z0-9][a-z0-9.-]*$/;
|
|
54
|
+
/**
|
|
55
|
+
* Returns true if `prefix` could be the leading substring of a valid key.
|
|
56
|
+
*
|
|
57
|
+
* The empty string is accepted: `listCredentials("")` selects everything,
|
|
58
|
+
* exactly as omitting the argument does.
|
|
59
|
+
*
|
|
60
|
+
* The length bound is one shorter for a prefix ending in a separator. A key
|
|
61
|
+
* must end alphanumeric and is capped at 64, so a 64-character prefix ending
|
|
62
|
+
* in `.` or `-` could only ever be extended into a 65-character key — no valid
|
|
63
|
+
* key starts with it. Accepting it would dispatch an unsatisfiable filter to
|
|
64
|
+
* the backend and return an empty list, which reads as "no such credentials"
|
|
65
|
+
* rather than the `InvalidCredentialKeyError` every other malformed prefix
|
|
66
|
+
* raises — the same not-found/malformed collapse this module exists to end.
|
|
67
|
+
*/
|
|
68
|
+
export function isValidKeyPrefix(prefix) {
|
|
69
|
+
if (prefix === "")
|
|
70
|
+
return true;
|
|
71
|
+
if (!VALID_KEY_PREFIX_RE.test(prefix))
|
|
72
|
+
return false;
|
|
73
|
+
// A trailing separator needs at least one more alphanumeric to become a key.
|
|
74
|
+
const max = /[.-]$/.test(prefix) ? 63 : 64;
|
|
75
|
+
return prefix.length <= max;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Throw `InvalidCredentialKeyError` unless `key` matches the key grammar.
|
|
79
|
+
*
|
|
80
|
+
* Backends call this instead of returning `false`/`null`, so a malformed key
|
|
81
|
+
* is never mistaken for a failed write or an absent credential (#168, P2).
|
|
82
|
+
*/
|
|
83
|
+
export function assertValidKey(key, operation) {
|
|
84
|
+
if (isValidKey(key))
|
|
85
|
+
return;
|
|
86
|
+
throw new InvalidCredentialKeyError(key, operation, "key");
|
|
87
|
+
}
|
|
88
|
+
/** Throw `InvalidCredentialKeyError` unless `prefix` is a valid key prefix. */
|
|
89
|
+
export function assertValidKeyPrefix(prefix, operation) {
|
|
90
|
+
if (isValidKeyPrefix(prefix))
|
|
91
|
+
return;
|
|
92
|
+
throw new InvalidCredentialKeyError(prefix, operation, "prefix");
|
|
93
|
+
}
|
|
33
94
|
//# 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;;;;;;;;;;;GAWG;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
|
/**
|
|
@@ -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;AA+C/C;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAa,YAAW,YAAY;IAC/C;;;;;;;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;IAmB1C;;;;;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
|
// =============================================================================
|
|
@@ -118,8 +122,7 @@ export class WindowsVault {
|
|
|
118
122
|
* @returns true on success, false if storage fails
|
|
119
123
|
*/
|
|
120
124
|
store(key, value) {
|
|
121
|
-
|
|
122
|
-
return false;
|
|
125
|
+
assertValidKey(key, "write");
|
|
123
126
|
const escapedKey = escapePsValue(key);
|
|
124
127
|
const escapedValue = escapePsValue(value);
|
|
125
128
|
const command = [
|
|
@@ -140,8 +143,7 @@ export class WindowsVault {
|
|
|
140
143
|
* @returns The stored password, or null if not found / retrieval fails
|
|
141
144
|
*/
|
|
142
145
|
retrieve(key) {
|
|
143
|
-
|
|
144
|
-
return null;
|
|
146
|
+
assertValidKey(key, "read");
|
|
145
147
|
const escapedKey = escapePsValue(key);
|
|
146
148
|
const command = [
|
|
147
149
|
"& {",
|
|
@@ -164,8 +166,7 @@ export class WindowsVault {
|
|
|
164
166
|
* @returns true on success (including "not found"), false on unexpected error
|
|
165
167
|
*/
|
|
166
168
|
delete(key) {
|
|
167
|
-
|
|
168
|
-
return false;
|
|
169
|
+
assertValidKey(key, "delete");
|
|
169
170
|
const escapedKey = escapePsValue(key);
|
|
170
171
|
const command = [
|
|
171
172
|
"& {",
|
|
@@ -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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"}
|