From 5f4ae53b10970ec31ba4aa1b16b5cac5831000f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 25 May 2026 19:31:57 +0000 Subject: [PATCH 1/2] fix(core): revert Function constructor back to inline IIFE for composition scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Function constructor (3bb0d1ef) breaks sub-composition scripts that call document.getElementById() — the constructor creates functions with global scope, losing access to the composition-scoped DOM proxy. Native method calls on proxy-returned elements throw "Illegal invocation". Reverts to the inline IIFE that preserves the closure over __hfScoped* variables. The original motivation (handling in source) is already handled by the compiler's script bundling path. Closes #1074 --- packages/core/src/compiler/compositionScoping.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/compiler/compositionScoping.ts b/packages/core/src/compiler/compositionScoping.ts index 72e9cbbf8..ba9412f30 100644 --- a/packages/core/src/compiler/compositionScoping.ts +++ b/packages/core/src/compiler/compositionScoping.ts @@ -216,7 +216,6 @@ export function wrapScopedCompositionScript( const authoredRootIdFormsLiteral = JSON.stringify( getAuthoredRootIdSelectorForms(authoredRootId?.trim() || ""), ); - const sourceLiteral = JSON.stringify(source); return `(function(){ var __hfCompId = ${compositionIdLiteral}; var __hfTimelineCompId = ${timelineCompositionIdLiteral}; @@ -486,8 +485,9 @@ export function wrapScopedCompositionScript( }); var __hfRun = function() { try { - var __hfScript = Function("document", "gsap", "window", "__hyperframes", ${sourceLiteral}); - __hfScript.call(window, __hfScopedDocument, __hfScopedGsap, __hfScopedWindow, __hfScopedHyperframes); + (function(document, gsap, window, __hyperframes) { +${source} + }).call(window, __hfScopedDocument, __hfScopedGsap, __hfScopedWindow, __hfScopedHyperframes); } catch (_err) { console.error(__hfErrorLabel, __hfCompId, _err); } From 6d97c3d8212fcdaae3625f25880c0a14bf58556a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 25 May 2026 19:41:14 +0000 Subject: [PATCH 2/2] fix(core): restore IIFE with escaping for composition scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Function constructor (3bb0d1ef) was a security hardening to prevent injection, but it broke sub-composition DOM proxy scoping. This restores the inline IIFE (preserving closure scope) while adding → <\/script> escaping to maintain the injection prevention. Updates tests to match the new IIFE output shape. Closes #1074 --- packages/core/src/compiler/compositionScoping.test.ts | 7 ++++--- packages/core/src/compiler/compositionScoping.ts | 2 +- packages/core/src/compiler/htmlBundler.test.ts | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/core/src/compiler/compositionScoping.test.ts b/packages/core/src/compiler/compositionScoping.test.ts index 5a0a0cd3a..5d806ccdc 100644 --- a/packages/core/src/compiler/compositionScoping.test.ts +++ b/packages/core/src/compiler/compositionScoping.test.ts @@ -572,14 +572,15 @@ window.__afterTimeline = window.__timelines.scene; expect(scoped).toContain('[data-composition-id="chrome-overlay"] .child-element'); }); - it("wraps scoped composition script source as a string literal", () => { + it("escapes in scoped composition script source to prevent injection", () => { const wrapped = wrapScopedCompositionScript( 'window.payload = "";', "scene", ); - expect(wrapped).toContain('Function("document", "gsap", "window", "__hyperframes", '); - expect(wrapped).toContain('\\"\\"'); + expect(wrapped).toContain("(function(document, gsap, window, __hyperframes)"); + expect(wrapped).not.toContain("