Repository navigation
[8.4-stable] feat(csp): Remove inline script from the preview frame - #4372
Merged
Merged
Conversation
The bundle opened by reading a bare Alchemy global, which only resolved because the preview partial rendered an inline script to define it. That forced host applications running their own content security policy to allow an inline script on every frontend page. Nothing outside this file ever used the element selector it hung off that global, so module level state serves it just as well. Without a namespace object to attach methods to, plain functions over module state replace the object literal and the shared this it existed to provide. The preview bundle ships to the frontend, where none of the globals the admin setup file defines exist, so its spec runs as its own test project rather than borrowing the admin environment. (cherry picked from commit 8ab4a89)
With the bundle no longer reading a global, the inline script the partial rendered has nothing left to define: its only payload was a locale that nothing reads, since the admin resolves the current locale from the html lang attribute. Removing it takes an inline script off every frontend page of host applications that render the alchemy/edit_mode partial. (cherry picked from commit 5dbe047)
The preview script had no coverage at all, so nothing caught a break in the message protocol it shares with the admin window. These examples drive the real iframe on the page edit screen and assert both directions: clicking an element in the preview selects its editor, and clicking an editor selects the element in the preview. Exercising the built bundle in a browser also guards the assumption that the preview frame needs no inline script to boot. (cherry picked from commit fb60855)
The preview script is render blocking and host layouts decide where the alchemy/edit_mode partial goes, so any page content rendered below it did not exist yet when the script collected its elements. Those elements were silently not selectable in the preview frame. Waiting for DOMContentLoaded fixes that, guarded by a readyState check because the event has already fired when the script loads late. The dummy layout now renders the partial above its content, so the system specs cover the arrangement that used to break. (cherry picked from commit 030a52a)
The preview wrote a selected class onto host application markup, but nothing styles or reads it: the script only used it to remember which element to leave alone on hover. Worse, deselecting removed the class from every preview element, stripping a selected class the host application may have put there for its own purposes. A module level reference keeps that state without touching the host's markup. (cherry picked from commit 70e1a46)
The click listener sat on each element in the bubble phase, so handlers attached to links inside an element ran first. Client side routers like vue-router bind their click handler directly to the link and only skip navigation once the event was default prevented, which it was not yet, so clicking a link in the preview navigated the frame away. A single click listener on the window in the capture phase runs before any handler in the page. Capturing on the elements themselves would have reached the outermost of nested elements first, so the listener resolves the innermost element from the event target instead. Interactive content inside an element no longer receives clicks in the preview, consistent with links already being inert there. (cherry picked from commit f598bfe)
Capturing every click inside an element kept routers from navigating, but it also starved the page's own interactive content: tabs, accordions and carousel controls inside an element stopped reacting in the preview, where they used to work and select their element at the same time. Navigation only starts from links. RouterLink, NuxtLink and React Router's Link all render an anchor and navigate from its click handler, so only clicks inside an anchor are taken in the capture phase. Every other click reaches the page first and selects its element on the way out of the bubble phase, as before. Links rendered through the custom slot of RouterLink or NuxtLink without an anchor are not intercepted. (cherry picked from commit d0577eb)
Every click inside an element was default prevented after the page had handled it, so native behavior like toggling details or checking a checkbox never happened in the preview. Only navigation needs blocking. Clicks on links and on form submit buttons navigate the frame away, so those are taken in the capture phase as before. Every other click now only selects its element and keeps its default behavior. A button only counts as a submit button when it belongs to a form, since a button without a type reports submit even when it submits nothing. (cherry picked from commit c1fa5f3)
3 tasks done
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 8.4-stable #4372 +/- ##
===========================================
Coverage 98.42% 98.42%
===========================================
Files 369 369
Lines 9749 9749
===========================================
Hits 9595 9595
Misses 154 154
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
tvdeyen
approved these changes
Oct 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport
This will backport the following commits from
mainto8.4-stable:Questions ?
Please refer to the Backport tool documentation