fix(auth): close diagnostic filesystem races
This commit is contained in:
@@ -107,6 +107,10 @@ afterEach(() => {
|
||||
function root(): string {
|
||||
const path = mkdtempSync(join(realpathSync(tmpdir()), "thothii-auth-session-"));
|
||||
chmodSync(path, 0o700);
|
||||
for (const child of ["sessions", "oidc"]) {
|
||||
mkdirSync(join(path, child), { mode: 0o700 });
|
||||
chmodSync(join(path, child), 0o700);
|
||||
}
|
||||
roots.push(path);
|
||||
return path;
|
||||
}
|
||||
@@ -265,10 +269,75 @@ describe("file-backed auth session store", () => {
|
||||
}
|
||||
expect(existsSync(join(outer, "absent-parent"))).toBe(false);
|
||||
|
||||
for (const child of ["sessions", "oidc"] as const) {
|
||||
const childRoot = join(outer, `child-${child}`);
|
||||
mkdirSync(childRoot, { mode: 0o700 });
|
||||
chmodSync(childRoot, 0o700);
|
||||
const childPath = join(childRoot, child);
|
||||
const outside = root();
|
||||
symlinkSync(outside, childPath);
|
||||
expect(() => validateAuthSessionRoot(childRoot)).toThrow("auth_session_store_invalid");
|
||||
expect(readdirSync(outside).sort()).toEqual(["oidc", "sessions"]);
|
||||
unlinkSync(childPath);
|
||||
mkdirSync(childPath, { mode: 0o700 });
|
||||
chmodSync(childPath, 0o750);
|
||||
expect(() => validateAuthSessionRoot(childRoot)).toThrow("auth_session_store_invalid");
|
||||
}
|
||||
|
||||
const missingChildren = join(outer, "missing-children");
|
||||
mkdirSync(missingChildren, { mode: 0o700 });
|
||||
chmodSync(missingChildren, 0o700);
|
||||
expect(() => validateAuthSessionRoot(missingChildren)).not.toThrow();
|
||||
expect(existsSync(join(missingChildren, "sessions"))).toBe(false);
|
||||
expect(existsSync(join(missingChildren, "oidc"))).toBe(false);
|
||||
|
||||
chmodSync(valid, 0o750);
|
||||
expect(() => validateAuthSessionRoot(valid)).toThrow("auth_session_store_invalid");
|
||||
});
|
||||
|
||||
test.skipIf(process.platform === "win32")("delegates missing layout creation and then enforces static/runtime parity", async () => {
|
||||
const storageRoot = join(root(), "auth");
|
||||
const ensureLayout = vi.fn(async (requestedRoot: string) => {
|
||||
expect(requestedRoot).toBe(storageRoot);
|
||||
mkdirSync(requestedRoot, { mode: 0o700 });
|
||||
chmodSync(requestedRoot, 0o700);
|
||||
for (const child of ["sessions", "oidc"]) {
|
||||
mkdirSync(join(requestedRoot, child), { mode: 0o700 });
|
||||
chmodSync(join(requestedRoot, child), 0o700);
|
||||
}
|
||||
});
|
||||
const store = validStore(storageRoot, { posixStorageBridge: { ensureLayout } });
|
||||
|
||||
await expect(create(store)).resolves.toMatchObject({ record: { method: "local" } });
|
||||
expect(ensureLayout).toHaveBeenCalledOnce();
|
||||
expect(() => validateAuthSessionRoot(storageRoot)).not.toThrow();
|
||||
|
||||
chmodSync(join(storageRoot, "oidc"), 0o750);
|
||||
expect(() => validateAuthSessionRoot(storageRoot)).toThrow("auth_session_store_invalid");
|
||||
await expectStoreInvalid(store.createOidcState(oidcInput("n".repeat(16), "v".repeat(43)), base));
|
||||
});
|
||||
|
||||
test.skipIf(process.platform === "win32")("does not perform a path-based fallback when bridge layout creation loses an ancestor race", async () => {
|
||||
const outer = root();
|
||||
const outside = root();
|
||||
const parent = join(outer, "parent");
|
||||
const movedParent = join(outer, "parent-original");
|
||||
mkdirSync(parent, { mode: 0o700 });
|
||||
chmodSync(parent, 0o700);
|
||||
const storageRoot = join(parent, "auth");
|
||||
const ensureLayout = vi.fn(async () => {
|
||||
renameSync(parent, movedParent);
|
||||
symlinkSync(outside, parent);
|
||||
throw new Error(`${storageRoot} rejected`);
|
||||
});
|
||||
const store = validStore(storageRoot, { posixStorageBridge: { ensureLayout } });
|
||||
|
||||
await expectStoreInvalid(create(store));
|
||||
expect(ensureLayout).toHaveBeenCalledOnce();
|
||||
expect(existsSync(join(outside, "auth"))).toBe(false);
|
||||
expect(existsSync(join(movedParent, "auth"))).toBe(false);
|
||||
});
|
||||
|
||||
test.skipIf(process.platform === "win32")("never follows a symlinked ancestor while creating a missing session root", async () => {
|
||||
const outer = root();
|
||||
const outside = root();
|
||||
|
||||
Reference in New Issue
Block a user