fix(ipc-oauth): use shared registerHandle for trust and error sanitization (I-01)
The self-built `ok/err/handle` helpers called `ipcMain.handle` directly, which meant every OAuth IPC channel skipped the trusted-renderer check in `ipc-support.registerHandle` and its `ZodError -> IPC_INVALID_PAYLOAD` sanitizer. Any renderer origin could invoke these channels, and thrown zod errors would leak the raw validator message. Take `registerHandle` and `fail` as dependencies so callers wire in the hardened variants from `createIpcSupport`, drop the local helpers, and import `ok` from `ipc-core-common`. Zod inputs stay on `safeParse` to preserve the explicit `INVALID_INPUT` code + message path.
This commit is contained in:
1 parent
a1f021ff4a
commit
f86b3ddbac
1 file changed
+64
@@ -0,0 +1,64 @@
|
||||
import { ipcMain, type IpcMainInvokeEvent } from "electron";
|
||||
import type { DesktopResult } from "@shared/contracts";
|
||||
import { IPC_CHANNELS } from "@shared/contracts";
|
||||
import {
|
||||
oauthCancelInputSchema,
|
||||
oauthManualCodeResponseSchema,
|
||||
oauthPromptResponseSchema,
|
||||
oauthStartInputSchema,
|
||||
} from "@shared/contracts/schemas-oauth";
|
||||
import type { OAuthLoginService } from "./credentials/oauth-login-service";
|
||||
import { ok } from "./ipc-core-common";
|
||||
|
||||
interface OAuthHandlerDeps {
|
||||
service: OAuthLoginService;
|
||||
registerHandle: (
|
||||
channel: string,
|
||||
handler: (event: IpcMainInvokeEvent, ...args: unknown[]) => Promise<DesktopResult<unknown>> | DesktopResult<unknown>,
|
||||
) => void;
|
||||
fail: (code: string, message: string) => DesktopResult<never>;
|
||||
logger: { info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void };
|
||||
}
|
||||
|
||||
export function registerOAuthIpcHandlers(deps: OAuthHandlerDeps): () => void {
|
||||
const channels: string[] = [
|
||||
IPC_CHANNELS.oauthStart,
|
||||
IPC_CHANNELS.oauthCancel,
|
||||
IPC_CHANNELS.oauthPromptResponse,
|
||||
IPC_CHANNELS.oauthManualCodeResponse,
|
||||
];
|
||||
|
||||
deps.registerHandle(IPC_CHANNELS.oauthStart, async (_event, raw) => {
|
||||
const parsed = oauthStartInputSchema.safeParse(raw);
|
||||
if (!parsed.success) return deps.fail("INVALID_INPUT", parsed.error.message);
|
||||
deps.service.start(parsed.data.connectorId).catch((error) => {
|
||||
deps.logger.warn(`[oauth] login flow ended: ${error instanceof Error ? error.message : String(error)}`);
|
||||
});
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
deps.registerHandle(IPC_CHANNELS.oauthCancel, async (_event, raw) => {
|
||||
const parsed = oauthCancelInputSchema.safeParse(raw);
|
||||
if (!parsed.success) return deps.fail("INVALID_INPUT", parsed.error.message);
|
||||
deps.service.cancel(parsed.data.connectorId);
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
deps.registerHandle(IPC_CHANNELS.oauthPromptResponse, async (_event, raw) => {
|
||||
const parsed = oauthPromptResponseSchema.safeParse(raw);
|
||||
if (!parsed.success) return deps.fail("INVALID_INPUT", parsed.error.message);
|
||||
const accepted = deps.service.submitPromptResponse(parsed.data.requestId, parsed.data.value);
|
||||
return ok(accepted);
|
||||
});
|
||||
|
||||
deps.registerHandle(IPC_CHANNELS.oauthManualCodeResponse, async (_event, raw) => {
|
||||
const parsed = oauthManualCodeResponseSchema.safeParse(raw);
|
||||
if (!parsed.success) return deps.fail("INVALID_INPUT", parsed.error.message);
|
||||
const accepted = deps.service.submitManualCode(parsed.data.requestId, parsed.data.code);
|
||||
return ok(accepted);
|
||||
});
|
||||
|
||||
return () => {
|
||||
for (const channel of channels) ipcMain.removeHandler(channel);
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user