From f4f21fdc755fcf7f41f4813cccf0516320f18a9f Mon Sep 17 00:00:00 2001
From: TheEmeraldStarr <46467239+Epicloudygamer@users.noreply.github.com>
Date: Fri, 18 Sep 2020 10:24:00 -0700
Subject: [PATCH] Updated Alloy Proxy
XSS Fix Update.
---
app.js | 572 +++++++++++++++++++++++-----------------------
app.json | 7 +-
package-lock.json | 25 +-
package.json | 5 +-
4 files changed, 315 insertions(+), 294 deletions(-)
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"
}
}