From f7665d88effd850576f434a61636aaaef9d1c9a0 Mon Sep 17 00:00:00 2001 From: QuiteAFancyEmerald <46467239+QuiteAFancyEmerald@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:25:27 -0800 Subject: [PATCH] fix: scramjet implementation --- proxyServiceValidator.js | 101 +++++++++++++++++++++- views/assets/js/register-sw.js | 125 ++++++++++++++++----------- views/pages/frame.html | 1 + views/pages/proxnav/scramjet.html | 3 +- views/pages/proxnav/ultraviolet.html | 1 + 5 files changed, 176 insertions(+), 55 deletions(-) diff --git a/proxyServiceValidator.js b/proxyServiceValidator.js index 91aa4fd8..ca62a615 100644 --- a/proxyServiceValidator.js +++ b/proxyServiceValidator.js @@ -312,12 +312,107 @@ xx xx return uvTestPassed; }; - // Run tests for Rammerhead and Ultraviolet. + const testScramjet = async () => { + const omniboxId = 'pr-sj', + errorPrefix = 'failure', + // For the hacky URL test further below, use the URL page's EXACT title. + website = Object.freeze({ + path: 'example.com', + title: 'Example Domain', + }); + await page.goto('http://localhost:8080/scramjet'); + const generatedUrl = await page.evaluate( + generateUrl, + omniboxId, + website.path, + errorPrefix + ); + + const testResults = await page.evaluate( + async (generatedUrl, pageTitle) => { + const results = [{}, {}]; + + await new Promise((resolve) => { + const waitForDocument = () => { + if (document.readyState === 'complete') resolve(); + else window.addEventListener('load', resolve); + }, + // Wait until a service worker is registered before continuing. + // Also check again to make sure the document is loaded. + waitForWorker = async () => { + setTimeout(async () => { + (await navigator.serviceWorker.getRegistrations()).length > 0 + ? waitForDocument() + : waitForWorker(); + }, 1000); + }; + + waitForWorker(); + }); + + try { + results[0].scramjet = generatedUrl; + + // Test to see if the document title for example.com has loaded, + // by appending an IFrame to the document and grabbing its content. + const testGeneratedUrlHacky = async (url) => { + const exampleIFrame = document.createElement('iframe'); + const waitForDocument = new Promise((resolve) => { + document.documentElement.appendChild(exampleIFrame); + exampleIFrame.addEventListener('load', () => { + resolve( + exampleIFrame.contentWindow.document.title === pageTitle + ); + }); + }); + + // Give 10 seconds for the IFrame to load before manually checking. + const timeout = new Promise((resolve) => { + setTimeout(() => { + resolve( + exampleIFrame.contentWindow.document.title === pageTitle + ); + }, 10000); + }); + + exampleIFrame.src = url; + exampleIFrame.style.display = 'none'; + return await Promise.race([waitForDocument, timeout]); + }; + + results[1].sjTestPassed = + !!results[0].scramjet.indexOf(errorPrefix) && + (await testGeneratedUrlHacky(results[0].scramjet)); + } catch (e) { + results[0].scramjet = errorPrefix + ': ' + e.message; + } + + return results; + }, + generatedUrl, + website.title, + errorPrefix + ); + + console.log('Scramjet test results:', testResults[0]); + const sjTestPassed = + testResults[0].scramjet && + testResults[0].scramjet !== 'failure' && + testResults[1].scramjet; + console.log( + 'Scramjet test result:', + sjTestPassed ? 'success' : 'failure' + ); + return sjTestPassed; + }; + + // Run tests for Rammerhead, Ultraviolet and Scramjet const rammerheadPassed = await testRammerhead(); const ultravioletPassed = await testUltraviolet(); + const scramjetPassed = await testScramjet(); - if (rammerheadPassed && ultravioletPassed) { - console.log('Both tests passed.'); + if (rammerheadPassed && ultravioletPassed && scramjetPassed) { + console.log('All proxy endpoint tests passed.'); process.exitCode = 0; } else { console.error('Tests failed.'); diff --git a/views/assets/js/register-sw.js b/views/assets/js/register-sw.js index b2ae518f..af39f2f3 100644 --- a/views/assets/js/register-sw.js +++ b/views/assets/js/register-sw.js @@ -1,56 +1,43 @@ -// Encase everything in a new scope so that variables are not accidentally -// attached to the global scope. (() => { const stockSW = '/uv/sw.js', blacklistSW = '/uv/sw-blacklist.js', swAllowedHostnames = ['localhost', '127.0.0.1'], - connection = new BareMux.BareMuxConnection('/baremux/worker.js'), wispUrl = (location.protocol === 'https:' ? 'wss' : 'ws') + '://' + location.host + '/wisp/', - scramjet = new ScramjetController({ - prefix: "/scram/service/", - files: { - wasm: "/scram/scramjet.wasm.js", - worker: "/scram/scramjet.worker.js", - client: "/scram/scramjet.client.js", - shared: "/scram/scramjet.shared.js", - sync: "/scram/scramjet.sync.js" - }, - flags: { - serviceworkers: true, - syncxhr: true, - scramitize: true, - }, - }), - // Proxy configuration - proxyUrl = 'socks5h://localhost:9050', // Replace with your TOR proxy URL (or any) + proxyUrl = 'socks5h://localhost:9050', // Replace with your TOR proxy URL transports = { epoxy: '/epoxy/index.mjs', libcurl: '/libcurl/index.mjs', bare: '/baremux/index.mjs', }, - // The following two variables are copied and pasted here from csel.js. readCookie = async (name) => { - // Get the first cookie that has the same name. - for (let cookie of document.cookie.split('; ')) - if (!cookie.indexOf(name + '=')) - // Return the cookie's stored content. + for (let cookie of document.cookie.split('; ')) { + if (!cookie.indexOf(name + '=')) { return decodeURIComponent(cookie.slice(name.length + 1)); + } + } }, - // Sets the default transport mode based on the browser. Firefox is not - // supported by epoxy yet, which is why this is implemented. defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent) ? 'epoxy' : 'libcurl'; - + transports.default = transports[defaultMode]; - - // Prevent the transports object from accidentally being edited. + Object.freeze(transports); - + + const waitForScramjetController = () => + new Promise((resolve) => { + const interval = setInterval(() => { + if (typeof ScramjetController !== 'undefined') { + clearInterval(interval); + resolve(); + } + }, 50); + }); + const registerSW = async () => { if (!navigator.serviceWorker) { if ( @@ -58,30 +45,32 @@ !swAllowedHostnames.includes(location.hostname) ) throw new Error('Service workers cannot be registered without https.'); - + throw new Error("Your browser doesn't support service workers."); } - - // If the user has changed the transport mode, use that over the default. + + // Set the transport mode const transportMode = transports[await readCookie('HBTransport')] || transports.default; let transportOptions = { wisp: wispUrl }; - - // Only use Tor with the proxy if the user has enabled it in settings. - if ((await readCookie('HBUseOnion')) === 'true') + + if ((await readCookie('HBUseOnion')) === 'true') { transportOptions.proxy = proxyUrl; - + console.log('Using Onion Proxy:', proxyUrl); + } + + console.log('Transport mode:', transportMode); + + const connection = new BareMux.BareMuxConnection('/baremux/worker.js'); await connection.setTransport(transportMode, [transportOptions]); - - /* Choose a service worker to register based on whether or not the user - * has adblocking enabled. If the user changes this setting, this script needs - * to be reloaded for this to update, such as by refreshing the page. - */ + const registrations = await navigator.serviceWorker.getRegistrations(), usedSW = (await readCookie('HBHideAds')) !== 'false' ? blacklistSW : stockSW; - - // Unregister a service worker if it isn't the one being used. + + console.log('Service Worker being registered:', usedSW); + + // Unregister outdated service workers for (const registration of registrations) if ( registration.active && @@ -89,11 +78,47 @@ new URL(usedSW, location.origin).pathname ) await registration.unregister(); - + await navigator.serviceWorker.register(usedSW); }; - // Register the service worker - registerSW(); - scramjet.init("/scram/scramjet.sw.js"); -})(); \ No newline at end of file + const initializeScramjet = async () => { + try { + + await waitForScramjetController(); + + const scramjet = new ScramjetController({ + prefix: '/scram/service/', + files: { + wasm: '/scram/scramjet.wasm.js', + worker: '/scram/scramjet.worker.js', + client: '/scram/scramjet.client.js', + shared: '/scram/scramjet.shared.js', + sync: '/scram/scramjet.sync.js', + }, + flags: { + serviceworkers: true, + syncxhr: true, + scramitize: true, + }, + }); + + console.log('Initializing ScramjetController'); + scramjet.init('/scram/scramjet.sw.js'); + } catch (err) { + console.error('Scramjet initialization failed:', err); + } + }; + + const initialize = async () => { + try { + await registerSW(); + + await initializeScramjet(); + } catch (err) { + console.error('Initialization failed:', err); + } + }; + + initialize(); +})(); diff --git a/views/pages/frame.html b/views/pages/frame.html index fb118141..a4b40d5b 100644 --- a/views/pages/frame.html +++ b/views/pages/frame.html @@ -42,6 +42,7 @@
+ - + diff --git a/views/pages/proxnav/ultraviolet.html b/views/pages/proxnav/ultraviolet.html index c718e8ed..7a37078a 100644 --- a/views/pages/proxnav/ultraviolet.html +++ b/views/pages/proxnav/ultraviolet.html @@ -104,6 +104,7 @@ +