worker rewriting

This commit is contained in:
velzie 2024-07-21 15:17:31 -04:00
parent 2598bee87b
commit f6c3c13d1e
No known key found for this signature in database
GPG key ID: 048413F95F0DDE1F
22 changed files with 543 additions and 392 deletions

View file

@ -1,7 +1,7 @@
import { client } from "."; import { client } from ".";
// goodybye spyware~ // goodybye spyware~
client.Proxy(navigator, "sendBeacon", { client.Proxy("navigator.sendBeacon", {
apply(ctx) { apply(ctx) {
ctx.return(null); ctx.return(null);
}, },

View file

@ -1,10 +1,47 @@
Object.defineProperty(document, "cookie", { import { parse } from "set-cookie-parser";
get() { import { client } from ".";
return "";
client.Trap("Document.prototype.cookie", {
get(ctx) {
const cookiestring = ctx.get();
dbg.log("original cookiestring", cookiestring);
//
// if (!cookiestring) return "";
//
//
// let string = "";
//
// for (const cookiestr of cookiestring.split(";")) {
// const cookie = parse(cookiestr.trim())[0];
// if (cookie.name.startsWith(client.url.hostname + "@")) {
// let name = cookie.name.substring(client.url.hostname.length + 1);
// string += `${name}=${cookie.value}; `;
// }
// }
//
// return string.trimEnd();
return "sp_t=00246e00653d39d1341bbe9d10f138c4; OptanonConsent=isGpcEnabled=0&datestamp=Sat+Jul+20+2024+16%3A11%3A26+GMT-0400+(Eastern+Daylight+Time)&version=202405.2.0&browserGpcFlag=0&isIABGlobal=false&hosts=&landingPath=https%3A%2F%2Fopen.spotify.com%2F&groups=BG169%3A1%2Ct00%3A1%2Ci00%3A1%2CBG170%3A1%2Cs00%3A1%2Cf00%3A1%2Cm00%3A1%2Cf11%3A1";
}, },
set(value) { set(ctx, value: string) {
console.log("COOKIE SET", value); dbg.debug("setting cookie", value);
const cookie = parse(value)[0];
let date = new Date();
let expires = cookie.expires;
dbg.error("expires", expires);
// if (expires instanceof Date) {
// if (isNaN(expires.getTime())) return;
// if (expires.getTime() < date.getTime()) return;
// }
// set.call(document, `${cookie.name}=${cookie.value}`);
}, },
}); });
delete window.cookieStore; // @ts-ignore
delete self.cookieStore;
// sp_t=00e49dc8-59d0-4b5f-9beb-ec7b67368498; path=/; expires=Invalid Date
// OTZ=7653361_72_76_104100_72_446760;path=/;expires=Mon, 19 Aug 2024 20:01:06 GMT;secure

View file

@ -14,7 +14,7 @@ const cssProperties = [
]; ];
// const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"]; // const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"];
client.Proxy(CSSStyleDeclaration.prototype, "setProperty", { client.Proxy("CSSStyleDeclaration.prototype.setProperty", {
apply(ctx) { apply(ctx) {
if (cssProperties.includes(ctx.args[0])) if (cssProperties.includes(ctx.args[0]))
ctx.args[1] = rewriteCss(ctx.args[1]); ctx.args[1] = rewriteCss(ctx.args[1]);

View file

@ -9,137 +9,142 @@ import {
} from "./shared"; } from "./shared";
import { documentProxy } from "./window"; import { documentProxy } from "./window";
const attrObject = { if ("window" in self) {
nonce: [HTMLElement], const attrObject = {
integrity: [HTMLScriptElement, HTMLLinkElement], nonce: [HTMLElement],
csp: [HTMLIFrameElement], integrity: [HTMLScriptElement, HTMLLinkElement],
src: [ csp: [HTMLIFrameElement],
HTMLImageElement, src: [
HTMLMediaElement, HTMLImageElement,
HTMLIFrameElement, HTMLMediaElement,
HTMLEmbedElement, HTMLIFrameElement,
HTMLScriptElement, HTMLEmbedElement,
], HTMLScriptElement,
href: [HTMLAnchorElement, HTMLLinkElement], ],
data: [HTMLObjectElement], href: [HTMLAnchorElement, HTMLLinkElement],
action: [HTMLFormElement], data: [HTMLObjectElement],
formaction: [HTMLButtonElement, HTMLInputElement], action: [HTMLFormElement],
srcdoc: [HTMLIFrameElement], formaction: [HTMLButtonElement, HTMLInputElement],
srcset: [HTMLImageElement, HTMLSourceElement], srcdoc: [HTMLIFrameElement],
imagesrcset: [HTMLLinkElement], srcset: [HTMLImageElement, HTMLSourceElement],
}; imagesrcset: [HTMLLinkElement],
};
const attrs = Object.keys(attrObject); const attrs = Object.keys(attrObject);
for (const attr of attrs) { for (const attr of attrs) {
for (const element of attrObject[attr]) { for (const element of attrObject[attr]) {
const descriptor = Object.getOwnPropertyDescriptor(element.prototype, attr); const descriptor = Object.getOwnPropertyDescriptor(
Object.defineProperty(element.prototype, attr, { element.prototype,
get() { attr
if (["src", "data", "href", "action", "formaction"].includes(attr)) { );
return decodeUrl(descriptor.get.call(this)); Object.defineProperty(element.prototype, attr, {
} get() {
if (["src", "data", "href", "action", "formaction"].includes(attr)) {
return decodeUrl(descriptor.get.call(this));
}
if (this.$origattrs[attr]) { if (this.$origattrs[attr]) {
return this.$origattrs[attr]; return this.$origattrs[attr];
} }
return descriptor.get.call(this); return descriptor.get.call(this);
}, },
set(value) { set(value) {
this.$origattrs[attr] = value; this.$origattrs[attr] = value;
if (["nonce", "integrity", "csp"].includes(attr)) { if (["nonce", "integrity", "csp"].includes(attr)) {
return;
} else if (
["src", "data", "href", "action", "formaction"].includes(attr)
) {
value = encodeUrl(value);
} else if (attr === "srcdoc") {
value = rewriteHtml(value);
} else if (["srcset", "imagesrcset"].includes(attr)) {
value = rewriteSrcset(value);
}
descriptor.set.call(this, value);
},
});
}
}
declare global {
interface Element {
$origattrs: Record<string, string>;
}
}
Element.prototype.$origattrs = {};
Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0]) && thisArg.$origattrs[argArray[0]]) {
return thisArg.$origattrs[argArray[0]];
}
return Reflect.apply(target, thisArg, argArray);
},
});
Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0])) {
thisArg.$origattrs[argArray[0]] = argArray[1];
if (["nonce", "integrity", "csp"].includes(argArray[0])) {
return; return;
} else if ( } else if (
["src", "data", "href", "action", "formaction"].includes(attr) ["src", "data", "href", "action", "formaction"].includes(argArray[0])
) { ) {
value = encodeUrl(value); argArray[1] = encodeUrl(argArray[1]);
} else if (attr === "srcdoc") { } else if (argArray[0] === "srcdoc") {
value = rewriteHtml(value); argArray[1] = rewriteHtml(argArray[1]);
} else if (["srcset", "imagesrcset"].includes(attr)) { } else if (["srcset", "imagesrcset"].includes(argArray[0])) {
value = rewriteSrcset(value); argArray[1] = rewriteSrcset(argArray[1]);
} else if (argArray[1] === "style") {
argArray[1] = rewriteCss(argArray[1]);
} }
descriptor.set.call(this, value);
},
});
}
}
declare global {
interface Element {
$origattrs: Record<string, string>;
}
}
Element.prototype.$origattrs = {};
Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0]) && thisArg.$origattrs[argArray[0]]) {
return thisArg.$origattrs[argArray[0]];
}
return Reflect.apply(target, thisArg, argArray);
},
});
Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, {
apply(target, thisArg, argArray) {
if (attrs.includes(argArray[0])) {
thisArg.$origattrs[argArray[0]] = argArray[1];
if (["nonce", "integrity", "csp"].includes(argArray[0])) {
return;
} else if (
["src", "data", "href", "action", "formaction"].includes(argArray[0])
) {
argArray[1] = encodeUrl(argArray[1]);
} else if (argArray[0] === "srcdoc") {
argArray[1] = rewriteHtml(argArray[1]);
} else if (["srcset", "imagesrcset"].includes(argArray[0])) {
argArray[1] = rewriteSrcset(argArray[1]);
} else if (argArray[1] === "style") {
argArray[1] = rewriteCss(argArray[1]);
} }
}
return Reflect.apply(target, thisArg, argArray); return Reflect.apply(target, thisArg, argArray);
}, },
}); });
const innerHTML = Object.getOwnPropertyDescriptor( const innerHTML = Object.getOwnPropertyDescriptor(
Element.prototype, Element.prototype,
"innerHTML" "innerHTML"
); );
Object.defineProperty(Element.prototype, "innerHTML", { Object.defineProperty(Element.prototype, "innerHTML", {
set(value) { set(value) {
if (this instanceof HTMLScriptElement) { if (this instanceof HTMLScriptElement) {
value = rewriteJs(value); value = rewriteJs(value);
} else if (this instanceof HTMLStyleElement) { } else if (this instanceof HTMLStyleElement) {
value = rewriteCss(value); value = rewriteCss(value);
} else { } else {
value = rewriteHtml(value); value = rewriteHtml(value);
}
return innerHTML.set.call(this, value);
},
});
for (const target of [Node.prototype, MutationObserver.prototype, document]) {
for (const prop in target) {
try {
if (typeof target[prop] === "function") {
client.Proxy(target, prop, {
apply(ctx) {
for (const i in ctx.args) {
if (ctx.args[i] === documentProxy) ctx.args[i] = document;
}
},
});
} }
} catch (e) {}
return innerHTML.set.call(this, value);
},
});
for (const target of [Node.prototype, MutationObserver.prototype, document]) {
for (const prop in target) {
try {
if (typeof target[prop] === "function") {
client.RawProxy(target, prop, {
apply(ctx) {
for (const i in ctx.args) {
if (ctx.args[i] === documentProxy) ctx.args[i] = document;
}
},
});
}
} catch (e) {}
}
} }
} }

