diff --git a/app.js b/app.js index e558e92c..4da5e731 100644 --- a/app.js +++ b/app.js @@ -6,26 +6,26 @@ var fs = require('fs'); var app = express(); var cookieParser = require('cookie-parser'); var session = require('express-session'); +var xss = require("xss"); 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); - }); + 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; @@ -35,304 +35,304 @@ else server = http.createServer(app).listen(port, config.listenip, ready); app.use(cookieParser()); app.use(session({ - secret: 'alloy', - saveUninitialized: true, - resave: true +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(); - }); +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') + 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') + 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('/') - } - if (websitePath == '/') { - return `${websiteURL}` - } else return `${websiteURL}${websitePath}` + 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('/') + } + 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 (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!`) + if (statusCode && info) { + return fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error ${statusCode}: ${info}`) + } + 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!`)) } -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))) - } +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))) + } }) var prefix = '/fetch'; -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!')) +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 + }); + + var fetchHeaders = req.headers + fetchHeaders['referer'] = location.href + fetchHeaders['origin'] = location.origin + fetchHeaders['host'] = location.hostname + 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; + } } + }; + + if (req.method == 'POST') { + // Have to do try catch for this POST data parser until we create our own one that won't have a syntax error sometimes. + try { + // str_body is a string containing the requests body + options['body'] = req.str_body; + }catch(err){ + return; + } + } + 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; + } + } + 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) { - location = req.session.rvURL + req.url.slice(3) + req.session.rvURL = 'https://old.reddit.com' + return res.redirect(307, '/fetch/rv' + location.path) } - 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 - }); + return res.redirect(307, '/fetch/' + base64Encode('https://old.reddit.com') + location.path) + } + 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' - var fetchHeaders = req.headers - fetchHeaders['referer'] = location.href - fetchHeaders['origin'] = location.origin - fetchHeaders['host'] = location.hostname - 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; - } - } - }; - - if (req.method == 'POST') { - // Have to do try catch for this POST data parser until we create our own one that won't have a syntax error sometimes. - try { - // str_body is a string containing the requests body - options['body'] = req.str_body; - } catch (err) { - return; - } - } - 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; - } - } - 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) - } - const response = await fetch(location.href, options).catch(err => res.send(error('404', `"${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; - }); - 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]]) - ); - 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']))) - } - delete serverHeaders['content-encoding'] - delete serverHeaders['x-frame-options'] - delete serverHeaders['strict-transport-security'] - delete serverHeaders['content-security-policy'] - delete serverHeaders['location'] - 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' + '")') - } - res.send(resbody) -}) - -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.use('/alloy/', function(req, res, next) { - - 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, '/') - } + response.headers.forEach((e, i, a) => { + if (i == 'content-type') contentType = e; + }); + 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]]) + ); + 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']))) + } + delete serverHeaders['content-encoding'] + delete serverHeaders['x-frame-options'] + delete serverHeaders['strict-transport-security'] + delete serverHeaders['content-security-policy'] + delete serverHeaders['location'] + 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' + '")') + } + res.send(resbody) +}) + +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.use('/alloy/',function (req, res, next) { + +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, '/') + } +}) }) -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, '/') - } - }) +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, '/') + } +}) -}); \ No newline at end of file +}); diff --git a/app.json b/app.json index e097f559..7dcc9345 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,7 @@ { - "name": "Holy Unblocker", - "description": "A website that can be used to bypass web filters; both extension and firewall. Hosted on Alloy Proxy. Node Unblocker hosted externally.", - "repository": "https://github.com/QuiteAFancyEmerald/HolyUB/", + "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/package-lock.json b/package-lock.json index d968370f..1fce891c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,11 @@ "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==" + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -72,6 +77,11 @@ "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", @@ -277,9 +287,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "on-finished": { "version": "2.3.0", @@ -438,6 +448,15 @@ "version": "1.1.2", "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==", + "requires": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + } } } } diff --git a/package.json b/package.json index 92797b82..a6583f6c 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "express": "^4.17.1", "express-session": "^1.17.1", "follow-redirects": "^1.13.0", - "node-fetch": "^2.6.0", - "parse-raw-http": "0.0.1" + "node-fetch": ">=2.6.1", + "parse-raw-http": "0.0.1", + "xss": "^1.0.8" } }