fix(auth): harden Windows storage bridge
This commit is contained in:
@@ -59,6 +59,7 @@ import {
|
||||
type AuthSessionStore,
|
||||
type SessionCreateInput,
|
||||
} from "../src/auth/session-store.js";
|
||||
import { createWindowsAuthStorageBridge } from "../src/auth/windows-auth-storage.js";
|
||||
|
||||
const roots: string[] = [];
|
||||
const base = new Date("2030-01-02T03:04:05.000Z");
|
||||
@@ -579,6 +580,65 @@ describe("file-backed auth session store", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("prunes nonempty Windows directories through the Go lower-camel list DTO", async () => {
|
||||
const records = new Map<string, Buffer>();
|
||||
const key = (directory: string, entry: string) => `${directory}/${entry}`;
|
||||
const response = (value: Record<string, unknown>) => ({
|
||||
code: 0,
|
||||
stdout: Buffer.from(`${JSON.stringify({ version: 1, ok: true, ...value })}\n`),
|
||||
stderr: Buffer.alloc(0),
|
||||
});
|
||||
const bridge = createWindowsAuthStorageBridge({
|
||||
thtExecutable: "C:\\tht.exe",
|
||||
invoke: async ({ input }) => {
|
||||
const request = JSON.parse(input.toString("utf8")) as {
|
||||
operation: string;
|
||||
directory: "sessions" | "oidc";
|
||||
filename?: string;
|
||||
contentBase64?: string;
|
||||
};
|
||||
const entry = request.filename === undefined ? undefined : key(request.directory, request.filename);
|
||||
switch (request.operation) {
|
||||
case "create":
|
||||
if (entry === undefined || request.contentBase64 === undefined || records.has(entry)) return response({ created: false });
|
||||
records.set(entry, Buffer.from(request.contentBase64, "base64"));
|
||||
return response({ created: true });
|
||||
case "read":
|
||||
return entry === undefined || !records.has(entry)
|
||||
? response({})
|
||||
: response({ found: true, contentBase64: records.get(entry)?.toString("base64") });
|
||||
case "remove":
|
||||
return response({ removed: entry !== undefined && records.delete(entry) });
|
||||
case "list":
|
||||
return response({
|
||||
// Raw lower-camel entry objects, exactly as authstorage's Go response emits them.
|
||||
entries: [...records.keys()]
|
||||
.filter((value) => value.startsWith(`${request.directory}/`))
|
||||
.map((value) => ({ name: value.slice(request.directory.length + 1), modifiedUnixMs: base.getTime() })),
|
||||
});
|
||||
default:
|
||||
throw new Error("unexpected bridge operation");
|
||||
}
|
||||
},
|
||||
});
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
|
||||
if (!originalPlatform) throw new Error("platform descriptor unavailable");
|
||||
Object.defineProperty(process, "platform", { configurable: true, value: "win32" });
|
||||
try {
|
||||
const store = createFileAuthSessionStore("C:\\ProgramData\\ThothII\\auth", {
|
||||
currentAuthConfigRevision: () => revision,
|
||||
findLocalUser: async () => validLocalUser,
|
||||
}, { windowsStorageBridge: bridge });
|
||||
await create(store, { idleTtlMs: 60_000, absoluteTtlMs: 60_000 });
|
||||
await store.createOidcState({ nonce: "n".repeat(43), codeVerifier: "v".repeat(43), returnTo: "/" }, base);
|
||||
|
||||
await expect(store.prune(new Date(base.getTime() + 11 * 60_000))).resolves.toBe(2);
|
||||
expect(records).toHaveLength(0);
|
||||
} finally {
|
||||
Object.defineProperty(process, "platform", originalPlatform);
|
||||
}
|
||||
});
|
||||
|
||||
test("revokes on config, local-user, revision, enabled, or role mismatch before returning", async () => {
|
||||
const storageRoot = root();
|
||||
let currentRevision = revision;
|
||||
|
||||
Reference in New Issue
Block a user