fix: harden identity HTTP transport
This commit is contained in:
@@ -75,9 +75,12 @@ describe("Core identity runtime construction boundary", () => {
|
||||
expect(betterAuthMock).toHaveBeenCalledTimes(1);
|
||||
const [options] = betterAuthMock.mock.calls[0] as [Record<string, unknown>];
|
||||
expect(Object.keys(options).sort()).toEqual([
|
||||
"advanced",
|
||||
"baseURL",
|
||||
"database",
|
||||
"emailAndPassword",
|
||||
"logger",
|
||||
"onAPIError",
|
||||
"rateLimit",
|
||||
"secret",
|
||||
"trustedOrigins",
|
||||
@@ -86,6 +89,8 @@ describe("Core identity runtime construction boundary", () => {
|
||||
autoSignIn: false,
|
||||
disableSignUp: false,
|
||||
enabled: true,
|
||||
maxPasswordLength: 128,
|
||||
minPasswordLength: 8,
|
||||
});
|
||||
expect(options.rateLimit).toEqual({ enabled: true, max: 100, window: 10 });
|
||||
expect(options.baseURL).toBe(validConfig().baseUrl);
|
||||
@@ -93,6 +98,96 @@ describe("Core identity runtime construction boundary", () => {
|
||||
expect(options.trustedOrigins).toEqual([...validConfig().trustedOrigins]);
|
||||
});
|
||||
|
||||
it("explicitly disables Better Auth's default test-environment origin/CSRF skip", () => {
|
||||
betterAuthMock.mockReturnValue({ api: { getSession: vi.fn() }, handler: vi.fn() });
|
||||
|
||||
createIdentityRuntime({ config: validConfig(), db: fakeDb() });
|
||||
|
||||
const [options] = betterAuthMock.mock.calls[0] as [Record<string, unknown>];
|
||||
expect(options.advanced).toEqual({ disableCSRFCheck: false, disableOriginCheck: false });
|
||||
});
|
||||
|
||||
it("configures onAPIError.throw so an unexpected failure escapes Better Auth's own unsanitized fallback logger instead of being swallowed internally", () => {
|
||||
betterAuthMock.mockReturnValue({ api: { getSession: vi.fn() }, handler: vi.fn() });
|
||||
|
||||
createIdentityRuntime({ config: validConfig(), db: fakeDb() });
|
||||
|
||||
const [options] = betterAuthMock.mock.calls[0] as [Record<string, unknown>];
|
||||
expect(options.onAPIError).toEqual({ throw: true });
|
||||
});
|
||||
|
||||
it("configures an explicit, error-only Better Auth logger that never forwards Better Auth's own message or arguments", () => {
|
||||
betterAuthMock.mockReturnValue({ api: { getSession: vi.fn() }, handler: vi.fn() });
|
||||
|
||||
createIdentityRuntime({ config: validConfig(), db: fakeDb() });
|
||||
|
||||
const [options] = betterAuthMock.mock.calls[0] as [Record<string, unknown>];
|
||||
const loggerOption = options.logger as {
|
||||
disabled?: boolean;
|
||||
level?: string;
|
||||
log?: (...args: unknown[]) => void;
|
||||
};
|
||||
expect(loggerOption.disabled).toBe(false);
|
||||
expect(loggerOption.level).toBe("error");
|
||||
expect(typeof loggerOption.log).toBe("function");
|
||||
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
try {
|
||||
const hostileException = new Error(
|
||||
"password=hunter2 host=db.internal.example port=5432 statement_timeout at /var/app/src/secret.ts:10:5",
|
||||
);
|
||||
loggerOption.log?.(
|
||||
"error",
|
||||
"Invalid origin: https://evil.example.test",
|
||||
hostileException,
|
||||
"select * from ekklesia_core_session",
|
||||
);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledTimes(1);
|
||||
const emitted = errorSpy.mock.calls
|
||||
.flat()
|
||||
.map((value) =>
|
||||
value instanceof Error ? `${value.message}\n${value.stack ?? ""}` : String(value),
|
||||
)
|
||||
.join("\n");
|
||||
expect(emitted).not.toContain("hunter2");
|
||||
expect(emitted).not.toContain("db.internal.example");
|
||||
expect(emitted).not.toContain("5432");
|
||||
expect(emitted).not.toContain("/var/app");
|
||||
expect(emitted).not.toContain("evil.example.test");
|
||||
expect(emitted.toLowerCase()).not.toContain("select");
|
||||
expect(emitted.toLowerCase()).not.toContain("stack");
|
||||
} finally {
|
||||
errorSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("never publishes a Better Auth log at or below warn level", () => {
|
||||
betterAuthMock.mockReturnValue({ api: { getSession: vi.fn() }, handler: vi.fn() });
|
||||
|
||||
createIdentityRuntime({ config: validConfig(), db: fakeDb() });
|
||||
|
||||
const [options] = betterAuthMock.mock.calls[0] as [Record<string, unknown>];
|
||||
const loggerOption = options.logger as { log?: (...args: unknown[]) => void };
|
||||
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
||||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
||||
try {
|
||||
loggerOption.log?.("warn", "a warning", "detail");
|
||||
loggerOption.log?.("info", "some info", "detail");
|
||||
loggerOption.log?.("debug", "some debug", "detail");
|
||||
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
expect(logSpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
errorSpy.mockRestore();
|
||||
warnSpy.mockRestore();
|
||||
logSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("only widens the sign-in/sign-up rate-limit window when a narrow override is explicitly injected", () => {
|
||||
betterAuthMock.mockReturnValue({ api: { getSession: vi.fn() }, handler: vi.fn() });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user