Skip to content

Commit 991c6c3

Browse files
committed
fix: address critical security and reliability issues
Address all critical issues identified in code review: Security fixes: - Add UUID validation for recordingId parameters (prevents path traversal) - Improve platform detection to only fallback to title-based when URL unavailable - Add warning logs when using title-only detection Reliability improvements: - Set sdkInitialized flag early to prevent race conditions - Add check for existing posthogClient to prevent duplicate initialization - Improve error messages in IPC handlers with actionable guidance - Add SDK state checks alongside client checks These changes eliminate potential attack vectors and improve robustness of the Recall SDK integration.
1 parent fca969f commit 991c6c3

File tree

2 files changed

+77
-15
lines changed

2 files changed

+77
-15
lines changed

src/api/posthogClient.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,20 @@ export class PostHogAPIClient {
251251
}
252252

253253
// Desktop Recordings API
254+
private validateRecordingId(recordingId: string): void {
255+
if (!recordingId || typeof recordingId !== "string") {
256+
throw new Error("Recording ID is required");
257+
}
258+
// UUID format validation (PostHog uses UUIDs for recording IDs)
259+
if (
260+
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
261+
recordingId,
262+
)
263+
) {
264+
throw new Error("Invalid recording ID format");
265+
}
266+
}
267+
254268
async createDesktopRecordingUpload(platform: string = "desktop_audio") {
255269
const teamId = await this.getTeamId();
256270
const url = new URL(
@@ -275,6 +289,7 @@ export class PostHogAPIClient {
275289
}
276290

277291
async getDesktopRecording(recordingId: string) {
292+
this.validateRecordingId(recordingId);
278293
const teamId = await this.getTeamId();
279294
const url = new URL(
280295
`${this.api.baseUrl}/api/environments/${teamId}/desktop_recordings/${recordingId}/`,
@@ -293,6 +308,7 @@ export class PostHogAPIClient {
293308
}
294309

295310
async getDesktopRecordingTranscript(recordingId: string) {
311+
this.validateRecordingId(recordingId);
296312
const teamId = await this.getTeamId();
297313
const url = new URL(
298314
`${this.api.baseUrl}/api/environments/${teamId}/desktop_recordings/${recordingId}/transcript/`,
@@ -341,6 +357,7 @@ export class PostHogAPIClient {
341357
}
342358

343359
async deleteDesktopRecording(recordingId: string) {
360+
this.validateRecordingId(recordingId);
344361
const teamId = await this.getTeamId();
345362
const url = new URL(
346363
`${this.api.baseUrl}/api/environments/${teamId}/desktop_recordings/${recordingId}/`,
@@ -365,6 +382,7 @@ export class PostHogAPIClient {
365382
video_url?: string;
366383
},
367384
) {
385+
this.validateRecordingId(recordingId);
368386
const teamId = await this.getTeamId();
369387
const url = new URL(
370388
`${this.api.baseUrl}/api/environments/${teamId}/desktop_recordings/${recordingId}/`,

src/main/services/recallRecording.ts

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,20 @@ export function initializeRecallSDK(
2222
posthogHost: string,
2323
) {
2424
if (sdkInitialized) {
25+
console.warn("[Recall SDK] Already initialized, skipping");
26+
return;
27+
}
28+
29+
if (posthogClient) {
30+
console.warn(
31+
"[Recall SDK] Client already exists, preventing re-initialization",
32+
);
2533
return;
2634
}
2735

2836
console.log("[Recall SDK] Initializing...");
2937

38+
sdkInitialized = true;
3039
posthogClient = new PostHogAPIClient(posthogKey, posthogHost);
3140

3241
RecallAiSdk.init({
@@ -39,8 +48,6 @@ export function initializeRecallSDK(
3948
restartOnError: true,
4049
});
4150

42-
sdkInitialized = true;
43-
4451
console.log("[Recall SDK] Ready. Listening for meetings...");
4552

4653
RecallAiSdk.addEventListener("permissions-granted", async () => {
@@ -199,28 +206,50 @@ function detectPlatform(window: { url?: string; title?: string }): string {
199206
// Invalid URL, fall back to title-based detection only
200207
}
201208

202-
if (
203-
hostname === "zoom.us" ||
204-
hostname.endsWith(".zoom.us") ||
205-
title.includes("zoom")
206-
) {
209+
if (hostname === "zoom.us" || hostname.endsWith(".zoom.us")) {
207210
return "zoom";
208211
}
209212

210213
if (
211214
hostname === "teams.microsoft.com" ||
212-
hostname.endsWith(".teams.microsoft.com") ||
213-
title.includes("teams")
215+
hostname.endsWith(".teams.microsoft.com")
214216
) {
215217
return "teams";
216218
}
217219

218-
if (hostname === "meet.google.com" || title.includes("google meet")) {
220+
if (hostname === "meet.google.com") {
219221
return "meet";
220222
}
221223

222-
if (title.includes("slack")) {
223-
return "slack";
224+
// Fallback to title-based detection only if URL is unavailable
225+
if (!hostname) {
226+
if (title.includes("zoom")) {
227+
console.warn(
228+
"[Recall SDK] Detecting Zoom via window title only (no URL available)",
229+
);
230+
return "zoom";
231+
}
232+
233+
if (title.includes("teams")) {
234+
console.warn(
235+
"[Recall SDK] Detecting Teams via window title only (no URL available)",
236+
);
237+
return "teams";
238+
}
239+
240+
if (title.includes("google meet")) {
241+
console.warn(
242+
"[Recall SDK] Detecting Meet via window title only (no URL available)",
243+
);
244+
return "meet";
245+
}
246+
247+
if (title.includes("slack")) {
248+
console.warn(
249+
"[Recall SDK] Detecting Slack via window title only (no URL available)",
250+
);
251+
return "slack";
252+
}
224253
}
225254

226255
return "desktop_audio";
@@ -262,21 +291,36 @@ export function registerRecallIPCHandlers() {
262291

263292
ipcMain.handle("notetaker:get-recordings", async () => {
264293
if (!posthogClient) {
265-
throw new Error("PostHog client not initialized");
294+
throw new Error(
295+
"PostHog client not initialized. Please authenticate first.",
296+
);
297+
}
298+
if (!sdkInitialized) {
299+
throw new Error("Recall SDK not initialized");
266300
}
267301
return await posthogClient.listDesktopRecordings();
268302
});
269303

270304
ipcMain.handle("notetaker:get-recording", async (_event, recordingId) => {
271305
if (!posthogClient) {
272-
throw new Error("PostHog client not initialized");
306+
throw new Error(
307+
"PostHog client not initialized. Please authenticate first.",
308+
);
309+
}
310+
if (!sdkInitialized) {
311+
throw new Error("Recall SDK not initialized");
273312
}
274313
return await posthogClient.getDesktopRecording(recordingId);
275314
});
276315

277316
ipcMain.handle("notetaker:delete-recording", async (_event, recordingId) => {
278317
if (!posthogClient) {
279-
throw new Error("PostHog client not initialized");
318+
throw new Error(
319+
"PostHog client not initialized. Please authenticate first.",
320+
);
321+
}
322+
if (!sdkInitialized) {
323+
throw new Error("Recall SDK not initialized");
280324
}
281325
return await posthogClient.deleteDesktopRecording(recordingId);
282326
});

0 commit comments

Comments
 (0)