fix(auth): paginate session maintenance safely

This commit is contained in:
2026-08-17 09:05:20 +02:00
parent 3e7bb11313
commit c86f01e886
12 changed files with 1021 additions and 79 deletions
+52
View File
@@ -215,6 +215,58 @@ describe("Windows auth-storage bridge", () => {
});
});
test("uses a strict, bounded continuation page for ordinary Windows session maintenance", async () => {
const invoke = vi.fn(async () => ({
code: 0,
stdout: Buffer.from(`{"version":1,"ok":true,"entries":[{"name":"${filename}","modifiedUnixMs":1}],"more":false}\n`),
stderr: Buffer.alloc(0),
}));
const bridge = createWindowsAuthStorageBridge({ thtExecutable: "C:\\tht.exe", invoke }) as unknown as {
listPage(root: string, directory: "sessions", after: string | undefined, maximumEntries: number): Promise<{
entries: Array<{ name: string; modifiedUnixMs: number }>;
more: boolean;
}>;
};
await expect(bridge.listPage(root, "sessions", "0".repeat(64) + ".json", 512)).resolves.toEqual({
entries: [{ name: filename, modifiedUnixMs: 1 }],
more: false,
});
expect(JSON.parse(invoke.mock.calls[0][0].input.toString("utf8"))).toMatchObject({
operation: "list",
directory: "sessions",
maximumEntries: 512,
continuation: true,
afterName: "0".repeat(64) + ".json",
});
});
test("rejects ambiguous ordinary-session continuation responses", async () => {
const after = "f".repeat(64) + ".json";
const low = "a".repeat(64) + ".json";
const high = "b".repeat(64) + ".json";
const cases = [
{ label: "missing more marker", body: { entries: [{ name: filename, modifiedUnixMs: 1 }] } },
{ label: "more without a full page", body: { entries: [{ name: filename, modifiedUnixMs: 1 }], more: true } },
{ label: "non-progressing name", body: { entries: [{ name: low, modifiedUnixMs: 1 }], more: false } },
{ label: "duplicate names", body: { entries: [{ name: high, modifiedUnixMs: 1 }, { name: high, modifiedUnixMs: 2 }], more: false } },
];
for (const { body } of cases) {
const bridge = createWindowsAuthStorageBridge({
thtExecutable: "C:\\tht.exe",
invoke: async () => ({
code: 0,
stdout: Buffer.from(`${JSON.stringify({ version: 1, ok: true, ...body })}\n`),
stderr: Buffer.alloc(0),
}),
}) as unknown as {
listPage(root: string, directory: "sessions", afterName: string | undefined, maximumEntries: number): Promise<unknown>;
};
await expect(bridge.listPage(root, "sessions", after, 512)).rejects.toThrow("auth_session_store_invalid");
}
});
test("accepts a bounded ordinary-session page larger than the legacy 256-entry limit", async () => {
const entries = Array.from({ length: 300 }, (_unused, index) => ({
name: `${index.toString(16).padStart(64, "0")}.json`,