58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { utf8ToBytes } from "../../src/crypto/encoding";
|
|
import {
|
|
compareCollectionSnapshots,
|
|
createCollectionSnapshot,
|
|
parseCollectionSnapshot,
|
|
} from "../../src/otp/collection";
|
|
import type { OtpProfile } from "../../src/otp/profile";
|
|
|
|
function profile(account: string, secret: string): OtpProfile {
|
|
return {
|
|
kind: "totp",
|
|
issuer: "Example",
|
|
account,
|
|
secret: utf8ToBytes(secret),
|
|
algorithm: "SHA-1",
|
|
digits: 6,
|
|
period: 30,
|
|
counter: 0n,
|
|
extensions: new Map(),
|
|
};
|
|
}
|
|
|
|
describe("OTP collection snapshots", () => {
|
|
it("exports fingerprints without secrets", async () => {
|
|
const snapshot = await createCollectionSnapshot([
|
|
profile("alice", "top-secret-value"),
|
|
]);
|
|
const serialized = JSON.stringify(snapshot);
|
|
expect(serialized).not.toContain("top-secret-value");
|
|
expect(parseCollectionSnapshot(serialized)).toEqual(snapshot);
|
|
});
|
|
|
|
it("detects renames, parameter changes and secret reuse", async () => {
|
|
const before = await createCollectionSnapshot([
|
|
profile("alice", "secret-a"),
|
|
profile("bob", "secret-b"),
|
|
]);
|
|
const bob = profile("bob", "secret-b");
|
|
bob.digits = 8;
|
|
const after = await createCollectionSnapshot([
|
|
profile("alice-renamed", "secret-a"),
|
|
bob,
|
|
profile("carol", "secret-b"),
|
|
]);
|
|
const result = compareCollectionSnapshots(before, after);
|
|
expect(result.changes.map(({ kind }) => kind)).toEqual(
|
|
expect.arrayContaining([
|
|
"renamed",
|
|
"parameters-changed",
|
|
"added",
|
|
"secret-reused",
|
|
]),
|
|
);
|
|
expect(result.rotationPlan[0]?.kind).toBe("secret-reused");
|
|
});
|
|
});
|