From 084d7ee81bfa0db1cc2de1c6a631dc34aa3789d0 Mon Sep 17 00:00:00 2001 From: Maarten <59102109+conceptblenders@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:43:19 +0200 Subject: [PATCH 1/3] fix(outlook): add Trusted Types policy and extend auth timeout Outlook on cloud.microsoft enforces a Trusted Types CSP policy that causes zod's allowsEval probe (new Function("")) to log a console violation. Creating a 'default' Trusted Types policy before any zod code runs lets the probe complete silently via its existing try/catch. Also extends waitForAuth timeout from 5000ms to 30000ms. When Outlook redirects through an OAuth flow (#code= URL fragment), MSAL tokens are not yet in localStorage when the adapter first initializes. The previous 5s window was too short for the OAuth handshake to complete on slower connections or enterprise SSO flows. Co-Authored-By: Claude Sonnet 4.6 --- plugins/outlook/src/index.ts | 13 +++++++++++++ plugins/outlook/src/outlook-api.ts | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/plugins/outlook/src/index.ts b/plugins/outlook/src/index.ts index b4e88edf3..63cb343b2 100644 --- a/plugins/outlook/src/index.ts +++ b/plugins/outlook/src/index.ts @@ -1,3 +1,16 @@ +// Outlook enforces Trusted Types (CSP). Zod's allowsEval probe calls +// new Function("") which requires a 'default' Trusted Types policy to exist. +// Creating it here (before any zod code runs) lets the try/catch in zod +// succeed silently instead of logging a console violation. +if (typeof window !== 'undefined') { + try { + const tt = (window as unknown as { trustedTypes?: { createPolicy?: (name: string, rules: Record string>) => void } }).trustedTypes; + tt?.createPolicy?.('default', { createScript: (s: string) => s }); + } catch { + // 'default' policy already exists, or Trusted Types not supported — safe to ignore. + } +} + import { OpenTabsPlugin } from '@opentabs-dev/plugin-sdk'; import type { ToolDefinition } from '@opentabs-dev/plugin-sdk'; import { isAuthenticated, waitForAuth } from './outlook-api.js'; diff --git a/plugins/outlook/src/outlook-api.ts b/plugins/outlook/src/outlook-api.ts index 31d714f80..0a349280e 100644 --- a/plugins/outlook/src/outlook-api.ts +++ b/plugins/outlook/src/outlook-api.ts @@ -161,7 +161,7 @@ const getAuth = (): OutlookAuth | null => { export const isAuthenticated = (): boolean => getAuth() !== null; export const waitForAuth = (): Promise => - waitUntil(() => isAuthenticated(), { interval: 500, timeout: 5000 }).then( + waitUntil(() => isAuthenticated(), { interval: 500, timeout: 30000 }).then( () => true, () => false, ); From 0e6878984cd28e4d13bb3971d406a905d9387d0f Mon Sep 17 00:00:00 2001 From: Maarten <59102109+conceptblenders@users.noreply.github.com> Date: Mon, 20 Apr 2026 11:15:19 +0200 Subject: [PATCH 2/3] fix(outlook): narrow Trusted Types default policy to zod probe only Per CodeRabbit review: the identity createScript function on the default Trusted Types policy was too permissive, allowing any string to become TrustedScript. Restrict it to only accept the empty string ("") that zod's allowsEval probe passes to new Function(), and throw for all other inputs to preserve Trusted Types protections. Also check tt.defaultPolicy before creating to avoid duplicate creation. Co-Authored-By: Claude Sonnet 4.6 --- plugins/outlook/src/index.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/outlook/src/index.ts b/plugins/outlook/src/index.ts index 63cb343b2..880ef5cd3 100644 --- a/plugins/outlook/src/index.ts +++ b/plugins/outlook/src/index.ts @@ -4,8 +4,18 @@ // succeed silently instead of logging a console violation. if (typeof window !== 'undefined') { try { - const tt = (window as unknown as { trustedTypes?: { createPolicy?: (name: string, rules: Record string>) => void } }).trustedTypes; - tt?.createPolicy?.('default', { createScript: (s: string) => s }); + const tt = (window as Window & { + trustedTypes?: TrustedTypePolicyFactory & { defaultPolicy?: TrustedTypePolicy | null }; + }).trustedTypes; + if (tt && !tt.defaultPolicy) { + tt.createPolicy('default', { + createScript: (s: string) => { + // Only allow the empty-string probe used by zod's allowsEval feature-detect. + if (s !== '') throw new TypeError('Blocked Trusted Types script conversion'); + return s; + }, + }); + } } catch { // 'default' policy already exists, or Trusted Types not supported — safe to ignore. } From 338ff5b8a96ade0fcbcd3dc375a0fcdf3e08fe32 Mon Sep 17 00:00:00 2001 From: Maarten <59102109+conceptblenders@users.noreply.github.com> Date: Mon, 20 Apr 2026 23:00:05 +0200 Subject: [PATCH 3/3] fix(outlook): address maintainer review feedback - Revert waitForAuth timeout to 5000ms (no-op: platform caps isReady at 5s, re-polls every 30s anyway) - Fix comment: clarify that zod's allowsEval is lazy/memoized, so the default Trusted Types policy just needs to exist before first runtime parser call, not before hoisted import statements - Add #code= fragment guard in isAuthenticated(): return false immediately during OAuth redirect so the platform's 30s re-poll handles token detection once MSAL completes the handshake Co-Authored-By: Claude Sonnet 4.6 --- plugins/outlook/src/index.ts | 9 +++++---- plugins/outlook/src/outlook-api.ts | 11 +++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/outlook/src/index.ts b/plugins/outlook/src/index.ts index 880ef5cd3..199c2fbba 100644 --- a/plugins/outlook/src/index.ts +++ b/plugins/outlook/src/index.ts @@ -1,7 +1,8 @@ -// Outlook enforces Trusted Types (CSP). Zod's allowsEval probe calls -// new Function("") which requires a 'default' Trusted Types policy to exist. -// Creating it here (before any zod code runs) lets the try/catch in zod -// succeed silently instead of logging a console violation. +// Outlook on cloud.microsoft enforces Trusted Types (CSP). Zod's allowsEval +// feature-detect calls new Function("") the first time a zod parser runs +// (lazy, memoized via cached()). This policy just needs to exist before that +// first runtime call — import statements are hoisted regardless, but the probe +// only fires when a tool handler first invokes a zod schema at runtime. if (typeof window !== 'undefined') { try { const tt = (window as Window & { diff --git a/plugins/outlook/src/outlook-api.ts b/plugins/outlook/src/outlook-api.ts index 0a349280e..c620ec223 100644 --- a/plugins/outlook/src/outlook-api.ts +++ b/plugins/outlook/src/outlook-api.ts @@ -158,10 +158,17 @@ const getAuth = (): OutlookAuth | null => { return auth; }; -export const isAuthenticated = (): boolean => getAuth() !== null; +export const isAuthenticated = (): boolean => { + // During OAuth redirect the #code= fragment is present but MSAL tokens are + // not yet in localStorage. Return false early so the platform's 30s re-poll + // catches the token once the handshake completes, rather than burning the + // 5s isReady window on token searches that will all fail. + if (typeof window !== 'undefined' && window.location.hash.includes('code=')) return false; + return getAuth() !== null; +}; export const waitForAuth = (): Promise => - waitUntil(() => isAuthenticated(), { interval: 500, timeout: 30000 }).then( + waitUntil(() => isAuthenticated(), { interval: 500, timeout: 5000 }).then( () => true, () => false, );