diff --git a/assistant/src/__tests__/extension-id-sync-guard.test.ts b/assistant/src/__tests__/extension-id-sync-guard.test.ts index fd6e0789ea8..170ec8f3e7e 100644 --- a/assistant/src/__tests__/extension-id-sync-guard.test.ts +++ b/assistant/src/__tests__/extension-id-sync-guard.test.ts @@ -41,11 +41,15 @@ function parseCanonicalConfig(): AllowlistConfig { const parsed = JSON.parse(raw) as Partial; if (!Number.isInteger(parsed.version) || (parsed.version ?? 0) <= 0) { - throw new Error("Invalid canonical config: version must be a positive integer"); + throw new Error( + "Invalid canonical config: version must be a positive integer", + ); } if (!Array.isArray(parsed.allowedExtensionIds)) { - throw new Error("Invalid canonical config: allowedExtensionIds must be an array"); + throw new Error( + "Invalid canonical config: allowedExtensionIds must be an array", + ); } if (parsed.allowedExtensionIds.length === 0) { @@ -95,6 +99,7 @@ function listTextFilesRecursively(root: string): string[] { ".turbo", ".idea", ".vscode", + ".codex-worktrees", ]); const allowedExtensions = new Set([ @@ -196,12 +201,20 @@ describe("Chrome extension allowlist guard", () => { expect(new Set(ALLOWED_EXTENSION_ORIGINS)).toEqual(expectedOrigins); }); - test("concrete extension IDs appear only in canonical config", () => { + test("concrete extension IDs appear only in canonical config or CWS URLs", () => { + // The canonical extension ID may also appear in Chrome Web Store URLs + // (e.g. chromewebstore.google.com/detail/.../ID) or in documentation + // referencing the published extension. Those are acceptable — they + // reference the published extension, not duplicate config. We flag + // files where the ID appears outside of a CWS URL context. const config = parseCanonicalConfig(); const allFiles = listTextFilesRecursively(repoRoot); + const CWS_URL_PATTERN = + /chromewebstore\.google\.com\/detail\/[^/]+\/[a-p]{32}/; + for (const extensionId of config.allowedExtensionIds) { - const matches: string[] = []; + const unexpectedMatches: string[] = []; for (const absPath of allFiles) { const relPath = absPath.replace(`${repoRoot}/`, ""); let content: string; @@ -212,11 +225,17 @@ describe("Chrome extension allowlist guard", () => { if ((err as NodeJS.ErrnoException).code === "ENOENT") continue; throw err; } - if (content.includes(extensionId)) { - matches.push(relPath); + if (!content.includes(extensionId)) continue; + if (relPath === CANONICAL_CONFIG_REL_PATH) continue; + + // Strip all CWS URLs and check if the ID still appears — if it + // does, the file is using the ID as a standalone config value. + const stripped = content.replace(CWS_URL_PATTERN, ""); + if (stripped.includes(extensionId)) { + unexpectedMatches.push(relPath); } } - expect(matches).toEqual([CANONICAL_CONFIG_REL_PATH]); + expect(unexpectedMatches).toEqual([]); } }); }); diff --git a/clients/chrome-extension/README.md b/clients/chrome-extension/README.md index 95fbbf127f0..2d47d1352bf 100644 --- a/clients/chrome-extension/README.md +++ b/clients/chrome-extension/README.md @@ -122,10 +122,10 @@ cd clients/chrome-extension/native-host bun install ``` -2. Export your extension ID(s). Include both the CWS ID and your dev ID if you want both to work: +2. Export your extension ID(s). Include both the CWS ID (from the canonical allowlist) and your dev ID if you want both to work: ```bash -export CWS_EXTENSION_ID=hphbdmpffeigpcdjkckleobjmhhokpne +export CWS_EXTENSION_ID=$(cat ../../meta/browser-extension/chrome-extension-allowlist.json | grep -oE '[a-p]{32}') export DEV_EXTENSION_ID= ```