Skip to content

Commit 2baf9bd

Browse files
authored
enhance(libs/play): add logging + refine runner errors (#12645)
* enhance(play): add logging + distinguish runner responses * chore(play): use HTTP 204 for empty response * fix(play): return separately from response * fix(libs/play): avoid HTTP 204 for Safari * chore(libs/play): refine runner logging * refactor(libs/play): inline is{OnMDN,Iframe} variables * chore(libs/play): refine comment * chore(libs/play): be consistent about returning response We should just `return`, but now is not the time to fix that.
1 parent 76c2dde commit 2baf9bd

File tree

1 file changed

+38
-13
lines changed

1 file changed

+38
-13
lines changed

libs/play/index.js

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -623,10 +623,9 @@ function playSubdomain(hostname) {
623623
}
624624

625625
/**
626-
* @param {URL} referer
626+
* @param {string} hostname
627627
*/
628-
function isMDNReferer(referer) {
629-
const { hostname } = referer;
628+
function isMDNHost(hostname) {
630629
return (
631630
hostname === ORIGIN_MAIN ||
632631
// Review Companion (old/new).
@@ -655,21 +654,47 @@ export async function handleRunner(req, res) {
655654
"https://example.com"
656655
);
657656
const stateParam = url.searchParams.get("state");
658-
const { state, hash } = await decompressFromBase64(stateParam);
659657

660-
const isLocalhost = req.hostname === "localhost";
661-
const hasMatchingHash = playSubdomain(req.hostname) === hash;
662-
const isIframeOnMDN =
663-
isMDNReferer(referer) && req.headers["sec-fetch-dest"] === "iframe";
658+
if (!stateParam) {
659+
console.warn("[runner] Missing state parameter");
660+
return res.status(400).end();
661+
}
664662

665-
if (
666-
!stateParam ||
667-
!state ||
668-
(!isLocalhost && !hasMatchingHash && !isIframeOnMDN)
669-
) {
663+
const { state, hash } = await decompressFromBase64(stateParam);
664+
665+
if (!state) {
666+
console.warn("[runner] Invalid state value");
670667
return res.status(404).end();
671668
}
672669

670+
if (req.hostname !== "localhost") {
671+
// For security reasons, we only allow the runner:
672+
// 1. on localhost (without any restrictions),
673+
// 2. if the subdomain matches the hash (for embedded direct links), or
674+
// 3. in iframes on MDN.
675+
const subdomain = playSubdomain(req.hostname);
676+
677+
if (subdomain !== hash) {
678+
const secFetchDest = req.headers["sec-fetch-dest"];
679+
680+
if (secFetchDest !== "iframe") {
681+
console.warn(
682+
`[runner] Disallowed Sec-Fetch-Dest (expected "iframe", was ${JSON.stringify(secFetchDest)})`
683+
);
684+
return res.status(403).end();
685+
}
686+
687+
const { hostname } = referer;
688+
689+
if (!isMDNHost(hostname)) {
690+
console.warn(
691+
`[runner] Disallowed Referer (expected MDN host, was ${JSON.stringify(hostname)})`
692+
);
693+
return res.status(403).end();
694+
}
695+
}
696+
}
697+
673698
const json = JSON.parse(state);
674699
const codeParam = url.searchParams.get("code");
675700
const codeCookie = req.cookies["code"];

0 commit comments

Comments
 (0)