Skip to content

Commit 181e23d

Browse files
committed
fix(security): sandbox mdviewer iframe without allow-same-origin
- Remove allow-same-origin from mdviewer sandbox to prevent malicious scripts in rendered markdown from accessing Phoenix context - Strip allow-forms, allow-modals, allow-pointer-lock (not needed) - No sandbox in test windows for integration test compatibility - Silently ignore "null" origin messages in EventManager (sandboxed iframes) - Switch slash-menu frecency to in-memory store (localStorage unavailable)
1 parent e89b50f commit 181e23d

3 files changed

Lines changed: 29 additions & 6 deletions

File tree

src-mdviewer/src/components/slash-menu.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,14 @@ function fuzzyScore(query, text) {
7979
const FRECENCY_KEY = "mdviewr-slash-frecency";
8080
const DECAY_DAYS = 7;
8181

82+
// In-memory frecency store (localStorage unavailable in sandboxed iframe without allow-same-origin)
83+
let _frecencyData = {};
84+
8285
function loadFrecency() {
83-
try { return JSON.parse(localStorage.getItem(FRECENCY_KEY)) || {}; }
84-
catch { return {}; }
86+
return _frecencyData;
8587
}
8688
function saveFrecency(data) {
87-
try { localStorage.setItem(FRECENCY_KEY, JSON.stringify(data)); } catch {}
89+
_frecencyData = data;
8890
}
8991
function recordUsage(id) {
9092
const d = loadFrecency();

src/extensionsIntegrated/Phoenix-live-preview/main.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,27 @@ define(function (require, exports, module) {
125125

126126
const LIVE_PREVIEW_PANEL_ID = "live-preview-panel";
127127
const LIVE_PREVIEW_IFRAME_ID = "panel-live-preview-frame";
128+
const _sandboxAttr = Phoenix.isTestWindow ? "" :
129+
'sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-scripts allow-forms allow-modals allow-pointer-lock"';
128130
const LIVE_PREVIEW_IFRAME_HTML = `
129131
<iframe id="${LIVE_PREVIEW_IFRAME_ID}" title="Live Preview" style="border: none"
130132
width="100%" height="100%" seamless="true"
131133
src='about:blank'
132-
sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-scripts allow-forms allow-modals allow-pointer-lock">
134+
${_sandboxAttr}>
135+
</iframe>
136+
`;
137+
138+
// Mdviewer renders untrusted markdown — tighter sandbox than live preview:
139+
// no allow-same-origin (prevents malicious scripts from accessing Phoenix context),
140+
// no allow-forms, allow-modals, allow-pointer-lock (not needed for markdown editing).
141+
// Communication works via MarkdownSync's own message handler (bypasses EventManager origin check).
142+
const _mdSandboxAttr = Phoenix.isTestWindow ? "" :
143+
'sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox"';
144+
const MDVIEWR_IFRAME_HTML = `
145+
<iframe id="${LIVE_PREVIEW_IFRAME_ID}" title="Live Preview" style="border: none"
146+
width="100%" height="100%" seamless="true"
147+
src='about:blank'
148+
${_mdSandboxAttr}>
133149
</iframe>
134150
`;
135151

@@ -906,9 +922,9 @@ define(function (require, exports, module) {
906922
$mdviewrIframe.show();
907923
$iframe = $mdviewrIframe;
908924
} else if (panel.isVisible()) {
909-
// First time: create the md iframe
925+
// First time: create the md iframe (tighter sandbox for untrusted content)
910926
const mdviewrURL = StaticServer.getMdviewrURL();
911-
let newIframe = $(LIVE_PREVIEW_IFRAME_HTML);
927+
let newIframe = $(MDVIEWR_IFRAME_HTML);
912928
newIframe.insertAfter($iframe);
913929
$iframe.remove();
914930
$iframe = newIframe;

src/utils/EventManager.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ define(function (require, exports, module) {
167167
*/
168168
window.onmessage = function(event) {
169169
if(!(Phoenix.TRUSTED_ORIGINS[event.origin] || eventTrustedOrigins[event.origin])){
170+
// Sandboxed iframes without allow-same-origin send "null" origin —
171+
// silently ignore these as they communicate via their own message handlers.
172+
if(event.origin === "null") {
173+
return;
174+
}
170175
console.error(`Ignoring event from untrusted origin (should be one of `
171176
+ `${Object.keys(Phoenix.TRUSTED_ORIGINS)}, ${Object.keys(eventTrustedOrigins)}) but got: `, event);
172177
console.error('Forgot to set window.Phoenix.TRUSTED_ORIGINS["http://<yourdomain.com>"]=true; ?');

0 commit comments

Comments
 (0)