experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(tile-ipc): tile:chrome-extensions:* strict shims with trustedBuiltin gating (Phase 3.5d)

Add 6 tile:chrome-extensions:* handlers in tile-ipc.ts mirroring the
legacy chrome-ext:* channels in ipc.ts — list, enable, disable,
getStatus, getUiEntries, openPage — each gated behind trustedBuiltin
enforcement (handleViolation on failure). Update tile-preload.cts
api.chromeExtensions methods to route through the new strict channels
(token-passing pattern from Phase 3.5c). Legacy chrome-ext:* handlers
untouched; Wave 3.6d will delete them.

+140 -6
+129
backend/electron/tile-ipc.ts
··· 6336 6336 } 6337 6337 }); 6338 6338 6339 + // ── tile:chrome-extensions:* strict shims (Phase 3.5d) ────────────── 6340 + // 6341 + // Strict counterparts of the legacy `chrome-ext:*` channels in ipc.ts. 6342 + // All handlers require trustedBuiltin — feature tiles must not be able 6343 + // to enumerate or launch chrome extensions through the tile surface. 6344 + // Wave 3.6d will flip tile-preload.cts to call these channels and 6345 + // remove the legacy `chrome-ext:*` invocations. 6346 + 6347 + ipcMain.handle('tile:chrome-extensions:list', async (_event, args: { 6348 + token: string; 6349 + }) => { 6350 + if (!args?.token) return { success: false, error: 'Invalid token' }; 6351 + const grant = getGrantForToken(args.token); 6352 + if (!grant) return { success: false, error: 'Invalid token' }; 6353 + if (!grant.trustedBuiltin) { 6354 + handleViolation(grant, 'chrome-extensions', 'tile:chrome-extensions:list', 'trustedBuiltin required', args.token); 6355 + return { success: false, error: 'trustedBuiltin required for tile:chrome-extensions:list' }; 6356 + } 6357 + try { 6358 + const { getChromeExtensions } = await import('./chrome-extensions.js'); 6359 + const extensions = getChromeExtensions(); 6360 + return { success: true, data: extensions }; 6361 + } catch (err) { 6362 + return { success: false, error: err instanceof Error ? err.message : String(err) }; 6363 + } 6364 + }); 6365 + 6366 + ipcMain.handle('tile:chrome-extensions:enable', async (_event, args: { 6367 + token: string; 6368 + id: string; 6369 + }) => { 6370 + if (!args?.token) return { success: false, error: 'Invalid token' }; 6371 + const grant = getGrantForToken(args.token); 6372 + if (!grant) return { success: false, error: 'Invalid token' }; 6373 + if (!grant.trustedBuiltin) { 6374 + handleViolation(grant, 'chrome-extensions', 'tile:chrome-extensions:enable', 'trustedBuiltin required', args.token); 6375 + return { success: false, error: 'trustedBuiltin required for tile:chrome-extensions:enable' }; 6376 + } 6377 + try { 6378 + const { enableChromeExtension } = await import('./chrome-extensions.js'); 6379 + const result = await enableChromeExtension(args.id); 6380 + return { success: result }; 6381 + } catch (err) { 6382 + return { success: false, error: err instanceof Error ? err.message : String(err) }; 6383 + } 6384 + }); 6385 + 6386 + ipcMain.handle('tile:chrome-extensions:disable', async (_event, args: { 6387 + token: string; 6388 + id: string; 6389 + }) => { 6390 + if (!args?.token) return { success: false, error: 'Invalid token' }; 6391 + const grant = getGrantForToken(args.token); 6392 + if (!grant) return { success: false, error: 'Invalid token' }; 6393 + if (!grant.trustedBuiltin) { 6394 + handleViolation(grant, 'chrome-extensions', 'tile:chrome-extensions:disable', 'trustedBuiltin required', args.token); 6395 + return { success: false, error: 'trustedBuiltin required for tile:chrome-extensions:disable' }; 6396 + } 6397 + try { 6398 + const { disableChromeExtension } = await import('./chrome-extensions.js'); 6399 + const result = await disableChromeExtension(args.id); 6400 + return { success: result }; 6401 + } catch (err) { 6402 + return { success: false, error: err instanceof Error ? err.message : String(err) }; 6403 + } 6404 + }); 6405 + 6406 + ipcMain.handle('tile:chrome-extensions:getStatus', async (_event, args: { 6407 + token: string; 6408 + }) => { 6409 + if (!args?.token) return { success: false, error: 'Invalid token' }; 6410 + const grant = getGrantForToken(args.token); 6411 + if (!grant) return { success: false, error: 'Invalid token' }; 6412 + if (!grant.trustedBuiltin) { 6413 + handleViolation(grant, 'chrome-extensions', 'tile:chrome-extensions:getStatus', 'trustedBuiltin required', args.token); 6414 + return { success: false, error: 'trustedBuiltin required for tile:chrome-extensions:getStatus' }; 6415 + } 6416 + try { 6417 + const { getChromeExtensionStatus } = await import('./chrome-extensions.js'); 6418 + const status = getChromeExtensionStatus(); 6419 + return { success: true, data: status }; 6420 + } catch (err) { 6421 + return { success: false, error: err instanceof Error ? err.message : String(err) }; 6422 + } 6423 + }); 6424 + 6425 + ipcMain.handle('tile:chrome-extensions:getUiEntries', async (_event, args: { 6426 + token: string; 6427 + }) => { 6428 + if (!args?.token) return { success: false, error: 'Invalid token' }; 6429 + const grant = getGrantForToken(args.token); 6430 + if (!grant) return { success: false, error: 'Invalid token' }; 6431 + if (!grant.trustedBuiltin) { 6432 + handleViolation(grant, 'chrome-extensions', 'tile:chrome-extensions:getUiEntries', 'trustedBuiltin required', args.token); 6433 + return { success: false, error: 'trustedBuiltin required for tile:chrome-extensions:getUiEntries' }; 6434 + } 6435 + try { 6436 + const { getChromeExtensionUiEntries } = await import('./chrome-extensions.js'); 6437 + const entries = getChromeExtensionUiEntries(); 6438 + return { success: true, data: entries }; 6439 + } catch (err) { 6440 + return { success: false, error: err instanceof Error ? err.message : String(err) }; 6441 + } 6442 + }); 6443 + 6444 + ipcMain.handle('tile:chrome-extensions:openPage', async (_event, args: { 6445 + token: string; 6446 + id: string; 6447 + type: string; 6448 + }) => { 6449 + if (!args?.token) return { success: false, error: 'Invalid token' }; 6450 + const grant = getGrantForToken(args.token); 6451 + if (!grant) return { success: false, error: 'Invalid token' }; 6452 + if (!grant.trustedBuiltin) { 6453 + handleViolation(grant, 'chrome-extensions', 'tile:chrome-extensions:openPage', 'trustedBuiltin required', args.token); 6454 + return { success: false, error: 'trustedBuiltin required for tile:chrome-extensions:openPage' }; 6455 + } 6456 + try { 6457 + const { openChromeExtensionPage } = await import('./chrome-extensions.js'); 6458 + const win = openChromeExtensionPage(args.id, args.type as import('./chrome-extensions.js').ChromeExtensionUiEntryType); 6459 + if (win) { 6460 + return { success: true, data: { windowId: win.id } }; 6461 + } 6462 + return { success: false, error: `No ${args.type} page available for extension: ${args.id}` }; 6463 + } catch (err) { 6464 + return { success: false, error: err instanceof Error ? err.message : String(err) }; 6465 + } 6466 + }); 6467 + 6339 6468 DEBUG && console.log('[tile-ipc] All tile IPC handlers registered'); 6340 6469 } 6341 6470
+11 -6
backend/electron/tile-preload.cts
··· 2115 2115 // points (popup, options, new-tab, etc.) and register cmd palette 2116 2116 // commands for each openable entry. Gated on trustedBuiltin — feature 2117 2117 // tiles must not enumerate or launch chrome extensions. 2118 + // 2119 + // Phase 3.5d: strict shims added as tile:chrome-extensions:* channels in 2120 + // tile-ipc.ts (trustedBuiltin enforcement + capability-token validation). 2121 + // The legacy chrome-ext:* invocations below are preserved; Wave 3.6d 2122 + // will flip these to tile:chrome-extensions:* and delete the legacy handlers. 2118 2123 api.chromeExtensions = { 2119 2124 list: () => { 2120 2125 if (!trustedBuiltin) { 2121 2126 return Promise.resolve({ success: false, error: 'api.chromeExtensions requires trustedBuiltin' }); 2122 2127 } 2123 - return ipcRenderer.invoke('chrome-ext:list'); 2128 + return ipcRenderer.invoke('tile:chrome-extensions:list', { token: tileToken }); 2124 2129 }, 2125 2130 enable: (id: string) => { 2126 2131 if (!trustedBuiltin) { 2127 2132 return Promise.resolve({ success: false, error: 'api.chromeExtensions requires trustedBuiltin' }); 2128 2133 } 2129 - return ipcRenderer.invoke('chrome-ext:enable', { id }); 2134 + return ipcRenderer.invoke('tile:chrome-extensions:enable', { token: tileToken, id }); 2130 2135 }, 2131 2136 disable: (id: string) => { 2132 2137 if (!trustedBuiltin) { 2133 2138 return Promise.resolve({ success: false, error: 'api.chromeExtensions requires trustedBuiltin' }); 2134 2139 } 2135 - return ipcRenderer.invoke('chrome-ext:disable', { id }); 2140 + return ipcRenderer.invoke('tile:chrome-extensions:disable', { token: tileToken, id }); 2136 2141 }, 2137 2142 getStatus: () => { 2138 2143 if (!trustedBuiltin) { 2139 2144 return Promise.resolve({ success: false, error: 'api.chromeExtensions requires trustedBuiltin' }); 2140 2145 } 2141 - return ipcRenderer.invoke('chrome-ext:getStatus'); 2146 + return ipcRenderer.invoke('tile:chrome-extensions:getStatus', { token: tileToken }); 2142 2147 }, 2143 2148 getUiEntries: () => { 2144 2149 if (!trustedBuiltin) { 2145 2150 return Promise.resolve({ success: false, error: 'api.chromeExtensions requires trustedBuiltin' }); 2146 2151 } 2147 - return ipcRenderer.invoke('chrome-ext:getUiEntries'); 2152 + return ipcRenderer.invoke('tile:chrome-extensions:getUiEntries', { token: tileToken }); 2148 2153 }, 2149 2154 openPage: (id: string, type: string) => { 2150 2155 if (!trustedBuiltin) { 2151 2156 return Promise.resolve({ success: false, error: 'api.chromeExtensions requires trustedBuiltin' }); 2152 2157 } 2153 - return ipcRenderer.invoke('chrome-ext:openPage', { id, type }); 2158 + return ipcRenderer.invoke('tile:chrome-extensions:openPage', { token: tileToken, id, type }); 2154 2159 }, 2155 2160 }; 2156 2161