From d98679924159380dbe84d027b80774cde8334f2e Mon Sep 17 00:00:00 2001 From: Ryan Christian <33403762+rschristian@users.noreply.github.com> Date: Wed, 3 Jul 2024 00:53:27 -0500 Subject: [PATCH] fix: Support unicode in REPL query param (#1162) --- src/components/controllers/repl-page.jsx | 9 ++++----- src/components/controllers/repl/index.jsx | 3 ++- .../controllers/repl/query-encode.js | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 src/components/controllers/repl/query-encode.js diff --git a/src/components/controllers/repl-page.jsx b/src/components/controllers/repl-page.jsx index 888126769..638fc0359 100644 --- a/src/components/controllers/repl-page.jsx +++ b/src/components/controllers/repl-page.jsx @@ -1,5 +1,6 @@ import { useLocation, useRoute } from 'preact-iso'; import { Repl } from './repl'; +import { base64ToText } from './repl/query-encode.js'; import { fetchExample } from './repl/examples'; import { useContent, useResource } from '../../lib/use-resource'; import { useTitle, useDescription } from './utils'; @@ -39,7 +40,7 @@ async function getInitialCode(query) { const { route } = useLocation(); let code; if (query.code) { - code = querySafetyCheck(atob(query.code)); + code = querySafetyCheck() && base64ToText(query.code); } else if (query.example) { code = await fetchExample(query.example); if (!code) { @@ -62,13 +63,11 @@ async function getInitialCode(query) { return code; } -async function querySafetyCheck(code) { +function querySafetyCheck() { return ( !document.referrer || document.referrer.indexOf(location.origin) === 0 || // eslint-disable-next-line no-alert confirm('Are you sure you want to run the code contained in this link?') - ) - ? code - : undefined; + ); } diff --git a/src/components/controllers/repl/index.jsx b/src/components/controllers/repl/index.jsx index f6055c499..ed32d2316 100644 --- a/src/components/controllers/repl/index.jsx +++ b/src/components/controllers/repl/index.jsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'preact/hooks'; import { useLocation, useRoute } from 'preact-iso'; import { Splitter } from '../../splitter'; +import { textToBase64 } from './query-encode.js'; import { EXAMPLES, fetchExample } from './examples'; import { ErrorOverlay } from './error-overlay'; import { useStoredValue } from '../../../lib/localstorage'; @@ -67,7 +68,7 @@ export function Repl({ code }) { if (!query.example) { // We use `history.replaceState` here as the code is only relevant on mount. // There's no need to notify the router of the change. - history.replaceState(null, null, `/repl?code=${encodeURIComponent(btoa(editorCode))}`); + history.replaceState(null, null, `/repl?code=${encodeURIComponent(textToBase64(editorCode))}`); } try { diff --git a/src/components/controllers/repl/query-encode.js b/src/components/controllers/repl/query-encode.js new file mode 100644 index 000000000..126fa1971 --- /dev/null +++ b/src/components/controllers/repl/query-encode.js @@ -0,0 +1,19 @@ +/** + * Encode text to base64 with unicode support + * + * @param {string} text + */ +export function textToBase64(text) { + const bytes = new TextEncoder().encode(text); + return btoa(String.fromCharCode(...bytes)); +} + +/** + * Decode text from base64 with unicode support + * + * @param {string} base64 + */ +export function base64ToText(base64) { + const bytes = Uint8Array.from(atob(base64), c => c.charCodeAt(0)); + return new TextDecoder().decode(bytes); +}