mirror of
https://github.com/QuiteAFancyEmerald/Holy-Unblocker.git
synced 2025-05-12 19:40:02 -04:00
Minor Change (oops)
This commit is contained in:
parent
af2ca7e034
commit
6bd7e10c4d
31 changed files with 34771 additions and 1 deletions
2
app.js
2
app.js
|
@ -4,7 +4,7 @@
|
|||
* MIT license: http://opensource.org/licenses/MIT
|
||||
* ----------------------------------------------- */
|
||||
const
|
||||
corrosion = require('corrosion'),
|
||||
corrosion = require('./src/Corrosion'),
|
||||
path = require('path'),
|
||||
config = require('./config.json'),
|
||||
fs = require('fs'),
|
||||
|
|
223
src/Corrosion/README.md
Normal file
223
src/Corrosion/README.md
Normal file
|
@ -0,0 +1,223 @@
|
|||
# Corrosion
|
||||
Titanium Networks main web proxy.
|
||||
Successor to [Alloy](https://github.com/titaniumnetwork-dev/alloy)
|
||||
# Installation:
|
||||
```
|
||||
npm i corrosion
|
||||
```
|
||||
|
||||
# Example:
|
||||
```javascript
|
||||
const Corrosion = require('corrosion');
|
||||
const proxy = new Corrosion();
|
||||
const http = require('http')
|
||||
http.createServer((req, res) =>
|
||||
proxy.request(req, res) // Request Proxy
|
||||
).on('upgrade', (req, socket, head) =>
|
||||
proxy.upgrade(req, socket, head) // WebSocket Proxy
|
||||
).listen(80);
|
||||
```
|
||||
Much more in depth one is in the [demo folder](demo/).
|
||||
|
||||
# API:
|
||||
|
||||
|
||||
## Index
|
||||
- `config`
|
||||
- `prefix` String - URL Prefix
|
||||
- `title` (Boolean / String) - Title used for HTML documents
|
||||
- `ws` Boolean - WebSocket rewriting
|
||||
- `cookie` Boolean - Request Cookies
|
||||
- `codec` String - URL encoding (base64, plain, xor).
|
||||
- `requestMiddleware` Array - Array of [middleware](#middleware) functions for proxy request (Server).
|
||||
- `responseMiddleware` Array - Array of [middleware](#middleware) functions for proxy response (Server).
|
||||
- `standardMiddleware` Boolean - Use the prebuilt [middleware](#middleware) used by default (Server).
|
||||
|
||||
#### request
|
||||
- `request` Request
|
||||
- `response` Response
|
||||
|
||||
#### upgrade
|
||||
- `request` Request
|
||||
- `socket` Socket
|
||||
- `head` Head
|
||||
|
||||
#### bundleScripts
|
||||
Bundles scripts for client injection. Important when updating proxy.
|
||||
|
||||
## Properties
|
||||
- [url](#url)
|
||||
- [html](#html)
|
||||
- [js](#js)
|
||||
- [css](#css)
|
||||
- [cookies](#cookies)
|
||||
- [config](#index)
|
||||
- [codec](#codec)
|
||||
- [prefix](#url)
|
||||
|
||||
|
||||
|
||||
## url
|
||||
|
||||
#### wrap
|
||||
- `val` String
|
||||
- `config` Configuration
|
||||
- `base` WHATWG URL
|
||||
- `origin` Location origin - Adds a location origin before the proxy url
|
||||
- `flags` Array - ['xhr'] => /service/xhr_/https%3A%2F%2Fexample.org/
|
||||
|
||||
#### unwrap
|
||||
- `val` String
|
||||
- `config` Configuration
|
||||
- `origin` Location origin - Required if a location origin starts before the proxy url
|
||||
- `flags` Boolean - Returns with both the URL and flags found { value: 'https://example.org', flags: ['xhr'], })
|
||||
- `leftovers` Boolean - Use any leftovers if any after the encoded proxy url
|
||||
|
||||
|
||||
## Properties
|
||||
- `regex` Regex used to determine to rewrite the URL or not.
|
||||
|
||||
- `prefix` URL Prefix
|
||||
|
||||
- `codec` (base64, plain, xor)
|
||||
|
||||
|
||||
## js
|
||||
|
||||
#### process
|
||||
- `source` JS script
|
||||
- `url` URL for heading
|
||||
|
||||
#### iterate
|
||||
- `ast` JS AST
|
||||
- `Callback` Handler initated on AST node
|
||||
|
||||
#### createHead
|
||||
- `url` URL for heading
|
||||
|
||||
#### createCallExperssion
|
||||
- `callee` Acorn.js Node
|
||||
- `args` Array
|
||||
|
||||
#### createArrayExpression
|
||||
- `elements` Array
|
||||
|
||||
#### createIdentifier
|
||||
- `name` Identifier name
|
||||
- `preventRewrite` Prevent further rewrites
|
||||
|
||||
#### createLiteral
|
||||
- `value` Literal value
|
||||
|
||||
## css
|
||||
|
||||
#### process
|
||||
- `source` CSS
|
||||
- `config` Configuration
|
||||
- `base` WHATWG URL
|
||||
- `origin` Location origin
|
||||
- `context` CSS-Tree context
|
||||
|
||||
## html
|
||||
|
||||
#### process
|
||||
- `source` HTML Source
|
||||
- `config` Configuration
|
||||
- `document` Determines of its a document or fragment for parsing
|
||||
- `base` WHATWG URL
|
||||
- `origin` Location origin
|
||||
|
||||
#### source
|
||||
- `processed` Rewritten HTML
|
||||
- `config` Configuration
|
||||
- `document` Determines of its a document or fragment for parsing
|
||||
|
||||
### Properties
|
||||
- `map` Map for attribute rewriting
|
||||
|
||||
|
||||
## cookies
|
||||
|
||||
#### encode
|
||||
- `input` New (Cookie / Cookies)
|
||||
- `config` Configuration
|
||||
- `url` WHATWG URL
|
||||
- `domain` Cookie Domain
|
||||
- `secure` Cookie Secure
|
||||
|
||||
#### decode
|
||||
- `store` Encoded Cookies
|
||||
- `config` Configuration
|
||||
- `url` WHATWG URL
|
||||
|
||||
## codec
|
||||
|
||||
#### encode
|
||||
#### decode
|
||||
- `str` String
|
||||
|
||||
## middleware
|
||||
|
||||
Middleware are functions that will be executed either before request or after response. These can alter the way a request is made or response is sent.
|
||||
|
||||
```javascript
|
||||
function(ctx) {r
|
||||
ctx.body; // (Request / Response) Body (Will return null if none)
|
||||
ctx.headers; // (Request / Response) Headers
|
||||
ctx.url; // WHATWG URL
|
||||
ctx.flags; // URL Flags
|
||||
ctx.origin; // Request origin
|
||||
ctx.method; // Request method
|
||||
ctx.rewrite; // Corrosion object
|
||||
ctx.statusCode; // Response status (Only available on response)
|
||||
ctx.agent; // HTTP agent
|
||||
ctx.address; // Address used to make remote request
|
||||
ctx.clientSocket; // Node.js Server Socket (Only available on upgrade)
|
||||
ctx.clientRequest; // Node.js Server Request
|
||||
ctx.clientResponse; // Node.js Server Response
|
||||
ctx.remoteResponse; // Node.js Remote Response (Only available on response)
|
||||
};
|
||||
```
|
||||
|
||||
### Default middleware
|
||||
|
||||
- Request
|
||||
- requestHeaders
|
||||
|
||||
- Response
|
||||
- responseHeaders
|
||||
- decompress
|
||||
- rewriteBody
|
||||
|
||||
### Available Middleware
|
||||
|
||||
#### address (Request)
|
||||
- `arr` Array of IP addresses to use in request
|
||||
|
||||
```javascript
|
||||
const Corrosion = require('corrosion');
|
||||
const proxy = new Corrosion({
|
||||
requestMiddleware: [
|
||||
Corrosion.middleware.address([
|
||||
0.0.0.0,
|
||||
0.0.0.0
|
||||
]),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### blacklist
|
||||
- `arr` Array of hostnames to block clients from seeing
|
||||
- `page` Block page
|
||||
|
||||
```javascript
|
||||
const Corrosion = require('corrosion');
|
||||
const proxy = new Corrosion({
|
||||
requestMiddleware: [
|
||||
Corrosion.middleware.blacklist([
|
||||
'example.org',
|
||||
'example.com',
|
||||
], 'Page is blocked'),
|
||||
],
|
||||
});
|
||||
```
|
16
src/Corrosion/demo/index.html
Normal file
16
src/Corrosion/demo/index.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/service/gateway/" method="POST">
|
||||
<input name="url" placeholder="Search the web">
|
||||
<input type="submit" value="Go">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
19
src/Corrosion/demo/index.js
Normal file
19
src/Corrosion/demo/index.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const ssl = {
|
||||
key: fs.readFileSync(path.join(__dirname, '/ssl.key')),
|
||||
cert: fs.readFileSync(path.join(__dirname, '/ssl.cert')),
|
||||
};
|
||||
const server = https.createServer(ssl);
|
||||
const Corrosion = require('../');
|
||||
const proxy = new Corrosion({
|
||||
codec: 'xor',
|
||||
});
|
||||
|
||||
proxy.bundleScripts();
|
||||
|
||||
server.on('request', (request, response) => {
|
||||
if (request.url.startsWith(proxy.prefix)) return proxy.request(request, response);
|
||||
response.end(fs.readFileSync(__dirname + '/index.html', 'utf-8'));
|
||||
}).on('upgrade', (clientRequest, clientSocket, clientHead) => proxy.upgrade(clientRequest, clientSocket, clientHead)).listen(443);
|
22
src/Corrosion/demo/ssl.cert
Normal file
22
src/Corrosion/demo/ssl.cert
Normal file
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDqzCCApOgAwIBAgIJAJnCkScWtmL0MA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRgwFgYDVQQKDA9UaXRhbml1bU5l
|
||||
dHdvcmsxDjAMBgNVBAsMBWdhbWVyMR4wHAYDVQQDDBUqLnRpdGFuaXVtbmV0d29y
|
||||
ay5vcmcwHhcNMjAwNjEzMTg0OTU2WhcNMjEwNjEzMTg0OTU2WjBsMQswCQYDVQQG
|
||||
EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEYMBYGA1UECgwPVGl0YW5pdW1OZXR3
|
||||
b3JrMQ4wDAYDVQQLDAVnYW1lcjEeMBwGA1UEAwwVKi50aXRhbml1bW5ldHdvcmsu
|
||||
b3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPL69+RE6r8RrFh4
|
||||
njzC8ZRnLB+yNtuGw14C0dvNb5JwgdLl5g9/wK/s0V5NGlqwxlQlxQ/gUSuYEcUR
|
||||
6MYjcnaUmZZe/gaKVV0fkfkuigOWhLnI5AQxx7rhkzx1ujuyJ9D2pkDtZpSvv0yn
|
||||
2yrvWhJMtjuxGYip8jaLuRpbXoafvR7nrlDaNcE/GwIjnCCxsRnY2bGbxYK840mN
|
||||
fuMfF2nz+fXKPuQ/9PT48e3wOo9vM5s7yKhiHYwrogqzGN4cH4sSr1FE8C7flFyT
|
||||
Yw101u7fUaopfeGCo9Pg6IrfzyzE5Qb7OlqlVk2IkvXx7pPqVc6lZCJEhOX/qF9o
|
||||
n3mFqwIDAQABo1AwTjAdBgNVHQ4EFgQUC561ob2kGtFQ4az6y64b98+Fy+IwHwYD
|
||||
VR0jBBgwFoAUC561ob2kGtFQ4az6y64b98+Fy+IwDAYDVR0TBAUwAwEB/zANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAotvUsSLSzFyxQz329tEPyH6Tmi19FQoA5ZbLg6EqeTI9
|
||||
08qOByDGkSYJi0npaIlPO1I557NxRzdO0PxK3ybol6lnzuSlqCJP5nb1dr0z2Eax
|
||||
wgKht9P+ap/yozU5ye05ah2nkpcaeDPnwnnWFmfsnYNfgu62EshOS+5FETWEKVUb
|
||||
LXQhGInOdJq8KZvhoLZWJoUhyAqxBfW4oVvaqs+Ff96A2NNKrvbiAVYX30rVa+x0
|
||||
KIl0/DoVvDx2Q6TiL396cAXdKUW7edRQcSsGFcxwIrU5lePm0V05aN+oCoEBvXBG
|
||||
ArPN+a5kpGjJwfcpcBVf9cJ6IsvptGS9de3eTHoTyw==
|
||||
-----END CERTIFICATE-----
|
28
src/Corrosion/demo/ssl.key
Normal file
28
src/Corrosion/demo/ssl.key
Normal file
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDA8vr35ETqvxGs
|
||||
WHiePMLxlGcsH7I224bDXgLR281vknCB0uXmD3/Ar+zRXk0aWrDGVCXFD+BRK5gR
|
||||
xRHoxiNydpSZll7+BopVXR+R+S6KA5aEucjkBDHHuuGTPHW6O7In0PamQO1mlK+/
|
||||
TKfbKu9aEky2O7EZiKnyNou5Gltehp+9HueuUNo1wT8bAiOcILGxGdjZsZvFgrzj
|
||||
SY1+4x8XafP59co+5D/09Pjx7fA6j28zmzvIqGIdjCuiCrMY3hwfixKvUUTwLt+U
|
||||
XJNjDXTW7t9Rqil94YKj0+Doit/PLMTlBvs6WqVWTYiS9fHuk+pVzqVkIkSE5f+o
|
||||
X2ifeYWrAgMBAAECggEAbihK8Ev6rKr5RBQeiPjXs2SuoppV/MvIXLHHmliLKS/J
|
||||
29S0PGyM202VPtM/4dP1KMXR6nft8WmaIEsKtoKoqijZHfajtRO21pWb+JLy5wi1
|
||||
XoFTGBrs8MLZFl5mODTsuZ6rsq9O2kn5LJZvHsmcbSgVc9UQfytvG0HY840ArS3g
|
||||
kSDtUFb1xRui6wtCBKzHVvCT+FXhSBbwkHalmbqP6BefhJ3lW2VonkOcHDrdXPfW
|
||||
CEN18IJ2v8QYgXqZP6VUlAweNXLJ33ZOl+jXGdygcOG24MFqdw0VtP0XFGk0jnSS
|
||||
W6dX67BZKeZ71EKaTy02jw5LpQNXA70ismPJHQ2uQQKBgQDuROawnBIW1fC3xOle
|
||||
m+JmP0eMe0eIQycxRsMXsXhYAA0wV3qYZSLZrNK2eRhmSNt+ODSmZ2Vt11dwOv5u
|
||||
bo8WONrRlM097SmitS2S+8o7ASem2VKQzyRE72Y9517Q+aNBdLRVtjrRNSw/hfSu
|
||||
ayLuG36+yukSH7wq7mfoUX34ZwKBgQDPTrgyyw8n5XhZT/qTTRnQJ2GTvPxDzNoJ
|
||||
IAGhGJGFAb6wgLoSpGx6BC122vuRxcTjkjAiMDci5N2zNW+YZVni+F0KTVvNFfU2
|
||||
pOTJUg3luRTygCra6O02PxwpbP/9KCBAKq/kYw/eBW+gxhPwP3ZrbAirvBjgBh0I
|
||||
kIrFijNOHQKBgGUUAbFGZD4fwCCVflLOWnr5uUaVPcFGi6fR1w2EEgNy8iVh1vYz
|
||||
YVdqg3E5aepqWgLvoRY+or64LbXEsQ70A+tvbxSdxXvR0mnd5lmGS0JAuSuE4gvg
|
||||
dAhybrMwJf8NB/7KnX4G8mix3/WKxEQB2y2bqGcT+U/g+phTzuy1NXVdAoGBAIrl
|
||||
jVjK4J60iswcYCEteWwT1rbr2oF60WNnxG+xTF63apJLzWAMNnoSLnwCAKgMv/xR
|
||||
yFo/v9FrUnduCBUtYupFyeDLMATa/27bUEbq6VDPjw9jfFMr2TONWUsQMvvlVKZp
|
||||
c2wsS0dQkRhBXr6LZsZWngCiiHAg6HcCkVgFXpapAoGBAJ/8oLGt0Ar+0MTl+gyk
|
||||
xSqgHnsc5jgqhix3nIoI5oEAbfibdGmRD1S3rtWD9YsnPxMIl+6E5bOAHrmd+Zr8
|
||||
O7EP+CLvbz4JXidaaa85h9ThXSG5xk1A1UTtSFrp+KolLE1Vvmjjd+R844XsM2wZ
|
||||
OAHbihzk0iPPphjEWR4lU4Av
|
||||
-----END PRIVATE KEY-----
|
271
src/Corrosion/lib/browser/document.js
Normal file
271
src/Corrosion/lib/browser/document.js
Normal file
|
@ -0,0 +1,271 @@
|
|||
function createDocumentRewriter(ctx) {
|
||||
return function rewriteDocument() {
|
||||
if (ctx.serviceWorker) return;
|
||||
const {
|
||||
HTMLMediaElement,
|
||||
HTMLScriptElement,
|
||||
HTMLAudioElement,
|
||||
HTMLVideoElement,
|
||||
HTMLInputElement,
|
||||
HTMLEmbedElement,
|
||||
HTMLTrackElement,
|
||||
HTMLAnchorElement,
|
||||
HTMLIFrameElement,
|
||||
HTMLAreaElement,
|
||||
HTMLLinkElement,
|
||||
HTMLBaseElement,
|
||||
HTMLFormElement,
|
||||
HTMLImageElement,
|
||||
HTMLSourceElement,
|
||||
} = ctx.window;
|
||||
const cookie = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'cookie');
|
||||
const domain = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'domain');
|
||||
const title = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'title');
|
||||
const baseURI = Object.getOwnPropertyDescriptor(ctx.window.Node.prototype, 'baseURI');
|
||||
const cookieEnabled = Object.getOwnPropertyDescriptor(ctx.window.Navigator.prototype, 'cookieEnabled');
|
||||
let spoofTitle = '';
|
||||
let spoofDomain = ctx.location.hostname;
|
||||
|
||||
if (ctx.window.Document.prototype.write) {
|
||||
ctx.window.Document.prototype.write = new Proxy(ctx.window.Document.prototype.write, {
|
||||
apply: (target, that , args) => {
|
||||
if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.Document.prototype.hasOwnProperty('cookie')) {
|
||||
Object.defineProperty(ctx.window.Document.prototype, 'cookie', {
|
||||
get: new Proxy(cookie.get, {
|
||||
apply: (target, that, args) => {
|
||||
const cookies = Reflect.apply(target, that, args);
|
||||
return ctx.config.cookie ? ctx.cookies.decode(cookies, ctx.meta) : '';
|
||||
},
|
||||
}),
|
||||
set: new Proxy(cookie.set, {
|
||||
apply: (target, that, [ val ]) => {
|
||||
return Reflect.apply(target, that, [ ctx.config.cookie ? ctx.cookies.encode(val, ctx.meta) : '' ]);
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
if (ctx.window.Document.prototype.writeln) {
|
||||
ctx.window.Document.prototype.writeln = new Proxy(ctx.window.Document.prototype.writeln, {
|
||||
apply: (target, that , args) => {
|
||||
if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.Element.prototype.setAttribute) {
|
||||
ctx.window.Element.prototype.setAttribute = new Proxy(ctx.window.Element.prototype.setAttribute, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[0] && args[1]) {
|
||||
const handler = ctx.html.attributeRoute({
|
||||
name: args[0],
|
||||
value: args[1],
|
||||
node: that,
|
||||
});
|
||||
switch(handler) {
|
||||
case 'url':
|
||||
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
|
||||
//if (that.tagName == 'SCRIPT' && args[0] == 'src') flags.push('js');
|
||||
args[1] = ctx.url.wrap(args[1], ctx.meta);
|
||||
break;
|
||||
case 'srcset':
|
||||
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
|
||||
args[1] = ctx.html.srcset(args[1], ctx.meta);
|
||||
break;
|
||||
case 'css':
|
||||
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
|
||||
args[1] = ctx.css.process(args[1], { ...ctx.meta, context: 'declarationList' });
|
||||
break;
|
||||
case 'html':
|
||||
Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
|
||||
args[1] = ctx.html.process(args[1], ctx.meta);
|
||||
break;
|
||||
case 'delete':
|
||||
return Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
|
||||
};
|
||||
};
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.Element.prototype.getAttribute) {
|
||||
ctx.window.Element.prototype.getAttribute = new Proxy(ctx.window.Element.prototype.getAttribute, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[0] && that.hasAttribute(`corrosion-${args[0]}`)) args[0] = `corrosion-${args[0]}`;
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
ctx.window.CSSStyleDeclaration.prototype.setProperty = new Proxy(ctx.window.CSSStyleDeclaration.prototype.setProperty, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[1]) args[1] = ctx.css.process(args[1], { context: 'value', ...ctx.meta, });
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
if (ctx.window.Audio) {
|
||||
ctx.window.Audio = new Proxy(ctx.window.Audio, {
|
||||
construct: (target, args) => {
|
||||
if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
|
||||
return Reflect.construct(target, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
[
|
||||
'innerHTML',
|
||||
'outerHTML',
|
||||
].forEach(html => {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(ctx.window.Element.prototype, html);
|
||||
Object.defineProperty(ctx.window.Element.prototype, html, {
|
||||
get: new Proxy(descriptor.get, {
|
||||
apply: (target, that, args) => {
|
||||
const body = Reflect.apply(target, that, args);
|
||||
if (!body || html == 'innerHTML' && that.tagName == 'SCRIPT') return body;
|
||||
return ctx.html.source(body, ctx.meta);
|
||||
},
|
||||
}),
|
||||
set: new Proxy(descriptor.set, {
|
||||
apply(target, that, [ val ]) {
|
||||
return Reflect.apply(target, that, [ val ? ctx.html.process(val.toString(), ctx.meta) : val, ]);
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
[
|
||||
['background', 'background'],
|
||||
['backgroundImage', 'background-image'],
|
||||
['listStyleImage', 'list-style-image'],
|
||||
].forEach(([key, cssProperty]) => {
|
||||
Object.defineProperty(ctx.window.CSS2Properties ? ctx.window.CSS2Properties.prototype : ctx.window.CSSStyleDeclaration.prototype, key, {
|
||||
get() {
|
||||
return this.getPropertyValue(cssProperty);
|
||||
},
|
||||
set(val) {
|
||||
return this.setProperty(cssProperty, val);
|
||||
},
|
||||
});
|
||||
});
|
||||
Object.defineProperty(ctx.window.Document.prototype, 'domain', {
|
||||
get: new Proxy(domain.get, {
|
||||
apply: () => spoofDomain,
|
||||
}),
|
||||
set: new Proxy(domain.set, {
|
||||
apply: (target, that, [ val ]) => {
|
||||
if (!val.toString().endsWith(ctx.location.hostname.split('.').slice(-2).join('.'))) return Reflect.apply(target, that, ['']);
|
||||
return spoofDomain = val;
|
||||
},
|
||||
}),
|
||||
});
|
||||
if (ctx.config.title) Object.defineProperty(ctx.window.Document.prototype, 'title', {
|
||||
get: new Proxy(title.get, {
|
||||
apply: () => spoofTitle,
|
||||
}),
|
||||
set: new Proxy(title.set, {
|
||||
apply: (target, that, [ val ]) => spoofTitle = val,
|
||||
}),
|
||||
});
|
||||
Object.defineProperty(ctx.window.Navigator.prototype, 'cookieEnabled', {
|
||||
get: new Proxy(cookieEnabled.get, {
|
||||
apply: () => ctx.config.cookie,
|
||||
}),
|
||||
});
|
||||
Object.defineProperty(ctx.window.Node.prototype, 'baseURI', {
|
||||
get: new Proxy(baseURI.get, {
|
||||
apply: (target, that, args) => {
|
||||
const val = Reflect.apply(target, that, args);
|
||||
return val.startsWith(ctx.meta.origin) ? ctx.url.unwrap(val, ctx.meta) : val;
|
||||
},
|
||||
}),
|
||||
});
|
||||
[
|
||||
{
|
||||
elements: [ HTMLScriptElement, HTMLMediaElement, HTMLImageElement, HTMLAudioElement, HTMLVideoElement, HTMLInputElement, HTMLEmbedElement, HTMLIFrameElement, HTMLTrackElement, HTMLSourceElement],
|
||||
properties: ['src'],
|
||||
handler: 'url',
|
||||
},
|
||||
{
|
||||
elements: [ HTMLFormElement ],
|
||||
properties: ['action'],
|
||||
handler: 'url',
|
||||
},
|
||||
{
|
||||
elements: [ HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement, HTMLBaseElement ],
|
||||
properties: ['href'],
|
||||
handler: 'url',
|
||||
},
|
||||
{
|
||||
elements: [ HTMLImageElement, HTMLSourceElement ],
|
||||
properties: ['srcset'],
|
||||
handler: 'srcset',
|
||||
},
|
||||
{
|
||||
elements: [ HTMLScriptElement ],
|
||||
properties: ['integrity'],
|
||||
handler: 'delete',
|
||||
},
|
||||
{
|
||||
elements: [ HTMLIFrameElement ],
|
||||
properties: ['contentWindow'],
|
||||
handler: 'window',
|
||||
},
|
||||
].forEach(entry => {
|
||||
entry.elements.forEach(element => {
|
||||
if (!element) return;
|
||||
entry.properties.forEach(property => {
|
||||
if (!element.prototype.hasOwnProperty(property)) return;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(element.prototype, property);
|
||||
Object.defineProperty(element.prototype, property, {
|
||||
get: descriptor.get ? new Proxy(descriptor.get, {
|
||||
apply: (target, that, args) => {
|
||||
let val = Reflect.apply(target, that, args);
|
||||
let flags = [];
|
||||
switch(entry.handler) {
|
||||
case 'url':
|
||||
//if (that.tagName == 'SCRIPT' && property == 'src') flags.push('js');
|
||||
val = ctx.url.unwrap(val, ctx.meta);
|
||||
break;
|
||||
case 'srcset':
|
||||
val = ctx.html.unsrcset(val, ctx.meta);
|
||||
break;
|
||||
case 'delete':
|
||||
val = that.getAttribute(`corrosion-${property}`);
|
||||
break;
|
||||
case 'window':
|
||||
try {
|
||||
if (!val.$corrosion) {
|
||||
val.$corrosion = new ctx.constructor({ ...ctx.config, window: val, });
|
||||
val.$corrosion.init();
|
||||
val.$corrosion.meta = ctx.meta;
|
||||
};
|
||||
} catch(e) {};
|
||||
};
|
||||
return val;
|
||||
},
|
||||
}) : undefined,
|
||||
set: descriptor.set ? new Proxy(descriptor.set, {
|
||||
apply(target, that, [ val ]) {
|
||||
let newVal = val;
|
||||
switch(entry.handler) {
|
||||
case 'url':
|
||||
newVal = ctx.url.wrap(newVal, ctx.meta);
|
||||
break;
|
||||
case 'srcset':
|
||||
newVal = ctx.html.srcset(newVal, ctx.meta);
|
||||
break;
|
||||
case 'delete':
|
||||
that.setAttribute(property, newVal);
|
||||
return newVal;
|
||||
};
|
||||
return Reflect.apply(target, that, [ newVal ]);
|
||||
},
|
||||
}) : undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
module.exports = createDocumentRewriter;
|
26
src/Corrosion/lib/browser/history.js
Normal file
26
src/Corrosion/lib/browser/history.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
function createHistoryRewriter(ctx) {
|
||||
return function rewriteHistory() {
|
||||
if (ctx.serviceWorker) return;
|
||||
if (ctx.window.History.prototype.pushState) {
|
||||
ctx.window.History.prototype.pushState = new Proxy(ctx.window.History.prototype.pushState, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
|
||||
const ret = Reflect.apply(target, that, args);
|
||||
ctx.updateLocation();
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.History.prototype.replaceState) {
|
||||
ctx.window.History.prototype.replaceState = new Proxy(ctx.window.History.prototype.replaceState, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
|
||||
const ret = Reflect.apply(target, that, args);
|
||||
ctx.updateLocation();
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
module.exports = createHistoryRewriter;
|
92
src/Corrosion/lib/browser/http.js
Normal file
92
src/Corrosion/lib/browser/http.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
function createHttpRewriter(ctx = {}) {
|
||||
return function rewriteHttp() {
|
||||
if (ctx.window.Request) {
|
||||
const requestURL = Object.getOwnPropertyDescriptor(ctx.window.Request.prototype, 'url');
|
||||
ctx.window.Request = new Proxy(ctx.window.Request, {
|
||||
construct(target, args) {
|
||||
if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], })
|
||||
return Reflect.construct(target, args);
|
||||
},
|
||||
});
|
||||
Object.defineProperty(ctx.window.Request.prototype, 'url', {
|
||||
get: new Proxy(requestURL.get, {
|
||||
apply: (target, that, args) => {
|
||||
var url = Reflect.apply(target, that, args);
|
||||
return url ? ctx.url.unwrap(url, ctx.meta) : url;
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
if (ctx.window.Response) {
|
||||
const responseURL = Object.getOwnPropertyDescriptor(ctx.window.Response.prototype, 'url');
|
||||
Object.defineProperty(ctx.window.Response.prototype, 'url', {
|
||||
get: new Proxy(responseURL.get, {
|
||||
apply: (target, that, args) => {
|
||||
var url = Reflect.apply(target, that, args);
|
||||
return url ? ctx.url.unwrap(url, ctx.meta) : url;
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
if (ctx.window.open) {
|
||||
ctx.window.open = new Proxy(ctx.window.open, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
|
||||
return Reflect.apply(target, that, args)
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.fetch) {
|
||||
ctx.window.fetch = new Proxy(ctx.window.fetch, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[0] instanceof ctx.window.Request) return Reflect.apply(target, that, args);
|
||||
if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.Navigator && ctx.window.Navigator.prototype.sendBeacon) {
|
||||
ctx.window.Navigator.prototype.sendBeacon = new Proxy(ctx.window.Navigator.prototype.sendBeacon, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[0]) ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.XMLHttpRequest) {
|
||||
const responseURL = Object.getOwnPropertyDescriptor(ctx.window.XMLHttpRequest.prototype, 'responseURL');
|
||||
ctx.window.XMLHttpRequest.prototype.open = new Proxy(ctx.window.XMLHttpRequest.prototype.open, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[1]) args[1] = ctx.url.wrap(args[1], { ...ctx.meta, flags: ['xhr'], });
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
Object.defineProperty(ctx.window.XMLHttpRequest.prototype, 'responseURL', {
|
||||
get: new Proxy(responseURL.get, {
|
||||
apply: (target, that, args) => {
|
||||
const url = Reflect.apply(target, that, args);
|
||||
return url ? ctx.url.unwrap(url, ctx.meta) : url;
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
if (ctx.window.postMessage) {
|
||||
ctx.window.postMessage = new Proxy(ctx.window.postMessage, {
|
||||
apply: (target, that, args) => {
|
||||
if (!ctx.serviceWorker && args[1]) args[1] = ctx.meta.origin;
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.window.WebSocket && ctx.config.ws) {
|
||||
ctx.window.WebSocket = new Proxy(ctx.window.WebSocket, {
|
||||
construct: (target, args) => {
|
||||
if (args[0]) args[0] = ctx.url.wrap(args[0].toString().replace('ws', 'http'), ctx.meta).replace('http', 'ws') + '?origin=' + ctx.location.origin;
|
||||
return Reflect.construct(target, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = createHttpRewriter;
|
195
src/Corrosion/lib/browser/index.js
Normal file
195
src/Corrosion/lib/browser/index.js
Normal file
|
@ -0,0 +1,195 @@
|
|||
const createDocumentRewriter = require('./document');
|
||||
const createHistoryRewriter = require('./history');
|
||||
const createHttpRewriter = require('./http');
|
||||
const createLocation = require('./location');
|
||||
const createWorkerRewriter = require('./worker');
|
||||
const defaultConfig = {
|
||||
prefix: '/service/',
|
||||
codec: 'plain',
|
||||
ws: true,
|
||||
cookie: true,
|
||||
title: 'Service',
|
||||
serviceWorker: false,
|
||||
url: null,
|
||||
window: false,
|
||||
};
|
||||
|
||||
class Corrosion extends require('../rewrite') {
|
||||
constructor(config = defaultConfig) {
|
||||
super(Object.assign(defaultConfig, config));
|
||||
const corrosion = this;
|
||||
if (!this.config.window) throw 'Corrosion Error: No window was given.';
|
||||
this.serviceWorker = this.config.serviceWorker || false;
|
||||
this.window = this.config.window;
|
||||
this.document = this.serviceWorker ? this.window.document : {};
|
||||
this._url = new URL((this.config.url || this.url.unwrap(this.window.location.href, { origin: this.window.location.origin, })));
|
||||
this.originalXhr = this.window.XMLHttpRequest;
|
||||
this.meta = {
|
||||
origin: this.window.location.origin,
|
||||
get base() {
|
||||
if (corrosion.serviceWorker) return corrosion._url;
|
||||
return corrosion.window.document.baseURI != corrosion.location.href ? new URL(corrosion.window.document.baseURI) : corrosion._url;
|
||||
},
|
||||
url: this._url,
|
||||
};
|
||||
this.location = createLocation(this, this._url);
|
||||
this.rewriteHttp = createHttpRewriter(this);
|
||||
this.rewriteDocument = createDocumentRewriter(this);
|
||||
this.rewriteHistory = createHistoryRewriter(this);
|
||||
this.rewriteWorker = createWorkerRewriter(this);
|
||||
if (!this.serviceWorker && this.window.document.currentScript) this.window.document.currentScript.remove();
|
||||
};
|
||||
get parent() {
|
||||
if (this.serviceWorker) return false;
|
||||
try {
|
||||
return this.window.parent.$corrosion ? this.window.parent : this.window;
|
||||
} catch(e) {
|
||||
return this.window;
|
||||
};
|
||||
};
|
||||
get top() {
|
||||
if (this.serviceWorker) return false;
|
||||
try {
|
||||
return this.window.top.$corrosion ? this.window.top : this.parent;
|
||||
} catch(e) {
|
||||
return this.parent;
|
||||
};
|
||||
};
|
||||
init() {
|
||||
this.rewriteHttp();
|
||||
this.rewriteDocument();
|
||||
this.rewriteHistory();
|
||||
this.rewriteWorker();
|
||||
this.window.Location = createLocation.Location;
|
||||
this.window.$corrosionGet$ = this.get$.bind(this);
|
||||
this.window.$corrosionSet$ = this.set$.bind(this);
|
||||
this.window.$corrosionGet$m = this.get$m.bind(this);
|
||||
this.window.$corrosionSet$m = this.set$m.bind(this);
|
||||
this.window.$corrosionCall$m = this.call$m.bind(this);
|
||||
};
|
||||
get$m(obj, key) {
|
||||
if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
|
||||
return this.parent.$corrosion.get$m(this.parent, key);
|
||||
};
|
||||
if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
|
||||
return this.top.$corrosion.get$m(this.top, key);
|
||||
};
|
||||
if (obj == this.window && key == 'location' || !this.serviceWorker && obj == this.window.document && key == 'location') return this.location;
|
||||
if (!this.serviceWorker && obj == this.window && key == 'parent' && this.window != this.window.parent) return this.parent;
|
||||
if (!this.serviceWorker && obj == this.window && key == 'top' && this.window != this.window.top) return this.top;
|
||||
return obj[key];
|
||||
};
|
||||
set$m(obj, key, val, operator) {
|
||||
if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
|
||||
return this.parent.$corrosion.set$m(this.parent, key, val, operator);
|
||||
};
|
||||
if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
|
||||
return this.top.$corrosion.set$m(this.top, key, val, operator);
|
||||
};
|
||||
if (obj == this.window && key == 'location' || !this.serviceWorker && obj == this.window.document && key == 'location') obj = this;
|
||||
switch(operator) {
|
||||
case '+=':
|
||||
return obj[key] += val;
|
||||
case '-=':
|
||||
return obj[key] -= val;
|
||||
case '*=':
|
||||
return obj[key] *= val;
|
||||
case '/=':
|
||||
return obj[key] /= val;
|
||||
case '%=':
|
||||
return obj[key] %= val;
|
||||
case '**=':
|
||||
return obj[key] **= val;
|
||||
case '<<=':
|
||||
return obj[key] <<= val;
|
||||
case '>>=':
|
||||
return obj[key] >>= val;
|
||||
case '>>>=':
|
||||
return obj[key] >>>= val;
|
||||
case '&=':
|
||||
return obj[key] &= val;
|
||||
case '^=':
|
||||
return obj[key] ^= val;
|
||||
case '|=':
|
||||
return obj[key] |= val;
|
||||
case '&&=':
|
||||
return obj[key] &&= val;
|
||||
case '||=':
|
||||
return obj[key] ||= val;
|
||||
case '??=':
|
||||
return obj[key] ??= val;
|
||||
case '++':
|
||||
return obj[key]++;
|
||||
case '--':
|
||||
return obj[key]--;
|
||||
case '=':
|
||||
default:
|
||||
return obj[key] = val;
|
||||
};
|
||||
};
|
||||
call$m(obj, key, args) {
|
||||
if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
|
||||
return this.parent.$corrosion.call$m(this.parent, key, args);
|
||||
};
|
||||
if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
|
||||
return this.top.$corrosion.call$m(this.top, key, args);
|
||||
};
|
||||
return obj[key](...args);
|
||||
};
|
||||
get$(obj) {
|
||||
if (obj == this.window.location) return this.location;
|
||||
if (!this.serviceWorker && obj == this.window.parent) return this.parent;
|
||||
if (!this.serviceWorker && obj == this.window.top) return this.top;
|
||||
return obj;
|
||||
};
|
||||
set$(obj, val, operator) {
|
||||
if (obj == this.window.location) return this.set$(this.location, val, operator);
|
||||
if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) return this.parent.set$(this.parent, val, operator);
|
||||
if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) return this.top.set$(this.top, val, operator);
|
||||
switch(operator) {
|
||||
case '+=':
|
||||
return obj += val;
|
||||
case '-=':
|
||||
return obj -= val;
|
||||
case '*=':
|
||||
return obj *= val;
|
||||
case '/=':
|
||||
return obj /= val;
|
||||
case '%=':
|
||||
return obj %= val;
|
||||
case '**=':
|
||||
return obj **= val;
|
||||
case '<<=':
|
||||
return obj <<= val;
|
||||
case '>>=':
|
||||
return obj >>= val;
|
||||
case '>>>=':
|
||||
return obj >>>= val;
|
||||
case '&=':
|
||||
return obj &= val;
|
||||
case '^=':
|
||||
return obj ^= val;
|
||||
case '|=':
|
||||
return obj |= val;
|
||||
case '&&=':
|
||||
return obj &&= val;
|
||||
case '||=':
|
||||
return obj ||= val;
|
||||
case '??=':
|
||||
return obj ??= val;
|
||||
case '++':
|
||||
return obj++;
|
||||
case '--':
|
||||
return obj--;
|
||||
case '=':
|
||||
default:
|
||||
return obj = val;
|
||||
};
|
||||
};
|
||||
updateLocation() {
|
||||
this._url = new URL(this.url.unwrap(this.window.location.href, this.meta));
|
||||
this.location = createLocation(this, this._url);
|
||||
};
|
||||
};
|
||||
|
||||
globalThis.Corrosion = Corrosion;
|
56
src/Corrosion/lib/browser/location.js
Normal file
56
src/Corrosion/lib/browser/location.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
class Location {
|
||||
get [Symbol.toPrimitive]() {
|
||||
return () => this.href;
|
||||
};
|
||||
};
|
||||
|
||||
function createLocation(ctx, url) {
|
||||
const _location = new Location();
|
||||
const _url = new URL(url);
|
||||
[
|
||||
'hash',
|
||||
'host',
|
||||
'hostname',
|
||||
'href',
|
||||
'pathname',
|
||||
'port',
|
||||
'protocol',
|
||||
'search',
|
||||
'origin',
|
||||
].forEach(property => {
|
||||
Object.defineProperty(_location, property, {
|
||||
get() {
|
||||
return _url[property];
|
||||
},
|
||||
set(val) {
|
||||
if (ctx.serviceWorker || property == 'origin') return;
|
||||
if (property == 'href') {
|
||||
return ctx.window.location.href = ctx.url.wrap(new URL(val, _url).href);
|
||||
};
|
||||
_url[property] = val;
|
||||
return ctx.window.location.href = ctx.url.wrap(_url);
|
||||
},
|
||||
});
|
||||
});
|
||||
if (!ctx.serviceWorker) [
|
||||
'assign',
|
||||
'replace',
|
||||
'reload',
|
||||
].forEach(method => {
|
||||
_location[method] = new Proxy(ctx.window.location[method], {
|
||||
apply(target, that, args) {
|
||||
if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
|
||||
return Reflect.apply(target.bind(ctx.window.location), that, args);
|
||||
},
|
||||
});
|
||||
});
|
||||
_location.toString = new Proxy(_url.toString, {
|
||||
apply(target, that, args) {
|
||||
return Reflect.apply(target.bind(_url), that, args);
|
||||
},
|
||||
});
|
||||
return _location;
|
||||
};
|
||||
|
||||
createLocation.Location = Location;
|
||||
module.exports = createLocation;
|
33
src/Corrosion/lib/browser/worker.js
Normal file
33
src/Corrosion/lib/browser/worker.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
function createWorkerRewriter(ctx = {}) {
|
||||
return function rewriteWorker() {
|
||||
if (ctx.window.Worker) {
|
||||
ctx.window.Worker = new Proxy(ctx.window.Worker, {
|
||||
construct: (target, args) => {
|
||||
if (args[0]) {
|
||||
if (args[0].trim().startsWith(`blob:${ctx.window.location.origin}`)) {
|
||||
const xhr = new ctx.originalXhr();
|
||||
xhr.open('GET', args[0], false);
|
||||
xhr.send();
|
||||
const script = ctx.js.process(xhr.responseText, ctx.location.origin + args[0].trim().slice(`blob:${ctx.window.location.origin}`.length));
|
||||
const blob = new Blob([ script ], { type: 'application/javascript' });
|
||||
args[0] = URL.createObjectURL(blob);
|
||||
} else {
|
||||
args[0] = ctx.url.wrap(args[0], ctx.meta);
|
||||
};
|
||||
};
|
||||
return Reflect.construct(target, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
if (ctx.serviceWorker && ctx.window.importScripts) {
|
||||
ctx.window.importScripts = new Proxy(ctx.window.importScripts, {
|
||||
apply: (target, that, args) => {
|
||||
if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
|
||||
return Reflect.apply(target, that, args);
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = createWorkerRewriter;
|
66
src/Corrosion/lib/codec.js
Normal file
66
src/Corrosion/lib/codec.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
exports.xor = {
|
||||
encode(str){
|
||||
if (!str) return str;
|
||||
return encodeURIComponent(str.toString().split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join(''));
|
||||
},
|
||||
decode(str){
|
||||
if (!str) return str;
|
||||
return decodeURIComponent(str).split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join('');
|
||||
},
|
||||
};
|
||||
exports.plain = {
|
||||
encode(str) {
|
||||
if (!str) return str;
|
||||
return encodeURIComponent(str);
|
||||
},
|
||||
decode(str) {
|
||||
if (!str) return str;
|
||||
return decodeURIComponent(str);
|
||||
},
|
||||
};
|
||||
exports.base64 = {
|
||||
encode(str){
|
||||
if (!str) return str;
|
||||
str = str.toString();
|
||||
const b64chs = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=');
|
||||
let u32;
|
||||
let c0;
|
||||
let c1;
|
||||
let c2;
|
||||
let asc = '';
|
||||
let pad = str.length % 3;
|
||||
|
||||
for (let i = 0; i < str.length;) {
|
||||
if((c0 = str.charCodeAt(i++)) > 255 || (c1 = str.charCodeAt(i++)) > 255 || (c2 = str.charCodeAt(i++)) > 255)throw new TypeError('invalid character found');
|
||||
u32 = (c0 << 16) | (c1 << 8) | c2;
|
||||
asc += b64chs[u32 >> 18 & 63]
|
||||
+ b64chs[u32 >> 12 & 63]
|
||||
+ b64chs[u32 >> 6 & 63]
|
||||
+ b64chs[u32 & 63];
|
||||
}
|
||||
|
||||
return encodeURIComponent(pad ? asc.slice(0, pad - 3) + '==='.substr(pad) : asc);
|
||||
},
|
||||
decode(str){
|
||||
if (!str) return str;
|
||||
str = decodeURIComponent(str.toString());
|
||||
const b64tab = {"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"+":62,"/":63,"=":64};
|
||||
str = str.replace(/\s+/g, '');
|
||||
str += '=='.slice(2 - (str.length & 3));
|
||||
let u24;
|
||||
let bin = '';
|
||||
let r1;
|
||||
let r2;
|
||||
|
||||
for (let i = 0; i < str.length;) {
|
||||
u24 = b64tab[str.charAt(i++)] << 18
|
||||
| b64tab[str.charAt(i++)] << 12
|
||||
| (r1 = b64tab[str.charAt(i++)]) << 6
|
||||
| (r2 = b64tab[str.charAt(i++)]);
|
||||
bin += r1 === 64 ? String.fromCharCode(u24 >> 16 & 255)
|
||||
: r2 === 64 ? String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255)
|
||||
: String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
|
||||
};
|
||||
return bin;
|
||||
},
|
||||
};
|
95
src/Corrosion/lib/cookie-parser.js
Normal file
95
src/Corrosion/lib/cookie-parser.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
// -------------------
|
||||
// This file is shared both by the server and client.
|
||||
// Do not include any browser or node specific APIs
|
||||
// -------------------
|
||||
|
||||
class CookieStore {
|
||||
constructor(val = ''){
|
||||
this.data = {};
|
||||
val.split(';').map(cookie => {
|
||||
var [ name, val = ''] = cookie.trimStart().split('=');
|
||||
if (name) this.data[name] = val;
|
||||
});
|
||||
};
|
||||
has(name){
|
||||
if (!name || !this.data[name]) return false;
|
||||
return true;
|
||||
};
|
||||
get(name){
|
||||
return this.has(name) ? this.data[name] : null;
|
||||
};
|
||||
set(name, val){
|
||||
if (!name || !val) return;
|
||||
return this.data[name] = val;
|
||||
};
|
||||
delete(name){
|
||||
if (!name) return;
|
||||
return delete this.data[name];
|
||||
};
|
||||
forEach(action = (node, key) => null){
|
||||
for (let prop in this.data) action(this.data[prop], prop);
|
||||
};
|
||||
serialize(){
|
||||
var str = '';
|
||||
for (let i in this.data) str += ` ${i}=${this.data[i]};`;
|
||||
return str.substr(1);
|
||||
};
|
||||
};
|
||||
|
||||
class SetCookie {
|
||||
constructor(val = ''){
|
||||
|
||||
var [ [ name, value = '' ], ...data ] = val.split(';').map(str => str.trimStart().split('='));
|
||||
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.expires = null;
|
||||
this.maxAge = null;
|
||||
this.domain = null;
|
||||
this.secure = false;
|
||||
this.httpOnly = false;
|
||||
this.path = null;
|
||||
this.sameSite = null;
|
||||
|
||||
data.forEach(([name = null, value = null]) => {
|
||||
if (typeof name == 'string') switch(name.toLowerCase()){
|
||||
case 'domain':
|
||||
this.domain = value;
|
||||
break;
|
||||
case 'secure':
|
||||
this.secure = true;
|
||||
break;
|
||||
case 'httponly':
|
||||
this.httpOnly = true;
|
||||
break;
|
||||
case 'samesite':
|
||||
this.sameSite = value;
|
||||
break;
|
||||
case 'path':
|
||||
this.path = value;
|
||||
break;
|
||||
case 'expires':
|
||||
this.expires = value;
|
||||
break;
|
||||
case 'maxage':
|
||||
this.maxAge = value;
|
||||
break;
|
||||
};
|
||||
});
|
||||
};
|
||||
serialize(){
|
||||
if (!this.name) return;
|
||||
var str = `${this.name}=${this.value};`;
|
||||
if (this.expires) str += ` Expires=${this.expires};`;
|
||||
if (this.maxAge) str += ` Max-Age=${this.max_age};`;
|
||||
if (this.domain) str += ` Domain=${this.domain};`;
|
||||
if (this.secure) str += ` Secure;`;
|
||||
if (this.httpOnly) str += ` HttpOnly;`;
|
||||
if (this.path) str += ` Path=${this.path};`;
|
||||
if (this.sameSite) str += ` SameSite=${this.sameSite};`;
|
||||
return str;
|
||||
};
|
||||
};
|
||||
|
||||
exports.CookieStore = CookieStore;
|
||||
exports.SetCookie = SetCookie;
|
34
src/Corrosion/lib/cookie.js
Normal file
34
src/Corrosion/lib/cookie.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
const { SetCookie, CookieStore } = require('./cookie-parser');
|
||||
|
||||
class CookieRewriter {
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx;
|
||||
};
|
||||
decode(store, config = {}) {
|
||||
const url = new URL(config.url);
|
||||
const cookies = new CookieStore(store);
|
||||
cookies.forEach((val, key) => {
|
||||
if (!key.includes('@') || key.slice(key.length - url.hostname.length) != url.hostname) return cookies.delete(key);
|
||||
cookies.delete(key);
|
||||
cookies.set(key.substr(0, key.length - url.hostname.length - 1), val);
|
||||
});
|
||||
return cookies.serialize();
|
||||
};
|
||||
encode(input, config = {}) {
|
||||
if (Array.isArray(input)) {
|
||||
const rw = [ ...input ];
|
||||
for (let i in rw) rw[i] = this.encode(rw[i], config);
|
||||
return rw;
|
||||
};
|
||||
const url = new URL(config.url);
|
||||
const cookie = new SetCookie(input);
|
||||
if (!cookie.name) return null;
|
||||
cookie.domain = config.domain;
|
||||
cookie.secure = config.secure;
|
||||
cookie.name += `@${url.hostname}`;
|
||||
cookie.path = this.ctx.prefix;
|
||||
return cookie.serialize() || '';
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = CookieRewriter;
|
67
src/Corrosion/lib/css.js
Normal file
67
src/Corrosion/lib/css.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
const csstree = require('css-tree');
|
||||
|
||||
class CSSRewriter {
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx;
|
||||
};
|
||||
process(source, config = {}) {
|
||||
const ast = csstree.parse(source, {
|
||||
context: config.context || 'stylesheet',
|
||||
parseCustomProperty: true,
|
||||
});
|
||||
const urls = csstree.findAll(ast, node =>
|
||||
node.type == 'Url'
|
||||
);
|
||||
const imports = csstree.findAll(ast, node =>
|
||||
node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
|
||||
);
|
||||
urls.forEach(({ value }) => {
|
||||
switch(value.type) {
|
||||
case 'String':
|
||||
const quote = value.value.substring(0, 1);
|
||||
value.value = quote + this.ctx.url.wrap(value.value.slice(1).slice(0, -1), config) + quote;
|
||||
break;
|
||||
case 'Raw':
|
||||
value.value = this.ctx.url.wrap(value.value, config);
|
||||
break;
|
||||
};
|
||||
});
|
||||
imports.forEach(({ prelude }) => {
|
||||
const { data } = prelude.children.head;
|
||||
const quote = data.value.substring(0, 1);
|
||||
data.value = quote + this.ctx.url.wrap(data.value.slice(1).slice(0, -1), config) + quote;
|
||||
});
|
||||
return csstree.generate(ast);
|
||||
};
|
||||
source(processed, config = {}) {
|
||||
const ast = csstree.parse(processed, {
|
||||
context: config.context || 'stylesheet',
|
||||
parseCustomProperty: true,
|
||||
});
|
||||
const urls = csstree.findAll(ast, node =>
|
||||
node.type == 'Url'
|
||||
);
|
||||
const imports = csstree.findAll(ast, node =>
|
||||
node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
|
||||
);
|
||||
urls.forEach(({ value }) => {
|
||||
switch(value.type) {
|
||||
case 'String':
|
||||
const quote = value.value.substring(0, 1);
|
||||
value.value = quote + this.ctx.url.unwrap(value.value.slice(1).slice(0, -1), config) + quote;
|
||||
break;
|
||||
case 'Raw':
|
||||
value.value = this.ctx.url.unwrap(value.value, config);
|
||||
break;
|
||||
};
|
||||
});
|
||||
imports.forEach(({ prelude }) => {
|
||||
const { data } = prelude.children.head;
|
||||
const quote = data.value.substring(0, 1);
|
||||
data.value = quote + this.ctx.url.unwrap(data.value.slice(1).slice(0, -1), config) + quote;
|
||||
});
|
||||
return csstree.generate(ast);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = CSSRewriter;
|
2562
src/Corrosion/lib/esotope.js
Normal file
2562
src/Corrosion/lib/esotope.js
Normal file
File diff suppressed because it is too large
Load diff
226
src/Corrosion/lib/html.js
Normal file
226
src/Corrosion/lib/html.js
Normal file
|
@ -0,0 +1,226 @@
|
|||
const parse5 = require('parse5');
|
||||
|
||||
class HTMLRewriter {
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx;
|
||||
this.attrs = [
|
||||
{
|
||||
tags: ['form', 'object', 'a', 'link', 'area', 'base', 'script', 'img', 'audio', 'video', 'input', 'embed', 'iframe', 'track', 'source', 'html', 'table', 'head'],
|
||||
attrs: ['src', 'href', 'ping', 'data', 'movie', 'action', 'poster', 'profile', 'background'],
|
||||
handler: 'url',
|
||||
},
|
||||
{
|
||||
tags: ['iframe'],
|
||||
attrs: ['srcdoc'],
|
||||
handler: 'html',
|
||||
},
|
||||
{
|
||||
tags: ['img', 'link', 'source'],
|
||||
attrs: ['srcset', 'imagesrcset'],
|
||||
handler: 'srcset',
|
||||
},
|
||||
{
|
||||
tags: '*',
|
||||
attrs: ['style'],
|
||||
handler: 'css',
|
||||
},
|
||||
{
|
||||
tags: '*',
|
||||
attrs: ['http-equiv', 'integrity', 'nonce', 'crossorigin'],
|
||||
handler: 'delete',
|
||||
},
|
||||
];
|
||||
};
|
||||
process(source, config = {}) {
|
||||
const ast = parse5[config.document ? 'parse' : 'parseFragment'](source);
|
||||
const meta = {
|
||||
origin: config.origin,
|
||||
base: new URL(config.base),
|
||||
};
|
||||
iterate(ast, node => {
|
||||
if (!node.tagName) return;
|
||||
switch(node.tagName) {
|
||||
case 'STYLE':
|
||||
if (node.textContent) node.textContent = this.ctx.css.process(node.textContent, meta);
|
||||
break;
|
||||
case 'TITLE':
|
||||
if (node.textContent && this.ctx.config.title) node.textContent = this.ctx.config.title;
|
||||
break;
|
||||
case 'SCRIPT':
|
||||
if (node.getAttribute('type') != 'application/json' && node.textContent) node.textContent = this.ctx.js.process(node.textContent);
|
||||
break;
|
||||
case 'BASE':
|
||||
if (node.hasAttribute('href')) meta.base = new URL(node.getAttribute('href'), config.base);
|
||||
break;
|
||||
};
|
||||
node.attrs.forEach(attr => {
|
||||
const handler = this.attributeRoute({
|
||||
...attr,
|
||||
node,
|
||||
});
|
||||
let flags = [];
|
||||
if (node.tagName == 'SCRIPT' && attr.name == 'src') flags.push('js');
|
||||
if (node.tagName == 'LINK' && node.getAttribute('rel') == 'stylesheet') flags.push('css');
|
||||
switch(handler) {
|
||||
case 'url':
|
||||
node.setAttribute(`corrosion-${attr.name}`, attr.value);
|
||||
attr.value = this.ctx.url.wrap(attr.value, { ...meta, flags });
|
||||
break;
|
||||
case 'srcset':
|
||||
node.setAttribute(`corrosion-${attr.name}`, attr.value);
|
||||
attr.value = this.srcset(attr.value, meta);
|
||||
break;
|
||||
case 'css':
|
||||
attr.value = this.ctx.css.process(attr.value, { ...meta, context: 'declarationList' });
|
||||
break;
|
||||
case 'html':
|
||||
node.setAttribute(`corrosion-${attr.name}`, attr.value);
|
||||
attr.value = this.process(attr.value, { ...config, ...meta });
|
||||
break;
|
||||
case 'delete':
|
||||
node.removeAttribute(attr.name);
|
||||
break;
|
||||
};
|
||||
});
|
||||
});
|
||||
if (config.document) {
|
||||
for (let i in ast.childNodes) if (ast.childNodes[i].tagName == 'html') ast.childNodes[i].childNodes.forEach(node => {
|
||||
if (node.tagName == 'head') {
|
||||
node.childNodes.unshift(...this.injectHead(config.base));
|
||||
};
|
||||
});
|
||||
};
|
||||
return parse5.serialize(ast);
|
||||
};
|
||||
source(processed, config = {}) {
|
||||
const ast = parse5[config.document ? 'parse' : 'parseFragment'](processed);
|
||||
iterate(ast, node => {
|
||||
if (!node.tagName) return;
|
||||
node.attrs.forEach(attr => {
|
||||
if (node.hasAttribute(`corrosion-${attr.name}`)) {
|
||||
attr.value = node.getAttribute(`corrosion-${attr.name}`);
|
||||
node.removeAttribute(`corrosion-${attr.name}`)
|
||||
};
|
||||
});
|
||||
});
|
||||
return parse5.serialize(ast);
|
||||
};
|
||||
injectHead() {
|
||||
return [
|
||||
{
|
||||
nodeName: 'title',
|
||||
tagName: 'title',
|
||||
childNodes: [
|
||||
{
|
||||
nodeName: '#text',
|
||||
value: this.ctx.config.title || '',
|
||||
}
|
||||
],
|
||||
attrs: [],
|
||||
},
|
||||
{
|
||||
nodeName: 'script',
|
||||
tagName: 'script',
|
||||
childNodes: [],
|
||||
attrs: [
|
||||
{
|
||||
name: 'src',
|
||||
value: this.ctx.prefix + 'index.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
nodeName: 'script',
|
||||
tagName: 'script',
|
||||
childNodes: [
|
||||
{
|
||||
nodeName: '#text',
|
||||
value: `window.$corrosion = new Corrosion({ window, codec: '${this.ctx.config.codec || 'plain'}', prefix: '${this.ctx.prefix}', ws: ${this.ctx.config.ws}, cookie: ${this.ctx.config.cookie}, title: ${typeof this.ctx.config.title == 'boolean' ? this.ctx.config.title : '\'' + this.ctx.config.title + '\''}, }); $corrosion.init(); document.currentScript.remove();`
|
||||
},
|
||||
],
|
||||
attrs: [],
|
||||
}
|
||||
];
|
||||
}
|
||||
attributeRoute(data) {
|
||||
const match = this.attrs.find(entry => entry.tags == '*' && entry.attrs.includes(data.name) || entry.tags.includes(data.node.tagName.toLowerCase()) && entry.attrs.includes(data.name));
|
||||
return match ? match.handler : false;
|
||||
};
|
||||
srcset(val, config = {}) {
|
||||
return val.split(',').map(src => {
|
||||
const parts = src.trimStart().split(' ');
|
||||
if (parts[0]) parts[0] = this.ctx.url.wrap(parts[0], config);
|
||||
return parts.join(' ');
|
||||
}).join(', ');
|
||||
};
|
||||
unsrcset(val, config = {}) {
|
||||
return val.split(',').map(src => {
|
||||
const parts = src.trimStart().split(' ');
|
||||
if (parts[0]) parts[0] = this.ctx.url.unwrap(parts[0], config);
|
||||
return parts.join(' ');
|
||||
}).join(', ');
|
||||
};
|
||||
};
|
||||
|
||||
class Parse5Wrapper {
|
||||
constructor(node){
|
||||
this.raw = node || {
|
||||
attrs: [],
|
||||
childNodes: [],
|
||||
namespaceURI: '',
|
||||
nodeName: '',
|
||||
parentNode: {},
|
||||
tagName: '',
|
||||
};
|
||||
};
|
||||
hasAttribute(name){
|
||||
return this.raw.attrs.some(attr => attr.name == name);
|
||||
};
|
||||
removeAttribute(name){
|
||||
if (!this.hasAttribute(name)) return;
|
||||
this.raw.attrs.splice(this.raw.attrs.findIndex(attr => attr.name == name), 1);
|
||||
};
|
||||
setAttribute(name, val = ''){
|
||||
if (!name) return;
|
||||
this.removeAttribute(name);
|
||||
this.raw.attrs.push({
|
||||
name: name,
|
||||
value: val,
|
||||
});
|
||||
};
|
||||
getAttribute(name){
|
||||
return (this.raw.attrs.find(attr => attr.name == name) || { value: null }).value;
|
||||
};
|
||||
get textContent(){
|
||||
return (this.raw.childNodes.find(node => node.nodeName == '#text') || { value: '', }).value
|
||||
};
|
||||
set textContent(val){
|
||||
if (this.raw.childNodes.some(node => node.nodeName == '#text')) return this.raw.childNodes[this.raw.childNodes.findIndex(node => node.nodeName == '#text')].value = val;
|
||||
this.raw.childNodes.push({
|
||||
nodeName: '#text',
|
||||
value: val,
|
||||
});
|
||||
};
|
||||
get tagName(){
|
||||
return (this.raw.tagName || '').toUpperCase();
|
||||
};
|
||||
get nodeName(){
|
||||
return this.raw.nodeName;
|
||||
};
|
||||
get parentNode(){
|
||||
return this.raw.parentNode;
|
||||
};
|
||||
get childNodes(){
|
||||
return this.raw.childNodes || [];
|
||||
};
|
||||
get attrs() {
|
||||
return this.raw.attrs || [];
|
||||
};
|
||||
};
|
||||
|
||||
function iterate(ast, fn = (node = Parse5Wrapper.prototype) => null) {
|
||||
fn(new Parse5Wrapper(ast));
|
||||
if (ast.childNodes) for (let i in ast.childNodes) iterate(ast.childNodes[i], fn);
|
||||
};
|
||||
|
||||
module.exports = HTMLRewriter;
|
185
src/Corrosion/lib/js.js
Normal file
185
src/Corrosion/lib/js.js
Normal file
|
@ -0,0 +1,185 @@
|
|||
const { parse } = require('acorn-hammerhead');
|
||||
const { generate } = require('./esotope');
|
||||
|
||||
class JSRewriter {
|
||||
constructor(ctx) {
|
||||
this.parseOptions = {
|
||||
allowReturnOutsideFunction: true,
|
||||
allowImportExportEverywhere: true,
|
||||
ecmaVersion: 2021,
|
||||
};
|
||||
this.generationOptions = {
|
||||
format: {
|
||||
quotes: 'double',
|
||||
escapeless: true,
|
||||
compact: true,
|
||||
},
|
||||
};
|
||||
this.rewrite = ['location', 'parent', 'top'];
|
||||
this.map = [
|
||||
{
|
||||
type: 'MemberExpression',
|
||||
handler: (node, parent) => {
|
||||
let rewrite = false;
|
||||
if (parent.type == 'UnaryExpression' && parent.operator == 'delete') return;
|
||||
if (parent.type == 'NewExpression' && parent.callee == node) return;
|
||||
if (parent.type === 'CallExpression' && parent.callee === node) return;
|
||||
if (node.preventRewrite) return;
|
||||
switch(node.property.type) {
|
||||
case 'Identifier':
|
||||
//if (node.computed) rewrite = true;
|
||||
if (!node.computed && this.rewrite.includes(node.property.name)) {
|
||||
node.property = this.createLiteral(node.property.name);
|
||||
rewrite = true;
|
||||
};
|
||||
break;
|
||||
case 'Literal':
|
||||
if (this.rewrite.includes(node.property.name)) rewrite = true;
|
||||
break;
|
||||
case 'TemplateLiteral':
|
||||
rewrite = true;
|
||||
break;
|
||||
default:
|
||||
if (node.computed) rewrite = true;
|
||||
};
|
||||
if (rewrite) {
|
||||
let identifier = '$corrosionGet$m';
|
||||
let nodeToRewrite = node;
|
||||
const args = [
|
||||
node.object,
|
||||
node.property,
|
||||
];
|
||||
if (node.computed) args[1].preventRewrite = true;
|
||||
if (parent.type == 'AssignmentExpression' && parent.left == node) {
|
||||
identifier = '$corrosionSet$m';
|
||||
nodeToRewrite = parent;
|
||||
args.push(parent.right, this.createLiteral(parent.operator));
|
||||
};
|
||||
if (parent.type == 'CallExpression' && parent.callee == node) {
|
||||
identifier = '$corrosionCall$m';
|
||||
nodeToRewrite = parent;
|
||||
args.push(this.createArrayExpression(...parent.arguments))
|
||||
};
|
||||
if (parent.type == 'UpdateExpression') {
|
||||
identifier = '$corrosionSet$m';
|
||||
nodeToRewrite = parent;
|
||||
args.push(this.createLiteral(null), this.createLiteral(parent.operator));
|
||||
};
|
||||
Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier, }, args));
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Identifier',
|
||||
handler: (node, parent) => {
|
||||
if (parent.type == 'MemberExpression' && parent.property == node) return; // window.location;
|
||||
if (parent.type == 'LabeledStatement') return; // { location: null, };
|
||||
if (parent.type == 'VariableDeclarator' && parent.id == node) return;
|
||||
if (parent.type == 'Property' && parent.key == node) return;
|
||||
if (parent.type == 'MethodDefinition') return;
|
||||
if (parent.type == 'ClassDeclaration') return;
|
||||
if (parent.type == 'RestElement') return;
|
||||
if (parent.type == 'ExportSpecifier') return;
|
||||
if (parent.type == 'ImportSpecifier') return;
|
||||
if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression' || parent.type == 'ArrowFunctionExpression') && parent.params.includes(node)) return;
|
||||
if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression') && parent.id == node) return;
|
||||
if (parent.type == 'AssignmentPattern' && parent.left == node) return;
|
||||
if (!this.rewrite.includes(node.name)) return;
|
||||
if (node.preventRewrite) return;
|
||||
let identifier = '$corrosionGet$';
|
||||
let nodeToRewrite = node;
|
||||
const args = [
|
||||
this.createIdentifier(node.name, true),
|
||||
];
|
||||
|
||||
if (parent.type == 'AssignmentExpression' && parent.left == node) {
|
||||
identifier = '$corrosionSet$';
|
||||
nodeToRewrite = parent;
|
||||
args.push(parent.right);
|
||||
args.push(this.createLiteral(parent.operator));
|
||||
};
|
||||
|
||||
Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier }, args));
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'ImportDeclaration',
|
||||
handler: (node, parent, url) => {
|
||||
if (node.source.type != 'Literal' || !url) return;
|
||||
node.source = this.createLiteral(ctx.url.wrap(node.source.value, { base: url, }));
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'ImportExpression',
|
||||
handler: (node, parent) => {
|
||||
node.source = this.createCallExpression(this.createMemberExpression(this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('url')), this.createIdentifier('wrap')), [
|
||||
node.source,
|
||||
this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('meta')),
|
||||
]);
|
||||
},
|
||||
},
|
||||
];
|
||||
this.ctx = ctx;
|
||||
};
|
||||
process(source, url) {
|
||||
try {
|
||||
const ast = parse(source, this.parseOptions);
|
||||
this.iterate(ast, (node, parent) => {
|
||||
const fn = this.map.find(entry => entry.type == (node || {}).type);
|
||||
if (fn) fn.handler(node, parent, url);
|
||||
});
|
||||
return (url ? this.createHead(url) : '') + generate(ast, this.generationOptions);
|
||||
} catch(e) {
|
||||
return source;
|
||||
};
|
||||
};
|
||||
createHead(url) {
|
||||
return `
|
||||
if (!self.$corrosion && self.importScripts) {
|
||||
importScripts(location.origin + '${this.ctx.prefix}index.js');
|
||||
self.$corrosion = new Corrosion({ url: '${url}', codec: '${this.ctx.config.codec || 'plain'}', serviceWorker: true, window: self, prefix: '${this.ctx.prefix || '/service/'}', ws: ${this.ctx.config.ws || true}, cookies: ${this.ctx.config.cookies || false}, title: '${this.ctx.config.title}', }); $corrosion.init();
|
||||
};\n`;
|
||||
};
|
||||
iterate(ast, handler) {
|
||||
if (typeof ast != 'object' || !handler) return;
|
||||
walk(ast, null, handler);
|
||||
function walk(node, parent, handler) {
|
||||
if (typeof node != 'object' || !handler) return;
|
||||
handler(node, parent, handler);
|
||||
for (const child in node) {
|
||||
if (Array.isArray(node[child])) {
|
||||
node[child].forEach(entry => walk(entry, node, handler));
|
||||
} else {
|
||||
walk(node[child], node, handler);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
createCallExpression(callee, args) {
|
||||
return { type: 'CallExpression', callee, arguments: args, optional: false, };
|
||||
};
|
||||
createArrayExpression(...elements) {
|
||||
return {
|
||||
type: 'ArrayExpression',
|
||||
elements,
|
||||
};
|
||||
};
|
||||
createMemberExpression(object, property) {
|
||||
return {
|
||||
type: 'MemberExpression',
|
||||
object,
|
||||
property,
|
||||
};
|
||||
};
|
||||
createLiteral(value) {
|
||||
return {
|
||||
type: 'Literal',
|
||||
value,
|
||||
}
|
||||
};
|
||||
createIdentifier(name, preventRewrite) {
|
||||
return { type: 'Identifier', name, preventRewrite: preventRewrite || false, };
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = JSRewriter;
|
27
src/Corrosion/lib/rewrite.js
Normal file
27
src/Corrosion/lib/rewrite.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
const URLWrapper = require('./url');
|
||||
const CookieRewriter = require('./cookie');
|
||||
const CSSRewriter = require('./css');
|
||||
const HTMLRewriter = require('./html');
|
||||
const JSRewriter = require('./js');
|
||||
const defaultConfig = {
|
||||
prefix: '/service/',
|
||||
codec: 'plain',
|
||||
ws: true,
|
||||
cookie: true,
|
||||
title: 'Service',
|
||||
};
|
||||
|
||||
class Rewrite {
|
||||
constructor(config = defaultConfig) {
|
||||
this.config = Object.assign(defaultConfig, config);
|
||||
this.prefix = this.config.prefix;
|
||||
this.url = new URLWrapper(this.config || {});
|
||||
this.codec = this.url.codec;
|
||||
this.cookies = new CookieRewriter(this);
|
||||
this.css = new CSSRewriter(this);
|
||||
this.js = new JSRewriter(this);
|
||||
this.html = new HTMLRewriter(this);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Rewrite;
|
30109
src/Corrosion/lib/server/bundle.js
Normal file
30109
src/Corrosion/lib/server/bundle.js
Normal file
File diff suppressed because one or more lines are too long
22
src/Corrosion/lib/server/decompress.js
Normal file
22
src/Corrosion/lib/server/decompress.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
const zlib = require('zlib');
|
||||
|
||||
function decompress(ctx) {
|
||||
if (!ctx.body || !ctx.remoteResponse) return;
|
||||
try {
|
||||
switch(ctx.headers['content-encoding']) {
|
||||
case 'br':
|
||||
ctx.body = zlib.brotliDecompressSync(ctx.body);
|
||||
break;
|
||||
case 'gzip':
|
||||
ctx.body = zlib.gunzipSync(ctx.body);
|
||||
break;
|
||||
case 'deflate':
|
||||
ctx.body = zlib.inflateRawSync(ctx.body);
|
||||
break;
|
||||
};
|
||||
} catch(err) {};
|
||||
delete ctx.headers['content-encoding'];
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = decompress;
|
21
src/Corrosion/lib/server/gateway.js
Normal file
21
src/Corrosion/lib/server/gateway.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
function createGateway(ctx) {
|
||||
return function gateway(clientRequest, clientResponse) {
|
||||
const chunks = [];
|
||||
clientRequest.on('data', chunk =>
|
||||
chunks.push(chunk)
|
||||
).on('end', () => {
|
||||
const body = chunks.length ? Buffer.concat(chunks) : '';
|
||||
const query = clientRequest.method == 'POST' ? new URLSearchParams((body || '').toString()) : new URLSearchParams((clientRequest.url.split('?')[1] || ''));
|
||||
if (!query.has('url')) return clientResponse.end();
|
||||
const url = query.get('url');
|
||||
if (/https?:\/\/([a-zA-Z0-9\-\_])|([a-zA-Z0-9\-\_])\.([a-zA-Z])/.test(url)) {
|
||||
clientResponse.writeHead(301, { Location: ctx.url.wrap(/https?:\/\//.test(url) ? url : 'http://' + url) });
|
||||
clientResponse.end();
|
||||
} else {
|
||||
clientResponse.writeHead(301, { Location: ctx.url.wrap('https://www.google.com/search?q=' + url) });
|
||||
clientResponse.end();
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
module.exports = createGateway;
|
45
src/Corrosion/lib/server/headers.js
Normal file
45
src/Corrosion/lib/server/headers.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
function requestHeaders(ctx) {
|
||||
if (ctx.headers.cookie && ctx.rewrite.config.cookie) ctx.headers.cookie = ctx.rewrite.cookies.decode(ctx.headers.cookie, { url: ctx.url, });
|
||||
else delete ctx.headers.cookie;
|
||||
if (ctx.headers.origin) {
|
||||
if (ctx.clientSocket) {
|
||||
const params = new URLSearchParams((ctx.clientRequest.url.split('?')[1] || ''));
|
||||
delete ctx.headers.origin;
|
||||
delete ctx.headers.host;
|
||||
ctx.headers.Origin = params.get('origin') || ctx.url.origin;
|
||||
ctx.headers.Host = ctx.url.host;
|
||||
// Some websocket servers oddly only accept Host and Origin headers if the first character of the header is uppercase.
|
||||
} else {
|
||||
ctx.headers.origin = ctx.url.origin;
|
||||
};
|
||||
};
|
||||
|
||||
if (ctx.headers.referer) {
|
||||
try {
|
||||
ctx.headers.referer = new URL(ctx.rewrite.url.unwrap(ctx.headers.referer, { origin: ctx.origin, })).href;
|
||||
} catch(err) {
|
||||
ctx.headers.referer = ctx.url.href;
|
||||
};
|
||||
};
|
||||
for (let header in ctx.headers) {
|
||||
if (header.startsWith('cf-') || header.startsWith('x-forwarded') || header == 'cdn-loop') delete ctx.headers[header];
|
||||
};
|
||||
ctx.headers.host = ctx.url.host;
|
||||
return true;
|
||||
};
|
||||
|
||||
function responseHeaders(ctx) {
|
||||
if (ctx.headers.location) ctx.headers.location = ctx.rewrite.url.wrap(ctx.headers.location, { base: ctx.url, origin: ctx.origin, });
|
||||
if (ctx.headers['set-cookie']) ctx.headers['set-cookie'] = ctx.rewrite.cookies.encode(ctx.headers['set-cookie'], { domain: ctx.clientRequest.headers.host, url: ctx.url, });
|
||||
[
|
||||
'content-length',
|
||||
'content-security-policy',
|
||||
'content-security-policy-report-only',
|
||||
'strict-transport-security',
|
||||
'x-frame-options'
|
||||
].forEach(name => delete ctx.headers[name]);
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.requestHeaders = requestHeaders;
|
||||
exports.responseHeaders = responseHeaders;
|
60
src/Corrosion/lib/server/index.js
Normal file
60
src/Corrosion/lib/server/index.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
const webpack = require('webpack');
|
||||
const createWebSocketProxy = require('./upgrade');
|
||||
const createRequestProxy = require('./request');
|
||||
const createGateway = require('./gateway');
|
||||
const middleware = {
|
||||
...require('./headers'),
|
||||
...require('./middleware'),
|
||||
decompress: require('./decompress'),
|
||||
rewriteBody: require('./rewrite-body'),
|
||||
};
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const defaultConfig = {
|
||||
prefix: '/service/',
|
||||
codec: 'plain',
|
||||
ws: true,
|
||||
cookie: true,
|
||||
title: 'Service',
|
||||
requestMiddleware: [],
|
||||
responseMiddleware: [],
|
||||
standardMiddleware: true,
|
||||
};
|
||||
|
||||
class Corrosion extends require('../rewrite') {
|
||||
constructor(config = defaultConfig) {
|
||||
super(Object.assign(defaultConfig, config));
|
||||
if (this.config.standardMiddleware) {
|
||||
this.config.requestMiddleware.unshift(
|
||||
middleware.requestHeaders,
|
||||
);
|
||||
this.config.responseMiddleware.unshift(
|
||||
middleware.responseHeaders,
|
||||
middleware.decompress,
|
||||
middleware.rewriteBody,
|
||||
);
|
||||
};
|
||||
this.gateway = createGateway(this);
|
||||
this.upgrade = createWebSocketProxy(this);
|
||||
this.request = createRequestProxy(this);
|
||||
if (!fs.existsSync(path.join(__dirname, 'bundle.js'))) this.bundleScripts();
|
||||
};
|
||||
bundleScripts() {
|
||||
webpack({
|
||||
mode: 'none',
|
||||
entry: path.join(__dirname, '../../lib/browser/index.js'),
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: 'bundle.js',
|
||||
}
|
||||
}, err =>
|
||||
console.log(err || 'Bundled scripts')
|
||||
);
|
||||
};
|
||||
get script() {
|
||||
return fs.existsSync(path.join(__dirname, 'bundle.js')) ? fs.readFileSync(path.join(__dirname, 'bundle.js')) : 'Client script is still compiling or has crashed.'
|
||||
};
|
||||
};
|
||||
|
||||
Corrosion.middleware = middleware;
|
||||
module.exports = Corrosion;
|
14
src/Corrosion/lib/server/middleware.js
Normal file
14
src/Corrosion/lib/server/middleware.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
function address(arr = []) {
|
||||
return function (ctx) {
|
||||
ctx.address = arr[Math.floor(Math.random() * arr.length)];
|
||||
};
|
||||
};
|
||||
|
||||
function blacklist(arr = [], page = '') {
|
||||
return function (ctx) {
|
||||
if (arr.includes(ctx.url.hostname)) ctx.clientResponse.end(page);
|
||||
};
|
||||
};
|
||||
|
||||
exports.address = address;
|
||||
exports.blacklist = blacklist;
|
83
src/Corrosion/lib/server/request.js
Normal file
83
src/Corrosion/lib/server/request.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
const http = require('http');
|
||||
const https = require('https');
|
||||
function createRequestProxy(ctx) {
|
||||
return async function onRequest(clientRequest, clientResponse) {
|
||||
try {
|
||||
if (new RegExp(`^${ctx.prefix}gateway/?`).test(clientRequest.url)) return ctx.gateway(clientRequest, clientResponse);
|
||||
if (clientRequest.url.startsWith(`${ctx.prefix}index.js`)) {
|
||||
clientResponse.setHeader('Content-Type', 'application/javascript');
|
||||
return clientResponse.end(ctx.script);
|
||||
};
|
||||
const urlData = ctx.url.unwrap(clientRequest.url, { flags: true, leftovers: true, });
|
||||
urlData.value = new URL(urlData.value);
|
||||
const requestContext = {
|
||||
url: urlData.value,
|
||||
flags: urlData.flags,
|
||||
origin: (clientRequest.socket.encrypted ? 'https://' : 'http://') + clientRequest.headers.host,
|
||||
body: await getChunks(clientRequest),
|
||||
headers: { ...clientRequest.headers },
|
||||
method: clientRequest.method,
|
||||
rewrite: ctx,
|
||||
agent: new (urlData.value.protocol == 'https:' ? https : http).Agent({
|
||||
rejectUnauthorized: false,
|
||||
}),
|
||||
address: null,
|
||||
clientRequest,
|
||||
clientResponse,
|
||||
};
|
||||
for (let i in ctx.config.requestMiddleware) ctx.config.requestMiddleware[i](requestContext);
|
||||
if (clientResponse.writableEnded) return;
|
||||
(requestContext.url.protocol == 'https:' ? https : http).request({
|
||||
headers: requestContext.headers,
|
||||
method: requestContext.method,
|
||||
hostname: requestContext.url.hostname,
|
||||
port: requestContext.url.port,
|
||||
path: requestContext.url.pathname + requestContext.url.search,
|
||||
agent: requestContext.agent,
|
||||
localAddress: requestContext.address,
|
||||
rejectUnauthorized: false,
|
||||
}, async remoteResponse => {
|
||||
const responseContext = {
|
||||
url: requestContext.url,
|
||||
flags: requestContext.flags,
|
||||
origin: requestContext.origin,
|
||||
body: await getChunks(remoteResponse),
|
||||
headers: { ...remoteResponse.headers },
|
||||
statusCode: remoteResponse.statusCode,
|
||||
agent: requestContext.agent,
|
||||
address: requestContext.address,
|
||||
method: requestContext.method,
|
||||
rewrite: ctx,
|
||||
clientRequest,
|
||||
clientResponse,
|
||||
remoteResponse,
|
||||
};
|
||||
for (let i in ctx.config.responseMiddleware) ctx.config.responseMiddleware[i](responseContext);
|
||||
if (clientResponse.writableEnded) return;
|
||||
clientResponse.writeHead(responseContext.statusCode, responseContext.headers);
|
||||
clientResponse.end((responseContext.body || ''));
|
||||
}).on('error', err => {
|
||||
if (clientResponse.writableEnded) return;
|
||||
clientResponse.setHeader('Content-Type', 'text/plain');
|
||||
clientResponse.end(err.toString())
|
||||
}).end(requestContext.body);
|
||||
} catch(err) {
|
||||
if (clientResponse.writableEnded) return;
|
||||
clientResponse.setHeader('Content-Type', 'text/plain');
|
||||
clientResponse.end(err.toString());
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function getChunks(stream) {
|
||||
const chunks = [];
|
||||
return new Promise(resolve =>
|
||||
stream.on('data', chunk =>
|
||||
chunks.push(chunk)
|
||||
).on('end', () =>
|
||||
chunks.length ? resolve(Buffer.concat(chunks)) : resolve(null)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = createRequestProxy;
|
36
src/Corrosion/lib/server/rewrite-body.js
Normal file
36
src/Corrosion/lib/server/rewrite-body.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
const route = [
|
||||
{
|
||||
types: ['text/html'],
|
||||
handler: 'html',
|
||||
},
|
||||
{
|
||||
types: ['text/css'],
|
||||
handler: 'css',
|
||||
},
|
||||
{
|
||||
types: ['application/javascript', 'application/x-javascript', 'text/javascript', 'text/x-javascript'],
|
||||
handler: 'js',
|
||||
},
|
||||
]
|
||||
function rewriteBody(ctx) {
|
||||
if (!ctx.body || !ctx.remoteResponse || ctx.flags.includes('xhr')) return;
|
||||
const meta = {
|
||||
base: ctx.url,
|
||||
origin: ctx.origin,
|
||||
};
|
||||
const data = route.find(entry => ctx.flags == entry.handler) || route.find(entry => entry.types.includes((ctx.headers['content-type'] || '').split(';')[0])) || {};
|
||||
|
||||
switch(data.handler) {
|
||||
case 'html':
|
||||
ctx.body = ctx.rewrite.html.process(ctx.body.toString(), { ...meta, document: true });
|
||||
break;
|
||||
case 'css':
|
||||
ctx.body = ctx.rewrite.css.process(ctx.body.toString(), meta);
|
||||
break;
|
||||
case 'js':
|
||||
ctx.body = ctx.rewrite.js.process(ctx.body.toString(), ctx.url);
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = rewriteBody;
|
56
src/Corrosion/lib/server/upgrade.js
Normal file
56
src/Corrosion/lib/server/upgrade.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
const http = require('http');
|
||||
const https = require('https');
|
||||
|
||||
function createWebSocketProxy(ctx) {
|
||||
return function onUpgrade(clientRequest, clientSocket, clientHead) {
|
||||
try {
|
||||
const urlData = ctx.url.unwrap(clientRequest.url, { flags: true, });
|
||||
urlData.value = new URL(urlData.value);
|
||||
const requestContext = {
|
||||
url: urlData.value,
|
||||
flags: urlData.flags,
|
||||
body: null,
|
||||
headers: { ...clientRequest.headers },
|
||||
method: clientRequest.method,
|
||||
rewrite: ctx,
|
||||
agent: new (urlData.value.protocol == 'https:' ? https : http).Agent({
|
||||
rejectUnauthorized: false,
|
||||
}),
|
||||
address: null,
|
||||
clientRequest,
|
||||
clientSocket,
|
||||
clientHead,
|
||||
};
|
||||
ctx.config.requestMiddleware.forEach(fn => fn(requestContext));
|
||||
(requestContext.url.protocol == 'https:' ? https : http).request({
|
||||
headers: requestContext.headers,
|
||||
method: requestContext.method,
|
||||
hostname: requestContext.url.hostname,
|
||||
port: requestContext.url.port,
|
||||
path: requestContext.url.pathname + requestContext.url.search,
|
||||
agent: requestContext.agent,
|
||||
localAddress: requestContext.address,
|
||||
}).on('upgrade', (remoteResponse, remoteSocket, remoteHead) => {
|
||||
let handshake = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n';
|
||||
for (let key in remoteResponse.headers) {
|
||||
handshake += `${key}: ${remoteResponse.headers[key]}\r\n`;
|
||||
};
|
||||
handshake += '\r\n';
|
||||
clientSocket.write(handshake);
|
||||
clientSocket.write(remoteHead);
|
||||
remoteSocket.on('close', () => clientSocket.end());
|
||||
clientSocket.on('close', () => remoteSocket.end());
|
||||
remoteSocket.on('error', () => clientSocket.end());
|
||||
clientSocket.on('error', () => remoteSocket.end());
|
||||
remoteSocket.pipe(clientSocket);
|
||||
clientSocket.pipe(remoteSocket);
|
||||
}).on('error', () => {
|
||||
clientSocket.end()
|
||||
}).end();
|
||||
} catch(err) {
|
||||
clientSocket.end();
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = createWebSocketProxy;
|
34
src/Corrosion/lib/url.js
Normal file
34
src/Corrosion/lib/url.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
const codec = require('./codec');
|
||||
const defaultConfig = {
|
||||
prefix: '/service/',
|
||||
codec: 'plain'
|
||||
};
|
||||
|
||||
class URLWrapper {
|
||||
constructor(config = defaultConfig) {
|
||||
this.prefix = config.prefix || defaultConfig.prefix;
|
||||
this.codec = codec[config.codec || 'plain'] || codec['plain'];
|
||||
this.regex = /^(#|about:|data:|blob:|mailto:|javascript:)/;
|
||||
};
|
||||
wrap(val, config = {}) {
|
||||
if (!val || this.regex.test(val)) return val;
|
||||
let flags = '';
|
||||
(config.flags || []).forEach(flag => flags += `${flag}_/`);
|
||||
if (config.base) try {
|
||||
if (!['http:', 'https:', 'ws:', 'wss:'].includes(new URL(val, config.base).protocol)) return val;
|
||||
} catch(e) {
|
||||
return val;
|
||||
};
|
||||
return (config.origin || '') + this.prefix + flags + this.codec.encode(config.base ? new URL(val, config.base) : val) + '/';
|
||||
};
|
||||
unwrap(val, config = {}) {
|
||||
if (!val || this.regex.test(val)) return val;
|
||||
let processed = val.slice((config.origin || '').length + this.prefix.length);
|
||||
const flags = ('/' + processed).match(/(?<=\/)(.*?)(?=_\/)/g) || [];
|
||||
flags.forEach(flag => processed = processed.slice(`${flag}_/`.length));
|
||||
let [ url, leftovers ] = processed.split(/\/(.+)?/);
|
||||
return config.flags ? { value: this.codec.decode((url || '')) + (config.leftovers && leftovers ? leftovers : ''), flags } : this.codec.decode((url || '')) + (config.leftovers && leftovers ? leftovers : '');
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = URLWrapper;
|
47
src/Corrosion/package.json
Normal file
47
src/Corrosion/package.json
Normal file
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"_from": "corrosion",
|
||||
"_id": "corrosion@1.0.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-jRKoOTWBmpylgOARW6vsVE4DaSsg9Qnt0E7c7Rp5q2kFAlYrr8KrhjUWJrKmlHL8wbLi8EUdlNId4vvL0PHGdA==",
|
||||
"_location": "/corrosion",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "corrosion",
|
||||
"name": "corrosion",
|
||||
"escapedName": "corrosion",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/corrosion/-/corrosion-1.0.0.tgz",
|
||||
"_shasum": "22aa376a6bd72720f3994f8e091b880b87c02cc4",
|
||||
"_spec": "corrosion",
|
||||
"_where": "C:\\Users\\Not A Porxy\\Documents\\HolyUnblockerWorkspace\\HolyUB",
|
||||
"author": "",
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"acorn-hammerhead": "^0.5.0",
|
||||
"css-tree": "^1.1.3",
|
||||
"esotope-hammerhead": "^0.6.1",
|
||||
"parse5": "^6.0.1",
|
||||
"webpack": "^5.46.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Titanium Networks main web proxy.\r Successor to [Alloy](https://github.com/titaniumnetwork-dev/alloy)\r # Installation:\r ```\r npm i corrosion\r ```",
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"license": "ISC",
|
||||
"main": "lib/server/index.js",
|
||||
"name": "corrosion",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue