diff --git a/proxyServiceValidator.js b/proxyServiceValidator.js index 1fe45922..eff55eb1 100644 --- a/proxyServiceValidator.js +++ b/proxyServiceValidator.js @@ -76,7 +76,7 @@ const testServerResponse = async () => { console.error( "One or more endpoints failed to respond with status code 200. Test failed." ); - process.exit(1); + process.exitCode = 1; } }; @@ -303,14 +303,14 @@ xx xx if (rammerheadPassed && ultravioletPassed) { console.log("Both tests passed."); - process.exit(0); + process.exitCode = 0; } else { console.error("Tests failed."); - process.exit(1); + process.exitCode = 1; } } catch (error) { console.error("Error in testCommonJSOnPage:", error.message); - process.exit(1); + process.exitCode = 1; } finally { await browser.close(); } diff --git a/views/assets/json/blacklist.json b/views/assets/json/blacklist.json new file mode 100644 index 00000000..596e9800 --- /dev/null +++ b/views/assets/json/blacklist.json @@ -0,0 +1 @@ +["*.google.com"] diff --git a/views/uv/WWError.js b/views/uv/WWError.js new file mode 100644 index 00000000..1494d6df --- /dev/null +++ b/views/uv/WWError.js @@ -0,0 +1,12 @@ +/* ----------------------------------------------- +/* Authors: wearrrrr +/* GNU General Public License v3.0: https://www.gnu.org/licenses/gpl-3.0.en.html +/* Workerware Error Script +/* ----------------------------------------------- */ + +class WWError extends Error { + constructor(message) { + super(message); + this.name = "[WorkerWare Exception]"; + } +} diff --git a/views/uv/sw.js b/views/uv/sw.js index b077e1a0..e51cb6d0 100644 --- a/views/uv/sw.js +++ b/views/uv/sw.js @@ -2,15 +2,59 @@ importScripts("uv.bundle.js"); importScripts("uv.config.js"); importScripts(__uv$config.sw || "uv.sw.js"); +/* + +Workerware does not work yet due to one of the following possibilities: + +1. UV or the bare client is not updated to support workerware yet. +2. Workerware is unfinished. +3. We are doofuses and do not know how to use workerware properly. + +Going to implement a ghetto domain blacklist for now. + +importScripts("./workerware.js"); + +const ww = new WorkerWare({ + debug: true, + randomNames: true, + timing: true +}); + + +ww.use({ + function: event => console.log(event), + events: ["fetch", "message"] +}); + +*/ + + const uv = new UVServiceWorker(); +// Get list of blacklisted domains. +let blacklist; +fetch("/assets/json/blacklist.json").then(request => { + request.json().then(jsonData => { + blacklist = jsonData.map( + domain => new RegExp(domain.replaceAll(".", "\\.").replaceAll("*", ".")) + ); + }); +}); + self.addEventListener("fetch", (event) => { event.respondWith( (async () => { +// The one and only ghetto domain blacklist. + if (!new URL(event.request.url).pathname.indexOf("/uv/service/")) { + const url = new URL(uv.config.decodeUrl(new URL(event.request.url).pathname.replace(/^\/uv\/service\//, ""))); + if (blacklist.some(domain => domain.test(url.hostname))) + return new Response(new Blob(), {status: 406}); + } + if (uv.route(event)) { return await uv.fetch(event); } return await fetch(event.request); })() ); -}); +}); \ No newline at end of file diff --git a/views/uv/workerware.js b/views/uv/workerware.js new file mode 100644 index 00000000..c4ea734d --- /dev/null +++ b/views/uv/workerware.js @@ -0,0 +1,174 @@ +/* ----------------------------------------------- +/* Authors: wearrrrr +/* GNU General Public License v3.0: https://www.gnu.org/licenses/gpl-3.0.en.html +/* Service Worker Middleware Script +/* ----------------------------------------------- */ + +importScripts("./WWError.js"); +const dbg = console.log.bind(console, "[WorkerWare]"); +const time = console.time.bind(console, "[WorkerWare]"); +const timeEnd = console.timeEnd.bind(console, "[WorkerWare]"); + +/* + OPTS: + debug - Enables debug logging. + randomNames - Generate random names for middlewares. + timing - Logs timing for each middleware. +*/ + +const defaultOpt = { + debug: false, + randomNames: false, + timing: false, +}; + +const validEvents = [ + "abortpayment", + "activate", + "backgroundfetchabort", + "backgroundfetchclick", + "backgroundfetchfail", + "backgroundfetchsuccess", + "canmakepayment", + "contentdelete", + "cookiechange", + "fetch", + "install", + "message", + "messageerror", + "notificationclick", + "notificationclose", + "paymentrequest", + "periodicsync", + "push", + "pushsubscriptionchange", + "sync", +]; + +class WorkerWare { + constructor(opt) { + this._opt = Object.assign({}, defaultOpt, opt); + this._middlewares = []; + } + info() { + return { + version: "0.1.0", + middlewares: this._middlewares, + options: this._opt, + }; + } + use(middleware) { + let validateMW = this.validateMiddleware(middleware); + if (validateMW.error) throw new WWError(validateMW.error); + // This means the middleware is an anonymous function, or the user is silly and named their function "function" + if (middleware.function.name == "function") middleware.name = crypto.randomUUID(); + if (!middleware.name) middleware.name = middleware.function.name; + if (this._opt.randomNames) middleware.name = crypto.randomUUID(); + if (this._opt.debug) dbg("Adding middleware:", middleware.name); + this._middlewares.push(middleware); + } + // Run all middlewares for the event type passed in. + run(event) { + const middlewares = this._middlewares; + const returnList = []; + let fn = async () => { + for (let i = 0; i < middlewares.length; i++) { + if (middlewares[i].events.includes(event.type)) { + if (this._opt.timing) console.time(middlewares[i].name); + // Add the configuration to the event object. + event.workerware = { + config: middlewares[i].configuration || {}, + }; + if (!middlewares[i].explicitCall) { + let res = await middlewares[i].function(event); + if (this._opt.timing) console.timeEnd(middlewares[i].name); + returnList.push(res); + } + } + } + return returnList; + }; + return fn; + } + deleteByName(middlewareID) { + if (this._opt.debug) dbg("Deleting middleware:", middlewareID); + this._middlewares = this._middlewares.filter((mw) => mw.name !== middlewareID); + } + deleteByEvent(middlewareEvent) { + if (this._opt.debug) dbg("Deleting middleware by event:", middlewareEvent); + this._middlewares = this._middlewares.filter((mw) => !mw.events.includes(middlewareEvent)); + } + get() { + return this._middlewares; + } + /* + Run a single middleware by ID. + This assumes that the user knows what they're doing, and is running the middleware on an event that it's supposed to run on. + */ + runMW(name, event) { + const middlewares = this._middlewares; + if (this._opt.debug) dbg("Running middleware:", name); + // if (middlewares.includes(name)) { + // return middlewares[name](event); + // } else { + // throw new WWError("Middleware not found!"); + // } + let didCall = false; + for (let i = 0; i < middlewares.length; i++) { + if (middlewares[i].name == name) { + didCall = true; + event.workerware = { + config: middlewares[i].configuration || {}, + } + if (this._opt.timing) console.time(middlewares[i].name); + let call = middlewares[i].function(event); + if (this._opt.timing) console.timeEnd(middlewares[i].name); + return call; + } + } + if (!didCall) { + throw new WWError("Middleware not found!"); + } + } + // type middlewareManifest = { + // function: Function, + // name?: string, + // events: string[], // Should be a union of validEvents. + // configuration?: Object // Optional configuration for the middleware. + // } + validateMiddleware(middleware) { + if (!middleware.function) + return { + error: "middleware.function is required", + }; + if (typeof middleware.function !== "function") + return { + error: "middleware.function must be typeof function", + }; + if (typeof middleware.configuration !== "object" && middleware.configuration !== undefined) { + return { + error: "middleware.configuration must be typeof object", + }; + } + if (!middleware.events) + return { + error: "middleware.events is required", + }; + if (!Array.isArray(middleware.events)) + return { + error: "middleware.events must be an array", + }; + if (middleware.events.some((ev) => !validEvents.includes(ev))) + return { + error: "Invalid event type! Must be one of the following: " + validEvents.join(", "), + }; + if (middleware.explicitCall && typeof middleware.explicitCall !== "boolean") { + return { + error: "middleware.explicitCall must be typeof boolean", + }; + } + return { + error: undefined, + }; + } +}