View file

@ -1,13 +1,13 @@
import { client } from "."; import { client } from ".";
import { encodeUrl } from "./shared"; import { encodeUrl } from "./shared";
client.Proxy(window.history, "pushState", { client.Proxy("history.pushState", {
apply(ctx) { apply(ctx) {
ctx.args[2] = encodeUrl(ctx.args[2]); ctx.args[2] = encodeUrl(ctx.args[2]);
}, },
}); });
client.Proxy(window.history, "replaceState", { client.Proxy("history.replaceState", {
apply(ctx) { apply(ctx) {
ctx.args[2] = encodeUrl(ctx.args[2]); ctx.args[2] = encodeUrl(ctx.args[2]);
}, },

View file

@ -1,6 +1,6 @@
import { encodeUrl } from "../shared/rewriters/url"; import { encodeUrl } from "../shared/rewriters/url";
window.$sImport = function (base) { self.$sImport = function (base) {
return function (url) { return function (url) {
const resolved = new URL(url, base).href; const resolved = new URL(url, base).href;

View file

@ -7,7 +7,9 @@ declare global {
$sImport: any; $sImport: any;
} }
} }
type AnyFunction = (...args: any[]) => any;
//eslint-disable-next-line
type AnyFunction = Function;
type ProxyCtx = { type ProxyCtx = {
fn: AnyFunction; fn: AnyFunction;
@ -16,17 +18,43 @@ type ProxyCtx = {
newTarget: AnyFunction; newTarget: AnyFunction;
return: (r: any) => void; return: (r: any) => void;
}; };
type Proxy = {
construct?(ctx: ProxyCtx): any;
apply?(ctx: ProxyCtx): any;
};
type TrapCtx<T> = {
this: any;
get: () => T;
set: (v: T) => void;
};
type Trap<T> = {
writable?: boolean;
value?: any;
enumerable?: boolean;
configurable?: boolean;
get?: (ctx: TrapCtx<T>) => T;
set?: (ctx: TrapCtx<T>, v: T) => void;
};
class ScramjetClient { class ScramjetClient {
Proxy( Proxy(name: string | string[], handler: Proxy) {
target: any, if (Array.isArray(name)) {
prop: string, for (const n of name) {
handler: { this.Proxy(n, handler);
construct?(ctx: ProxyCtx): any; }
apply?(ctx: ProxyCtx): any;
return;
} }
) {
const split = name.split(".");
const prop = split.pop();
const target = split.reduce((a, b) => a?.[b], self);
this.RawProxy(target, prop, handler);
}
RawProxy(target: any, prop: string, handler: Proxy) {
if (!target) return; if (!target) return;
if (!prop) return;
if (!Reflect.has(target, prop)) return; if (!Reflect.has(target, prop)) return;
const value = Reflect.get(target, prop); const value = Reflect.get(target, prop);
@ -36,14 +64,14 @@ class ScramjetClient {
if (handler.construct) { if (handler.construct) {
h.construct = function ( h.construct = function (
target: any, constructor: any,
argArray: any[], argArray: any[],
newTarget: AnyFunction newTarget: AnyFunction
) { ) {
let returnValue: any = null; let returnValue: any = null;
const ctx: ProxyCtx = { const ctx: ProxyCtx = {
fn: target, fn: constructor,
this: null, this: null,
args: argArray, args: argArray,
newTarget: newTarget, newTarget: newTarget,
@ -63,11 +91,11 @@ class ScramjetClient {
} }
if (handler.apply) { if (handler.apply) {
h.apply = function (target: any, thisArg: any, argArray: any[]) { h.apply = function (fn: any, thisArg: any, argArray: any[]) {
let returnValue: any = null; let returnValue: any = null;
const ctx: ProxyCtx = { const ctx: ProxyCtx = {
fn: target, fn,
this: thisArg, this: thisArg,
args: argArray, args: argArray,
newTarget: null, newTarget: null,
@ -88,36 +116,77 @@ class ScramjetClient {
target[prop] = new Proxy(value, h); target[prop] = new Proxy(value, h);
} }
Trap<T>(name: string | string[], descriptor: Trap<T>) {
if (Array.isArray(name)) {
for (const n of name) {
this.Trap(n, descriptor);
}
return;
}
const split = name.split(".");
const prop = split.pop();
const target = split.reduce((a, b) => a?.[b], self);
this.RawTrap(target, prop, descriptor);
}
RawTrap<T>(target: any, prop: string, descriptor: Trap<T>) {
if (!target) return;
if (!prop) return;
if (!Reflect.has(target, prop)) return;
const oldDescriptor = Object.getOwnPropertyDescriptor(target, prop);
const ctx: TrapCtx<T> = {
this: null,
get: function () {
return oldDescriptor && oldDescriptor.get.call(this.this);
},
set: function (v: T) {
oldDescriptor && oldDescriptor.set.call(this.this, v);
},
};
delete target[prop];
const desc: PropertyDescriptor = {};
if (descriptor.get) {
desc.get = function () {
ctx.this = this;
return descriptor.get(ctx);
};
} else if (oldDescriptor?.get) {
desc.get = oldDescriptor.get;
}
if (descriptor.set) {
desc.set = function (v: T) {
ctx.this = this;
descriptor.set(ctx, v);
};
} else if (oldDescriptor?.set) {
desc.set = oldDescriptor.set;
}
if (descriptor.enumerable) desc.enumerable = descriptor.enumerable;
else if (oldDescriptor?.enumerable)
desc.enumerable = oldDescriptor.enumerable;
if (descriptor.configurable) desc.configurable = descriptor.configurable;
else if (oldDescriptor?.configurable)
desc.configurable = oldDescriptor.configurable;
Object.defineProperty(target, prop, desc);
}
get url(): URL { get url(): URL {
return new URL(decodeUrl(location.href)); return new URL(decodeUrl(location.href));
} }
async init() { async init() {}
function b64(buffer) {
let binary = "";
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
const arraybuffer = await (await fetch("/scramjet.png")).arrayBuffer();
console.log(
"%cb",
`
background-image: url(data:image/png;base64,${b64(arraybuffer)});
color: transparent;
padding-left: 200px;
padding-bottom: 100px;
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
`
);
}
} }
export const client = new ScramjetClient(); export const client = new ScramjetClient();

View file

@ -1,18 +1,25 @@
import { client } from ".";
import { decodeUrl } from "../shared/rewriters/url"; import { decodeUrl } from "../shared/rewriters/url";
// const descriptor = Object.getOwnPropertyDescriptor(window, "origin"); client.Trap("origin", {
delete window.origin;
Object.defineProperty(window, "origin", {
get() { get() {
return new URL(decodeUrl(location.href)).origin; return client.url.origin;
}, },
set() { set() {
return false; return false;
}, },
}); });
Object.defineProperty(document, "URL", { client.Trap("document.URL", {
get() {
return client.url;
},
set() {
return false;
},
});
client.Trap("document.baseURI", {
get() { get() {
return decodeUrl(location.href); return decodeUrl(location.href);
}, },
@ -21,18 +28,9 @@ Object.defineProperty(document, "URL", {
}, },
}); });
Object.defineProperty(document, "baseURI", { client.Trap("document.domain", {
get() { get() {
return decodeUrl(location.href); return client.url.hostname;
},
set() {
return false;
},
});
Object.defineProperty(document, "domain", {
get() {
return new URL(decodeUrl(location.href)).hostname;
}, },
set() { set() {
return false; return false;

View file

@ -1,6 +1,6 @@
import { client } from "."; import { client } from ".";
client.Proxy(window, "postMessage", { client.Proxy("window.postMessage", {
apply(ctx) { apply(ctx) {
if (typeof ctx.args[1] === "string") ctx.args[1] = "*"; if (typeof ctx.args[1] === "string") ctx.args[1] = "*";
}, },

View file

@ -3,25 +3,25 @@
import { client } from ".."; import { client } from "..";
import { encodeUrl, rewriteHeaders } from "../shared"; import { encodeUrl, rewriteHeaders } from "../shared";
client.Proxy(window, "fetch", { client.Proxy("fetch", {
apply(ctx) { apply(ctx) {
ctx.args[0] = encodeUrl(ctx.args[0]); ctx.args[0] = encodeUrl(ctx.args[0]);
}, },
}); });
client.Proxy(window, "Headers", { client.Proxy("Headers", {
construct(ctx) { construct(ctx) {
ctx.args[0] = rewriteHeaders(ctx.args[0]); ctx.args[0] = rewriteHeaders(ctx.args[0]);
}, },
}); });
client.Proxy(window, "Request", { client.Proxy("Request", {
construct(ctx) { construct(ctx) {
if (typeof ctx.args[0] === "string") ctx.args[0] = encodeUrl(ctx.args[0]); if (typeof ctx.args[0] === "string") ctx.args[0] = encodeUrl(ctx.args[0]);
}, },
}); });
client.Proxy(Response, "redirect", { client.Proxy("Response.redirect", {
apply(ctx) { apply(ctx) {
ctx.args[0] = encodeUrl(ctx.args[0]); ctx.args[0] = encodeUrl(ctx.args[0]);
}, },

View file

@ -1,20 +1,23 @@
import { client } from ".."; import { client } from "..";
import { BareClient } from "../shared"; import { BareClient } from "../shared";
const bare = new BareClient();
client.Proxy(window, "WebSocket", { if ("window" in self) {
construct(ctx) { const bare = new BareClient();
ctx.return(
bare.createWebSocket( client.Proxy("WebSocket", {
ctx.args[0], construct(ctx) {
ctx.args[1], ctx.return(
ctx.fn as typeof WebSocket, bare.createWebSocket(
{ ctx.args[0],
"User-Agent": navigator.userAgent, ctx.args[1],
Origin: client.url.origin, ctx.fn as typeof WebSocket,
}, {
ArrayBuffer.prototype "User-Agent": navigator.userAgent,
) Origin: client.url.origin,
); },
}, ArrayBuffer.prototype
}); )
);
},
});
}

View file

@ -1,13 +1,13 @@
import { client } from ".."; import { client } from "..";
import { encodeUrl, rewriteHeaders } from "../shared"; import { encodeUrl, rewriteHeaders } from "../shared";
client.Proxy(XMLHttpRequest.prototype, "open", { client.Proxy("XMLHttpRequest.prototype.open", {
apply(ctx) { apply(ctx) {
if (ctx.args[1]) ctx.args[1] = encodeUrl(ctx.args[1]); if (ctx.args[1]) ctx.args[1] = encodeUrl(ctx.args[1]);
}, },
}); });
client.Proxy(XMLHttpRequest.prototype, "setRequestHeader", { client.Proxy("XMLHttpRequest.prototype.setRequestHeader", {
apply(ctx) { apply(ctx) {
let headerObject = Object.fromEntries([ctx.args]); let headerObject = Object.fromEntries([ctx.args]);
headerObject = rewriteHeaders(headerObject); headerObject = rewriteHeaders(headerObject);

View file

@ -1,27 +1,39 @@
import { locationProxy } from "./location"; import { locationProxy } from "./location";
import { documentProxy, windowProxy } from "./window"; import { documentProxy, windowProxy } from "./window";
export const iswindow = "window" in self;
export const isworker = "WorkerGlobalScope" in self;
export const issw = "ServiceWorkerGlobalScope" in self;
export const isdedicated = "DedicatedWorkerGlobalScope" in self;
export const isshared = "SharedWorkerGlobalScope" in self;
function scope(identifier: any) { function scope(identifier: any) {
// this will break iframe postmessage! // this will break iframe postmessage!
if ( if (
identifier instanceof Window || iswindow &&
identifier instanceof top.window.Window || (identifier instanceof Window ||
identifier instanceof parent.window.Window identifier instanceof top.window.Window ||
identifier instanceof parent.window.Window)
) { ) {
return windowProxy; return windowProxy;
} else if (identifier instanceof Location) { } else if (
(iswindow && identifier instanceof Location) ||
(isworker && identifier instanceof WorkerLocation)
) {
return locationProxy; return locationProxy;
} else if (identifier instanceof Document) { } else if (iswindow && identifier instanceof Document) {
return documentProxy; return documentProxy;
} else if (isworker && identifier instanceof WorkerGlobalScope) {
return windowProxy;
} }
return identifier; return identifier;
} }
// shorthand because this can get out of hand reall quickly // shorthand because this can get out of hand reall quickly
window.$s = scope; self.$s = scope;
window.$tryset = function (lhs: any, op: string, rhs: any) { self.$tryset = function (lhs: any, op: string, rhs: any) {
if (lhs instanceof Location) { if (lhs instanceof Location) {
// @ts-ignore // @ts-ignore
locationProxy.href = rhs; locationProxy.href = rhs;

View file

@ -1,97 +1,99 @@
import { client } from "."; import { client } from ".";
const handler: ProxyHandler<Storage> = { if ("window" in self) {
get(target, prop) { const handler: ProxyHandler<Storage> = {
switch (prop) { get(target, prop) {
case "getItem": switch (prop) {
return (key: string) => { case "getItem":
return target.getItem(client.url.host + "@" + key); return (key: string) => {
}; return target.getItem(client.url.host + "@" + key);
};
case "setItem": case "setItem":
return (key: string, value: string) => { return (key: string, value: string) => {
return target.setItem(client.url.host + "@" + key, value); return target.setItem(client.url.host + "@" + key, value);
}; };
case "removeItem": case "removeItem":
return (key: string) => { return (key: string) => {
return target.removeItem(client.url.host + "@" + key); return target.removeItem(client.url.host + "@" + key);
}; };
case "clear": case "clear":
return () => { return () => {
for (const key in Object.keys(target)) { for (const key in Object.keys(target)) {
if (key.startsWith(client.url.host)) { if (key.startsWith(client.url.host)) {
target.removeItem(key); target.removeItem(key);
}
} }
} };
};
case "key": case "key":
return (index: number) => { return (index: number) => {
const keys = Object.keys(target).filter((key) => const keys = Object.keys(target).filter((key) =>
key.startsWith(client.url.host)
);
return target.getItem(keys[index]);
};
case "length":
return Object.keys(target).filter((key) =>
key.startsWith(client.url.host) key.startsWith(client.url.host)
); ).length;
return target.getItem(keys[index]); default:
}; if (prop in Object.prototype) {
return Reflect.get(target, prop);
}
console.log("GET", prop, target == realLocalStorage);
case "length": return target.getItem(client.url.host + "@" + (prop as string));
return Object.keys(target).filter((key) => }
key.startsWith(client.url.host) },
).length;
default: set(target, prop, value) {
if (prop in Object.prototype) { if (target == realLocalStorage)
return Reflect.get(target, prop); console.log("SET", prop, value, target === realLocalStorage);
} target.setItem(client.url.host + "@" + (prop as string), value);
console.log("GET", prop, target == realLocalStorage);
return target.getItem(client.url.host + "@" + (prop as string)); return true;
} },
},
set(target, prop, value) { ownKeys(target) {
if (target == realLocalStorage) return Reflect.ownKeys(target)
console.log("SET", prop, value, target === realLocalStorage); .filter((f) => typeof f === "string" && f.startsWith(client.url.host))
target.setItem(client.url.host + "@" + (prop as string), value); .map((f) => f.substring(client.url.host.length + 1));
},
return true; getOwnPropertyDescriptor(target, property) {
}, return {
value: target.getItem(client.url.host + "@" + (property as string)),
enumerable: true,
configurable: true,
writable: true,
};
},
ownKeys(target) { defineProperty(target, property, attributes) {
return Reflect.ownKeys(target) target.setItem(
.filter((f) => typeof f === "string" && f.startsWith(client.url.host)) client.url.host + "@" + (property as string),
.map((f) => f.substring(client.url.host.length + 1)); attributes.value
}, );
getOwnPropertyDescriptor(target, property) { return true;
return { },
value: target.getItem(client.url.host + "@" + (property as string)), };
enumerable: true,
configurable: true,
writable: true,
};
},
defineProperty(target, property, attributes) { const realLocalStorage = window.localStorage;
target.setItem( const realSessionStorage = window.sessionStorage;
client.url.host + "@" + (property as string),
attributes.value
);
return true; const localStorageProxy = new Proxy(window.localStorage, handler);
}, const sessionStorageProxy = new Proxy(window.sessionStorage, handler);
};
const realLocalStorage = window.localStorage; delete window.localStorage;
const realSessionStorage = window.sessionStorage; delete window.sessionStorage;
const localStorageProxy = new Proxy(window.localStorage, handler); window.localStorage = localStorageProxy;
const sessionStorageProxy = new Proxy(window.sessionStorage, handler); window.sessionStorage = sessionStorageProxy;
}
delete window.localStorage;
delete window.sessionStorage;
window.localStorage = localStorageProxy;
window.sessionStorage = sessionStorageProxy;

View file

@ -30,10 +30,12 @@
// }, // },
// }); // });
//@ts-nocheck if ("window" in self) {
delete window.TrustedHTML; //@ts-nocheck
delete window.TrustedScript; delete window.TrustedHTML;
delete window.TrustedScriptURL; delete window.TrustedScript;
delete window.TrustedTypePolicy; delete window.TrustedScriptURL;
delete window.TrustedTypePolicyFactory; delete window.TrustedTypePolicy;
delete window.trustedTypes; delete window.TrustedTypePolicyFactory;
delete window.trustedTypes;
}

View file

@ -2,7 +2,7 @@ import { client } from ".";
import { encodeUrl } from "../shared/rewriters/url"; import { encodeUrl } from "../shared/rewriters/url";
import { locationProxy } from "./location"; import { locationProxy } from "./location";
export const windowProxy = new Proxy(window, { export const windowProxy = new Proxy(self, {
get(target, prop) { get(target, prop) {
const propIsString = typeof prop === "string"; const propIsString = typeof prop === "string";
if (propIsString && prop === "location") { if (propIsString && prop === "location") {
@ -13,7 +13,7 @@ export const windowProxy = new Proxy(window, {
) { ) {
return windowProxy; return windowProxy;
} else if (propIsString && prop == "parent") { } else if (propIsString && prop == "parent") {
return window.parent; return self.parent;
} else if (propIsString && prop === "$scramjet") { } else if (propIsString && prop === "$scramjet") {
return; return;
} }
@ -24,7 +24,7 @@ export const windowProxy = new Proxy(window, {
if (typeof value === "function") { if (typeof value === "function") {
return new Proxy(value, { return new Proxy(value, {
apply(_target, thisArg, argArray) { apply(_target, thisArg, argArray) {
return value.apply(window, argArray); return value.apply(self, argArray);
}, },
}); });
} }
@ -47,7 +47,7 @@ export const windowProxy = new Proxy(window, {
}, },
}); });
export const documentProxy = new Proxy(document, { export const documentProxy = new Proxy(self.document || {}, {
get(target, prop) { get(target, prop) {
const propIsString = typeof prop === "string"; const propIsString = typeof prop === "string";
@ -60,7 +60,7 @@ export const documentProxy = new Proxy(document, {
if (typeof value === "function") { if (typeof value === "function") {
return new Proxy(value, { return new Proxy(value, {
apply(_target, thisArg, argArray) { apply(_target, thisArg, argArray) {
return value.apply(document, argArray); return value.apply(self.document, argArray);
}, },
}); });
} }
@ -79,32 +79,16 @@ export const documentProxy = new Proxy(document, {
}, },
}); });
client.Proxy(Function.prototype, "apply", { client.Proxy(
apply(ctx) { [
if (ctx.args[0] === windowProxy) { "Function.prototype.call",
ctx.args[0] = window; "Function.prototype.bind",
} else if (ctx.args[0] === documentProxy) { "Function.prototype.apply",
ctx.args[0] = document; ],
} {
}, apply(ctx) {
}); if (ctx.args[0] === windowProxy) ctx.args[0] = window;
if (ctx.args[0] === documentProxy) ctx.args[0] = document;
client.Proxy(Function.prototype, "call", { },
apply(ctx) { }
if (ctx.args[0] === windowProxy) { );
ctx.args[0] = window;
} else if (ctx.args[0] === documentProxy) {
ctx.args[0] = document;
}
},
});
client.Proxy(Function.prototype, "bind", {
apply(ctx) {
if (ctx.args[0] === windowProxy) {
ctx.args[0] = window;
} else if (ctx.args[0] === documentProxy) {
ctx.args[0] = document;
}
},
});

View file

@ -1,37 +1,38 @@
import { client } from ".";
import { encodeUrl } from "./shared"; import { encodeUrl } from "./shared";
Worker = new Proxy(Worker, { client.Proxy("Worker", {
construct(target, argArray) { construct({ args }) {
argArray[0] = encodeUrl(argArray[0]); if (args[0] instanceof URL) args[0] = args[0].href;
if (args[0].startsWith("blob:") || args[0].startsWith("data:")) {
// TODO
return;
}
// target is a reference to the object that you are proxying args[0] = encodeUrl(args[0]) + "?dest=worker";
// Reflect.construct is just a wrapper for calling target
// you could do new target(...argArray) and it would work the same effectively
return Reflect.construct(target, argArray); if (args[1] && args[1].type === "module") {
args[0] += "&type=module";
}
}, },
}); });
Worklet.prototype.addModule = new Proxy(Worklet.prototype.addModule, { if ("window" in self) {
apply(target, thisArg, argArray) { Worklet.prototype.addModule = new Proxy(Worklet.prototype.addModule, {
argArray[0] = encodeUrl(argArray[0]); apply(target, thisArg, argArray) {
argArray[0] = encodeUrl(argArray[0]);
return Reflect.apply(target, thisArg, argArray); return Reflect.apply(target, thisArg, argArray);
}, },
}); });
if ("serviceWorker" in window.navigator) {
//@ts-expect-error temporary until nested sw support //@ts-expect-error temporary until nested sw support
delete window.Navigator.prototype.serviceWorker; delete window.Navigator.prototype.serviceWorker;
} }
// broken
// window.importScripts = new Proxy(window.importScripts, { client.Proxy("importScripts", {
// apply(target, thisArg, argArray) { apply(ctx) {
// for (const i in argArray) { for (const i in ctx.args) {
// argArray[i] = encodeUrl(argArray[i]); ctx.args[i] = encodeUrl(ctx.args[i]);
// } }
},
// return Reflect.apply(target, thisArg, argArray); });
// },
// });

View file

@ -46,16 +46,16 @@ const plain = {
/* /*
const aes = { const aes = {
encode: (str: string | undefined) => { encode: (str: string | undefined) => {
if (!str) return str; if (!str) return str;
return encodeURIComponent(enc(str, "dynamic").substring(10)); return encodeURIComponent(enc(str, "dynamic").substring(10));
}, },
decode: (str: string | undefined) => { decode: (str: string | undefined) => {
if (!str) return str; if (!str) return str;
return dec("U2FsdGVkX1" + decodeURIComponent(str), "dynamic"); return dec("U2FsdGVkX1" + decodeURIComponent(str), "dynamic");
} }
} }
*/ */

View file

@ -1,25 +1,24 @@
const _dbg = { export default {
fmt: function (severity: string, message: string, ...args: any[]) { fmt: function (severity: string, message: string, ...args: any[]) {
const old = Error.prepareStackTrace; const old = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => { Error.prepareStackTrace = (_, stack) => {
stack.shift(); // stack(); stack.shift(); // stack();
stack.shift(); // fmt(); stack.shift(); // fmt();
stack.shift();
for (let i = 0; i < stack.length; i++) { let fmt = "";
if (Object.values(this).includes(stack[i].getFunction())) { for (let i = 1; i < Math.min(2, stack.length); i++) {
stack.splice(i, 1); if (stack[i].getFunctionName()) {
// const f = stack[i].getThis()?.constructor?.name;
// if (f) fmt += `${f}.`
fmt += `${stack[i].getFunctionName()} -> ` + fmt;
} }
} }
let frame = stack[0]; fmt += stack[0].getFunctionName();
while (!frame?.getFunctionName() || !frame) {
frame = stack.shift();
}
const fn = stack[0].getFunctionName(); return fmt;
return `${fn}()`;
}; };
const fmt = (function stack() { const fmt = (function stack() {
@ -59,6 +58,8 @@ background-color: ${bg};
color: ${fg}; color: ${fg};
padding: ${padding}px; padding: ${padding}px;
font-weight: bold; font-weight: bold;
font-family: monospace;
font-size: 0.9em;
`, `,
` `
${severity === "debug" ? "color: gray" : ""} ${severity === "debug" ? "color: gray" : ""}
@ -79,10 +80,3 @@ ${severity === "debug" ? "color: gray" : ""}
this.fmt("debug", message, ...args); this.fmt("debug", message, ...args);
}, },
}; };
for (const key in _dbg) {
_dbg[key] = (0, eval)("(" + _dbg[key].toString() + ")");
}
self._dbg = _dbg;
export default _dbg;

View file

@ -1,11 +1,32 @@
import { rewriteJs } from "./js"; import { rewriteJs } from "./js";
export function rewriteWorkers(js: string, origin?: URL) {
let str = new String().toString();
//
// ["codecs", "config", "shared", "client"].forEach((script) => {
// str += `import "${self.$scramjet.config[script]}"\n`;
// });
// str += rewriteJs(js, origin);
return js; const clientscripts = ["codecs", "config", "shared", "client"];
export function rewriteWorkers(js: string | ArrayBuffer, origin?: URL) {
let dest = origin.searchParams.get("dest");
let type = origin.searchParams.get("type");
origin.search = "";
let str = "";
if (type === "module") {
for (const script of clientscripts) {
console.log("Import", script, self.$scramjet);
str += `import "${self.$scramjet.config[script]}"\n`;
}
} else {
for (const script of clientscripts) {
str += `importScripts("${self.$scramjet.config[script]}");\n`;
}
}
let rewritten = rewriteJs(js, origin);
if (rewritten instanceof Uint8Array) {
rewritten = new TextDecoder().decode(rewritten);
}
str += "\n" + rewritten;
dbg.log("Rewrite", type, dest, str);
return str;
} }

View file

@ -89,7 +89,7 @@ async function handleResponse(
break; break;
case "sharedworker": case "sharedworker":
case "worker": case "worker":
responseBody = rewriteWorkers(await response.text(), url); responseBody = rewriteWorkers(await response.arrayBuffer(), url);
break; break;
default: default:
responseBody = response.body; responseBody = response.body;

View file

@ -145,6 +145,29 @@ function App() {
`; `;
} }
window.addEventListener("load", () => { window.addEventListener("load", async () => {
document.body.appendChild(h(App)); document.body.appendChild(h(App));
function b64(buffer) {
let binary = "";
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
const arraybuffer = await (await fetch("/scramjet.png")).arrayBuffer();
console.log(
"%cb",
`
background-image: url(data:image/png;base64,${b64(arraybuffer)});
color: transparent;
padding-left: 200px;
padding-bottom: 100px;
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
`
);
}); });