From 33bcfaedab8408e90ec50bc27777434b68a07f2f Mon Sep 17 00:00:00 2001 From: velzie Date: Tue, 8 Oct 2024 17:08:09 -0400 Subject: [PATCH] fix buggy impl of innerHTML and outerHTML --- src/client/dom/element.ts | 23 +++--- src/shared/rewriters/html.ts | 145 ++++++++++++++++++----------------- 2 files changed, 90 insertions(+), 78 deletions(-) diff --git a/src/client/dom/element.ts b/src/client/dom/element.ts index 54c8fad..526f40a 100644 --- a/src/client/dom/element.ts +++ b/src/client/dom/element.ts @@ -48,7 +48,7 @@ export default function (client: ScramjetClient, self: typeof window) { for (const element of attrObject[attr]) { const descriptor = nativeGetOwnPropertyDescriptor( element.prototype, - attr + attr, ); Object.defineProperty(element.prototype, attr, { get() { @@ -75,7 +75,7 @@ export default function (client: ScramjetClient, self: typeof window) { base: new URL(client.url.origin), origin: new URL(client.url.origin), } as URLMeta, - true + true, ); } else if (["srcset", "imagesrcset"].includes(attr)) { value = rewriteSrcset(value, client.meta); @@ -174,10 +174,15 @@ export default function (client: ScramjetClient, self: typeof window) { ctx.set(newval); }, get(ctx) { - if ( - ctx.this instanceof self.HTMLScriptElement || - ctx.this instanceof self.HTMLStyleElement - ) { + if (ctx.this instanceof self.HTMLScriptElement) { + return atob( + client.natives["Element.prototype.getAttribute"].call( + ctx.this, + "data-scramjet-script-source-src", + ), + ); + } + if (ctx.this instanceof self.HTMLStyleElement) { return ctx.get(); } @@ -264,7 +269,7 @@ export default function (client: ScramjetClient, self: typeof window) { ctx.args[0], client.cookieStore, client.meta, - false + false, ); } }, @@ -276,7 +281,7 @@ export default function (client: ScramjetClient, self: typeof window) { ctx.args[0], client.cookieStore, client.meta, - true + true, ); }, }); @@ -287,7 +292,7 @@ export default function (client: ScramjetClient, self: typeof window) { ctx.args[0], client.cookieStore, client.meta, - false + false, ); }, }); diff --git a/src/shared/rewriters/html.ts b/src/shared/rewriters/html.ts index 1b5a6c1..1b3d0db 100644 --- a/src/shared/rewriters/html.ts +++ b/src/shared/rewriters/html.ts @@ -10,7 +10,7 @@ export function rewriteHtml( html: string, cookieStore: CookieStore, meta: URLMeta, - fromTop: boolean = false + fromTop: boolean = false, ) { const handler = new DomHandler((err, dom) => dom); const parser = new Parser(handler); @@ -56,7 +56,7 @@ export function rewriteHtml( script(self.$scramjet.config["codecs"]), script("data:application/javascript;base64," + btoa(injected)), script(self.$scramjet.config["shared"]), - script(self.$scramjet.config["client"]) + script(self.$scramjet.config["client"]), ); } @@ -78,6 +78,12 @@ export function unrewriteHtml(html: string) { function traverse(node: ChildNode) { if ("attribs" in node) { for (const key in node.attribs) { + if (key == "data-scramjet-script-source-src") { + if (node.children[0] && "data" in node.children[0]) + node.children[0].data = atob(node.attribs[key]); + continue; + } + if (key.startsWith("data-scramjet-")) { node.attribs[key.slice(13)] = node.attribs[key]; delete node.attribs[key]; @@ -101,80 +107,80 @@ export const htmlRules: { [key: string]: "*" | string[] | ((...any: any[]) => string | null); fn: (value: string, meta: URLMeta, cookieStore: CookieStore) => string | null; }[] = [ - { - fn: (value: string, meta: URLMeta) => { - return encodeUrl(value, meta); + { + fn: (value: string, meta: URLMeta) => { + return encodeUrl(value, meta); + }, + + // url rewrites + src: [ + "embed", + "script", + "img", + "image", + "iframe", + "source", + "video", + "audio", + "input", + "track", + ], + href: ["a", "link", "area"], + data: ["object"], + action: ["form"], + formaction: ["button", "input", "textarea", "submit"], + poster: ["video"], + "xlink:href": ["image"], }, + { + fn: () => null, - // url rewrites - src: [ - "embed", - "script", - "img", - "image", - "iframe", - "source", - "video", - "audio", - "input", - "track", - ], - href: ["a", "link", "area"], - data: ["object"], - action: ["form"], - formaction: ["button", "input", "textarea", "submit"], - poster: ["video"], - "xlink:href": ["image"], - }, - { - fn: () => null, - - // csp stuff that must be deleted - nonce: "*", - integrity: ["script", "link"], - csp: ["iframe"], - }, - { - fn: (value: string, meta: URLMeta) => rewriteSrcset(value, meta), - - // srcset - srcset: ["img", "source"], - imagesrcset: ["link"], - }, - { - fn: (value: string, meta: URLMeta, cookieStore: CookieStore) => - rewriteHtml( - value, - cookieStore, - { - // for srcdoc origin is the origin of the page that the iframe is on. base and path get dropped - origin: new URL(meta.origin.origin), - base: new URL(meta.origin.origin), - }, - true - ), - - // srcdoc - srcdoc: ["iframe"], - }, - { - fn: (value: string, meta: URLMeta) => rewriteCss(value, meta), - style: "*", - }, - { - fn: (value: string) => { - if (["_parent", "_top", "_unfencedTop"].includes(value)) return "_self"; + // csp stuff that must be deleted + nonce: "*", + integrity: ["script", "link"], + csp: ["iframe"], }, - target: ["a", "base"], - }, -]; + { + fn: (value: string, meta: URLMeta) => rewriteSrcset(value, meta), + + // srcset + srcset: ["img", "source"], + imagesrcset: ["link"], + }, + { + fn: (value: string, meta: URLMeta, cookieStore: CookieStore) => + rewriteHtml( + value, + cookieStore, + { + // for srcdoc origin is the origin of the page that the iframe is on. base and path get dropped + origin: new URL(meta.origin.origin), + base: new URL(meta.origin.origin), + }, + true, + ), + + // srcdoc + srcdoc: ["iframe"], + }, + { + fn: (value: string, meta: URLMeta) => rewriteCss(value, meta), + style: "*", + }, + { + fn: (value: string) => { + if (["_parent", "_top", "_unfencedTop"].includes(value)) return "_self"; + }, + target: ["a", "base"], + }, + ]; // i need to add the attributes in during rewriting function traverseParsedHtml( node: any, cookieStore: CookieStore, - meta: URLMeta + meta: URLMeta, ) { if (node.name === "base" && node.attribs.href !== undefined) { meta.base = new URL(node.attribs.href, meta.origin); @@ -207,11 +213,12 @@ function traverseParsedHtml( if ( node.name === "script" && /(application|text)\/javascript|module|importmap|undefined/.test( - node.attribs.type + node.attribs.type, ) && node.children[0] !== undefined ) { let js = node.children[0].data; + node.attribs[`data-scramjet-script-source-src`] = btoa(js); const htmlcomment = //g; js = js.replace(htmlcomment, ""); node.children[0].data = rewriteJs(js, meta); @@ -238,7 +245,7 @@ function traverseParsedHtml( node.childNodes[childNode] = traverseParsedHtml( node.childNodes[childNode], cookieStore, - meta + meta, ); } }