Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,252 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { VAULT_CODES } from "../src/errors/codes.js";
import { VaultError } from "../src/errors/vault-error.js";
import type {
DriveLike,
DriveSharingLike,
SyncDelegationResult,
SyncSnapshot,
VaultSyncLike,
} from "../src/types/adapters.js";
import type { VaultConfig } from "../src/types/types.js";
import { Vault } from "../src/vault/vault.js";
function memoryDrive(): DriveLike & { blobs: Map<string, Uint8Array> } {
const blobs = new Map<string, Uint8Array>();
return {
blobs,
async read(path) {
const v = blobs.get(path);
if (v === undefined) throw new Error(`missing ${path}`);
return v;
},
async write(path, data) {
blobs.set(path, new Uint8Array(data));
},
async delete(path) {
return blobs.delete(path);
},
async exists(path) {
return blobs.has(path);
},
};
}
function noopSharing(): DriveSharingLike {
return {
async createLink() {
return { id: "x", url: "https://x.example/x" };
},
async revokeLink() {},
async isActive() {
return true;
},
};
}
class CountingSync implements VaultSyncLike {
public lastSnapshot: SyncSnapshot = { items: [], snapshotAt: 0 };
public hydrateCount = 0;
public hydrate(s: SyncSnapshot): void {
this.hydrateCount += 1;
this.lastSnapshot = s;
}
public async pull(): Promise<SyncDelegationResult> {
return {
merged: this.lastSnapshot.items.map((i) => i.id),
conflicts: [],
deleted: [],
durationMs: 0,
};
}
public async push(): Promise<SyncDelegationResult> {
return {
merged: this.lastSnapshot.items.map((i) => i.id),
conflicts: [],
deleted: [],
durationMs: 0,
};
}
public async sync(): Promise<SyncDelegationResult> {
return {
merged: this.lastSnapshot.items.map((i) => i.id),
conflicts: [],
deleted: [],
durationMs: 0,
};
}
public currentSnapshot(): SyncSnapshot {
return this.lastSnapshot;
}
}
function makeConfig(
drive = memoryDrive(),
sync: VaultSyncLike = new CountingSync()
): VaultConfig {
return {
vaultId: "personal",
email: "user@example.com",
containerPath: "/personal.vault",
attachmentsPrefix: "/personal/att",
argon2id: {
memoryKB: 65536,
iterations: 3,
parallelism: 1,
outputLength: 32,
},
argon2idSalt: new Uint8Array(16),
drive,
driveSharing: noopSharing(),
sync,
};
}
describe("vault — integration", () => {
it("end-to-end: unlock, create item with concealed field, lock, re-unlock, get item", {
timeout: 30000,
}, async () => {
const drive = memoryDrive();
const config = makeConfig(drive);
const vault = Vault.open(config);
await vault.unlock("pass", "secret");
const created = vault.items.create({
title: "GitHub",
category: "login",
fields: { username: "alice", url: "https://github.com" },
concealedFields: { password: "p@ssw0rd" },
tags: ["work"],
favorite: true,
});
expect(created.concealedFields.password).toBe("p@ssw0rd");
await vault.flush();
await vault.lock();
expect(vault.isLocked).toBe(true);
const v2 = Vault.open(config);
await v2.unlock("pass", "secret");
const fetched = v2.items.get(created.id);
expect(fetched).not.toBeNull();
expect(fetched?.title).toBe("GitHub");
expect(fetched?.concealedFields.password).toBe("p@ssw0rd");
await v2.lock();
});
it("wrong password rejects with DECRYPT_FAILED, vault stays locked", {
timeout: 30000,
}, async () => {
const drive = memoryDrive();
const config = makeConfig(drive);
const v1 = Vault.open(config);
await v1.unlock("pass", "secret");
await v1.flush();
await v1.lock();
const v2 = Vault.open(config);
try {
await v2.unlock("wrong", "secret");
throw new Error("should have thrown");
} catch (err) {
expect(VaultError.is(err)).toBe(true);
expect((err as VaultError).code).toBe(VAULT_CODES.DECRYPT_FAILED);
}
expect(v2.isLocked).toBe(true);
});
it("tampered drive blob rejects with DECRYPT_FAILED", {
timeout: 30000,
}, async () => {
const drive = memoryDrive();
const config = makeConfig(drive);
const v1 = Vault.open(config);
await v1.unlock("pass", "secret");
await v1.flush();
await v1.lock();
const blob = drive.blobs.get(config.containerPath) as Uint8Array;
const tampered = new Uint8Array(blob);
tampered[tampered.length - 5] ^= 0x42;
drive.blobs.set(config.containerPath, tampered);
const v2 = Vault.open(config);
try {
await v2.unlock("pass", "secret");
throw new Error("should have thrown");
} catch (err) {
expect(VaultError.is(err)).toBe(true);
expect((err as VaultError).code).toBe(VAULT_CODES.DECRYPT_FAILED);
}
});
it("attachments survive lock + re-unlock through drive", {
timeout: 30000,
}, async () => {
const drive = memoryDrive();
const config = makeConfig(drive);
const vault = Vault.open(config);
await vault.unlock("pass", "secret");
const data = new TextEncoder().encode("hello attachment");
const meta = await vault.attachments.upload({
itemId: "i",
filename: "f.txt",
mimeType: "text/plain",
data,
});
await vault.flush();
await vault.lock();
const v2 = Vault.open(config);
await v2.unlock("pass", "secret");
// Attachment metadata is per-runtime; re-injecting is the
// caller's job. We still verify the drive blob is decrypted
// correctly when metadata is known.
(
v2.attachments as unknown as {
metadata: Map<string, typeof meta>;
}
).metadata.set(meta.id, meta);
const recovered = await v2.attachments.download(meta.id);
expect(new TextDecoder().decode(recovered)).toBe("hello attachment");
await v2.lock();
});
it("sync delegation works end-to-end through the orchestrator", {
timeout: 30000,
}, async () => {
const sync = new CountingSync();
const drive = memoryDrive();
const vault = Vault.open(makeConfig(drive, sync));
await vault.unlock("pass", "secret");
vault.items.create({
title: "x",
category: "login",
fields: {},
concealedFields: {},
tags: [],
favorite: false,
});
const result = await vault.sync.sync();
expect(result.merged.length).toBe(1);
expect(sync.hydrateCount).toBeGreaterThan(0);
await vault.lock();
});
it("shares delegation works end-to-end", { timeout: 30000 }, async () => {
const drive = memoryDrive();
const vault = Vault.open(makeConfig(drive));
await vault.unlock("pass", "secret");
const link = await vault.shares.createLink({
itemId: "i",
itemPlaintext: new TextEncoder().encode("payload"),
expiresAt: null,
maxViews: null,
});
expect(link.url).toContain("https://");
expect(await vault.shares.isActive(link.id)).toBe(true);
await vault.shares.revokeLink(link.id);
expect(await vault.shares.isActive(link.id)).toBe(false);
await vault.lock();
});
});