From c87d44e60cfae8974d6a13b42001369899088178 Mon Sep 17 00:00:00 2001 From: Ben White Date: Fri, 27 Dec 2024 08:50:36 +0000 Subject: [PATCH] Adds option for nonce support (#1630) --- .../utils/external-scripts-loader.test.ts | 19 +++++++++++++++++++ src/entrypoints/external-scripts-loader.ts | 10 +++++++++- src/types.ts | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/__tests__/utils/external-scripts-loader.test.ts b/src/__tests__/utils/external-scripts-loader.test.ts index 9bed74b28..7725649c8 100644 --- a/src/__tests__/utils/external-scripts-loader.test.ts +++ b/src/__tests__/utils/external-scripts-loader.test.ts @@ -61,5 +61,24 @@ describe('external-scripts-loader', () => { const new_script = scripts[0] expect(new_script.src).toBe('https://us-assets.i.posthog.com/static/toolbar.js?v=1.0.0&t=1726067100000') }) + + it('allows adding a nonce via the prepare_external_dependency_script config', () => { + mockPostHog.config.prepare_external_dependency_script = (script) => { + script.nonce = '123' + return script + } + assignableWindow.__PosthogExtensions__.loadExternalDependency(mockPostHog, 'toolbar', callback) + const scripts = document!.getElementsByTagName('script') + const new_script = scripts[0] + expect(new_script.nonce).toBe('123') + }) + + it('does not load script if prepare_external_dependency_script returns null', () => { + mockPostHog.config.prepare_external_dependency_script = () => null + assignableWindow.__PosthogExtensions__.loadExternalDependency(mockPostHog, 'toolbar', callback) + const scripts = document!.getElementsByTagName('script') + expect(scripts.length).toBe(0) + expect(callback).toHaveBeenCalledWith('prepare_external_dependency_script returned null') + }) }) }) diff --git a/src/entrypoints/external-scripts-loader.ts b/src/entrypoints/external-scripts-loader.ts index 6d3a0441c..f1d6b8322 100644 --- a/src/entrypoints/external-scripts-loader.ts +++ b/src/entrypoints/external-scripts-loader.ts @@ -14,13 +14,21 @@ const loadScript = (posthog: PostHog, url: string, callback: (error?: string | E if (!document) { return callback('document not found') } - const scriptTag = document.createElement('script') + let scriptTag: HTMLScriptElement | null = document.createElement('script') scriptTag.type = 'text/javascript' scriptTag.crossOrigin = 'anonymous' scriptTag.src = url scriptTag.onload = (event) => callback(undefined, event) scriptTag.onerror = (error) => callback(error) + if (posthog.config.prepare_external_dependency_script) { + scriptTag = posthog.config.prepare_external_dependency_script(scriptTag) + } + + if (!scriptTag) { + return callback('prepare_external_dependency_script returned null') + } + const scripts = document.querySelectorAll('body > script') if (scripts.length > 0) { scripts[0].parentNode?.insertBefore(scriptTag, scripts[0]) diff --git a/src/types.ts b/src/types.ts index 92dd5c7f7..db9c41378 100644 --- a/src/types.ts +++ b/src/types.ts @@ -251,6 +251,7 @@ export interface PostHogConfig { disable_web_experiments: boolean /** If set, posthog-js will never load external scripts such as those needed for Session Replay or Surveys. */ disable_external_dependency_loading?: boolean + prepare_external_dependency_script?: (script: HTMLScriptElement) => HTMLScriptElement | null enable_recording_console_log?: boolean secure_cookie: boolean ip: boolean