@ouro.bot/cli 0.1.0-alpha.400 → 0.1.0-alpha.401
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.json +10 -0
- package/dist/repertoire/bitwarden-store.js +60 -2
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.401",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Bitwarden-backed credential writes now prove success by reading the saved item back immediately and verifying its name, username, password, and notes before returning success.",
|
|
8
|
+
"Post-save verification now prefers `bw get item <id>` when create/edit returns a usable id, falls back cleanly when stdout is malformed or id-less, and retries once when the local Bitwarden session expires mid-verification.",
|
|
9
|
+
"Malformed or invalid `bw get item` responses now fail with short sanitized errors instead of leaving auth and vault save flows in an ambiguous half-success state.",
|
|
10
|
+
"Runtime auth's Bitwarden harness coverage now exercises the same post-save readback path as the real vault store, and the store test suite covers missing items, malformed responses, field-by-field mismatches, and multi-field mismatch guidance.",
|
|
11
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the vault write readback verification release."
|
|
12
|
+
]
|
|
13
|
+
},
|
|
4
14
|
{
|
|
5
15
|
"version": "0.1.0-alpha.400",
|
|
6
16
|
"changes": [
|
|
@@ -184,6 +184,34 @@ function parseBwItems(stdout, context) {
|
|
|
184
184
|
throw new Error(`bw CLI error: invalid JSON from ${context}`);
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
|
+
function parseBwItem(stdout, context) {
|
|
188
|
+
let parsed;
|
|
189
|
+
try {
|
|
190
|
+
parsed = JSON.parse(stdout);
|
|
191
|
+
if (!isBwLoginItem(parsed)) {
|
|
192
|
+
throw new Error("expected login item");
|
|
193
|
+
}
|
|
194
|
+
return parsed;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
if (parsed !== undefined) {
|
|
198
|
+
throw new Error(`bw CLI error: invalid item from ${context}`);
|
|
199
|
+
}
|
|
200
|
+
throw new Error(`bw CLI error: invalid JSON from ${context}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function parseBwItemId(stdout) {
|
|
204
|
+
try {
|
|
205
|
+
const parsed = JSON.parse(stdout);
|
|
206
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
207
|
+
return null;
|
|
208
|
+
const id = parsed.id;
|
|
209
|
+
return typeof id === "string" && id.trim().length > 0 ? id : null;
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
187
215
|
// ---------------------------------------------------------------------------
|
|
188
216
|
// BitwardenCredentialStore
|
|
189
217
|
// ---------------------------------------------------------------------------
|
|
@@ -387,12 +415,20 @@ class BitwardenCredentialStore {
|
|
|
387
415
|
notes: data.notes ?? null,
|
|
388
416
|
};
|
|
389
417
|
const encoded = Buffer.from(JSON.stringify(item)).toString("base64");
|
|
418
|
+
let savedItem;
|
|
390
419
|
if (existing) {
|
|
391
|
-
await execBw(["edit", "item", existing.id], session, this.appDataDir, encoded);
|
|
420
|
+
const stdout = await execBw(["edit", "item", existing.id], session, this.appDataDir, encoded);
|
|
421
|
+
const savedItemId = parseBwItemId(stdout) ?? existing.id;
|
|
422
|
+
savedItem = await this.findItemById(savedItemId, session);
|
|
392
423
|
}
|
|
393
424
|
else {
|
|
394
|
-
await execBw(["create", "item"], session, this.appDataDir, encoded);
|
|
425
|
+
const stdout = await execBw(["create", "item"], session, this.appDataDir, encoded);
|
|
426
|
+
const savedItemId = parseBwItemId(stdout);
|
|
427
|
+
savedItem = savedItemId
|
|
428
|
+
? await this.findItemById(savedItemId, session)
|
|
429
|
+
: await this.findItemByDomain(domain, session);
|
|
395
430
|
}
|
|
431
|
+
this.assertStoredCredentialMatches(domain, data, savedItem);
|
|
396
432
|
});
|
|
397
433
|
(0, runtime_1.emitNervesEvent)({
|
|
398
434
|
event: "repertoire.bw_credential_store_end",
|
|
@@ -457,5 +493,27 @@ class BitwardenCredentialStore {
|
|
|
457
493
|
// Find exact match by name
|
|
458
494
|
return items.find((item) => item.name === domain) ?? null;
|
|
459
495
|
}
|
|
496
|
+
async findItemById(id, session) {
|
|
497
|
+
const stdout = await execBw(["get", "item", id], session, this.appDataDir);
|
|
498
|
+
return parseBwItem(stdout, "bw get item");
|
|
499
|
+
}
|
|
500
|
+
assertStoredCredentialMatches(domain, data, item) {
|
|
501
|
+
if (!item) {
|
|
502
|
+
throw new Error(`bw CLI error: credential save verification failed for ${domain}: saved item could not be read back after write`);
|
|
503
|
+
}
|
|
504
|
+
const mismatches = [];
|
|
505
|
+
if (item.name !== domain)
|
|
506
|
+
mismatches.push("name");
|
|
507
|
+
if ((item.login?.username ?? "") !== (data.username ?? ""))
|
|
508
|
+
mismatches.push("username");
|
|
509
|
+
if ((item.login?.password ?? "") !== data.password)
|
|
510
|
+
mismatches.push("password");
|
|
511
|
+
if ((item.notes ?? null) !== (data.notes ?? null))
|
|
512
|
+
mismatches.push("notes");
|
|
513
|
+
if (mismatches.length > 0) {
|
|
514
|
+
const label = mismatches.length === 1 ? "field" : "fields";
|
|
515
|
+
throw new Error(`bw CLI error: credential save verification failed for ${domain}: saved item did not match requested ${label} ${mismatches.join(", ")}`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
460
518
|
}
|
|
461
519
|
exports.BitwardenCredentialStore = BitwardenCredentialStore;
|