diff --git a/.eslintrc.json b/.eslintrc.json index 5b15783..900c01e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -20,7 +20,6 @@ "no-useless-return": "error", "no-shadow": "error", "prefer-const": "warn", - "prefer-reflect": "warn", "no-unreachable": "warn", "no-undef": "off" } diff --git a/src/bundle/index.ts b/src/bundle/index.ts index 63ea7a4..6040a63 100644 --- a/src/bundle/index.ts +++ b/src/bundle/index.ts @@ -2,6 +2,7 @@ import { encodeUrl, decodeUrl } from "./rewriters/url"; import { rewriteCss } from "./rewriters/css"; import { rewriteHtml, rewriteSrcset } from "./rewriters/html"; import { rewriteJs } from "./rewriters/js"; +import { rewriteHeaders } from "./rewriters/headers"; const bundle = { rewriters: { @@ -11,7 +12,8 @@ const bundle = { rewriteCss, rewriteHtml, rewriteSrcset, - rewriteJs + rewriteJs, + rewriteHeaders } } diff --git a/src/bundle/rewriters/headers.ts b/src/bundle/rewriters/headers.ts index e69de29..b2f31e5 100644 --- a/src/bundle/rewriters/headers.ts +++ b/src/bundle/rewriters/headers.ts @@ -0,0 +1,50 @@ +import { encodeUrl } from "./url"; + +const cspHeaders = [ + "cross-origin-embedder-policy", + "cross-origin-opener-policy", + "cross-origin-resource-policy", + "content-security-policy", + "content-security-policy-report-only", + "expect-ct", + "feature-policy", + "origin-isolation", + "strict-transport-security", + "upgrade-insecure-requests", + "x-content-type-options", + "x-download-options", + "x-frame-options", + "x-permitted-cross-domain-policies", + "x-powered-by", + "x-xss-protection", +]; + +const urlHeaders = [ + "location", + "content-location", + "referer" +]; + +export function rewriteHeaders(headers: Headers, origin?: string) { + cspHeaders.forEach((header) => { + if (headers.has(header)) { + headers.delete(header); + } + }); + + urlHeaders.forEach((header) => { + if (headers.has(header)) { + headers.set(header, encodeUrl(headers.get(header), origin)); + } + }); + + if (headers.has("link")) { + let link = headers.get("link"); + + link = link.replace(/<(.*?)>/g, (match, g1) => { + return `<${encodeUrl(g1, origin)}>`; + }); + + headers.set("link", link); + } +} \ No newline at end of file diff --git a/src/bundle/rewriters/html.ts b/src/bundle/rewriters/html.ts index 659fa12..0f12275 100644 --- a/src/bundle/rewriters/html.ts +++ b/src/bundle/rewriters/html.ts @@ -4,6 +4,7 @@ import { hasAttrib, getAttributeValue } from "domutils"; import render from "dom-serializer"; import { encodeUrl } from "./url"; import { rewriteCss } from "./css"; +import { rewriteJs } from "./js"; // html nodes to rewrite // meta @@ -18,9 +19,10 @@ export function rewriteHtml(html: string, origin?: string) { return render(traverseParsedHtml(handler.root, origin)); } -// typescript error hell -// the code still works but the types provided from domhandler are shit -function traverseParsedHtml(node: any, origin?: string) { +function traverseParsedHtml(node, origin?: string) { + // apparently nonce is a global attribute so i'll just delete it at the beginning of the file + delete node.attribs.nonce; + if (node.name === "a" && hasAttrib(node, "href")) { node.attribs.href = encodeUrl(node.attribs.href, origin); } else if (node.name === "iframe") { @@ -31,12 +33,25 @@ function traverseParsedHtml(node: any, origin?: string) { if (hasAttrib(node, "srcdoc")) { node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin); } + + if (hasAttrib(node, "csp")) { + delete node.attribs.csp; + } } else if (node.name === "link") { + delete node.attribs.integrity; + node.attribs.href = encodeUrl(node.attribs.href, origin); + + if (hasAttrib(node, "imagesrcset")) { + node.attribs.imagesrcset = rewriteSrcset(node.attribs.imagesrcset); + } + console.log(node.attribs.href) } else if (node.name === "style") { node.children[0].data = rewriteCss(node.children[0].data, origin); } else if (node.name === "script") { + delete node.attribs.integrity; + if (hasAttrib(node, "type") && /(application|text)\/javascript|importmap/.test(getAttributeValue(node, "type"))) { if (hasAttrib(node, "src")) { node.attribs.src = encodeUrl(node.attribs.src, origin); @@ -44,7 +59,7 @@ function traverseParsedHtml(node: any, origin?: string) { } } - // implement js rewriting when done + node.children[0].data = rewriteJs(node.children[0].data, origin); } else if (node.name === "img" && hasAttrib(node, "src")) { if (hasAttrib(node, "src")) { node.attribs.src = encodeUrl(node.attribs.src, origin); diff --git a/src/bundle/rewriters/url.ts b/src/bundle/rewriters/url.ts index db97c69..d0c69a2 100644 --- a/src/bundle/rewriters/url.ts +++ b/src/bundle/rewriters/url.ts @@ -1,3 +1,5 @@ +import { rewriteJs } from "./js"; + function canParseUrl(url: string, origin?: string) { if (URL.canParse) { console.log(URL.canParse(url, origin) + "\n" + url + "\n" + origin); @@ -21,14 +23,11 @@ export function encodeUrl(url: string, origin?: string) { // } if (url.startsWith("javascript:")) { - // implement when js rewriting is done - return url; + return "javascript:" + rewriteJs(url.slice("javascript:".length)); } else if (/^(#|mailto|about|data)/.test(url)) { return url; } else if (canParseUrl(url, origin)) { - console.log(self.__scramjet$config.prefix + self.__scramjet$config.codec.encode(new URL(url, origin).href)); - - return self.__scramjet$config.prefix + self.__scramjet$config.codec.encode(new URL(url, origin).href); + return location.origin + self.__scramjet$config.prefix + self.__scramjet$config.codec.encode(new URL(url, origin).href); } } diff --git a/src/worker/index.ts b/src/worker/index.ts index e4b52cb..e8b85d8 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -19,6 +19,8 @@ self.ScramjetServiceWorker = class ScramjetServiceWorker { async fetch(event: FetchEvent) { const url = new URL(self.__scramjet$bundle.rewriters.url.decodeUrl(event.request.url)); + self.__scramjet$bundle.rewriters.rewriteHeaders(event.request.headers) + // implement header rewriting later const response = await this.client.fetch(url, { method: event.request.method, @@ -26,6 +28,8 @@ self.ScramjetServiceWorker = class ScramjetServiceWorker { headers: event.request.headers }); + self.__scramjet$bundle.rewriters.rewriteHeaders(response.headers); + let responseBody; if (event.request.destination === "document") {