diff --git a/builders/ssg.js b/builders/ssg.js index 27dc3a72..c629ff39 100644 --- a/builders/ssg.js +++ b/builders/ssg.js @@ -39,7 +39,7 @@ module.exports = async function ssg({ output, cache, environment }) { .find((line) => line.indexOf(stateLookup) > -1) .split(stateLookup)[1] .slice(0, -2) - const { instances, page } = JSON.parse(decodeURIComponent(state).replace(/<\\/g, '<')) + const { instances, page } = JSON.parse(decodeURIComponent(state)) if (url !== `/nullstack/${application.environment.key}/offline` && url !== '/404') { pages[url] = page diff --git a/client/render.js b/client/render.js index a71e4eac..14673b10 100644 --- a/client/render.js +++ b/client/render.js @@ -1,3 +1,4 @@ +import { sanitizeInnerHtml } from '../shared/sanitizeString' import generateTruthyString from '../shared/generateTruthyString' import { isFalse, isText } from '../shared/nodes' import { anchorableElement } from './anchorableNode' @@ -28,7 +29,7 @@ export default function render(node, options) { for (const name in node.attributes) { if (name === 'debounce') continue if (name === 'html') { - node.element.innerHTML = node.attributes[name] + node.element.innerHTML = sanitizeInnerHtml(node.attributes[name]) node.head || anchorableElement(node.element) } else if (name.startsWith('on')) { if (node.attributes[name] !== undefined) { diff --git a/client/rerender.js b/client/rerender.js index 9117b739..34f93f4d 100644 --- a/client/rerender.js +++ b/client/rerender.js @@ -1,3 +1,4 @@ +import { sanitizeInnerHtml } from '../shared/sanitizeString' import generateTruthyString from '../shared/generateTruthyString' import { isFalse, isText, isUndefined } from '../shared/nodes' import { anchorableElement } from './anchorableNode' @@ -14,7 +15,7 @@ function updateAttributes(selector, currentAttributes, nextAttributes) { reref(nextAttributes, selector) } else if (name === 'html') { if (nextAttributes[name] !== currentAttributes[name]) { - selector.innerHTML = nextAttributes[name] + selector.innerHTML = sanitizeInnerHtml(nextAttributes[name]) anchorableElement(selector) } } else if (name === 'checked' || name === 'value') { diff --git a/client/router.js b/client/router.js index cbf1e536..ab0b9b12 100644 --- a/client/router.js +++ b/client/router.js @@ -6,6 +6,7 @@ import { updateParams } from './params' import segments from './segments' import windowEvent from './windowEvent' import worker from './worker' +import deserialize from '../shared/deserialize' let redirectTimer = null @@ -40,7 +41,8 @@ class Router { const endpoint = path === '/' ? api : path + api try { const response = await fetch(endpoint) - const payload = await response.json(url) + const meta = await response.text() + const payload = deserialize(meta) client.memory = payload.instances for (const key in payload.page) { page[key] = payload.page[key] diff --git a/shared/sanitizeString.js b/shared/sanitizeString.js index d72f66a2..ba031cc6 100644 --- a/shared/sanitizeString.js +++ b/shared/sanitizeString.js @@ -6,3 +6,8 @@ export function sanitizeHtml(unsafe) { export function sanitizeString(unsafe) { return unsafe.replace(/<\//g, `<\\\/`) } + +export function sanitizeInnerHtml(unsafe) { + if (unsafe === undefined || typeof(unsafe) !== 'string') return '' + return unsafe.replaceAll('<\\', '<') +} \ No newline at end of file