mirror of
https://github.com/coder/coder.git
synced 2026-11-09 22:23:27 +08:00
Split out from #23000. ## Problem `openAppInNewWindow()` opens workspace apps in a slim popup without `noopener`, leaving `window.opener` intact. Since workspace apps can proxy arbitrary user-hosted content under the dashboard origin, this exposes the Coder dashboard to tabnabbing and same-origin DOM access. We cannot simply pass `"noopener"` to `window.open()` because the WHATWG spec mandates that `window.open()` returns `null` when `"noopener"` is present — indistinguishable from a blocked popup — which would break the popup-blocked error toast. ## Solution Use a two-step approach in `openAppInNewWindow()`: 1. Open `about:blank` first (without `noopener`) so we can detect popup blockers via the `null` return 2. If the popup succeeded, sever the opener reference with `popup.opener = null` 3. Navigate the popup to the target URL via `popup.location.href` This preserves popup-block detection while eliminating the security exposure. ## Tests A vitest case in `AppLink.test.tsx` asserts that `open_in="slim-window"` anchors do not carry `target` or `rel` attributes (since slim-window opening is handled programmatically via `onClick` / `window.open()`, not anchor attributes). --------- Co-authored-by: Kayla はな <kayla@tree.camp>