From de2a42bd356fd3880d765ac897a0730fbd09f12b Mon Sep 17 00:00:00 2001 From: topkoa Date: Sun, 12 Jul 2026 20:20:15 -0400 Subject: [PATCH] fix(panes): coerce plugin-supplied pane sizes to numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `spec.width` / `spec.height` are plugin-controlled, and the window host builds window.open()'s feature string by concatenation: 'popup,width=' + spec.width + ',height=' + spec.height `spec.width || 380` passed anything truthy straight through. So a width of '300,menubar=1' would not merely be an invalid size — it would inject window features. Less dramatically, any non-numeric value produced a malformed feature string and a pane that failed to open for no visible reason. They now go through _size(): Number, round, reject anything not finite and positive, clamp to 120..4000. A hostile or careless value falls back to the default instead of reaching window.open() at all. Verified against the obvious inputs: '300,menubar=1' -> 380 (default), '300' -> 300, 0/-50/NaN/{}/'abc' -> 380, 1e9 -> 4000, 5 -> 120. Signed-off-by: topkoa --- static/panes/pane-manager.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/static/panes/pane-manager.js b/static/panes/pane-manager.js index 0cd2de9..b7562fb 100644 --- a/static/panes/pane-manager.js +++ b/static/panes/pane-manager.js @@ -85,6 +85,17 @@ // ── Spec ───────────────────────────────────────────────────────────────── + // A pane window's initial size. Plugin-controlled, and the window host builds + // window.open()'s feature string by concatenation — so this has to come out the + // other side as a number, not merely as something number-ish. + const MIN_PANE_PX = 120; + const MAX_PANE_PX = 4000; // wider than any real display; a guard, not a policy + function _size(v, fallback) { + const n = Math.round(Number(v)); + if (!Number.isFinite(n) || n <= 0) return fallback; + return Math.min(MAX_PANE_PX, Math.max(MIN_PANE_PX, n)); + } + function _normalize(spec) { if (!spec || typeof spec !== 'object') throw new TypeError('panes.register: spec must be an object'); if (!spec.id || typeof spec.id !== 'string') throw new TypeError('panes.register: spec.id is required'); @@ -102,8 +113,14 @@ // later (Camera Director rebuilds its panel on every mode change). // Asking for it at open time means we always move the live one. element: typeof spec.element === 'function' ? spec.element : () => spec.element, - width: spec.width || 380, - height: spec.height || 560, + // Coerced to real numbers, because these are plugin-controlled and the + // window host concatenates them into window.open()'s feature string. A + // `width` of '300,menubar=1' would not merely be an invalid size — it + // would inject window features. Anything that isn't a finite positive + // number falls back to the default, and absurd sizes are clamped rather + // than honoured. + width: _size(spec.width, 380), + height: _size(spec.height, 560), defaultHost: spec.defaultHost || 'window', // Called after the element lands in (or returns from) a pane window, // for a plugin that needs to re-measure or re-anchor something.