← back to Kickbacks Ai Upstream
test/vault.test.ts
127 lines
import { describe, it, expect, vi } from "vitest";
import { createVault, type Run, type RunResult } from "../src/auth/vault";
const ok = (stdout = ""): RunResult => ({ code: 0, stdout, stderr: "" });
const fail = (stderr = "err"): RunResult => ({ code: 1, stdout: "", stderr });
describe("SecretVault — OS-native at-rest with a universal plaintext floor", () => {
it("darwin: seals into the Keychain (no secret in the file) and round-trips", async () => {
const store = new Map<string, string>();
const run: Run = vi.fn(async (cmd, args, opts) => {
expect(cmd).toBe("security");
if (args[0] === "add-generic-password") {
// Account is the -a value; secret is the -w value.
const acct = args[args.indexOf("-a") + 1];
const secret = args[args.indexOf("-w") + 1];
store.set(acct, secret);
return ok();
}
if (args[0] === "find-generic-password")
return ok((store.get(args[args.indexOf("-a") + 1]) ?? "") + "\n");
if (args[0] === "delete-generic-password") {
store.delete(args[args.indexOf("-a") + 1]);
return ok();
}
return fail();
});
const v = createVault("darwin", run);
expect(v.scheme()).toBe("keychain");
const env = await v.seal("cid-1", "RT-secret");
expect(env.startsWith("keychain:1:")).toBe(true);
expect(env).not.toContain("RT-secret"); // secret NOT in the envelope
expect(await v.open(env)).toBe("RT-secret");
await v.clear(env);
expect(await v.open(env)).toBeNull(); // gone => honest signed-out
});
it("win32: DPAPI-encrypts the blob into the file and round-trips", async () => {
// Model ProtectedData as reversible base64 wrapping; secret arrives via env,
// never argv.
// Model ProtectedData as base64 (reversible, non-identity — so the
// "no plaintext in the envelope" invariant is actually testable).
const run: Run = vi.fn(async (cmd, _args, opts) => {
expect(/powershell|pwsh/i.test(cmd)).toBe(true);
const script = _args.join(" ");
const inp = opts?.env?.VIBE_ADS_SECRET ?? "";
// PS_UNPROTECT runs FromBase64String first, so order matters: a value
// that is valid base64 is treated as ciphertext to decrypt.
if (/Unprotect/.test(script)) {
try { return ok(Buffer.from(inp, "base64").toString("utf8") + "\n"); }
catch { return fail(); }
}
if (/Protect/.test(script))
return ok(Buffer.from(inp, "utf8").toString("base64") + "\n");
return fail();
});
const v = createVault("win32", run);
expect(v.scheme()).toBe("dpapi");
const env = await v.seal("cid-2", "RT-win");
expect(env.startsWith("dpapi:1:")).toBe(true);
expect(env).not.toContain("RT-win"); // ciphertext only
expect(await v.open(env)).toBe("RT-win");
});
it("linux: secret-tool via stdin (no argv leak); round-trips", async () => {
const store = new Map<string, string>();
const run: Run = vi.fn(async (cmd, args, opts) => {
expect(cmd).toBe("secret-tool");
const acct = args[args.indexOf("account") + 1];
if (args[0] === "store") {
expect(opts?.input).toBe("RT-lin"); // secret on stdin, not argv
store.set(acct, opts!.input!);
return ok();
}
if (args[0] === "lookup")
return store.has(acct) ? ok(store.get(acct)!) : fail();
if (args[0] === "clear") { store.delete(acct); return ok(); }
return fail();
});
const v = createVault("linux", run);
const env = await v.seal("cid-3", "RT-lin");
expect(env.startsWith("libsecret:1:")).toBe(true);
expect(env).not.toContain("RT-lin");
expect(await v.open(env)).toBe("RT-lin");
});
it("linux without a Secret Service: falls straight through to the plaintext floor", async () => {
const run: Run = vi.fn(async () => fail("No such service"));
const v = createVault("linux", run);
const env = await v.seal("cid-4", "RT-floor");
expect(env).toBe("plain:1:RT-floor");
expect(await v.open(env)).toBe("RT-floor"); // floor still works
});
it("any OS tool throwing never escapes: seal degrades to plain", async () => {
const run: Run = vi.fn(async () => { throw new Error("ENOENT"); });
for (const p of ["darwin", "win32", "linux"] as const) {
const v = createVault(p, run);
const env = await v.seal("cid", "S");
expect(env).toBe("plain:1:S");
expect(await v.open(env)).toBe("S");
}
});
it("opening an OS envelope whose entry was wiped externally => null, not throw", async () => {
const run: Run = vi.fn(async (_c, args) =>
args[0] === "find-generic-password" || args[0] === "lookup"
? fail() : ok());
expect(await createVault("darwin", run).open("keychain:1:gone")).toBeNull();
expect(await createVault("linux", run).open("libsecret:1:gone")).toBeNull();
});
it("plain envelopes need no exec at all (works with a throwing runner)", async () => {
const run: Run = vi.fn(async () => { throw new Error("must not be called"); });
const v = createVault("linux", run);
expect(await v.open("plain:1:abc")).toBe("abc");
await v.clear("plain:1:abc"); // no-op, no exec
expect(run).not.toHaveBeenCalled();
});
it("unknown/garbage envelope => null (forward/back-compat safe)", async () => {
const v = createVault("linux", vi.fn(async () => ok()));
expect(await v.open("")).toBeNull();
expect(await v.open("bogus")).toBeNull();
expect(await v.open("future:9:xyz")).toBeNull();
});
});