Skip to content

Commit 5f08d30

Browse files
committed
fix(live-preview): prevent reflected XSS in browser-not-supported page
The error page reflected URL params back via innerHTML, so a crafted URL could inject arbitrary HTML/JS. Switch to safe DOM construction (textContent + createElement) and reduce the strings to plain text with a "\n" line-break marker and a "{0}" placeholder for the phcode.io link, which the page now builds as a hardcoded anchor.
1 parent 9532ce2 commit 5f08d30

2 files changed

Lines changed: 36 additions & 10 deletions

File tree

src/assets/phoenix-splash/live-preview-error.html

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,40 @@
55
<link rel="stylesheet" href="styles.css">
66
<meta name="robots" content="noindex">
77
<script type="text/javascript">
8+
// URL params are reflected into the page, so they must NEVER reach
9+
// innerHTML — that would be reflected XSS. Build the DOM with
10+
// textContent and createElement only. The strings are plain text:
11+
// mainHeading uses "\n" for line breaks, mainSpan uses "{0}" as
12+
// the placeholder for the phcode.io link.
813
function applyTranslations() {
9-
const urlSearchParams = new URLSearchParams(window.location.search);
10-
const params = Object.fromEntries(urlSearchParams.entries());
11-
if(params.mainHeading){
12-
document.getElementById("mainHeading").innerHTML = decodeURIComponent(params.mainHeading);
14+
const params = Object.fromEntries(
15+
new URLSearchParams(window.location.search).entries()
16+
);
17+
if (params.mainHeading) {
18+
const el = document.getElementById("mainHeading");
19+
el.textContent = "";
20+
const lines = decodeURIComponent(params.mainHeading).split("\n");
21+
lines.forEach(function(line, i) {
22+
if (i > 0) {
23+
el.appendChild(document.createElement("br"));
24+
}
25+
el.appendChild(document.createTextNode(line));
26+
});
1327
}
14-
if(params.mainSpan){
15-
document.getElementById("mainSpan").innerHTML = decodeURIComponent(params.mainSpan);
28+
if (params.mainSpan) {
29+
const el = document.getElementById("mainSpan");
30+
el.textContent = "";
31+
const parts = decodeURIComponent(params.mainSpan).split("{0}");
32+
parts.forEach(function(part, i) {
33+
if (i > 0) {
34+
const a = document.createElement("a");
35+
a.href = "https://phcode.io";
36+
a.style.color = "white";
37+
a.textContent = "phcode.io";
38+
el.appendChild(a);
39+
}
40+
el.appendChild(document.createTextNode(part));
41+
});
1642
}
1743
}
1844
</script>
@@ -23,9 +49,9 @@
2349
<div id="mainDiv">
2450
<img id="logo" src="images/phoenix-logo-broken.svg"/>
2551
<div id="MainText" class="safari-text">
26-
<h2 id="mainHeading">Uh Oh! <br>Your current browser doesn't support live preview.</h2>
52+
<h2 id="mainHeading">Uh Oh!<br>Your current browser doesn't support live preview.</h2>
2753
<span id="mainSpan">
28-
Get the best live preview experience by downloading our native apps for Windows, Mac, and Linux from <a href="https://phcode.io" style="color: white">phcode.io</a>.<br>
54+
Get the best live preview experience by downloading our native apps for Windows, Mac, and Linux from <a href="https://phcode.io" style="color: white">phcode.io</a>.
2955
</span><br>
3056
</div>
3157
</div>

src/nls/root/strings.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,8 +1503,8 @@ define({
15031503
"DESCRIPTION_LIVEDEV_NO_PREVIEW_DETAILS": "Please select an HTML file to preview",
15041504
"DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED": "Preview Unavailable!",
15051505
"DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED_DETAILS": "This HTML file is not part of the current project. For security reasons, only project files can be live-previewed. To preview this file, open its containing folder as a separate project.",
1506-
"DESCRIPTION_LIVEDEV_MAIN_HEADING": "Uh Oh! <br>Your current browser doesn't support live preview.",
1507-
"DESCRIPTION_LIVEDEV_MAIN_SPAN": "Get the best live preview experience by downloading our native apps for Windows, Mac, and Linux from <a href=\"https://phcode.io\" style=\"color: white\">phcode.io</a>.<br>",
1506+
"DESCRIPTION_LIVEDEV_MAIN_HEADING": "Uh Oh!\nYour current browser doesn't support live preview.",
1507+
"DESCRIPTION_LIVEDEV_MAIN_SPAN": "Get the best live preview experience by downloading our native apps for Windows, Mac, and Linux from {0}.",
15081508
"DESCRIPTION_LIVEDEV_SECURITY_POPOUT_MESSAGE": "You are about to open a file for live preview. Please proceed only if you trust the source of this project. Click 'Trust Project' to continue, or close this window if you do not trust the source.",
15091509
"DESCRIPTION_LIVEDEV_SECURITY_TRUST_MESSAGE": "You are about to open a file for live preview. Please proceed by clicking 'Trust Project' only if you trust the source of this project!",
15101510
"CONFIRM_EXTERNAL_BROWSER_TITLE": "Pop-ups Blocked",

0 commit comments

Comments
 (0)