From c8a8c37b458cc60a00cc9c73ccf0295a101a6c9f Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:49:04 +0530 Subject: [PATCH] fix(companion): prevent localhost URL from being baked into production extension builds (#26032) * fix(companion): separate dev and prod build scripts to prevent localhost in production - Add ext:build-dev and ext:build-prod scripts with clear separation - Add BUILD_FOR_STORE flag to force production URL in store builds - Add visual build indicators (store / dev) for clarity - Prevent EXPO_PUBLIC_COMPANION_DEV_URL from leaking into production builds * update commands to work for ci * update version --------- Co-authored-by: Volnei Munhoz --- companion/extension/entrypoints/content.ts | 3 + companion/package.json | 6 +- companion/wxt.config.ts | 126 ++++++++++++--------- 3 files changed, 78 insertions(+), 57 deletions(-) diff --git a/companion/extension/entrypoints/content.ts b/companion/extension/entrypoints/content.ts index af7876f4e7..f6a23dbd0b 100644 --- a/companion/extension/entrypoints/content.ts +++ b/companion/extension/entrypoints/content.ts @@ -56,6 +56,9 @@ export default defineContentScript({ iframeContainer.style.pointerEvents = "none"; const iframe = document.createElement("iframe"); + // URL is determined at build time: + // - ext:build-dev → uses EXPO_PUBLIC_COMPANION_DEV_URL (localhost) + // - ext:build-prod → uses https://companion.cal.com const COMPANION_URL = (import.meta.env.EXPO_PUBLIC_COMPANION_DEV_URL as string) || "https://companion.cal.com"; iframe.src = COMPANION_URL; diff --git a/companion/package.json b/companion/package.json index 390117da38..a6d0eac369 100644 --- a/companion/package.json +++ b/companion/package.json @@ -1,7 +1,7 @@ { "name": "cal-companion", "displayName": "Cal.com Companion", - "version": "1.7.1", + "version": "1.7.3", "main": "index.js", "scripts": { "start": "expo start", @@ -9,10 +9,14 @@ "ios": "expo run:ios", "web": "expo start --web", "ext:build": "wxt build", + "ext:build-prod": "BUILD_FOR_STORE=true wxt build", "build:firefox": "wxt build -b firefox", + "build:firefox-prod": "BUILD_FOR_STORE=true wxt build -b firefox", "build:safari": "wxt build -b safari", + "build:safari-prod": "BUILD_FOR_STORE=true wxt build -b safari", "ext": "wxt", "ext:zip": "wxt zip", + "ext:zip-prod": "BUILD_FOR_STORE=true wxt zip", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"", "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\"", "prepare": "cd .. && husky companion/.husky" diff --git a/companion/wxt.config.ts b/companion/wxt.config.ts index abb7d00e32..9239c68fb5 100644 --- a/companion/wxt.config.ts +++ b/companion/wxt.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from "wxt"; -const devUrl = process.env.EXPO_PUBLIC_COMPANION_DEV_URL; -const isLocalDev = Boolean(devUrl && devUrl.includes("localhost")); +// BUILD_FOR_STORE=true is set by ext:build-prod, ext:zip-prod, etc. +// Forces production URL (https://companion.cal.com) and excludes localhost permissions +const isBuildForStore = process.env.BUILD_FOR_STORE === "true"; export default defineConfig({ hooks: { @@ -19,7 +20,7 @@ export default defineConfig({ outDir: ".output", manifest: { name: "Cal.com Companion", - version: "1.7.1", + version: "1.7.3", description: "Your calendar companion for quick booking and scheduling", permissions: ["activeTab", "storage", "identity"], host_permissions: [ @@ -27,10 +28,11 @@ export default defineConfig({ "https://api.cal.com/*", "https://app.cal.com/*", "https://mail.google.com/*", - ...(isLocalDev ? ["http://localhost:*/*"] : []), + // Include localhost permission for dev builds (needed for iframe to load) + ...(!isBuildForStore ? ["http://localhost:*/*"] : []), ], content_security_policy: { - extension_pages: isLocalDev + extension_pages: !isBuildForStore ? "script-src 'self'; object-src 'self'; frame-src 'self' https://companion.cal.com http://localhost:*" : "script-src 'self'; object-src 'self'; frame-src 'self' https://companion.cal.com", }, @@ -48,57 +50,69 @@ export default defineConfig({ "128": "icon-128.png", }, }, - vite: () => ({ - resolve: { - alias: { - "react-native": "react-native-web", - }, - }, - define: { - global: "globalThis", - __DEV__: JSON.stringify(false), - "import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID": JSON.stringify( - process.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID - ), - "import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI": JSON.stringify( - process.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI - ), - "import.meta.env.EXPO_PUBLIC_COMPANION_DEV_URL": JSON.stringify( - process.env.EXPO_PUBLIC_COMPANION_DEV_URL || "" - ), - "import.meta.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES": JSON.stringify( - process.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES - ), - "import.meta.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES": JSON.stringify( - process.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES - ), - "import.meta.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES": JSON.stringify( - process.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES - ), - "import.meta.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES": JSON.stringify( - process.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES - ), - "import.meta.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES": JSON.stringify( - process.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES - ), - "import.meta.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES": JSON.stringify( - process.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES - ), - ...(process.env.NODE_ENV !== "production" && process.env.EXPO_PUBLIC_CAL_API_KEY - ? { - "import.meta.env.EXPO_PUBLIC_CAL_API_KEY": JSON.stringify( - process.env.EXPO_PUBLIC_CAL_API_KEY - ), - } - : {}), - }, - optimizeDeps: { - include: ["react-native-web"], - esbuildOptions: { - loader: { - ".js": "jsx", + vite: () => { + // Determine companion URL based on build type + const devUrl = isBuildForStore ? "" : process.env.EXPO_PUBLIC_COMPANION_DEV_URL || ""; + const isLocalDev = Boolean(devUrl && devUrl.includes("localhost")); + + // Log build mode for clarity + if (isBuildForStore) { + console.log("\nšŸŖ STORE BUILD: Using https://companion.cal.com\n"); + } else if (isLocalDev) { + console.log(`\nšŸ”§ DEV BUILD: Using ${devUrl}\n`); + } + + return { + resolve: { + alias: { + "react-native": "react-native-web", }, }, - }, - }), + define: { + global: "globalThis", + __DEV__: JSON.stringify(false), + "import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID": JSON.stringify( + process.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID + ), + "import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI": JSON.stringify( + process.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI + ), + // Use devUrl which respects BUILD_FOR_STORE flag + "import.meta.env.EXPO_PUBLIC_COMPANION_DEV_URL": JSON.stringify(devUrl), + "import.meta.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES": JSON.stringify( + process.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES + ), + "import.meta.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES": JSON.stringify( + process.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES + ), + "import.meta.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES": JSON.stringify( + process.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES + ), + "import.meta.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES": JSON.stringify( + process.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES + ), + "import.meta.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES": JSON.stringify( + process.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES + ), + "import.meta.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES": JSON.stringify( + process.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES + ), + ...(process.env.NODE_ENV !== "production" && process.env.EXPO_PUBLIC_CAL_API_KEY + ? { + "import.meta.env.EXPO_PUBLIC_CAL_API_KEY": JSON.stringify( + process.env.EXPO_PUBLIC_CAL_API_KEY + ), + } + : {}), + }, + optimizeDeps: { + include: ["react-native-web"], + esbuildOptions: { + loader: { + ".js": "jsx", + }, + }, + }, + }; + }, });