Updated Alloy Proxy

XSS Fix Update.
This commit is contained in:
TheEmeraldStarr 2020-09-18 10:24:00 -07:00
parent 7070e8eeb3
commit f4f21fdc75
4 changed files with 315 additions and 294 deletions

572
app.js
View file

@ -6,26 +6,26 @@ var fs = require('fs');
var app = express(); var app = express();
var cookieParser = require('cookie-parser'); var cookieParser = require('cookie-parser');
var session = require('express-session'); var session = require('express-session');
var xss = require("xss");
var config = JSON.parse(fs.readFileSync('config.json', 'utf-8')), var config = JSON.parse(fs.readFileSync('config.json', 'utf-8')),
httpsAgent = new https.Agent({ httpsAgent = new https.Agent({
rejectUnauthorized: false, rejectUnauthorized: false,
keepAlive: true, keepAlive: true,
}), }),
httpAgent = new http.Agent({ httpAgent = new http.Agent({
rejectUnauthorized: false, rejectUnauthorized: false,
keepAlive: true, keepAlive: true,
}), }),
ssl = { key: fs.readFileSync('ssl/default.key', 'utf8'), cert: fs.readFileSync('ssl/default.crt', 'utf8') }, ssl = { key: fs.readFileSync('ssl/default.key', 'utf8'), cert: fs.readFileSync('ssl/default.crt', 'utf8') },
server, server,
port = process.env.PORT || config.port, port = process.env.PORT || config.port,
ready = (() => { ready = (() => {
var a = 'http://', var a = 'http://', b = config.listenip;
b = config.listenip; if (config.ssl) a = 'https://';
if (config.ssl) a = 'https://'; if (b == '0.0.0.0' || b == '127.0.0.1') b = 'localhost';
if (b == '0.0.0.0' || b == '127.0.0.1') b = 'localhost'; console.log('AlloyProxy is now running at', a + b + ':' + port);
console.log('AlloyProxy is now running at', a + b + ':' + port); });
});
http.globalAgent.maxSockets = Infinity; http.globalAgent.maxSockets = Infinity;
https.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(cookieParser());
app.use(session({ app.use(session({
secret: 'alloy', secret: 'alloy',
saveUninitialized: true, saveUninitialized: true,
resave: true resave: true
})); }));
app.use((req, res, next) => { app.use((req, res, next)=>{
// nice bodyparser alternative that wont cough up errors // nice bodyparser alternative that wont cough up errors
req.setEncoding('utf8'); req.setEncoding('utf8');
req.raw_body = '' req.raw_body = ''
req.body = new Object() req.body = new Object()
req.on('data', chunk => { req.raw_body += chunk }); req.on('data', chunk=>{ req.raw_body += chunk });
req.on('end', () => { req.on('end', ()=>{
req.str_body = req.raw_body.toString('utf8'); req.str_body = req.raw_body.toString('utf8');
try { try{
var result = new Object(); var result = new Object();
req.str_body.split('&').forEach((pair) => { req.str_body.split('&').forEach((pair)=>{
pair = pair.split('='); pair = pair.split('=');
req.body[pair[0]] = decodeURIComponent(pair[1] || ''); req.body[pair[0]] = decodeURIComponent(pair[1] || '');
}); });
} catch (err) { }catch(err){
req.body = {} req.body = {}
} }
return next(); return next();
}); });
}); });
function base64Encode(data) { 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 // How to use: base64Decode('string') will return any input base64 decoded
function base64Decode(data) { 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 // How to use: rewritingURL('https://example.org/assets/main.js') will rewrite any external URL. Output: aHR0cHM6Ly9leGFtcGxlLm9yZw==/assets/main.js
function rewriteURL(dataURL, option) { function rewriteURL(dataURL, option) {
var websiteURL var websiteURL
var websitePath var websitePath
if (option == 'decode') { if (option == 'decode') {
websiteURL = base64Decode(dataURL.split('/').splice(0, 1).join('/')) websiteURL = base64Decode(dataURL.split('/').splice(0, 1).join('/'))
websitePath = '/' + dataURL.split('/').splice(1).join('/') websitePath = '/' + dataURL.split('/').splice(1).join('/')
} else { } else {
websiteURL = base64Encode(dataURL.split('/').splice(0, 3).join('/')) websiteURL = base64Encode(dataURL.split('/').splice(0, 3).join('/'))
websitePath = '/' + dataURL.split('/').splice(3).join('/') websitePath = '/' + dataURL.split('/').splice(3).join('/')
} }
if (websitePath == '/') { if (websitePath == '/') {
return `${websiteURL}` return `${websiteURL}`
} else return `${websiteURL}${websitePath}` } 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!')) // 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) { function error(statusCode, info) {
if (statusCode && info) { if (statusCode && info) {
return fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error ${statusCode}: ${info}`) return fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error ${statusCode}: ${info}`)
} }
if (info && !statusCode) { if (info && !statusCode) {
return fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error: ${info}`) return (fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error: ${info}`))
} }
if (statusCode && !info) { if (statusCode && !info) {
return fs.readFileSync('alloy/assets/error.html', 'utf8').toString().replace('%ERROR%', `Error ${statusCode}`) 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!`) return (fs.readFileSync('public/assets/error.html', 'utf8').toString().replace('%ERROR%', `An error has occurred!`))
} }
app.post('/createSession', async(req, res) => { app.post('/createSession', async (req, res) => {
if (req.body.url.startsWith('//')) { if (req.body.url.startsWith('//')) {
req.body.url = 'http:' + req.body.url; req.body.url = 'http:' + req.body.url;
} else if (req.body.url.startsWith('https://') || req.body.url.startsWith('http://')) { } else if (req.body.url.startsWith('https://') || req.body.url.startsWith('http://')) {
req.body.url = req.body.url; req.body.url = req.body.url;
} else { } else {
req.body.url = 'http://' + req.body.url; req.body.url = 'http://' + req.body.url;
} }
if (req.body.rv) { if (req.body.rv) {
req.session.rvURL = String(req.body.url).split('/').splice(0, 3).join('/') 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('/')) return res.redirect('/fetch/rv/' + String(req.body.url).split('/').splice(3).join('/'))
} else { } else {
return res.redirect('/fetch/' + rewriteURL(String(req.body.url))) return res.redirect('/fetch/' + rewriteURL(String(req.body.url)))
} }
}) })
var prefix = '/fetch'; var prefix = '/fetch';
app.use(prefix, async(req, res, next) => { app.use(prefix, async (req, res, next) => {
var location = rewriteURL(req.url.slice(1), 'decode'); var location = rewriteURL(req.url.slice(1), 'decode');
if (req.url.startsWith('/rv') && !req.session.rvURL) { if (req.url.startsWith('/rv') && !req.session.rvURL) {
res.send(error('400', 'No valid session URL for reverse proxy mode was found!')) 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) { 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 = { return res.redirect(307, '/fetch/' + base64Encode('https://old.reddit.com') + location.path)
href: location, }
hostname: location.split('/').splice(2).splice(0, 1).join('/'), const response = await fetch(location.href, options).catch(err => res.send(error('404', `"${xss(location.href)}" was not found!`)));
origin: location.split('/').splice(0, 3).join('/'), if(typeof response.buffer != 'function')return;
origin_encoded: base64Encode(location.split('/').splice(0, 3).join('/')), var resbody = await response.buffer();
path: '/' + location.split('/').splice(3).join('/'), var contentType = 'text/plain'
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 response.headers.forEach((e, i, a) => {
fetchHeaders['referer'] = location.href if (i == 'content-type') contentType = e;
fetchHeaders['origin'] = location.origin });
fetchHeaders['host'] = location.hostname if (contentType == null || typeof contentType == 'undefined') ct = 'text/html';
if (fetchHeaders['cookie']) { var serverHeaders = Object.fromEntries(
delete fetchHeaders['cookie'] Object.entries(JSON.parse(JSON.stringify(response.headers.raw())))
} .map(([key, val]) => [key, val[0]])
var options = { );
method: req.method, if (serverHeaders['location']) {
headers: fetchHeaders, if (req.url.startsWith('/rv') && req.session.rvURL) {
redirect: 'manual', req.session.rvURL = String(serverHeaders['location']).split('/').splice(0, 3).join('/')
agent: function(_parsedURL) { return res.redirect(307, '/fetch/rv/' + String(serverHeaders['location']).split('/').splice(3).join('/'))
if (_parsedURL.protocol == 'http:') { } else return res.redirect(307, '/fetch/' + rewriteURL(String(serverHeaders['location'])))
return httpAgent; }
} else { delete serverHeaders['content-encoding']
return httpsAgent; delete serverHeaders['x-frame-options']
} delete serverHeaders['strict-transport-security']
} delete serverHeaders['content-security-policy']
}; delete serverHeaders['location']
res.status(response.status)
if (req.method == 'POST') { res.set(serverHeaders)
// Have to do try catch for this POST data parser until we create our own one that won't have a syntax error sometimes. res.contentType(contentType)
try { if (response.redirected == true) {
// str_body is a string containing the requests body if (req.url.startsWith('/rv') && req.session.rvURL) {
options['body'] = req.str_body; req.session.rvURL = response.url.split('/').splice(0, 3).join('/')
} catch (err) { return res.redirect(307, '/fetch/rv/' + response.url.split('/').splice(3).join('/'))
return; } else return res.redirect(307, '/fetch/' + rewriteURL(response.url))
} }
} if (contentType.startsWith('text/html')) {
if (req.url.startsWith('/rv')) { req.session.fetchURL = location.origin_encoded
location.origin_encoded = 'rv' resbody = resbody.toString()
} .replace(/integrity="(.*?)"/gi, '')
if (!req.url.startsWith(`/${location.origin_encoded}/`)) { .replace(/nonce="(.*?)"/gi, '')
try { .replace(/(href|src|poster|data|action)="\/\/(.*?)"/gi, `$1` + `="http://` + `$2` + `"`)
return res.redirect(307, `/fetch/${location.origin_encoded}/`) .replace(/(href|src|poster|data|action)='\/\/(.*?)'/gi, `$1` + `='http://` + `$2` + `'`)
} catch (err) { .replace(/(href|src|poster|data|action)="\/(.*?)"/gi, `$1` + `="/fetch/${location.origin_encoded}/` + `$2` + `"`)
return; .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(``);
if (location.href == 'https://discord.com' || location.href == 'https://discord.com/new') { return `'/fetch/${rewriteURL(str)}'`
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(/<html(.*?)>/gi, '<html' + '$1' + '><script id="alloyData" data-alloyURL="' + location.origin_encoded + '"' + ' src="/alloy/assets/inject.js"></script>')
} 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, '/')
}
}) })
.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(/<html(.*?)>/gi, '<html' + '$1' + '><script id="alloyData" data-alloyURL="' + location.origin_encoded + '"' + ' src="/alloy/assets/inject.js"></script>')
} 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) { app.use(function (req, res, next) {
res.sendFile(__dirname + '/public' + req.url, function(err) { res.sendFile(__dirname + '/public' + req.url, function (err) {
if (err) { if (err) {
if (req.session.fetchURL) { if (req.session.fetchURL) {
return res.redirect(307, '/fetch/' + req.session.fetchURL + req.url) return res.redirect(307, '/fetch/' + req.session.fetchURL + req.url)
} else return res.redirect(307, '/') } else return res.redirect(307, '/')
} }
}) })
}); });

View file

@ -1,6 +1,7 @@
{ {
"name": "Holy Unblocker", "name": "Alloy Proxy",
"description": "A website that can be used to bypass web filters; both extension and firewall. Hosted on Alloy Proxy. Node Unblocker hosted externally.", "description": "A node.js web proxy featuring URL encoding, and amazing compatablity!",
"repository": "https://github.com/QuiteAFancyEmerald/HolyUB/", "repository": "https://github.com/titaniumnetwork-dev/alloyproxy/",
"logo": "https://avatars1.githubusercontent.com/u/47227492?s=200&v=4",
"keywords": ["node", "proxy", "unblocker"] "keywords": ["node", "proxy", "unblocker"]
} }

25
package-lock.json generated
View file

@ -40,6 +40,11 @@
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" "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": { "content-disposition": {
"version": "0.5.3", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "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", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
}, },
"cssfilter": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
"integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4="
},
"debug": { "debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -277,9 +287,9 @@
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"node-fetch": { "node-fetch": {
"version": "2.6.0", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
}, },
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
@ -438,6 +448,15 @@
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" "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"
}
} }
} }
} }

View file

@ -26,7 +26,8 @@
"express": "^4.17.1", "express": "^4.17.1",
"express-session": "^1.17.1", "express-session": "^1.17.1",
"follow-redirects": "^1.13.0", "follow-redirects": "^1.13.0",
"node-fetch": "^2.6.0", "node-fetch": ">=2.6.1",
"parse-raw-http": "0.0.1" "parse-raw-http": "0.0.1",
"xss": "^1.0.8"
} }
} }