-
Notifications
You must be signed in to change notification settings - Fork 55
fix(LiveControls): restrict postMessage handler to trusted origins #1183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |||||||||||||||||||||||||||||
| import { context } from "../deco.ts"; | ||||||||||||||||||||||||||||||
| import { DomInspector, DomInspectorActivators } from "../deps.ts"; | ||||||||||||||||||||||||||||||
| import type { Flag, Site } from "../types.ts"; | ||||||||||||||||||||||||||||||
| import { adminDomains } from "../utils/admin.ts"; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const IS_LOCALHOST = context.deploymentId === undefined; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
@@ -110,9 +111,45 @@ const main = () => { | |||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const STATIC_TRUSTED_ORIGINS = [ | ||||||||||||||||||||||||||||||
| "https://deco.cx", | ||||||||||||||||||||||||||||||
| "https://admin.deco.cx", | ||||||||||||||||||||||||||||||
| "https://play.deco.cx", | ||||||||||||||||||||||||||||||
| "https://admin-cx.deco.page", | ||||||||||||||||||||||||||||||
| "https://deco.chat", | ||||||||||||||||||||||||||||||
| "https://admin.decocms.com", | ||||||||||||||||||||||||||||||
| "https://decocms.com", | ||||||||||||||||||||||||||||||
| "https://studio.decocms.com", | ||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||
| const runtimeTrustedOrigins = (() => { | ||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||
| const el = document.getElementById("__DECO_TRUSTED_ORIGINS"); | ||||||||||||||||||||||||||||||
| const parsed = JSON.parse(el?.textContent || "[]"); | ||||||||||||||||||||||||||||||
| return Array.isArray(parsed) ? parsed.filter((o) => typeof o === "string") : []; | ||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||
| return []; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| })(); | ||||||||||||||||||||||||||||||
| const TRUSTED_ORIGINS = STATIC_TRUSTED_ORIGINS.concat(runtimeTrustedOrigins); | ||||||||||||||||||||||||||||||
| const isTrustedOrigin = (origin: string) => | ||||||||||||||||||||||||||||||
| TRUSTED_ORIGINS.indexOf(origin) !== -1 || | ||||||||||||||||||||||||||||||
| (origin.startsWith("https://") && origin.endsWith(".deco.cx")) || | ||||||||||||||||||||||||||||||
| origin === WINDOW.location.origin; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const onMessage = (event: MessageEvent<LiveEvent>) => { | ||||||||||||||||||||||||||||||
| if (!isTrustedOrigin(event.origin)) { | ||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const { data } = event; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||
| typeof data !== "object" || data === null || | ||||||||||||||||||||||||||||||
| typeof (data as { type?: unknown }).type !== "string" | ||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| switch (data.type) { | ||||||||||||||||||||||||||||||
| case "scrollToComponent": { | ||||||||||||||||||||||||||||||
| const findById = document | ||||||||||||||||||||||||||||||
|
|
@@ -183,6 +220,13 @@ function LiveControls({ site, page, flags }: Props) { | |||||||||||||||||||||||||||||
| __html: JSON.stringify({ page, site, flags }), | ||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||
| <script | ||||||||||||||||||||||||||||||
| type="application/json" | ||||||||||||||||||||||||||||||
| id="__DECO_TRUSTED_ORIGINS" | ||||||||||||||||||||||||||||||
| dangerouslySetInnerHTML={{ | ||||||||||||||||||||||||||||||
| __html: JSON.stringify(adminDomains), | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Escape Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||
|
Comment on lines
+223
to
+229
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Inspect adminDomains declaration and its source in utils/admin.ts
fd -i 'admin.ts' --type f | xargs grep -n 'adminDomains\|ADMIN_DOMAINS\|env\|Deno.env\|process.env' -A3 -B1Repository: deco-cx/deco Length of output: 868 🏁 Script executed: fd -type f -name 'admin.ts' | xargs head -60 | cat -nRepository: deco-cx/deco Length of output: 227 🏁 Script executed: fd -t f -name 'admin.ts' | xargs cat | head -60Repository: deco-cx/deco Length of output: 287 🏁 Script executed: fd -t f -name 'admin.ts' -x cat {} | head -60Repository: deco-cx/deco Length of output: 287 🏁 Script executed: git ls-files | grep -i 'admin.ts' | xargs catRepository: deco-cx/deco Length of output: 1829
While The fix is a one-liner and should be applied to prevent this pattern from being copied elsewhere: 🛡️ Proposed fix- __html: JSON.stringify(adminDomains),
+ __html: JSON.stringify(adminDomains).replace(/</g, "\\u003c"),📝 Committable suggestion
Suggested change
🧰 Tools🪛 ast-grep (0.42.1)[warning] 225-225: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks. (react-unsafe-html-injection) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| <script | ||||||||||||||||||||||||||||||
| type="module" | ||||||||||||||||||||||||||||||
| dangerouslySetInnerHTML={{ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.