diff --git a/alloy/assets/inject.js b/alloy/assets/inject.js deleted file mode 100644 index 76c28bac..00000000 --- a/alloy/assets/inject.js +++ /dev/null @@ -1,209 +0,0 @@ -// Ajax Rewriting - -let apData = document.getElementById('alloyData'); -let urlData = apData.getAttribute('data-alloyURL'); - - -function rewriteURL(url, encoding) { - var websiteURL - if (encoding == 'base64') { - websiteURL = btoa(url.split('/').splice(0, 3).join('/')) - } else { - websiteURL = url.split('/').splice(0, 3).join('/') - } - const path = '/' + url.split('/').splice(3).join('/') - var rewritten - if (path == '/') { - rewritten = '/fetch/' + websiteURL - } else { - rewritten = `/fetch/${websiteURL}${path}` - } - return rewritten -} -let ajaxRewrite = window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) { - if (url.startsWith(`${window.location.protocol}//${window.location.hostname}`) && !url.startsWith(`${window.location.protocol}//${window.location.hostname}/fetch/`)) { - url = `/fetch/${urlData}/` + url.split('/').splice(3).join('/') - } else if (url.startsWith('http')) { - const hostname = url.split('/').slice(0, 3).join('/') - const path = url.split('/').slice(3).join('/') - const encodedHost = btoa(hostname) - const fullURL = encodedHost + '/' + path - url = '/fetch/' + fullURL - } else if (url.startsWith('//')) { - const encodedURL = btoa('http:' + url) - url = '/alloy/?url=' + encodedURL - } else if (url.startsWith('/')) { - if (url.startsWith('/fetch')) { - url = url - } else if (url.startsWith('/alloy')) { - url = url - } else { - let apData = document.getElementById('alloyData'); - let urlData = apData.getAttribute('data-alloyURL'); - url = '/fetch/' + urlData + url - } - } - - - return ajaxRewrite.apply(this, arguments); - } - - let windowFetchRewrite = window.fetch;window.fetch = function(url) { - if (url.startsWith(`https://${window.location.hostname}`)) { - url = url - } else if (url.startsWith('http')) { - const hostname = url.split('/').slice(0, 3).join('/') - const path = url.split('/').slice(3).join('/') - const encodedHost = btoa(hostname) - const fullURL = encodedHost + '/' + path - url = '/fetch/' + fullURL - } else if (url.startsWith('//')) { - const encodedURL = btoa('http:' + url) - url = '/alloy/?url=' + encodedURL - } else if (url.startsWith('/')) { - if (url.startsWith('/fetch')) { - url = url - } else if (url.startsWith('/alloy')) { - url = url - } else { - let apData = document.getElementById('alloyData'); - let urlData = apData.getAttribute('data-alloyURL'); - url = '/fetch/' + urlData + url - } - } - return windowFetchRewrite.apply(this, arguments); - } - - - - //Create Element rewriting - var original = document.createElement; - document.createElement = function (tag) { - var element = original.call(document, tag); - if (tag.toLowerCase() === 'script') { - Object.defineProperty(element.__proto__, 'src', { - set: function(newValue) { - if (newValue.startsWith('/fetch/')) { - element.setAttribute('src', newValue) - } else if (newValue.startsWith('/alloy/')) { - element.setAttribute('src', newValue) - } else if (newValue.startsWith(`https://${window.location.hostname}`)) { - element.setAttribute('src', newValue) - } else if (newValue.startsWith('//')) { - const encodedURL = btoa('http:' + newValue) - element.setAttribute('src', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('https://')) { - const encodedURL = btoa(newValue) - element.setAttribute('src', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('http://')) { - const encodedURL = btoa(newValue) - element.setAttribute('src', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('/')) { - element.setAttribute('src', '/fetch/' + urlData + newValue) - } else { - element.setAttribute('src', newValue) - } - } - }); - } else if (tag.toLowerCase() === 'iframe') { - Object.defineProperty(element.__proto__, 'src', { - set: function(newValue) { - if (newValue.startsWith('/fetch/')) { - element.setAttribute('src', newValue) - } else if (newValue.startsWith('/alloy/')) { - element.setAttribute('src', newValue) - } else if (newValue.startsWith(`https://${window.location.hostname}`)) { - element.setAttribute('src', newValue) - } else if (newValue.startsWith('//')) { - const encodedURL = btoa('http:' + newValue) - element.setAttribute('src', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('https://')) { - const encodedURL = btoa(newValue) - element.setAttribute('src', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('http://')) { - const encodedURL = btoa(newValue) - element.setAttribute('src', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('/')) { - element.setAttribute('src', '/fetch/' + urlData + newValue) - - } else { - element.setAttribute('src', newValue) - } - } - }); - } - else if (tag.toLowerCase() === 'link') { - Object.defineProperty(element.__proto__, 'href', { - set: function(newValue) { - if (newValue.startsWith('/fetch/')) { - element.setAttribute('href', newValue) - } else if (newValue.startsWith('/alloy/')) { - element.setAttribute('href', newValue) - } else if (newValue.startsWith(`https://${window.location.hostname}`)) { - element.setAttribute('href', newValue) - } else if (newValue.startsWith('//')) { - const encodedURL = btoa('http:' + newValue) - element.setAttribute('href', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('https://')) { - const encodedURL = btoa(newValue) - element.setAttribute('href', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('http://')) { - const encodedURL = btoa(newValue) - element.setAttribute('href', '/alloy/?url=' + encodedURL) - } else if (newValue.startsWith('/')) { - element.setAttribute('href', '/fetch/' + urlData + newValue) - - } else { - element.setAttribute('href', newValue) - } - } - }); - } - return element; - } - - - let setAttributeRewrite = window.Element.prototype.setAttribute;window.Element.prototype.setAttribute = function(name, value) { - switch(name) { - case 'src': - if (value.startsWith('/fetch/')) { - value = value - } else if (value.startsWith('/alloy/')) { - value = value - } else if (value.startsWith('//')) { - value = rewriteURL('http:' + value, 'base64') - } else if (value.startsWith('/')) { - value = rewriteURL(urlData + value) - break; - } else if (value.startsWith('https://') || value.startsWith('http://')) { - value = rewriteURL(value, 'base64') - } else { - value = value - } - break; - case 'href': - if (value.startsWith('/fetch/')) { - value = value - } else if (value.startsWith('//')) { - value = rewriteURL('http:' + value, 'base64') - } else if (value.startsWith('/')) { - value = rewriteURL(urlData + value) - break; - } else if (value.startsWith('https://') || value.startsWith('http://')) { - value = rewriteURL(value, 'base64') - } else { - value = value - } - break; - } - - - return setAttributeRewrite.apply(this, arguments); - } - - var previousState = window.history.state; - setInterval(function() { - if (!window.location.pathname.startsWith(`/fetch/${urlData}/`)) { - history.replaceState('', '', `/fetch/${urlData}/${window.location.href.split('/').splice(3).join('/')}`); - } - }, 0.1); \ No newline at end of file diff --git a/app.js b/app.js index a8ada1f1..f3a735f1 100644 --- a/app.js +++ b/app.js @@ -1,347 +1,291 @@ -var https = require('https'); -var http = require('http'); -var fetch = require('node-fetch'); -var express = require('express'); -var fs = require('fs'); -var app = express(); -var cookieParser = require('cookie-parser'); -var session = require('express-session'); -var xss = require("xss"); + const express = require('express'), + app = express(), + http = require('http'), + https = require('https'), + fs = require('fs'), + querystring = require('querystring'), + session = require('express-session'), + sanitizer = require('sanitizer'), + fetch = require('node-fetch'); -var config = JSON.parse(fs.readFileSync('config.json', 'utf-8')), - httpsAgent = new https.Agent({ - rejectUnauthorized: false, - keepAlive: true, - }), - httpAgent = new http.Agent({ - rejectUnauthorized: false, - keepAlive: true, - }), - ssl = { key: fs.readFileSync('ssl/default.key', 'utf8'), cert: fs.readFileSync('ssl/default.crt', 'utf8') }, - server, - port = process.env.PORT || config.port, - ready = (() => { - var a = 'http://', b = config.listenip; - if (config.ssl) a = 'https://'; - if (b == '0.0.0.0' || b == '127.0.0.1') b = 'localhost'; - console.log('AlloyProxy is now running at', a + b + ':' + port); - }); - -http.globalAgent.maxSockets = Infinity; -https.globalAgent.maxSockets = Infinity; - -if (config.ssl) server = https.createServer(ssl, app).listen(port, config.listenip, ready); -else server = http.createServer(app).listen(port, config.listenip, ready); - -app.use(cookieParser()); -app.use(session({ -secret: 'alloy', -saveUninitialized: true, -resave: true -})); - -app.use((req, res, next)=>{ - // nice bodyparser alternative that wont cough up errors - - req.setEncoding('utf8'); - req.raw_body = '' - req.body = new Object() - - req.on('data', chunk=>{ req.raw_body += chunk }); - - req.on('end', ()=>{ - req.str_body = req.raw_body.toString('utf8'); - - try{ - var result = new Object(); - - req.str_body.split('&').forEach((pair)=>{ - pair = pair.split('='); - req.body[pair[0]] = decodeURIComponent(pair[1] || ''); - }); - }catch(err){ - req.body = {} - } - - return next(); - }); -}); - -function base64Encode(data) { - return new Buffer.from(data).toString('base64') -} - -// How to use: base64Decode('string') will return any input base64 decoded -function base64Decode(data) { - return new Buffer.from(data, 'base64').toString('ascii') -} - -// How to use: rewritingURL('https://example.org/assets/main.js') will rewrite any external URL. Output: aHR0cHM6Ly9leGFtcGxlLm9yZw==/assets/main.js -function rewriteURL(dataURL, option) { - var websiteURL - var websitePath - if (option == 'decode') { - websiteURL = base64Decode(dataURL.split('/').splice(0, 1).join('/')) - websitePath = '/' + dataURL.split('/').splice(1).join('/') - } else { - websiteURL = base64Encode(dataURL.split('/').splice(0, 3).join('/')) - websitePath = '/' + dataURL.split('/').splice(3).join('/') + const config = JSON.parse(fs.readFileSync('./config.json', {encoding:'utf8'})); + if (!config.prefix.startsWith('/')) { + config.prefix = `/${config.prefix}`; } - if (websitePath == '/') { - return `${websiteURL}` - } else return `${websiteURL}${websitePath}` -} -// To be used with res.send() to send error. Example: res.send(error('404', 'No valid directory or file was found!')) -function error(statusCode, info) { - if (statusCode && info) { - return fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error ${statusCode}: ${info}`) + if (!config.prefix.endsWith('/')) { + config.prefix = `${config.prefix}/`; } - if (info && !statusCode) { - return (fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error: ${info}`)) - } - if (statusCode && !info) { - return (fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error ${statusCode}`)) - } - return (fs.readFileSync('public/assets/error.html', 'utf8').toString().replace('%ERROR%', `An error has occurred!`)) -} -// Post data to set URl's for reverse proxy URL's, or safer redirecting to proxied websites. -app.post('/createSession', async (req, res) => { - if (req.body.url.startsWith('//')) { - req.body.url = 'http:' + req.body.url; - } else if (req.body.url.startsWith('https://') || req.body.url.startsWith('http://')) { - req.body.url = req.body.url; - } else { - req.body.url = 'http://' + req.body.url; - } - if (req.body.rv) { - req.session.rvURL = String(req.body.url).split('/').splice(0, 3).join('/') - return res.redirect('/fetch/rv/' + String(req.body.url).split('/').splice(3).join('/')) - } else { - return res.redirect('/fetch/' + rewriteURL(String(req.body.url))) - } -}) -// Custom prefix support will be in full effect soon! -var prefix = '/fetch'; + let server; + let server_protocol; + const server_options = { + key: fs.readFileSync('./ssl/default.key'), + cert: fs.readFileSync('./ssl/default.crt') + } + if (config.ssl == true) { server = https.createServer(server_options, app); server_protocol = 'https://';} + else { server = http.createServer(app); server_protocol = 'http://';}; -app.use(prefix, async (req, res, next) => { - var location = rewriteURL(req.url.slice(1), 'decode'); - if (req.url.startsWith('/rv') && !req.session.rvURL) { - res.send(error('400', 'No valid session URL for reverse proxy mode was found!')) - } - if (req.url.startsWith('/rv') && req.session.rvURL) { - location = req.session.rvURL + req.url.slice(3) - } - location = { - href: location, - hostname : location.split('/').splice(2).splice(0, 1).join('/'), - origin : location.split('/').splice(0, 3).join('/'), - origin_encoded : base64Encode(location.split('/').splice(0, 3).join('/')), - path : '/' + location.split('/').splice(3).join('/'), - protocol : location.split('\:').splice(0, 1).join(''), - } - var httpAgent = new http.Agent({ - keepAlive: true - }); - var httpsAgent = new https.Agent({ - keepAlive: true - }); - - // We are using the clients request headers as the headers to send the request so that headers such as Authorization will be passed through in a XML or fetch() request. - // The host header has to be set to the websites host and not the apps hostname so that there won't be issues. - var fetchHeaders = req.headers - fetchHeaders['referer'] = location.href - fetchHeaders['origin'] = location.origin - fetchHeaders['host'] = location.hostname - // Cookie header causing issues sometimes :cursed: - if (fetchHeaders['cookie']) { - delete fetchHeaders['cookie'] - } - var options = { - method: req.method, - headers: fetchHeaders, - redirect: 'manual', - agent: function(_parsedURL) { - if (_parsedURL.protocol == 'http:') { - return httpAgent; - } else { - return httpsAgent; - } - } + + console.log(`Alloy Proxy now running on ${server_protocol}0.0.0.0:${config.port}! Proxy prefix is "${config.prefix}"!`); + server.listen(process.env.PORT || config.port); + + btoa = (str) => { + str = new Buffer.from(str).toString('base64'); + return str; }; - - if (req.method == 'POST') { - try { - // str_body is a string containing the requests body - options['body'] = req.str_body; - }catch(err){ - return; - } - } - // Makes sure to use the session URL that is contained so RV mode works. - if (req.url.startsWith('/rv')) { - location.origin_encoded = 'rv' - } - if (!req.url.startsWith(`/${location.origin_encoded}/`)) { - try{ - return res.redirect(307,`/fetch/${location.origin_encoded}/`) - }catch(err){ - return; - } - } - // Custom fixes for websites such as Discord and Reddit. - if (location.href == 'https://discord.com' || location.href == 'https://discord.com/new') { - return res.redirect(307, `/fetch/${location.origin_encoded}/login`) - } - if (location.origin == 'https://www.reddit.com') { - if (req.url.startsWith('/rv') && req.session.rvURL) { - req.session.rvURL = 'https://old.reddit.com' - return res.redirect(307, '/fetch/rv' + location.path) - } - return res.redirect(307, '/fetch/' + base64Encode('https://old.reddit.com') + location.path) - } - // This is where I am making the request, and getting the buffer and headers. - const response = await fetch(location.href, options).catch(err => res.send(error('404', `"${xss(location.href)}" was not found!`))); - if(typeof response.buffer != 'function')return; - var resbody = await response.buffer(); - var contentType = 'text/plain' - response.headers.forEach((e, i, a) => { - if (i == 'content-type') contentType = e; + atob = (str) => { + str = new Buffer.from(str, 'base64').toString('utf-8'); + return str; + }; + + rewrite_url = (dataURL, option) => { + var websiteURL; + var websitePath; + if (option == 'decode') { + websiteURL = atob(dataURL.split('/').splice(0, 1).join('/')); + websitePath = '/' + dataURL.split('/').splice(1).join('/'); + } else { + websiteURL = btoa(dataURL.split('/').splice(0, 3).join('/')); + websitePath = '/' + dataURL.split('/').splice(3).join('/'); + } + if (websitePath == '/') { return `${websiteURL}`; } else return `${websiteURL}${websitePath}`; + }; + + app.use(session({ + secret: 'alloy', + saveUninitialized: true, + resave: true +})); +// We made our own version of body-parser instead, due to issues. + app.use((req, res, next) => { + if (req.method == 'POST') { + req.raw_body = ''; + req.on('data', chunk => { + req.raw_body += chunk.toString(); // convert Buffer to string + }); + req.on('end', () => { + req.str_body = req.raw_body; + try { + req.body = JSON.parse(req.raw_body); + } catch(err) { + req.body = {} + } + next(); + }); + } else return next(); }); - if (contentType == null || typeof contentType == 'undefined') ct = 'text/html'; - var serverHeaders = Object.fromEntries( - Object.entries(JSON.parse(JSON.stringify(response.headers.raw()))) - .map(([key, val]) => [key, val[0]]) - ); - // Making sure redirects are proxied. - if (serverHeaders['location']) { - if (req.url.startsWith('/rv') && req.session.rvURL) { - req.session.rvURL = String(serverHeaders['location']).split('/').splice(0, 3).join('/') - return res.redirect(307, '/fetch/rv/' + String(serverHeaders['location']).split('/').splice(3).join('/')) - } else return res.redirect(307, '/fetch/' + rewriteURL(String(serverHeaders['location']))) - } - // These headers can be conflicting. - delete serverHeaders['content-encoding'] - delete serverHeaders['x-frame-options'] - delete serverHeaders['strict-transport-security'] - delete serverHeaders['content-security-policy'] - delete serverHeaders['location'] - - // Setting status, headers, and content-type. - res.status(response.status) - res.set(serverHeaders) - res.contentType(contentType) - if (response.redirected == true) { - if (req.url.startsWith('/rv') && req.session.rvURL) { - req.session.rvURL = response.url.split('/').splice(0, 3).join('/') - return res.redirect(307, '/fetch/rv/' + response.url.split('/').splice(3).join('/')) - } else return res.redirect(307, '/fetch/' + rewriteURL(response.url)) - } - if (contentType.startsWith('text/html')) { - req.session.fetchURL = location.origin_encoded - resbody = resbody.toString() - .replace(/integrity="(.*?)"/gi, '') - .replace(/nonce="(.*?)"/gi, '') - .replace(/(href|src|poster|data|action)="\/\/(.*?)"/gi, `$1` + `="http://` + `$2` + `"`) - .replace(/(href|src|poster|data|action)='\/\/(.*?)'/gi, `$1` + `='http://` + `$2` + `'`) - .replace(/(href|src|poster|data|action)="\/(.*?)"/gi, `$1` + `="/fetch/${location.origin_encoded}/` + `$2` + `"`) - .replace(/(href|src|poster|data|action)='\/(.*?)'/gi, `$1` + `='/fetch/${location.origin_encoded}/` + `$2` + `'`) - .replace(/'(https:\/\/|http:\/\/)(.*?)'/gi, function(str) { - str = str.split(`'`).slice(1).slice(0, -1).join(``); - return `'/fetch/${rewriteURL(str)}'` - }) - .replace(/"(https:\/\/|http:\/\/)(.*?)"/gi, function(str) { - str = str.split(`"`).slice(1).slice(0, -1).join(``); - return `"/fetch/${rewriteURL(str)}"` - }) - .replace(/(window|document).location.href/gi, `"${location.href}"`) - .replace(/(window|document).location.hostname/gi, `"${location.hostname}"`) - .replace(/(window|document).location.pathname/gi, `"${location.path}"`) - .replace(/location.href/gi, `"${location.href}"`) - .replace(/location.hostname/gi, `"${location.hostname}"`) - .replace(/location.pathname/gi, `"${location.path}"`) - .replace(//gi, '') - } else if (contentType.startsWith('text/css')) { - resbody = resbody.toString() - .replace(/url\("\/\/(.*?)"\)/gi, `url("http://` + `$1` + `")`) - .replace(/url\('\/\/(.*?)'\)/gi, `url('http://` + `$1` + `')`) - .replace(/url\(\/\/(.*?)\)/gi, `url(http://` + `$1` + `)`) - .replace(/url\("\/(.*?)"\)/gi, `url("/fetch/${location.origin_encoded}/` + `$1` + `")`) - .replace(/url\('\/(.*?)'\)/gi, `url('/fetch/${location.origin_encoded}/` + `$1` + `')`) - .replace(/url\(\/(.*?)\)/gi, `url(/fetch/${location.origin_encoded}/` + `$1` + `)`) - .replace(/"(https:\/\/|http:\/\/)(.*?)"/gi, function(str) { - str = str.split(`"`).slice(1).slice(0, -1).join(``); - return `"/fetch/${rewriteURL(str)}"` - }) - .replace(/'(https:\/\/|http:\/\/)(.*?)'/gi, function(str) { - str = str.split(`'`).slice(1).slice(0, -1).join(``); - return `'/fetch/${rewriteURL(str)}'` - }) - .replace(/\((https:\/\/|http:\/\/)(.*?)\)/gi, function(str) { - str = str.split(`(`).slice(1).join(``).split(')').slice(0, -1).join(''); - return `(/fetch/${rewriteURL(str)})` - }) - - } else if (contentType.startsWith('text/javascript') || contentType.startsWith('application/javascript')) { - resbody = resbody.toString() - .replace(/xhttp.open\("GET",(.*?)"http(.*?)"(.*?),(.*?)true\);/gi, ' xhttp.open("GET",' + '$1' + '"/alloy/url/http' + '$2' + '"' + '$3' + ',' + '$4' + 'true') - .replace(/xhttp.open\("POST",(.*?)"http(.*?)"(.*?),(.*?)true\);/gi, ' xhttp.open("POST",' + '$1' + '"/alloy/url/http' + '$2' + '"' + '$3' + ',' + '$4' + 'true') - .replace(/xhttp.open\("OPTIONS",(.*?)"http(.*?)"(.*?),(.*?)true\);/gi, ' xhttp.open("OPTIONS",' + '$1' + '"/alloy/url/http' + '$2' + '"' + '$3' + ',' + '$4' + 'true') - - .replace(/xhr.open\("GET",(.*?)"http(.*?)"(.*?),(.*?)true\);/gi, ' xhr.open("GET",' + '$1' + '"/alloy/url/http' + '$2' + '"' + '$3' + ',' + '$4' + 'true') - .replace(/xhr.open\("POST",(.*?)"http(.*?)"(.*?),(.*?)true\);/gi, ' xhr.open("POST",' + '$1' + '"/alloy/url/http' + '$2' + '"' + '$3' + ',' + '$4' + 'true') - .replace(/xhr.open\("OPTIONS",(.*?)"http(.*?)"(.*?),(.*?)true\);/gi, ' xhr.open("OPTIONS",' + '$1' + '"/alloy/url/http' + '$2' + '"' + '$3' + ',' + '$4' + 'true') - .replace(/ajax\("http:\/\/(.*?)"\)/gi, 'ajax("/alloy/url/http://' + '$1' + '")') - .replace(/ajax\("https:\/\/(.*?)"\)/gi, 'ajax("/alloy/url/https://' + '$1' + '")') + app.use(`${config.prefix}utils/`, async(req, res, next) => { + if (req.url.startsWith('/assets/')){res.sendFile(__dirname + '/utils' + req.url);} + if (req.query.url) { + let url = atob(req.query.url); + if (url.startsWith('https://') || url.startsWith('http://')) { + url = url; + } else if (url.startsWith('//')) { + url = 'http:' + url; + } else { + url = 'http://' + url; } - res.send(resbody) -}) + return res.redirect(307, config.prefix + rewrite_url(url)); + } + }); -app.use('/alloy/url/',function (req, res, next) { - const mainurl = req.url.split('/').slice(1).join('/') - const host = mainurl.split('/').slice(0, 3).join('/') - const buff = new Buffer(host); - const host64 = buff.toString('base64'); - const path = mainurl.split('/').slice(3).join('/') - const fullURL = host64 + '/' + path - res.redirect(307, '/fetch/' + fullURL) -}) + app.post(`${config.prefix}session/`, async(req, res, next) => { + let url = querystring.parse(req.raw_body).url; + if (url.startsWith('//')) { url = 'http:' + url; } + else if (url.startsWith('https://') || url.startsWith('http://')) { url = url } + else {url = 'http://' + url}; + return res.redirect(config.prefix + rewrite_url(url)); + }); -// Utils section. Where stuff such as inject scripts are found! -app.use('/alloy/',function (req, res, next) { + app.use(config.prefix, async(req, res, next) => { + var proxy = {}; + proxy.url = rewrite_url(req.url.slice(1), 'decode'); + proxy.url = { + href: proxy.url, + hostname : proxy.url.split('/').splice(2).splice(0, 1).join('/'), + origin : proxy.url.split('/').splice(0, 3).join('/'), + encoded_origin : btoa(proxy.url.split('/').splice(0, 3).join('/')), + path : '/' + proxy.url.split('/').splice(3).join('/'), + protocol : proxy.url.split('\:').splice(0, 1).join(''), + } -if (req.query.url) { -var clientInput = base64Decode(req.query.url) -var fetchURL; -if (clientInput.startsWith('//')) { - fetchURL = rewriteURL('http:' + clientInput) -} else if (clientInput.startsWith('http://') || clientInput.startsWith('https://')) { - fetchURL = rewriteURL(clientInput) -} else { - fetchURL = rewriteURL('http://' + clientInput) -} - return res.redirect(307, '/fetch/' + fetchURL) -} -res.sendFile(__dirname + '/alloy' + req.url, function (err) { - if (err) { - if (req.session.fetchURL) { - return res.redirect(307, '/fetch/' + req.session.fetchURL + req.url) - } else return res.redirect(307, '/') + proxy.url.encoded_origin = btoa(proxy.url.origin); + + proxy.requestHeaders = req.headers; + proxy.requestHeaders['host'] = proxy.url.hostname; + if (proxy.requestHeaders['referer']) { + let referer = '/' + String(proxy.requestHeaders['referer']).split('/').splice(3).join('/'); + + referer = rewrite_url(referer.replace(config.prefix, ''), 'decode'); + + if (referer.startsWith('https://') || referer.startsWith('http://')) { + referer = referer; + + } else referer = proxy.url.href; + + proxy.requestHeaders['referer'] = referer; + } + + + if (proxy.requestHeaders['origin']) { + let origin = '/' + String(proxy.requestHeaders['origin']).split('/').splice(3).join('/'); + + origin = rewrite_url(origin.replace(config.prefix, ''), 'decode'); + + if (origin.startsWith('https://') || origin.startsWith('http://')) { + + origin = origin.split('/').splice(0, 3).join('/'); + + } else origin = proxy.url.origin; + + proxy.requestHeaders['origin'] = origin; + } + + if (proxy.requestHeaders.cookie) { + delete proxy.requestHeaders.cookie; + } + const httpAgent = new http.Agent({ + keepAlive: true + }); + const httpsAgent = new https.Agent({ + keepAlive: true + }); + proxy.options = { + method: req.method, + headers: proxy.requestHeaders, + redirect: 'manual', + agent: function(_parsedURL) { + if (_parsedURL.protocol == 'http:') { + return httpAgent; + } else { + return httpsAgent; + } + } + }; + + if (req.method == 'POST') { + proxy.options.body = req.str_body; } -}) + if (proxy.url.hostname == 'discord.com' && proxy.url.path == '/') { return res.redirect(307, config.prefix + rewrite_url('https://discord.com/login'));}; -}) + if (proxy.url.hostname == 'www.reddit.com') { return res.redirect(307, config.prefix + rewrite_url('https://old.reddit.com'));}; -app.use(function (req, res, next) { -res.sendFile(__dirname + '/public' + req.url, function (err) { - if (err) { - if (req.session.fetchURL) { - return res.redirect(307, '/fetch/' + req.session.fetchURL + req.url) - } else return res.redirect(307, '/') + if (!req.url.slice(1).startsWith(`${proxy.url.encoded_origin}/`)) { return res.redirect(307, config.prefix + proxy.url.encoded_origin + '/');}; + + proxy.response = await fetch(proxy.url.href, proxy.options).catch(err => res.send(fs.readFileSync('./utils/error/error.html', 'utf8').toString().replace('%ERROR%', `Error 400: Could not make request to '${sanitizer.sanitize(proxy.url.href)}'!`))); + + if(typeof proxy.response.buffer != 'function')return; + + proxy.buffer = await proxy.response.buffer(); + + proxy.content_type = 'text/plain'; + + proxy.response.headers.forEach((e, i, a) => { + if (i == 'content-type') proxy.content_type = e; + }); + if (proxy.content_type == null || typeof proxy.content_type == 'undefined') proxy.content_type = 'text/html'; + + proxy.sendResponse = proxy.buffer; + + // Parsing the headers from the response to remove square brackets so we can set them as the response headers. + proxy.headers = Object.fromEntries( + Object.entries(JSON.parse(JSON.stringify(proxy.response.headers.raw()))) + .map(([key, val]) => [key, val[0]]) + ); + + // Parsing all the headers to remove all of the bad headers that could affect proxies performance. + Object.entries(proxy.headers).forEach(([header_name, header_value]) => { + if (header_name.startsWith('content-encoding') || header_name.startsWith('x-') || header_name.startsWith('cf-') || header_name.startsWith('strict-transport-security') || header_name.startsWith('content-security-policy')) { + delete proxy.headers[header_name]; + } + }); + + // If theres a location for a redirect in the response, then the proxy will get the response location then redirect you to the proxied version of the url. + if (proxy.response.headers.get('location')) { + return res.redirect(307, config.prefix + rewrite_url(String(proxy.response.headers.get('location')))); } -}) -}); + res.status(proxy.response.status); + res.set(proxy.headers); + res.contentType(proxy.content_type); + if (proxy.content_type.startsWith('text/html')) { + req.session.url = proxy.url.origin; + proxy.sendResponse = proxy.sendResponse.toString() + .replace(/integrity="(.*?)"/gi, '') + .replace(/nonce="(.*?)"/gi, '') + .replace(/(href|src|poster|data|action|srcset)="\/\/(.*?)"/gi, `$1` + `="http://` + `$2` + `"`) + .replace(/(href|src|poster|data|action|srcset)='\/\/(.*?)'/gi, `$1` + `='http://` + `$2` + `'`) + .replace(/(href|src|poster|data|action|srcset)="\/(.*?)"/gi, `$1` + `="${config.prefix}${proxy.url.encoded_origin}/` + `$2` + `"`) + .replace(/(href|src|poster|data|action|srcset)='\/(.*?)'/gi, `$1` + `='${config.prefix}${proxy.url.encoded_origin}/` + `$2` + `'`) + .replace(/'(https:\/\/|http:\/\/)(.*?)'/gi, function(str) { + str = str.split(`'`).slice(1).slice(0, -1).join(``); + return `'${config.prefix}${rewrite_url(str)}'` + }) + .replace(/"(https:\/\/|http:\/\/)(.*?)"/gi, function(str) { + str = str.split(`"`).slice(1).slice(0, -1).join(``); + return `"${config.prefix}${rewrite_url(str)}"` + }) + .replace(/(window|document).location.href/gi, `"${proxy.url.href}"`) + .replace(/(window|document).location.hostname/gi, `"${proxy.url.hostname}"`) + .replace(/(window|document).location.pathname/gi, `"${proxy.url.path}"`) + .replace(/location.href/gi, `"${proxy.url.href}"`) + .replace(/location.hostname/gi, `"${proxy.url.hostname}"`) + .replace(/location.pathname/gi, `"${proxy.url.path}"`) + .replace(//gi, ``); + + // Temp hotfix for Youtube search bar until my script injection can fix it. + + if (proxy.url.hostname == 'www.youtube.com') { proxy.sendResponse = proxy.sendResponse.replace(/\/results/gi, `${config.prefix}${proxy.url.encoded_origin}/results`);}; + } else if (proxy.content_type.startsWith('text/css')) { + proxy.sendResponse = proxy.sendResponse.toString() + .replace(/url\("\/\/(.*?)"\)/gi, `url("http://` + `$1` + `")`) + .replace(/url\('\/\/(.*?)'\)/gi, `url('http://` + `$1` + `')`) + .replace(/url\(\/\/(.*?)\)/gi, `url(http://` + `$1` + `)`) + .replace(/url\("\/(.*?)"\)/gi, `url("${config.prefix}${proxy.url.encoded_origin}/` + `$1` + `")`) + .replace(/url\('\/(.*?)'\)/gi, `url('${config.prefix}${proxy.url.encoded_origin}/` + `$1` + `')`) + .replace(/url\(\/(.*?)\)/gi, `url(${config.prefix}${proxy.url.encoded_origin}/` + `$1` + `)`) + .replace(/"(https:\/\/|http:\/\/)(.*?)"/gi, function(str) { + str = str.split(`"`).slice(1).slice(0, -1).join(``); + return `"${config.prefix}${rewrite_url(str)}"` + }) + .replace(/'(https:\/\/|http:\/\/)(.*?)'/gi, function(str) { + str = str.split(`'`).slice(1).slice(0, -1).join(``); + return `'${config.prefix}${rewrite_url(str)}'` + }) + .replace(/\((https:\/\/|http:\/\/)(.*?)\)/gi, function(str) { + str = str.split(`(`).slice(1).join(``).split(')').slice(0, -1).join(''); + return `(${config.prefix}${rewrite_url(str)})` + }); + + }; +// We send the response from the server rewritten. + res.send(proxy.sendResponse); + }); + + app.use('/', express.static('public')); + + app.use(async(req, res, next) => { + if (req.headers['referer']) { + + let referer = '/' + String(req.headers['referer']).split('/').splice(3).join('/'); + + referer = rewrite_url(referer.replace(config.prefix, ''), 'decode').split('/').splice(0, 3).join('/'); + + if (referer.startsWith('https://') || referer.startsWith('http://')) { + res.redirect(307, config.prefix + btoa(referer) + req.url) + } else { + if (req.session.url) { + + res.redirect(307, config.prefix + btoa(req.session.url) + req.url) + + } else return next(); + } + } else if (req.session.url) { + + res.redirect(307, config.prefix + btoa(req.session.url) + req.url) + + } else return next(); + }); diff --git a/app.json b/app.json index 540a6b88..7dcc9345 100644 --- a/app.json +++ b/app.json @@ -1,7 +1,7 @@ { - "name": "Holy Unblocker", - "description": "A website that can be used to bypass web filters; both extension and firewall. Alloy Proxy hosted locally. (Can be used as a template.)", - "repository": "https://github.com/QuiteAFancyEmerald/HolyUnblockerPublic/", - "logo": "https://www.holyubofficial.ml/assets/img/i.png", - "keywords": ["node", "proxy", "unblocker" , "webproxy" , "games" , "holyunblocker" , "alloy"] + "name": "Alloy Proxy", + "description": "A node.js web proxy featuring URL encoding, and amazing compatablity!", + "repository": "https://github.com/titaniumnetwork-dev/alloyproxy/", + "logo": "https://avatars1.githubusercontent.com/u/47227492?s=200&v=4", + "keywords": ["node", "proxy", "unblocker"] } diff --git a/config.json b/config.json index ca8b4a47..1e9eef96 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "listenip":"0.0.0.0", - "port":8080, - "ssl":false -} + "port": "8080", + "prefix": "/proxy/", + "ssl": false +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1fce891c..a1468f2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "alloyproxy", - "version": "1.0.0", + "name": "alloy-proxy", + "version": "2.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -18,6 +18,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -35,15 +40,24 @@ "type-is": "~1.6.17" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "content-disposition": { "version": "0.5.3", @@ -77,11 +91,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "cssfilter": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", - "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -100,6 +109,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -184,6 +198,11 @@ } } }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -198,11 +217,6 @@ "unpipe": "~1.0.0" } }, - "follow-redirects": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" - }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -213,6 +227,24 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -233,6 +265,15 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -276,6 +317,14 @@ "mime-db": "1.44.0" } }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -304,16 +353,24 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, - "parse-raw-http": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/parse-raw-http/-/parse-raw-http-0.0.1.tgz", - "integrity": "sha512-GndQvIQXviId7eHnc+fEcmtEjkj1tQ96EhNOplPwXA8L1jgOnrlx/xLmmOEew8Yj4ZoZpmoAh0IvypAaeMbILg==" + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -364,6 +421,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sanitizer": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/sanitizer/-/sanitizer-0.1.3.tgz", + "integrity": "sha1-1PCvdHXZp7ryqeWmEXGLqheKOeE=" + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -402,6 +464,14 @@ "send": "0.17.1" } }, + "session": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/session/-/session-0.1.0.tgz", + "integrity": "sha1-aumqbyYJO+Me0gBbzUJq6AqjoIU=", + "requires": { + "vows": "*" + } + }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -449,14 +519,20 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "xss": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.8.tgz", - "integrity": "sha512-3MgPdaXV8rfQ/pNn16Eio6VXYPTkqwa0vc7GkiymmY/DqR1SE/7VPAAVZz1GJsJFrllMYO3RHfEaiUGjab6TNw==", + "vows": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/vows/-/vows-0.8.3.tgz", + "integrity": "sha512-PVIxa/ovXhrw5gA3mz6M+ZF3PHlqX4tutR2p/y9NWPAaFVKcWBE8b2ktfr0opQM/qFmcOVWKjSCJVjnYOvjXhw==", "requires": { - "commander": "^2.20.3", - "cssfilter": "0.0.10" + "diff": "^4.0.1", + "eyes": "~0.1.6", + "glob": "^7.1.2" } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } } diff --git a/package.json b/package.json index a6583f6c..df489842 100644 --- a/package.json +++ b/package.json @@ -1,33 +1,25 @@ { - "name": "alloyproxy", - "version": "1.0.0", - "description": "A powerful web proxy!", + "name": "alloy-proxy", + "version": "2.2.0", + "description": "A web proxy capable of proxying websites!", "main": "app.js", "scripts": { - "test": "proxy", + "test": "test", "start": "node app.js" }, - "repository": { - "type": "git", - "url": "git+https://github.com/titaniumnetwork-dev/alloyproxy.git" - }, "keywords": [ - "web-proxy" + "proxy", + "node.js", + "unblocker" ], - "author": "titaniumnetwork-dev", + "author": "Titanium Network", "license": "ISC", - "bugs": { - "url": "https://github.com/titaniumnetwork-dev/alloyproxy/issues" - }, - "homepage": "https://github.com/titaniumnetwork-dev/alloyproxy#readme", "dependencies": { - "body-parser": "^1.19.0", "cookie-parser": "^1.4.5", "express": "^4.17.1", "express-session": "^1.17.1", - "follow-redirects": "^1.13.0", - "node-fetch": ">=2.6.1", - "parse-raw-http": "0.0.1", - "xss": "^1.0.8" + "node-fetch": "^2.6.1", + "sanitizer": "^0.1.3", + "session": "^0.1.0" } } diff --git a/utils/assets/inject.js b/utils/assets/inject.js new file mode 100644 index 00000000..391b9ac0 --- /dev/null +++ b/utils/assets/inject.js @@ -0,0 +1,79 @@ +var alloy_data = document.querySelector('#_alloy_data'); + +var url = alloy_data.getAttribute('url'); + +var prefix = alloy_data.getAttribute('prefix'); + +url = new URL(atob(url)) + +rewrite_url = (str) => { + proxied_url = ''; + if (str.startsWith(window.location.origin + '/') && !str.startsWith(window.location.origin + prefix)) { + str = '/' + str.split('/').splice(3).join('/'); + } + if (str.startsWith('//')) { + str = 'http:' + str; + } else if (str.startsWith('/') && !str.startsWith(prefix)) { + str = url.origin + str + } + if (str.startsWith('https://') || str.startsWith('http://')) { + path = "/" + str.split('/').splice(3).join('/'); + origin = btoa(str.split('/').splice(0, 3).join('/')); + return proxied_url = prefix + origin + path + } else { + proxied_url = str; + } + return proxied_url; + } + + +let fetch_rewrite = window.fetch; window.fetch = function(url, options) { + url = rewrite_url(url); + return fetch_rewrite.apply(this, arguments); +} + +let xml_rewrite = window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) { + url = rewrite_url(url); + return xml_rewrite.apply(this, arguments); + } + +let createelement_rewrite = document.createElement; document.createElement = function(tag) { + var element = createelement_rewrite.call(document, tag); + if (tag.toLowerCase() === 'script' || tag.toLowerCase() === 'iframe' || tag.toLowerCase() === 'embed') { + Object.defineProperty(element.__proto__, 'src', { + set: function(value) { + value = rewrite_url(value) + element.setAttribute('src', value) + } + }); + } else if (tag.toLowerCase() === 'link') { + Object.defineProperty(element.__proto__, 'href', { + set: function(value) { + value = rewrite_url(value) + element.setAttribute('href', value) + } + }); + } else if (tag.toLowerCase() === 'form') { + Object.defineProperty(element.__proto__, 'action', { + set: function(value) { + value = rewrite_url(value) + element.setAttribute('action', value) + } + }); + } + return element; +} + +let setattribute_rewrite = window.Element.prototype.setAttribute; window.Element.prototype.setAttribute = function(attribute, href) { + if (attribute == ('src') || attribute == ('href') || attribute == ('action')) { + href = rewrite_url(href) + } else href = href; + return setattribute_rewrite.apply(this, arguments) + } + +var previousState = window.history.state; +setInterval(function() { + if (!window.location.pathname.startsWith(`${prefix}${btoa(url.origin)}/`)) { + history.replaceState('', '', `${prefix}${btoa(url.origin)}/${window.location.href.split('/').splice(3).join('/')}`); + } +}, 0.1); \ No newline at end of file diff --git a/alloy/assets/error.html b/utils/error/error.html similarity index 97% rename from alloy/assets/error.html rename to utils/error/error.html index 3577583a..a24004e2 100644 --- a/alloy/assets/error.html +++ b/utils/error/error.html @@ -1,3 +1,4 @@ +
@@ -81,7 +82,7 @@AlloyProxy
+Alloy Proxy
%ERROR%