diff --git a/package.json b/package.json index 56b5f78..96d7863 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@mercuryworkshop/libcurl-transport": "^1.3.6", "@tomphttp/bare-server-node": "^2.0.3", "@types/eslint": "^8.56.10", + "@types/estraverse": "^5.1.7", "@types/serviceworker": "^0.0.85", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", @@ -44,11 +45,11 @@ "dependencies": { "@mercuryworkshop/bare-mux": "^2.0.2", "@webreflection/idb-map": "^0.3.1", - "astravel": "^0.6.1", "astring": "^1.8.6", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", + "estraverse": "^5.3.0", "htmlparser2": "^9.1.0", "meriyah": "^4.4.2" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5571e61..6baa3ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: '@webreflection/idb-map': specifier: ^0.3.1 version: 0.3.1 - astravel: - specifier: ^0.6.1 - version: 0.6.1 astring: specifier: ^1.8.6 version: 1.8.6 @@ -29,6 +26,9 @@ importers: domutils: specifier: ^3.1.0 version: 3.1.0 + estraverse: + specifier: ^5.3.0 + version: 5.3.0 htmlparser2: specifier: ^9.1.0 version: 9.1.0 @@ -54,6 +54,9 @@ importers: '@types/eslint': specifier: ^8.56.10 version: 8.56.10 + '@types/estraverse': + specifier: ^5.1.7 + version: 5.1.7 '@types/serviceworker': specifier: ^0.0.85 version: 0.0.85 @@ -410,6 +413,9 @@ packages: '@types/eslint@8.56.10': resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} + '@types/estraverse@5.1.7': + resolution: {integrity: sha512-JRVtdKYZz7VkNp7hMC/WKoiZ8DS3byw20ZGoMZ1R8eBrBPIY7iBaDAS1zcrnXQCwK44G4vbXkimeU7R0VLG8UQ==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -561,9 +567,6 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - astravel@0.6.1: - resolution: {integrity: sha512-ZIkgWFIV0Yo423Vqalz7VcF+BAiISvSgplnkV2abPGACPFKofsWTcvr9SFyYM/t/vMZWqmdP/Eze6ATX7r84Dg==} - astring@1.8.6: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} hasBin: true @@ -1692,6 +1695,10 @@ snapshots: '@types/estree': 1.0.5 '@types/json-schema': 7.0.15 + '@types/estraverse@5.1.7': + dependencies: + '@types/estree': 1.0.5 + '@types/estree@1.0.5': {} '@types/json-schema@7.0.15': {} @@ -1853,8 +1860,6 @@ snapshots: array-union@2.1.0: {} - astravel@0.6.1: {} - astring@1.8.6: {} async-exit-hook@2.0.1: {} diff --git a/src/bundle/rewriters/html.ts b/src/bundle/rewriters/html.ts index 88de237..cf3ceee 100644 --- a/src/bundle/rewriters/html.ts +++ b/src/bundle/rewriters/html.ts @@ -29,7 +29,7 @@ function traverseParsedHtml(node, origin?: URL) { } /* url attributes */ - for (const urlAttr of ["src", "href", "data", "action"]) { + for (const urlAttr of ["src", "href", "data", "action", "formaction"]) { if (hasAttrib(node, urlAttr) && !isScramjetFile(node.attribs[urlAttr])) { const value = node.attribs[urlAttr]; node.attribs[`data-${urlAttr}`] = value; diff --git a/src/bundle/rewriters/js.ts b/src/bundle/rewriters/js.ts index a9c1899..2435a1f 100644 --- a/src/bundle/rewriters/js.ts +++ b/src/bundle/rewriters/js.ts @@ -1,7 +1,7 @@ import { parseModule } from "meriyah"; import { generate } from "astring"; -import { makeTraveler } from "astravel"; import { encodeUrl } from "./url"; +import { replace } from "estraverse"; // i am a cat. i like to be petted. i like to be fed. i like to be @@ -18,10 +18,7 @@ import { encodeUrl } from "./url"; export function rewriteJs(js: string, origin?: URL) { try { - const ast = parseModule(js, { - module: true, - webcompat: true - }); + const ast = parseModule(js); // const identifierList = [ // "window", @@ -33,30 +30,20 @@ export function rewriteJs(js: string, origin?: URL) { // "" // ] - const customTraveler = makeTraveler({ - ImportDeclaration: (node) => { - node.source.value = encodeUrl(node.source.value, origin); + replace(ast, { + enter: (node, parent) => { + if (["ImportDeclaration", "ImportExpression", "ExportAllDeclaration", "ExportNamedDeclaration"].includes(node.type) && node.source) { + node.source.value = encodeUrl(node.source.value, origin); + } + + return node; }, - - ImportExpression: (node) => { - node.source.value = encodeUrl(node.source.value, origin); - }, - - ExportAllDeclaration: (node) => { - node.source.value = encodeUrl(node.source.value, origin); - }, - - ExportNamedDeclaration: (node) => { - if (node.source) node.source.value = encodeUrl(node.source.value, origin); - }, - }); - - customTraveler.go(ast); + + fallback: "iteration" + }) return generate(ast); - } catch { - console.log(js); - - return js; + } catch (err) { + throw new Error(err); } } \ No newline at end of file diff --git a/src/client/css.ts b/src/client/css.ts index 2e5e61b..f499ba2 100644 --- a/src/client/css.ts +++ b/src/client/css.ts @@ -16,8 +16,11 @@ jsProperties.forEach((prop) => { const propDescriptor = Object.getOwnPropertyDescriptor(CSSStyleDeclaration.prototype, prop); Object.defineProperty(CSSStyleDeclaration.prototype, prop, { + get() { + return propDescriptor.get.call(this); + }, set(v) { - propDescriptor.set.call(this, rewriteCss(v)); + return propDescriptor.set.call(this, rewriteCss(v)); }, }) -}); +}); \ No newline at end of file diff --git a/src/client/element.ts b/src/client/element.ts index 594abf6..8bc58cc 100644 --- a/src/client/element.ts +++ b/src/client/element.ts @@ -1,25 +1,6 @@ import { encodeUrl, rewriteCss, rewriteHtml, rewriteJs, rewriteSrcset } from "../bundle"; -// object -// iframe -// embed -// link -// style -// script -// img -// source -// form -// meta -// area -// base -// body -// input -// audio -// button -// track -// video - -const attribs = { +const attrObject = { "nonce": [HTMLElement], "integrity": [HTMLScriptElement, HTMLLinkElement], "csp": [HTMLIFrameElement], @@ -30,64 +11,56 @@ const attribs = { "formaction": [HTMLButtonElement, HTMLInputElement], "srcdoc": [HTMLIFrameElement], "srcset": [HTMLImageElement, HTMLSourceElement], - "imagesrcset": [HTMLLinkElement], - "style": [HTMLElement] + "imagesrcset": [HTMLLinkElement] } -Object.keys(attribs).forEach((attrib: string) => { - attribs[attrib].forEach((element) => { - const descriptor = Object.getOwnPropertyDescriptor(element.prototype, attrib); - Object.defineProperty(element.prototype, attrib, { +const attrs = Object.keys(attrObject); + +for (const attr of attrs) { + for (const element of attrObject[attr]) { + const descriptor = Object.getOwnPropertyDescriptor(element.prototype, attr); + Object.defineProperty(element.prototype, attr, { get() { - return this.dataset[attrib]; + return this.dataset[attr]; }, set(value) { - this.dataset[attrib] = value; - if (/nonce|integrity|csp/.test(attrib)) { - this.removeAttribute(attrib); - } else if (/src|href|data|action|formaction/.test(attrib)) { + this.dataset[attr] = value; + if (/nonce|integrity|csp/.test(attr)) { + return; + } else if (/src|href|data|action|formaction/.test(attr)) { // @ts-expect-error if (value instanceof TrustedScriptURL) { return; } value = encodeUrl(value); - } else if (attrib === "srcdoc") { + } else if (attr === "srcdoc") { value = rewriteHtml(value); - } else if (/(image)?srcset/.test(attrib)) { + } else if (/(image)?srcset/.test(attr)) { value = rewriteSrcset(value); - } else if (attrib === "style") { - value = rewriteCss(value); } descriptor.set.call(this, value); }, }); - }) -}); + } +} -HTMLElement.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, { +Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, { apply(target, thisArg, argArray) { - console.log(thisArg); - if (Object.keys(attribs).includes(argArray[0])) { - argArray[0] = `data-${argArray[0]}`; + if (attrs.includes(argArray[0]) && thisArg.dataset[argArray[0]]) { + return thisArg.dataset[argArray[0]]; } return Reflect.apply(target, thisArg, argArray); }, }); -// setAttribute proxy is currently broken - -HTMLElement.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, { +Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, { apply(target, thisArg, argArray) { - if (thisArg.dataset["scramjet"]) { - return; - } - console.log(argArray[1]) - if (Object.keys(attribs).includes(argArray[0])) { - thisArg.dataset[`_${argArray[0]}`] = argArray[1]; + if (attrs.includes(argArray[0])) { + thisArg.dataset[argArray[0]] = argArray[1]; if (/nonce|integrity|csp/.test(argArray[0])) { return; } else if (/src|href|data|action|formaction/.test(argArray[0])) { @@ -108,20 +81,16 @@ HTMLElement.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, { const innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML"); -Object.defineProperty(HTMLElement.prototype, "innerHTML", { +Object.defineProperty(Element.prototype, "innerHTML", { set(value) { - if (this instanceof HTMLScriptElement) { - // @ts-expect-error - if (!(value instanceof TrustedScript)) { - value = rewriteJs(value); - } + // @ts-expect-error + if (this instanceof HTMLScriptElement && !(value instanceof TrustedScript)) { + value = rewriteJs(value); } else if (this instanceof HTMLStyleElement) { value = rewriteCss(value); - } else { - // @ts-expect-error - if (!(value instanceof TrustedHTML)) { - value = rewriteHtml(value); - } + // @ts-expect-error + } else if (!(value instanceof TrustedHTML)) { + value = rewriteHtml(value); } return innerHTML.set.call(this, value); diff --git a/src/client/index.ts b/src/client/index.ts index d3b6987..d0616d8 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -7,7 +7,7 @@ import "./requests/websocket.ts" import "./element.ts"; import "./storage.ts"; import "./css.ts"; -import "./worker.ts" +import "./worker.ts"; declare global { interface Window { diff --git a/src/html/cloner.ts b/src/html/cloner.ts deleted file mode 100644 index dd18432..0000000 --- a/src/html/cloner.ts +++ /dev/null @@ -1,25 +0,0 @@ -export default class Clone { - el: HTMLElement; - copy: HTMLElement - - constructor(element: HTMLElement) { - this.el = element; - this.copy = document.createElement(element.tagName); - - for (const attr of element.getAttributeNames()) { - this.copy.setAttribute(attr, element.getAttribute(attr)); - } - - if (element.innerHTML) { - this.copy.innerHTML = element.innerHTML; - } - } - - insertCopy() { - this.el.insertAdjacentElement("afterend", this.copy); - } - - removeElement() { - this.el.remove(); - } -} \ No newline at end of file