├── .gitignore
├── .replit
├── LICENSE
├── README.md
├── app.js
├── app.json
├── config.json
├── lib
├── browser
│ ├── document.js
│ ├── history.js
│ ├── http.js
│ ├── index.js
│ ├── location.js
│ └── worker.js
├── codec.js
├── cookie-parser.js
├── cookie.js
├── css.js
├── esotope.js
├── html.js
├── js.js
├── rewrite.js
├── server
│ ├── bundle.js
│ ├── decompress.js
│ ├── gateway.js
│ ├── headers.js
│ ├── index.js
│ ├── middleware.js
│ ├── request.js
│ ├── rewrite-body.js
│ └── upgrade.js
└── url.js
├── package-lock.json
├── package.json
└── public
├── 404.html
├── css
└── style.css
├── img
└── logo.png
├── index.html
└── js
├── go.js
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.replit:
--------------------------------------------------------------------------------
1 | language = "nodejs"
2 | run = "npm start"
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Fog Network
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deprecated!
2 | This project is deprecated! I am no longer maintaining this project. Why not check out [Metallic](https://github.com/Metallic-Web/Metallic)?
3 |
4 | # Shadow
5 | Shadow is a simple yet stunning service built to access any website
6 |
7 | Join our [discord](https://discord.gg/yk33HZSZkU) for more links
8 |
9 |
10 |
11 |
12 |
13 | ### Features
14 | - Incredible single page design
15 | - Simple to use
16 | - Fully working omnibox
17 | - Easy tab cloaking
18 | - Quick links for easy access
19 |
20 | ### Setup
21 |
22 | ### Locally
23 |
24 | ```sh
25 | git clone https://github.com/FogNetwork/Shadow
26 |
27 | cd Shadow
28 |
29 | npm install
30 |
31 | npm start
32 | ```
33 |
34 | ### Deploy
35 |
36 | Click one of the buttons above and follow the steps
37 |
38 | ### Configuration
39 |
40 | ```json
41 | {
42 | "port": "8080",
43 | "prefix": "/service/",
44 | "codec": "xor"
45 | }
46 | ```
47 |
48 | `"port": "8080"` - Changes the port
49 |
50 | `"prefix": "/service/"` - Changes the proxy prefix
51 |
52 | `"codec": "xor"` - Changes the proxy encoding (plain, base64 or xor)
53 |
54 | ### Support
55 |
56 | Nebelung - [Nebelung#1335](https://discord.com/users/887118260963782686)
57 |
58 | ### Proxy
59 |
60 | [Corrosion](https://github.com/titaniumnetwork-dev/Corrosion)
61 |
62 | [Modified Corrosion](https://github.com/BinBashBanana/Corrosion-Heroku)
63 |
64 | ### Credits
65 |
66 | [Nebelung](https://github.com/Nebelung-Dev) - Owner and Main Developer
67 |
68 | [EnderKingJ](https://github.com/EnderKingJ) - Proxy Developer
69 |
70 | [Quite A Fancy Emerald](https://github.com/QuiteAFancyEmerald) - Holy Unblocker King
71 |
72 | [Caracal.js](https://github.com/caracal-js) - Proxy Developer
73 |
74 | [MikeLime](https://github.com/MikeLime-dev) - Developer
75 |
76 | [Binary Person](https://github.com/binary-person) - Creator of Womginx
77 |
78 | [Shirt](https://github.com/shirt-dev) - Proxy Developer
79 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © Fog Network
3 | Made by Nebelung
4 | MIT license: https://opensource.org/licenses/MIT
5 | */
6 |
7 | const express = require("express")
8 | const app = express()
9 | const fetch = require("node-fetch")
10 | const config = require("./config.json")
11 | const port = process.env.PORT || config.port
12 | const Corrosion = require("./lib/server")
13 |
14 | const proxy = new Corrosion({
15 | prefix: config.prefix,
16 | codec: config.codec,
17 | title: "Shadow",
18 | forceHttps: true,
19 | requestMiddleware: [
20 | Corrosion.middleware.blacklist([
21 | "accounts.google.com",
22 | ], "Page is blocked"),
23 | ]
24 | });
25 |
26 | proxy.bundleScripts();
27 |
28 | app.use(express.static("./public", {
29 | extensions: ["html"]
30 | }));
31 |
32 | app.get("/", function(req, res){
33 | res.sendFile("index.html", {root: "./public"});
34 | });
35 |
36 | app.get("/suggestions", function(req, res){
37 | async function getsuggestions() {
38 | var term = req.query.q || "";
39 | var response = await fetch("https://duckduckgo.com/ac/?q=" + term + "&type=list");
40 | var result = await response.json();
41 | var suggestions = result[1]
42 | res.send(suggestions)
43 | }
44 | getsuggestions()
45 | });
46 |
47 | app.use(function (req, res) {
48 | if (req.url.startsWith(proxy.prefix)) {
49 | proxy.request(req,res);
50 | } else {
51 | res.status(404).sendFile("404.html", {root: "./public"});
52 | }
53 | })
54 |
55 | app.listen(port, () => {
56 | console.log(`Shadow is running at localhost:${port}`)
57 | })
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Shadow",
3 | "description": "Shadow is a simple yet stunning service built to access any website",
4 | "repository": "https://github.com/FogNetwork/Shadow",
5 | "logo": "https://raw.githubusercontent.com/FogNetwork/Shadow/main/public/img/logo.png"
6 | }
7 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": "8080",
3 | "prefix": "/service/",
4 | "codec": "xor"
5 | }
--------------------------------------------------------------------------------
/lib/browser/document.js:
--------------------------------------------------------------------------------
1 | function createDocumentRewriter(ctx) {
2 | return function rewriteDocument() {
3 | if (ctx.serviceWorker) return;
4 | const {
5 | HTMLMediaElement,
6 | HTMLScriptElement,
7 | HTMLAudioElement,
8 | HTMLVideoElement,
9 | HTMLInputElement,
10 | HTMLEmbedElement,
11 | HTMLTrackElement,
12 | HTMLAnchorElement,
13 | HTMLIFrameElement,
14 | HTMLAreaElement,
15 | HTMLLinkElement,
16 | HTMLBaseElement,
17 | HTMLFormElement,
18 | HTMLImageElement,
19 | HTMLSourceElement,
20 | } = ctx.window;
21 | const cookie = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'cookie');
22 | const domain = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'domain');
23 | const title = Object.getOwnPropertyDescriptor(ctx.window.Document.prototype, 'title');
24 | const baseURI = Object.getOwnPropertyDescriptor(ctx.window.Node.prototype, 'baseURI');
25 | const cookieEnabled = Object.getOwnPropertyDescriptor(ctx.window.Navigator.prototype, 'cookieEnabled');
26 | let spoofTitle = '';
27 | let spoofDomain = ctx.location.hostname;
28 |
29 | if (ctx.window.Document.prototype.write) {
30 | ctx.window.Document.prototype.write = new Proxy(ctx.window.Document.prototype.write, {
31 | apply: (target, that , args) => {
32 | if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
33 | return Reflect.apply(target, that, args);
34 | },
35 | });
36 | };
37 | if (ctx.window.Document.prototype.hasOwnProperty('cookie')) {
38 | Object.defineProperty(ctx.window.Document.prototype, 'cookie', {
39 | get: new Proxy(cookie.get, {
40 | apply: (target, that, args) => {
41 | const cookies = Reflect.apply(target, that, args);
42 | return ctx.config.cookie ? ctx.cookies.decode(cookies, ctx.meta) : '';
43 | },
44 | }),
45 | set: new Proxy(cookie.set, {
46 | apply: (target, that, [ val ]) => {
47 | return Reflect.apply(target, that, [ ctx.config.cookie ? ctx.cookies.encode(val, ctx.meta) : '' ]);
48 | },
49 | }),
50 | });
51 | };
52 | if (ctx.window.Document.prototype.writeln) {
53 | ctx.window.Document.prototype.writeln = new Proxy(ctx.window.Document.prototype.writeln, {
54 | apply: (target, that , args) => {
55 | if (args.length) args = [ ctx.html.process(args.join(''), ctx.meta) ];
56 | return Reflect.apply(target, that, args);
57 | },
58 | });
59 | };
60 | if (ctx.window.Element.prototype.setAttribute) {
61 | ctx.window.Element.prototype.setAttribute = new Proxy(ctx.window.Element.prototype.setAttribute, {
62 | apply: (target, that, args) => {
63 | if (args[0] && args[1]) {
64 | const handler = ctx.html.attributeRoute({
65 | name: args[0],
66 | value: args[1],
67 | node: that,
68 | });
69 | switch(handler) {
70 | case 'url':
71 | Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
72 | //if (that.tagName == 'SCRIPT' && args[0] == 'src') flags.push('js');
73 | args[1] = ctx.url.wrap(args[1], ctx.meta);
74 | break;
75 | case 'srcset':
76 | Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
77 | args[1] = ctx.html.srcset(args[1], ctx.meta);
78 | break;
79 | case 'css':
80 | Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
81 | args[1] = ctx.css.process(args[1], { ...ctx.meta, context: 'declarationList' });
82 | break;
83 | case 'html':
84 | Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
85 | args[1] = ctx.html.process(args[1], ctx.meta);
86 | break;
87 | case 'delete':
88 | return Reflect.apply(target, that, [`corrosion-${args[0]}`, args[1]]);
89 | };
90 | };
91 | return Reflect.apply(target, that, args);
92 | },
93 | });
94 | };
95 | if (ctx.window.Element.prototype.getAttribute) {
96 | ctx.window.Element.prototype.getAttribute = new Proxy(ctx.window.Element.prototype.getAttribute, {
97 | apply: (target, that, args) => {
98 | if (args[0] && that.hasAttribute(`corrosion-${args[0]}`)) args[0] = `corrosion-${args[0]}`;
99 | return Reflect.apply(target, that, args);
100 | },
101 | });
102 | };
103 | ctx.window.CSSStyleDeclaration.prototype.setProperty = new Proxy(ctx.window.CSSStyleDeclaration.prototype.setProperty, {
104 | apply: (target, that, args) => {
105 | if (args[1]) args[1] = ctx.css.process(args[1], { context: 'value', ...ctx.meta, });
106 | return Reflect.apply(target, that, args);
107 | },
108 | });
109 | if (ctx.window.Audio) {
110 | ctx.window.Audio = new Proxy(ctx.window.Audio, {
111 | construct: (target, args) => {
112 | if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
113 | return Reflect.construct(target, args);
114 | },
115 | });
116 | };
117 | [
118 | 'innerHTML',
119 | 'outerHTML',
120 | ].forEach(html => {
121 | const descriptor = Object.getOwnPropertyDescriptor(ctx.window.Element.prototype, html);
122 | Object.defineProperty(ctx.window.Element.prototype, html, {
123 | get: new Proxy(descriptor.get, {
124 | apply: (target, that, args) => {
125 | const body = Reflect.apply(target, that, args);
126 | if (!body || html == 'innerHTML' && that.tagName == 'SCRIPT') return body;
127 | return ctx.html.source(body, ctx.meta);
128 | },
129 | }),
130 | set: new Proxy(descriptor.set, {
131 | apply(target, that, [ val ]) {
132 | return Reflect.apply(target, that, [ val ? ctx.html.process(val.toString(), ctx.meta) : val, ]);
133 | },
134 | }),
135 | });
136 | });
137 | [
138 | ['background', 'background'],
139 | ['backgroundImage', 'background-image'],
140 | ['listStyleImage', 'list-style-image'],
141 | ].forEach(([key, cssProperty]) => {
142 | Object.defineProperty(ctx.window.CSS2Properties ? ctx.window.CSS2Properties.prototype : ctx.window.CSSStyleDeclaration.prototype, key, {
143 | get() {
144 | return this.getPropertyValue(cssProperty);
145 | },
146 | set(val) {
147 | return this.setProperty(cssProperty, val);
148 | },
149 | });
150 | });
151 | Object.defineProperty(ctx.window.Document.prototype, 'domain', {
152 | get: new Proxy(domain.get, {
153 | apply: () => spoofDomain,
154 | }),
155 | set: new Proxy(domain.set, {
156 | apply: (target, that, [ val ]) => {
157 | if (!val.toString().endsWith(ctx.location.hostname.split('.').slice(-2).join('.'))) return Reflect.apply(target, that, ['']);
158 | return spoofDomain = val;
159 | },
160 | }),
161 | });
162 | if (ctx.config.title) Object.defineProperty(ctx.window.Document.prototype, 'title', {
163 | get: new Proxy(title.get, {
164 | apply: () => spoofTitle,
165 | }),
166 | set: new Proxy(title.set, {
167 | apply: (target, that, [ val ]) => spoofTitle = val,
168 | }),
169 | });
170 | Object.defineProperty(ctx.window.Navigator.prototype, 'cookieEnabled', {
171 | get: new Proxy(cookieEnabled.get, {
172 | apply: () => ctx.config.cookie,
173 | }),
174 | });
175 | Object.defineProperty(ctx.window.Node.prototype, 'baseURI', {
176 | get: new Proxy(baseURI.get, {
177 | apply: (target, that, args) => {
178 | const val = Reflect.apply(target, that, args);
179 | return val.startsWith(ctx.meta.origin) ? ctx.url.unwrap(val, ctx.meta) : val;
180 | },
181 | }),
182 | });
183 | [
184 | {
185 | elements: [ HTMLScriptElement, HTMLMediaElement, HTMLImageElement, HTMLAudioElement, HTMLVideoElement, HTMLInputElement, HTMLEmbedElement, HTMLIFrameElement, HTMLTrackElement, HTMLSourceElement],
186 | properties: ['src'],
187 | handler: 'url',
188 | },
189 | {
190 | elements: [ HTMLFormElement ],
191 | properties: ['action'],
192 | handler: 'url',
193 | },
194 | {
195 | elements: [ HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement, HTMLBaseElement ],
196 | properties: ['href'],
197 | handler: 'url',
198 | },
199 | {
200 | elements: [ HTMLImageElement, HTMLSourceElement ],
201 | properties: ['srcset'],
202 | handler: 'srcset',
203 | },
204 | {
205 | elements: [ HTMLScriptElement ],
206 | properties: ['integrity'],
207 | handler: 'delete',
208 | },
209 | {
210 | elements: [ HTMLIFrameElement ],
211 | properties: ['contentWindow'],
212 | handler: 'window',
213 | },
214 | ].forEach(entry => {
215 | entry.elements.forEach(element => {
216 | if (!element) return;
217 | entry.properties.forEach(property => {
218 | if (!element.prototype.hasOwnProperty(property)) return;
219 | const descriptor = Object.getOwnPropertyDescriptor(element.prototype, property);
220 | Object.defineProperty(element.prototype, property, {
221 | get: descriptor.get ? new Proxy(descriptor.get, {
222 | apply: (target, that, args) => {
223 | let val = Reflect.apply(target, that, args);
224 | let flags = [];
225 | switch(entry.handler) {
226 | case 'url':
227 | //if (that.tagName == 'SCRIPT' && property == 'src') flags.push('js');
228 | val = ctx.url.unwrap(val, ctx.meta);
229 | break;
230 | case 'srcset':
231 | val = ctx.html.unsrcset(val, ctx.meta);
232 | break;
233 | case 'delete':
234 | val = that.getAttribute(`corrosion-${property}`);
235 | break;
236 | case 'window':
237 | try {
238 | if (!val.$corrosion) {
239 | val.$corrosion = new ctx.constructor({ ...ctx.config, window: val, });
240 | val.$corrosion.init();
241 | val.$corrosion.meta = ctx.meta;
242 | };
243 | } catch(e) {};
244 | };
245 | return val;
246 | },
247 | }) : undefined,
248 | set: descriptor.set ? new Proxy(descriptor.set, {
249 | apply(target, that, [ val ]) {
250 | let newVal = val;
251 | switch(entry.handler) {
252 | case 'url':
253 | newVal = ctx.url.wrap(newVal, ctx.meta);
254 | break;
255 | case 'srcset':
256 | newVal = ctx.html.srcset(newVal, ctx.meta);
257 | break;
258 | case 'delete':
259 | that.setAttribute(property, newVal);
260 | return newVal;
261 | };
262 | return Reflect.apply(target, that, [ newVal ]);
263 | },
264 | }) : undefined,
265 | });
266 | });
267 | });
268 | });
269 | };
270 | };
271 | module.exports = createDocumentRewriter;
--------------------------------------------------------------------------------
/lib/browser/history.js:
--------------------------------------------------------------------------------
1 | function createHistoryRewriter(ctx) {
2 | return function rewriteHistory() {
3 | if (ctx.serviceWorker) return;
4 | if (ctx.window.History.prototype.pushState) {
5 | ctx.window.History.prototype.pushState = new Proxy(ctx.window.History.prototype.pushState, {
6 | apply: (target, that, args) => {
7 | if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
8 | const ret = Reflect.apply(target, that, args);
9 | ctx.updateLocation();
10 | return ret;
11 | },
12 | });
13 | };
14 | if (ctx.window.History.prototype.replaceState) {
15 | ctx.window.History.prototype.replaceState = new Proxy(ctx.window.History.prototype.replaceState, {
16 | apply: (target, that, args) => {
17 | if (args[2]) args[2] = ctx.url.wrap(args[2], ctx.meta);
18 | const ret = Reflect.apply(target, that, args);
19 | ctx.updateLocation();
20 | return ret;
21 | },
22 | });
23 | };
24 | };
25 | };
26 | module.exports = createHistoryRewriter;
--------------------------------------------------------------------------------
/lib/browser/http.js:
--------------------------------------------------------------------------------
1 | function createHttpRewriter(ctx = {}) {
2 | return function rewriteHttp() {
3 | if (ctx.window.Request) {
4 | const requestURL = Object.getOwnPropertyDescriptor(ctx.window.Request.prototype, 'url');
5 | ctx.window.Request = new Proxy(ctx.window.Request, {
6 | construct(target, args) {
7 | if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], })
8 | return Reflect.construct(target, args);
9 | },
10 | });
11 | Object.defineProperty(ctx.window.Request.prototype, 'url', {
12 | get: new Proxy(requestURL.get, {
13 | apply: (target, that, args) => {
14 | var url = Reflect.apply(target, that, args);
15 | return url ? ctx.url.unwrap(url, ctx.meta) : url;
16 | },
17 | }),
18 | });
19 | };
20 | if (ctx.window.Response) {
21 | const responseURL = Object.getOwnPropertyDescriptor(ctx.window.Response.prototype, 'url');
22 | Object.defineProperty(ctx.window.Response.prototype, 'url', {
23 | get: new Proxy(responseURL.get, {
24 | apply: (target, that, args) => {
25 | var url = Reflect.apply(target, that, args);
26 | return url ? ctx.url.unwrap(url, ctx.meta) : url;
27 | },
28 | }),
29 | });
30 | };
31 | if (ctx.window.open) {
32 | ctx.window.open = new Proxy(ctx.window.open, {
33 | apply: (target, that, args) => {
34 | if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
35 | return Reflect.apply(target, that, args)
36 | },
37 | });
38 | };
39 | if (ctx.window.fetch) {
40 | ctx.window.fetch = new Proxy(ctx.window.fetch, {
41 | apply: (target, that, args) => {
42 | if (args[0] instanceof ctx.window.Request) return Reflect.apply(target, that, args);
43 | if (args[0]) args[0] = ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
44 | return Reflect.apply(target, that, args);
45 | },
46 | });
47 | };
48 | if (ctx.window.Navigator && ctx.window.Navigator.prototype.sendBeacon) {
49 | ctx.window.Navigator.prototype.sendBeacon = new Proxy(ctx.window.Navigator.prototype.sendBeacon, {
50 | apply: (target, that, args) => {
51 | if (args[0]) ctx.url.wrap(args[0], { ...ctx.meta, flags: ['xhr'], });
52 | return Reflect.apply(target, that, args);
53 | },
54 | });
55 | };
56 | if (ctx.window.XMLHttpRequest) {
57 | const responseURL = Object.getOwnPropertyDescriptor(ctx.window.XMLHttpRequest.prototype, 'responseURL');
58 | ctx.window.XMLHttpRequest.prototype.open = new Proxy(ctx.window.XMLHttpRequest.prototype.open, {
59 | apply: (target, that, args) => {
60 | if (args[1]) args[1] = ctx.url.wrap(args[1], { ...ctx.meta, flags: ['xhr'], });
61 | return Reflect.apply(target, that, args);
62 | },
63 | });
64 | Object.defineProperty(ctx.window.XMLHttpRequest.prototype, 'responseURL', {
65 | get: new Proxy(responseURL.get, {
66 | apply: (target, that, args) => {
67 | const url = Reflect.apply(target, that, args);
68 | return url ? ctx.url.unwrap(url, ctx.meta) : url;
69 | },
70 | }),
71 | });
72 | };
73 | if (ctx.window.postMessage) {
74 | ctx.window.postMessage = new Proxy(ctx.window.postMessage, {
75 | apply: (target, that, args) => {
76 | if (!ctx.serviceWorker && args[1]) args[1] = ctx.meta.origin;
77 | return Reflect.apply(target, that, args);
78 | },
79 | });
80 | };
81 | if (ctx.window.WebSocket && ctx.config.ws) {
82 | ctx.window.WebSocket = new Proxy(ctx.window.WebSocket, {
83 | construct: (target, args) => {
84 | if (args[0]) args[0] = ctx.url.wrap(args[0].toString().replace('ws', 'http'), ctx.meta).replace('http', 'ws') + '?origin=' + ctx.location.origin;
85 | return Reflect.construct(target, args);
86 | },
87 | });
88 | };
89 | };
90 | };
91 |
92 | module.exports = createHttpRewriter;
--------------------------------------------------------------------------------
/lib/browser/index.js:
--------------------------------------------------------------------------------
1 | const createDocumentRewriter = require('./document');
2 | const createHistoryRewriter = require('./history');
3 | const createHttpRewriter = require('./http');
4 | const createLocation = require('./location');
5 | const createWorkerRewriter = require('./worker');
6 | const defaultConfig = {
7 | prefix: '/service/',
8 | codec: 'plain',
9 | ws: true,
10 | cookie: true,
11 | title: 'Service',
12 | serviceWorker: false,
13 | url: null,
14 | window: false,
15 | };
16 |
17 | class Corrosion extends require('../rewrite') {
18 | constructor(config = defaultConfig) {
19 | super(Object.assign(defaultConfig, config));
20 | const corrosion = this;
21 | if (!this.config.window) throw 'Corrosion Error: No window was given.';
22 | this.serviceWorker = this.config.serviceWorker || false;
23 | this.window = this.config.window;
24 | this.document = this.serviceWorker ? this.window.document : {};
25 | this._url = new URL((this.config.url || this.url.unwrap(this.window.location.href, { origin: this.window.location.origin, })));
26 | this.originalXhr = this.window.XMLHttpRequest;
27 | this.meta = {
28 | origin: this.window.location.origin,
29 | get base() {
30 | if (corrosion.serviceWorker) return corrosion._url;
31 | return corrosion.window.document.baseURI != corrosion.location.href ? new URL(corrosion.window.document.baseURI) : corrosion._url;
32 | },
33 | url: this._url,
34 | };
35 | this.location = createLocation(this, this._url);
36 | this.rewriteHttp = createHttpRewriter(this);
37 | this.rewriteDocument = createDocumentRewriter(this);
38 | this.rewriteHistory = createHistoryRewriter(this);
39 | this.rewriteWorker = createWorkerRewriter(this);
40 | if (!this.serviceWorker && this.window.document.currentScript) this.window.document.currentScript.remove();
41 | };
42 | get parent() {
43 | if (this.serviceWorker) return false;
44 | try {
45 | return this.window.parent.$corrosion ? this.window.parent : this.window;
46 | } catch(e) {
47 | return this.window;
48 | };
49 | };
50 | get top() {
51 | if (this.serviceWorker) return false;
52 | try {
53 | return this.window.top.$corrosion ? this.window.top : this.parent;
54 | } catch(e) {
55 | return this.parent;
56 | };
57 | };
58 | init() {
59 | this.rewriteHttp();
60 | this.rewriteDocument();
61 | this.rewriteHistory();
62 | this.rewriteWorker();
63 | this.window.Location = createLocation.Location;
64 | this.window.$corrosionGet$ = this.get$.bind(this);
65 | this.window.$corrosionSet$ = this.set$.bind(this);
66 | this.window.$corrosionGet$m = this.get$m.bind(this);
67 | this.window.$corrosionSet$m = this.set$m.bind(this);
68 | this.window.$corrosionCall$m = this.call$m.bind(this);
69 | };
70 | get$m(obj, key) {
71 | if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
72 | return this.parent.$corrosion.get$m(this.parent, key);
73 | };
74 | if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
75 | return this.top.$corrosion.get$m(this.top, key);
76 | };
77 | if (obj == this.window && key == 'location' || !this.serviceWorker && obj == this.window.document && key == 'location') return this.location;
78 | if (!this.serviceWorker && obj == this.window && key == 'parent' && this.window != this.window.parent) return this.parent;
79 | if (!this.serviceWorker && obj == this.window && key == 'top' && this.window != this.window.top) return this.top;
80 | return obj[key];
81 | };
82 | set$m(obj, key, val, operator) {
83 | if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
84 | return this.parent.$corrosion.set$m(this.parent, key, val, operator);
85 | };
86 | if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
87 | return this.top.$corrosion.set$m(this.top, key, val, operator);
88 | };
89 | if (obj == this.window && key == 'location' || !this.serviceWorker && obj == this.window.document && key == 'location') obj = this;
90 | switch(operator) {
91 | case '+=':
92 | return obj[key] += val;
93 | case '-=':
94 | return obj[key] -= val;
95 | case '*=':
96 | return obj[key] *= val;
97 | case '/=':
98 | return obj[key] /= val;
99 | case '%=':
100 | return obj[key] %= val;
101 | case '**=':
102 | return obj[key] **= val;
103 | case '<<=':
104 | return obj[key] <<= val;
105 | case '>>=':
106 | return obj[key] >>= val;
107 | case '>>>=':
108 | return obj[key] >>>= val;
109 | case '&=':
110 | return obj[key] &= val;
111 | case '^=':
112 | return obj[key] ^= val;
113 | case '|=':
114 | return obj[key] |= val;
115 | case '&&=':
116 | return obj[key] &&= val;
117 | case '||=':
118 | return obj[key] ||= val;
119 | case '??=':
120 | return obj[key] ??= val;
121 | case '++':
122 | return obj[key]++;
123 | case '--':
124 | return obj[key]--;
125 | case '=':
126 | default:
127 | return obj[key] = val;
128 | };
129 | };
130 | call$m(obj, key, args) {
131 | if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) {
132 | return this.parent.$corrosion.call$m(this.parent, key, args);
133 | };
134 | if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) {
135 | return this.top.$corrosion.call$m(this.top, key, args);
136 | };
137 | return obj[key](...args);
138 | };
139 | get$(obj) {
140 | if (obj == this.window.location) return this.location;
141 | if (!this.serviceWorker && obj == this.window.parent) return this.parent;
142 | if (!this.serviceWorker && obj == this.window.top) return this.top;
143 | return obj;
144 | };
145 | set$(obj, val, operator) {
146 | if (obj == this.window.location) return this.set$(this.location, val, operator);
147 | if (!this.serviceWorker && this.window != this.window.parent && obj == this.window.parent) return this.parent.set$(this.parent, val, operator);
148 | if (!this.serviceWorker && this.window != this.window.top && obj == this.window.top) return this.top.set$(this.top, val, operator);
149 | switch(operator) {
150 | case '+=':
151 | return obj += val;
152 | case '-=':
153 | return obj -= val;
154 | case '*=':
155 | return obj *= val;
156 | case '/=':
157 | return obj /= val;
158 | case '%=':
159 | return obj %= val;
160 | case '**=':
161 | return obj **= val;
162 | case '<<=':
163 | return obj <<= val;
164 | case '>>=':
165 | return obj >>= val;
166 | case '>>>=':
167 | return obj >>>= val;
168 | case '&=':
169 | return obj &= val;
170 | case '^=':
171 | return obj ^= val;
172 | case '|=':
173 | return obj |= val;
174 | case '&&=':
175 | return obj &&= val;
176 | case '||=':
177 | return obj ||= val;
178 | case '??=':
179 | return obj ??= val;
180 | case '++':
181 | return obj++;
182 | case '--':
183 | return obj--;
184 | case '=':
185 | default:
186 | return obj = val;
187 | };
188 | };
189 | updateLocation() {
190 | this._url = new URL(this.url.unwrap(this.window.location.href, this.meta));
191 | this.location = createLocation(this, this._url);
192 | };
193 | };
194 |
195 | globalThis.Corrosion = Corrosion;
--------------------------------------------------------------------------------
/lib/browser/location.js:
--------------------------------------------------------------------------------
1 | class Location {
2 | get [Symbol.toPrimitive]() {
3 | return () => this.href;
4 | };
5 | };
6 |
7 | function createLocation(ctx, url) {
8 | const _location = new Location();
9 | const _url = new URL(url);
10 | [
11 | 'hash',
12 | 'host',
13 | 'hostname',
14 | 'href',
15 | 'pathname',
16 | 'port',
17 | 'protocol',
18 | 'search',
19 | 'origin',
20 | ].forEach(property => {
21 | Object.defineProperty(_location, property, {
22 | get() {
23 | return _url[property];
24 | },
25 | set(val) {
26 | if (ctx.serviceWorker || property == 'origin') return;
27 | if (property == 'href') {
28 | return ctx.window.location.href = ctx.url.wrap(new URL(val, _url).href);
29 | };
30 | _url[property] = val;
31 | return ctx.window.location.href = ctx.url.wrap(_url);
32 | },
33 | });
34 | });
35 | if (!ctx.serviceWorker) [
36 | 'assign',
37 | 'replace',
38 | 'reload',
39 | ].forEach(method => {
40 | _location[method] = new Proxy(ctx.window.location[method], {
41 | apply(target, that, args) {
42 | if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
43 | return Reflect.apply(target.bind(ctx.window.location), that, args);
44 | },
45 | });
46 | });
47 | _location.toString = new Proxy(_url.toString, {
48 | apply(target, that, args) {
49 | return Reflect.apply(target.bind(_url), that, args);
50 | },
51 | });
52 | return _location;
53 | };
54 |
55 | createLocation.Location = Location;
56 | module.exports = createLocation;
--------------------------------------------------------------------------------
/lib/browser/worker.js:
--------------------------------------------------------------------------------
1 | function createWorkerRewriter(ctx = {}) {
2 | return function rewriteWorker() {
3 | if (ctx.window.Worker) {
4 | ctx.window.Worker = new Proxy(ctx.window.Worker, {
5 | construct: (target, args) => {
6 | if (args[0]) {
7 | if (args[0].trim().startsWith(`blob:${ctx.window.location.origin}`)) {
8 | const xhr = new ctx.originalXhr();
9 | xhr.open('GET', args[0], false);
10 | xhr.send();
11 | const script = ctx.js.process(xhr.responseText, ctx.location.origin + args[0].trim().slice(`blob:${ctx.window.location.origin}`.length));
12 | const blob = new Blob([ script ], { type: 'application/javascript' });
13 | args[0] = URL.createObjectURL(blob);
14 | } else {
15 | args[0] = ctx.url.wrap(args[0], ctx.meta);
16 | };
17 | };
18 | return Reflect.construct(target, args);
19 | },
20 | });
21 | };
22 | if (ctx.serviceWorker && ctx.window.importScripts) {
23 | ctx.window.importScripts = new Proxy(ctx.window.importScripts, {
24 | apply: (target, that, args) => {
25 | if (args[0]) args[0] = ctx.url.wrap(args[0], ctx.meta);
26 | return Reflect.apply(target, that, args);
27 | },
28 | });
29 | };
30 | };
31 | };
32 |
33 | module.exports = createWorkerRewriter;
--------------------------------------------------------------------------------
/lib/codec.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | exports.xor = {
6 | encode(str){
7 | if (!str) return str;
8 | return encodeURIComponent(str.toString().split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join(''));
9 | },
10 | decode(str){
11 | if (!str) return str;
12 | return decodeURIComponent(str).split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join('');
13 | },
14 | };
15 | exports.plain = {
16 | encode(str) {
17 | if (!str) return str;
18 | return encodeURIComponent(str);
19 | },
20 | decode(str) {
21 | if (!str) return str;
22 | return decodeURIComponent(str);
23 | },
24 | };
25 | exports.base64 = {
26 | encode(str){
27 | if (!str) return str;
28 | str = str.toString();
29 | const b64chs = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=');
30 | let u32;
31 | let c0;
32 | let c1;
33 | let c2;
34 | let asc = '';
35 | let pad = str.length % 3;
36 |
37 | for (let i = 0; i < str.length;) {
38 | if((c0 = str.charCodeAt(i++)) > 255 || (c1 = str.charCodeAt(i++)) > 255 || (c2 = str.charCodeAt(i++)) > 255)throw new TypeError('invalid character found');
39 | u32 = (c0 << 16) | (c1 << 8) | c2;
40 | asc += b64chs[u32 >> 18 & 63]
41 | + b64chs[u32 >> 12 & 63]
42 | + b64chs[u32 >> 6 & 63]
43 | + b64chs[u32 & 63];
44 | }
45 |
46 | return encodeURIComponent(pad ? asc.slice(0, pad - 3) + '==='.substr(pad) : asc);
47 | },
48 | decode(str){
49 | if (!str) return str;
50 | str = decodeURIComponent(str.toString());
51 | 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};
52 | str = str.replace(/\s+/g, '');
53 | str += '=='.slice(2 - (str.length & 3));
54 | let u24;
55 | let bin = '';
56 | let r1;
57 | let r2;
58 |
59 | for (let i = 0; i < str.length;) {
60 | u24 = b64tab[str.charAt(i++)] << 18
61 | | b64tab[str.charAt(i++)] << 12
62 | | (r1 = b64tab[str.charAt(i++)]) << 6
63 | | (r2 = b64tab[str.charAt(i++)]);
64 | bin += r1 === 64 ? String.fromCharCode(u24 >> 16 & 255)
65 | : r2 === 64 ? String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255)
66 | : String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
67 | };
68 | return bin;
69 | },
70 | };
71 |
--------------------------------------------------------------------------------
/lib/cookie-parser.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | class CookieStore {
6 | constructor(val = ''){
7 | this.data = {};
8 | val.split(';').map(cookie => {
9 | var [ name, val = ''] = cookie.trimStart().split('=');
10 | if (name) this.data[name] = val;
11 | });
12 | };
13 | has(name){
14 | if (!name || !this.data[name]) return false;
15 | return true;
16 | };
17 | get(name){
18 | return this.has(name) ? this.data[name] : null;
19 | };
20 | set(name, val){
21 | if (!name || !val) return;
22 | return this.data[name] = val;
23 | };
24 | delete(name){
25 | if (!name) return;
26 | return delete this.data[name];
27 | };
28 | forEach(action = (node, key) => null){
29 | for (let prop in this.data) action(this.data[prop], prop);
30 | };
31 | serialize(){
32 | var str = '';
33 | for (let i in this.data) str += ` ${i}=${this.data[i]};`;
34 | return str.substr(1);
35 | };
36 | };
37 |
38 | class SetCookie {
39 | constructor(val = ''){
40 |
41 | var [ [ name, value = '' ], ...data ] = val.split(';').map(str => str.trimStart().split('='));
42 |
43 | this.name = name;
44 | this.value = value;
45 | this.expires = null;
46 | this.maxAge = null;
47 | this.domain = null;
48 | this.secure = false;
49 | this.httpOnly = false;
50 | this.path = null;
51 | this.sameSite = null;
52 |
53 | data.forEach(([name = null, value = null]) => {
54 | if (typeof name == 'string') switch(name.toLowerCase()){
55 | case 'domain':
56 | this.domain = value;
57 | break;
58 | case 'secure':
59 | this.secure = true;
60 | break;
61 | case 'httponly':
62 | this.httpOnly = true;
63 | break;
64 | case 'samesite':
65 | this.sameSite = value;
66 | break;
67 | case 'path':
68 | this.path = value;
69 | break;
70 | case 'expires':
71 | this.expires = value;
72 | break;
73 | case 'maxage':
74 | this.maxAge = value;
75 | break;
76 | };
77 | });
78 | };
79 | serialize(){
80 | if (!this.name) return;
81 | var str = `${this.name}=${this.value};`;
82 | if (this.expires) str += ` Expires=${this.expires};`;
83 | if (this.maxAge) str += ` Max-Age=${this.max_age};`;
84 | if (this.domain) str += ` Domain=${this.domain};`;
85 | if (this.secure) str += ` Secure;`;
86 | if (this.httpOnly) str += ` HttpOnly;`;
87 | if (this.path) str += ` Path=${this.path};`;
88 | if (this.sameSite) str += ` SameSite=${this.sameSite};`;
89 | return str;
90 | };
91 | };
92 |
93 | exports.CookieStore = CookieStore;
94 | exports.SetCookie = SetCookie;
95 |
--------------------------------------------------------------------------------
/lib/cookie.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | const { SetCookie, CookieStore } = require('./cookie-parser');
6 |
7 | class CookieRewriter {
8 | constructor(ctx) {
9 | this.ctx = ctx;
10 | };
11 | decode(store, config = {}) {
12 | const url = new URL(config.url);
13 | const cookies = new CookieStore(store);
14 | cookies.forEach((val, key) => {
15 | if (!key.includes('@') || key.slice(key.length - url.hostname.length) != url.hostname) return cookies.delete(key);
16 | cookies.delete(key);
17 | cookies.set(key.substr(0, key.length - url.hostname.length - 1), val);
18 | });
19 | return cookies.serialize();
20 | };
21 | encode(input, config = {}) {
22 | if (Array.isArray(input)) {
23 | const rw = [ ...input ];
24 | for (let i in rw) rw[i] = this.encode(rw[i], config);
25 | return rw;
26 | };
27 | const url = new URL(config.url);
28 | const cookie = new SetCookie(input);
29 | if (!cookie.name) return null;
30 | cookie.domain = config.domain;
31 | cookie.secure = config.secure;
32 | cookie.name += `@${url.hostname}`;
33 | cookie.path = this.ctx.prefix;
34 | return cookie.serialize() || '';
35 | };
36 | };
37 |
38 | module.exports = CookieRewriter;
39 |
--------------------------------------------------------------------------------
/lib/css.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | const csstree = require('css-tree');
6 |
7 | class CSSRewriter {
8 | constructor(ctx) {
9 | this.ctx = ctx;
10 | };
11 | process(source, config = {}) {
12 | const ast = csstree.parse(source, {
13 | context: config.context || 'stylesheet',
14 | parseCustomProperty: true,
15 | });
16 | const urls = csstree.findAll(ast, node =>
17 | node.type == 'Url'
18 | );
19 | const imports = csstree.findAll(ast, node =>
20 | node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
21 | );
22 | urls.forEach(({ value }) => {
23 | switch(value.type) {
24 | case 'String':
25 | const quote = value.value.substring(0, 1);
26 | value.value = quote + this.ctx.url.wrap(value.value.slice(1).slice(0, -1), config) + quote;
27 | break;
28 | case 'Raw':
29 | value.value = this.ctx.url.wrap(value.value, config);
30 | break;
31 | };
32 | });
33 | imports.forEach(({ prelude }) => {
34 | const { data } = prelude.children.head;
35 | const quote = data.value.substring(0, 1);
36 | data.value = quote + this.ctx.url.wrap(data.value.slice(1).slice(0, -1), config) + quote;
37 | });
38 | return csstree.generate(ast);
39 | };
40 | source(processed, config = {}) {
41 | const ast = csstree.parse(processed, {
42 | context: config.context || 'stylesheet',
43 | parseCustomProperty: true,
44 | });
45 | const urls = csstree.findAll(ast, node =>
46 | node.type == 'Url'
47 | );
48 | const imports = csstree.findAll(ast, node =>
49 | node.type == 'Atrule' && node.name == 'import' && node.prelude && node.prelude.type == 'AtrulePrelude' && node.prelude.children.head.data.type == 'String'
50 | );
51 | urls.forEach(({ value }) => {
52 | switch(value.type) {
53 | case 'String':
54 | const quote = value.value.substring(0, 1);
55 | value.value = quote + this.ctx.url.unwrap(value.value.slice(1).slice(0, -1), config) + quote;
56 | break;
57 | case 'Raw':
58 | value.value = this.ctx.url.unwrap(value.value, config);
59 | break;
60 | };
61 | });
62 | imports.forEach(({ prelude }) => {
63 | const { data } = prelude.children.head;
64 | const quote = data.value.substring(0, 1);
65 | data.value = quote + this.ctx.url.unwrap(data.value.slice(1).slice(0, -1), config) + quote;
66 | });
67 | return csstree.generate(ast);
68 | };
69 | };
70 |
71 | module.exports = CSSRewriter;
72 |
--------------------------------------------------------------------------------
/lib/html.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | const parse5 = require('parse5');
6 |
7 | class HTMLRewriter {
8 | constructor(ctx) {
9 | this.ctx = ctx;
10 | this.attrs = [
11 | {
12 | tags: ['form', 'object', 'a', 'link', 'area', 'base', 'script', 'img', 'audio', 'video', 'input', 'embed', 'iframe', 'track', 'source', 'html', 'table', 'head'],
13 | attrs: ['src', 'href', 'ping', 'data', 'movie', 'action', 'poster', 'profile', 'background', 'target'],
14 | handler: 'url',
15 | },
16 | {
17 | tags: ['iframe'],
18 | attrs: ['srcdoc'],
19 | handler: 'html',
20 | },
21 | {
22 | tags: ['img', 'link', 'source'],
23 | attrs: ['srcset', 'imagesrcset'],
24 | handler: 'srcset',
25 | },
26 | {
27 | tags: '*',
28 | attrs: ['style'],
29 | handler: 'css',
30 | },
31 | {
32 | tags: '*',
33 | attrs: ['http-equiv', 'integrity', 'nonce', 'crossorigin'],
34 | handler: 'delete',
35 | },
36 | ];
37 | };
38 | process(source, config = {}) {
39 | const ast = parse5[config.document ? 'parse' : 'parseFragment'](source);
40 | const meta = {
41 | origin: config.origin,
42 | base: new URL(config.base),
43 | };
44 | iterate(ast, node => {
45 | if (!node.tagName) return;
46 | switch(node.tagName) {
47 | case 'STYLE':
48 | if (node.textContent) node.textContent = this.ctx.css.process(node.textContent, meta);
49 | break;
50 | case 'TITLE':
51 | if (node.textContent && this.ctx.config.title) node.textContent = this.ctx.config.title;
52 | break;
53 | case 'SCRIPT':
54 | if (node.getAttribute('type') != 'application/json' && node.textContent) node.textContent = this.ctx.js.process(node.textContent);
55 | break;
56 | case 'BASE':
57 | if (node.hasAttribute('href')) meta.base = new URL(node.getAttribute('href'), config.base);
58 | break;
59 | };
60 | node.attrs.forEach(attr => {
61 | const handler = this.attributeRoute({
62 | ...attr,
63 | node,
64 | });
65 | let flags = [];
66 | if (node.tagName == 'SCRIPT' && attr.name == 'src') flags.push('js');
67 | if (node.tagName == 'LINK' && node.getAttribute('rel') == 'stylesheet') flags.push('css');
68 | switch(handler) {
69 | case 'url':
70 | node.setAttribute(`corrosion-${attr.name}`, attr.value);
71 | attr.value = this.ctx.url.wrap(attr.value, { ...meta, flags });
72 | break;
73 | case 'srcset':
74 | node.setAttribute(`corrosion-${attr.name}`, attr.value);
75 | attr.value = this.srcset(attr.value, meta);
76 | break;
77 | case 'css':
78 | attr.value = this.ctx.css.process(attr.value, { ...meta, context: 'declarationList' });
79 | break;
80 | case 'html':
81 | node.setAttribute(`corrosion-${attr.name}`, attr.value);
82 | attr.value = this.process(attr.value, { ...config, ...meta });
83 | break;
84 | case 'delete':
85 | node.removeAttribute(attr.name);
86 | break;
87 | };
88 | });
89 | });
90 | if (config.document) {
91 | for (let i in ast.childNodes) if (ast.childNodes[i].tagName == 'html') ast.childNodes[i].childNodes.forEach(node => {
92 | if (node.tagName == 'head') {
93 | node.childNodes.unshift(...this.injectHead(config.base));
94 | };
95 | });
96 | };
97 | return parse5.serialize(ast);
98 | };
99 | source(processed, config = {}) {
100 | const ast = parse5[config.document ? 'parse' : 'parseFragment'](processed);
101 | iterate(ast, node => {
102 | if (!node.tagName) return;
103 | node.attrs.forEach(attr => {
104 | if (node.hasAttribute(`corrosion-${attr.name}`)) {
105 | attr.value = node.getAttribute(`corrosion-${attr.name}`);
106 | node.removeAttribute(`corrosion-${attr.name}`)
107 | };
108 | });
109 | });
110 | return parse5.serialize(ast);
111 | };
112 | injectHead() {
113 | return [
114 | {
115 | nodeName: 'title',
116 | tagName: 'title',
117 | childNodes: [
118 | {
119 | nodeName: '#text',
120 | value: this.ctx.config.title || '',
121 | }
122 | ],
123 | attrs: [],
124 | },
125 | {
126 | nodeName: 'script',
127 | tagName: 'script',
128 | childNodes: [],
129 | attrs: [
130 | {
131 | name: 'src',
132 | value: this.ctx.prefix + 'index.js',
133 | },
134 | ],
135 | },
136 | {
137 | nodeName: 'script',
138 | tagName: 'script',
139 | childNodes: [
140 | {
141 | nodeName: '#text',
142 | 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();`
143 | },
144 | ],
145 | attrs: [],
146 | }
147 | ];
148 | }
149 | attributeRoute(data) {
150 | 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));
151 | return match ? match.handler : false;
152 | };
153 | srcset(val, config = {}) {
154 | return val.split(',').map(src => {
155 | const parts = src.trimStart().split(' ');
156 | if (parts[0]) parts[0] = this.ctx.url.wrap(parts[0], config);
157 | return parts.join(' ');
158 | }).join(', ');
159 | };
160 | unsrcset(val, config = {}) {
161 | return val.split(',').map(src => {
162 | const parts = src.trimStart().split(' ');
163 | if (parts[0]) parts[0] = this.ctx.url.unwrap(parts[0], config);
164 | return parts.join(' ');
165 | }).join(', ');
166 | };
167 | };
168 |
169 | class Parse5Wrapper {
170 | constructor(node){
171 | this.raw = node || {
172 | attrs: [],
173 | childNodes: [],
174 | namespaceURI: '',
175 | nodeName: '',
176 | parentNode: {},
177 | tagName: '',
178 | };
179 | };
180 | hasAttribute(name){
181 | return this.raw.attrs.some(attr => attr.name == name);
182 | };
183 | removeAttribute(name){
184 | if (!this.hasAttribute(name)) return;
185 | this.raw.attrs.splice(this.raw.attrs.findIndex(attr => attr.name == name), 1);
186 | };
187 | setAttribute(name, val = ''){
188 | if (!name) return;
189 | this.removeAttribute(name);
190 | this.raw.attrs.push({
191 | name: name,
192 | value: val,
193 | });
194 | };
195 | getAttribute(name){
196 | return (this.raw.attrs.find(attr => attr.name == name) || { value: null }).value;
197 | };
198 | get textContent(){
199 | return (this.raw.childNodes.find(node => node.nodeName == '#text') || { value: '', }).value
200 | };
201 | set textContent(val){
202 | if (this.raw.childNodes.some(node => node.nodeName == '#text')) return this.raw.childNodes[this.raw.childNodes.findIndex(node => node.nodeName == '#text')].value = val;
203 | this.raw.childNodes.push({
204 | nodeName: '#text',
205 | value: val,
206 | });
207 | };
208 | get tagName(){
209 | return (this.raw.tagName || '').toUpperCase();
210 | };
211 | get nodeName(){
212 | return this.raw.nodeName;
213 | };
214 | get parentNode(){
215 | return this.raw.parentNode;
216 | };
217 | get childNodes(){
218 | return this.raw.childNodes || [];
219 | };
220 | get attrs() {
221 | return this.raw.attrs || [];
222 | };
223 | };
224 |
225 | function iterate(ast, fn = (node = Parse5Wrapper.prototype) => null) {
226 | fn(new Parse5Wrapper(ast));
227 | if (ast.childNodes) for (let i in ast.childNodes) iterate(ast.childNodes[i], fn);
228 | };
229 |
230 | module.exports = HTMLRewriter;
231 |
--------------------------------------------------------------------------------
/lib/js.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | const { parse } = require('acorn-hammerhead');
6 | const { generate } = require('./esotope');
7 |
8 | class JSRewriter {
9 | constructor(ctx) {
10 | this.parseOptions = {
11 | allowReturnOutsideFunction: true,
12 | allowImportExportEverywhere: true,
13 | ecmaVersion: 2021,
14 | };
15 | this.generationOptions = {
16 | format: {
17 | quotes: 'double',
18 | escapeless: true,
19 | compact: true,
20 | },
21 | };
22 | this.rewrite = ['location', 'parent', 'top'];
23 | this.map = [
24 | {
25 | type: 'MemberExpression',
26 | handler: (node, parent) => {
27 | let rewrite = false;
28 | if (parent.type == 'UnaryExpression' && parent.operator == 'delete') return;
29 | if (parent.type == 'NewExpression' && parent.callee == node) return;
30 | if (parent.type === 'CallExpression' && parent.callee === node) return;
31 | if (node.preventRewrite) return;
32 | switch(node.property.type) {
33 | case 'Identifier':
34 | //if (node.computed) rewrite = true;
35 | if (!node.computed && this.rewrite.includes(node.property.name)) {
36 | node.property = this.createLiteral(node.property.name);
37 | rewrite = true;
38 | };
39 | break;
40 | case 'Literal':
41 | if (this.rewrite.includes(node.property.name)) rewrite = true;
42 | break;
43 | case 'TemplateLiteral':
44 | rewrite = true;
45 | break;
46 | default:
47 | if (node.computed) rewrite = true;
48 | };
49 | if (rewrite) {
50 | let identifier = '$corrosionGet$m';
51 | let nodeToRewrite = node;
52 | const args = [
53 | node.object,
54 | node.property,
55 | ];
56 | if (node.computed) args[1].preventRewrite = true;
57 | if (parent.type == 'AssignmentExpression' && parent.left == node) {
58 | identifier = '$corrosionSet$m';
59 | nodeToRewrite = parent;
60 | args.push(parent.right, this.createLiteral(parent.operator));
61 | };
62 | if (parent.type == 'CallExpression' && parent.callee == node) {
63 | identifier = '$corrosionCall$m';
64 | nodeToRewrite = parent;
65 | args.push(this.createArrayExpression(...parent.arguments))
66 | };
67 | if (parent.type == 'UpdateExpression') {
68 | identifier = '$corrosionSet$m';
69 | nodeToRewrite = parent;
70 | args.push(this.createLiteral(null), this.createLiteral(parent.operator));
71 | };
72 | Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier, }, args));
73 | };
74 | },
75 | },
76 | {
77 | type: 'Identifier',
78 | handler: (node, parent) => {
79 | if (parent.type == 'MemberExpression' && parent.property == node) return; // window.location;
80 | if (parent.type == 'LabeledStatement') return; // { location: null, };
81 | if (parent.type == 'VariableDeclarator' && parent.id == node) return;
82 | if (parent.type == 'Property' && parent.key == node) return;
83 | if (parent.type == 'MethodDefinition') return;
84 | if (parent.type == 'ClassDeclaration') return;
85 | if (parent.type == 'RestElement') return;
86 | if (parent.type == 'ExportSpecifier') return;
87 | if (parent.type == 'ImportSpecifier') return;
88 | if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression' || parent.type == 'ArrowFunctionExpression') && parent.params.includes(node)) return;
89 | if ((parent.type == 'FunctionDeclaration' || parent.type == 'FunctionExpression') && parent.id == node) return;
90 | if (parent.type == 'AssignmentPattern' && parent.left == node) return;
91 | if (!this.rewrite.includes(node.name)) return;
92 | if (node.preventRewrite) return;
93 | let identifier = '$corrosionGet$';
94 | let nodeToRewrite = node;
95 | const args = [
96 | this.createIdentifier(node.name, true),
97 | ];
98 |
99 | if (parent.type == 'AssignmentExpression' && parent.left == node) {
100 | identifier = '$corrosionSet$';
101 | nodeToRewrite = parent;
102 | args.push(parent.right);
103 | args.push(this.createLiteral(parent.operator));
104 | };
105 |
106 | Object.assign(nodeToRewrite, this.createCallExpression({ type: 'Identifier', name: identifier }, args));
107 | },
108 | },
109 | {
110 | type: 'ImportDeclaration',
111 | handler: (node, parent, url) => {
112 | if (node.source.type != 'Literal' || !url) return;
113 | node.source = this.createLiteral(ctx.url.wrap(node.source.value, { base: url, }));
114 | },
115 | },
116 | {
117 | type: 'ImportExpression',
118 | handler: (node, parent) => {
119 | node.source = this.createCallExpression(this.createMemberExpression(this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('url')), this.createIdentifier('wrap')), [
120 | node.source,
121 | this.createMemberExpression(this.createIdentifier('$corrosion'), this.createIdentifier('meta')),
122 | ]);
123 | },
124 | },
125 | ];
126 | this.ctx = ctx;
127 | };
128 | process(source, url) {
129 | try {
130 | const ast = parse(source, this.parseOptions);
131 | this.iterate(ast, (node, parent) => {
132 | const fn = this.map.find(entry => entry.type == (node || {}).type);
133 | if (fn) fn.handler(node, parent, url);
134 | });
135 | return (url ? this.createHead(url) : '') + generate(ast, this.generationOptions);
136 | } catch(e) {
137 | return source;
138 | };
139 | };
140 | createHead(url) {
141 | return `
142 | if (!self.$corrosion && self.importScripts) {
143 | importScripts(location.origin + '${this.ctx.prefix}index.js');
144 | 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();
145 | };\n`;
146 | };
147 | iterate(ast, handler) {
148 | if (typeof ast != 'object' || !handler) return;
149 | walk(ast, null, handler);
150 | function walk(node, parent, handler) {
151 | if (typeof node != 'object' || !handler) return;
152 | handler(node, parent, handler);
153 | for (const child in node) {
154 | if (Array.isArray(node[child])) {
155 | node[child].forEach(entry => walk(entry, node, handler));
156 | } else {
157 | walk(node[child], node, handler);
158 | };
159 | };
160 | };
161 | };
162 | createCallExpression(callee, args) {
163 | return { type: 'CallExpression', callee, arguments: args, optional: false, };
164 | };
165 | createArrayExpression(...elements) {
166 | return {
167 | type: 'ArrayExpression',
168 | elements,
169 | };
170 | };
171 | createMemberExpression(object, property) {
172 | return {
173 | type: 'MemberExpression',
174 | object,
175 | property,
176 | };
177 | };
178 | createLiteral(value) {
179 | return {
180 | type: 'Literal',
181 | value,
182 | }
183 | };
184 | createIdentifier(name, preventRewrite) {
185 | return { type: 'Identifier', name, preventRewrite: preventRewrite || false, };
186 | };
187 | };
188 |
189 | module.exports = JSRewriter;
190 |
--------------------------------------------------------------------------------
/lib/rewrite.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | const URLWrapper = require('./url');
6 | const CookieRewriter = require('./cookie');
7 | const CSSRewriter = require('./css');
8 | const HTMLRewriter = require('./html');
9 | const JSRewriter = require('./js');
10 | const defaultConfig = {
11 | prefix: '/service/',
12 | codec: 'plain',
13 | ws: true,
14 | cookie: true,
15 | title: 'Service',
16 | };
17 |
18 | class Rewrite {
19 | constructor(config = defaultConfig) {
20 | this.config = Object.assign(defaultConfig, config);
21 | this.prefix = this.config.prefix;
22 | this.forceHttps = this.config.forceHttps;
23 | this.url = new URLWrapper(this.config || {});
24 | this.codec = this.url.codec;
25 | this.cookies = new CookieRewriter(this);
26 | this.css = new CSSRewriter(this);
27 | this.js = new JSRewriter(this);
28 | this.html = new HTMLRewriter(this);
29 | };
30 | };
31 |
32 | module.exports = Rewrite;
33 |
--------------------------------------------------------------------------------
/lib/server/decompress.js:
--------------------------------------------------------------------------------
1 | const zlib = require('zlib');
2 |
3 | function decompress(ctx) {
4 | if (!ctx.body || !ctx.remoteResponse) return;
5 | try {
6 | switch(ctx.headers['content-encoding']) {
7 | case 'br':
8 | ctx.body = zlib.brotliDecompressSync(ctx.body);
9 | break;
10 | case 'gzip':
11 | ctx.body = zlib.gunzipSync(ctx.body);
12 | break;
13 | case 'deflate':
14 | ctx.body = zlib.inflateRawSync(ctx.body);
15 | break;
16 | };
17 | } catch(err) {};
18 | delete ctx.headers['content-encoding'];
19 | return true;
20 | };
21 |
22 | module.exports = decompress;
--------------------------------------------------------------------------------
/lib/server/gateway.js:
--------------------------------------------------------------------------------
1 | function createGateway(ctx) {
2 | return function gateway(clientRequest, clientResponse) {
3 | const chunks = [];
4 | clientRequest.on('data', chunk =>
5 | chunks.push(chunk)
6 | ).on('end', () => {
7 | const body = chunks.length ? Buffer.concat(chunks) : '';
8 | const query = clientRequest.method == 'POST' ? new URLSearchParams((body || '').toString()) : new URLSearchParams((clientRequest.url.split('?')[1] || ''));
9 | if (!query.has('url')) return clientResponse.end();
10 | const url = query.get('url');
11 | if (/https?:\/\/([a-zA-Z0-9\-\_])|([a-zA-Z0-9\-\_])\.([a-zA-Z])/.test(url)) {
12 | clientResponse.writeHead(301, { Location: ctx.url.wrap(/https?:\/\//.test(url) ? url : 'http://' + url) });
13 | clientResponse.end();
14 | } else {
15 | clientResponse.writeHead(301, { Location: ctx.url.wrap('https://www.google.com/search?q=' + url) });
16 | clientResponse.end();
17 | };
18 | });
19 | };
20 | };
21 | module.exports = createGateway;
--------------------------------------------------------------------------------
/lib/server/headers.js:
--------------------------------------------------------------------------------
1 | function requestHeaders(ctx) {
2 | if (ctx.headers.cookie && ctx.rewrite.config.cookie) ctx.headers.cookie = ctx.rewrite.cookies.decode(ctx.headers.cookie, { url: ctx.url, });
3 | else delete ctx.headers.cookie;
4 | if (ctx.headers.origin) {
5 | if (ctx.clientSocket) {
6 | const params = new URLSearchParams((ctx.clientRequest.url.split('?')[1] || ''));
7 | delete ctx.headers.origin;
8 | delete ctx.headers.host;
9 | ctx.headers.Origin = params.get('origin') || ctx.url.origin;
10 | ctx.headers.Host = ctx.url.host;
11 | // Some websocket servers oddly only accept Host and Origin headers if the first character of the header is uppercase.
12 | } else {
13 | ctx.headers.origin = ctx.url.origin;
14 | };
15 | };
16 |
17 | if (ctx.headers.referer) {
18 | try {
19 | ctx.headers.referer = new URL(ctx.rewrite.url.unwrap(ctx.headers.referer, { origin: ctx.origin, })).href;
20 | } catch(err) {
21 | ctx.headers.referer = ctx.url.href;
22 | };
23 | };
24 | for (let header in ctx.headers) {
25 | if (header.startsWith('cf-') || header.startsWith('x-forwarded') || header == 'cdn-loop') delete ctx.headers[header];
26 | };
27 | ctx.headers.host = ctx.url.host;
28 | return true;
29 | };
30 |
31 | function responseHeaders(ctx) {
32 | if (ctx.headers.location) ctx.headers.location = ctx.rewrite.url.wrap(ctx.headers.location, { base: ctx.url, origin: ctx.origin, });
33 | 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, });
34 | [
35 | 'content-length',
36 | 'content-security-policy',
37 | 'content-security-policy-report-only',
38 | 'strict-transport-security',
39 | 'x-frame-options'
40 | ].forEach(name => delete ctx.headers[name]);
41 | return true;
42 | };
43 |
44 | exports.requestHeaders = requestHeaders;
45 | exports.responseHeaders = responseHeaders;
--------------------------------------------------------------------------------
/lib/server/index.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const createWebSocketProxy = require('./upgrade');
3 | const createRequestProxy = require('./request');
4 | const createGateway = require('./gateway');
5 | const middleware = {
6 | ...require('./headers'),
7 | ...require('./middleware'),
8 | decompress: require('./decompress'),
9 | rewriteBody: require('./rewrite-body'),
10 | };
11 | const path = require('path');
12 | const fs = require('fs');
13 | const defaultConfig = {
14 | prefix: '/service/',
15 | codec: 'plain',
16 | forceHttps: false,
17 | ws: true,
18 | cookie: true,
19 | title: 'Service',
20 | requestMiddleware: [],
21 | responseMiddleware: [],
22 | standardMiddleware: true,
23 | };
24 |
25 | class Corrosion extends require('../rewrite') {
26 | constructor(config = defaultConfig) {
27 | super(Object.assign(defaultConfig, config));
28 | if (this.config.standardMiddleware) {
29 | this.config.requestMiddleware.unshift(
30 | middleware.requestHeaders,
31 | );
32 | this.config.responseMiddleware.unshift(
33 | middleware.responseHeaders,
34 | middleware.decompress,
35 | middleware.rewriteBody,
36 | );
37 | };
38 | this.gateway = createGateway(this);
39 | this.upgrade = createWebSocketProxy(this);
40 | this.request = createRequestProxy(this);
41 | if (!fs.existsSync(path.join(__dirname, 'bundle.js'))) this.bundleScripts();
42 | };
43 | bundleScripts() {
44 | webpack({
45 | mode: 'none',
46 | entry: path.join(__dirname, '../../lib/browser/index.js'),
47 | output: {
48 | path: __dirname,
49 | filename: 'bundle.js',
50 | }
51 | }, err =>
52 | console.log(err || 'Bundled scripts')
53 | );
54 | };
55 | get script() {
56 | return fs.existsSync(path.join(__dirname, 'bundle.js')) ? fs.readFileSync(path.join(__dirname, 'bundle.js')) : 'Client script is still compiling or has crashed.'
57 | };
58 | };
59 |
60 | Corrosion.middleware = middleware;
61 | module.exports = Corrosion;
--------------------------------------------------------------------------------
/lib/server/middleware.js:
--------------------------------------------------------------------------------
1 | function address(arr = []) {
2 | return function (ctx) {
3 | ctx.address = arr[Math.floor(Math.random() * arr.length)];
4 | };
5 | };
6 |
7 | function blacklist(arr = [], page = '') {
8 | return function (ctx) {
9 | if (arr.includes(ctx.url.hostname)) ctx.clientResponse.end(page);
10 | };
11 | };
12 |
13 | exports.address = address;
14 | exports.blacklist = blacklist;
--------------------------------------------------------------------------------
/lib/server/request.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const https = require('https');
3 | function createRequestProxy(ctx) {
4 | return async function onRequest(clientRequest, clientResponse) {
5 | try {
6 | if (new RegExp(`^${ctx.prefix}gateway/?`).test(clientRequest.url)) return ctx.gateway(clientRequest, clientResponse);
7 | if (clientRequest.url.startsWith(`${ctx.prefix}index.js`)) {
8 | clientResponse.setHeader('Content-Type', 'application/javascript');
9 | return clientResponse.end(ctx.script);
10 | };
11 | const urlData = ctx.url.unwrap(clientRequest.url, { flags: true, leftovers: true, });
12 | urlData.value = new URL(urlData.value);
13 | const requestContext = {
14 | url: urlData.value,
15 | flags: urlData.flags,
16 | origin: ((clientRequest.socket.encrypted || ctx.config.forceHttps) ? 'https://' : 'http://') + clientRequest.headers.host,
17 | body: await getChunks(clientRequest),
18 | headers: { ...clientRequest.headers },
19 | method: clientRequest.method,
20 | rewrite: ctx,
21 | agent: new ((urlData.value.protocol == 'https:' || ctx.config.forceHttps) ? https : http).Agent({
22 | rejectUnauthorized: false,
23 | }),
24 | address: null,
25 | clientRequest,
26 | clientResponse,
27 | };
28 | for (let i in ctx.config.requestMiddleware) ctx.config.requestMiddleware[i](requestContext);
29 | if (clientResponse.writableEnded) return;
30 | ((requestContext.url.protocol == 'https:' || ctx.config.forceHttps) ? https : http).request({
31 | headers: requestContext.headers,
32 | method: requestContext.method,
33 | hostname: requestContext.url.hostname,
34 | port: requestContext.url.port,
35 | path: requestContext.url.pathname + requestContext.url.search,
36 | agent: requestContext.agent,
37 | localAddress: requestContext.address,
38 | rejectUnauthorized: false,
39 | }, async remoteResponse => {
40 | const responseContext = {
41 | url: requestContext.url,
42 | flags: requestContext.flags,
43 | origin: requestContext.origin,
44 | body: await getChunks(remoteResponse),
45 | headers: { ...remoteResponse.headers },
46 | statusCode: remoteResponse.statusCode,
47 | agent: requestContext.agent,
48 | address: requestContext.address,
49 | method: requestContext.method,
50 | rewrite: ctx,
51 | clientRequest,
52 | clientResponse,
53 | remoteResponse,
54 | };
55 | for (let i in ctx.config.responseMiddleware) ctx.config.responseMiddleware[i](responseContext);
56 | if (clientResponse.writableEnded) return;
57 | clientResponse.writeHead(responseContext.statusCode, responseContext.headers);
58 | clientResponse.end((responseContext.body || ''));
59 | }).on('error', err => {
60 | if (clientResponse.writableEnded) return;
61 | clientResponse.setHeader('Content-Type', 'text/plain');
62 | clientResponse.end(err.toString())
63 | }).end(requestContext.body);
64 | } catch(err) {
65 | if (clientResponse.writableEnded) return;
66 | clientResponse.setHeader('Content-Type', 'text/plain');
67 | clientResponse.end(err.toString());
68 | };
69 | };
70 | };
71 |
72 | function getChunks(stream) {
73 | const chunks = [];
74 | return new Promise(resolve =>
75 | stream.on('data', chunk =>
76 | chunks.push(chunk)
77 | ).on('end', () =>
78 | chunks.length ? resolve(Buffer.concat(chunks)) : resolve(null)
79 | )
80 | );
81 | };
82 |
83 | module.exports = createRequestProxy;
--------------------------------------------------------------------------------
/lib/server/rewrite-body.js:
--------------------------------------------------------------------------------
1 | const route = [
2 | {
3 | types: ['text/html'],
4 | handler: 'html',
5 | },
6 | {
7 | types: ['text/css'],
8 | handler: 'css',
9 | },
10 | {
11 | types: ['application/javascript', 'application/x-javascript', 'text/javascript', 'text/x-javascript'],
12 | handler: 'js',
13 | },
14 | ]
15 | function rewriteBody(ctx) {
16 | if (!ctx.body || !ctx.remoteResponse || ctx.flags.includes('xhr')) return;
17 | const meta = {
18 | base: ctx.url,
19 | origin: ctx.origin,
20 | };
21 | const data = route.find(entry => ctx.flags == entry.handler) || route.find(entry => entry.types.includes((ctx.headers['content-type'] || '').split(';')[0])) || {};
22 |
23 | switch(data.handler) {
24 | case 'html':
25 | ctx.body = ctx.rewrite.html.process(ctx.body.toString(), { ...meta, document: true });
26 | break;
27 | case 'css':
28 | ctx.body = ctx.rewrite.css.process(ctx.body.toString(), meta);
29 | break;
30 | case 'js':
31 | ctx.body = ctx.rewrite.js.process(ctx.body.toString(), ctx.url);
32 | break;
33 | };
34 | };
35 |
36 | module.exports = rewriteBody;
--------------------------------------------------------------------------------
/lib/server/upgrade.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const https = require('https');
3 |
4 | function createWebSocketProxy(ctx) {
5 | return function onUpgrade(clientRequest, clientSocket, clientHead) {
6 | try {
7 | const urlData = ctx.url.unwrap(clientRequest.url, { flags: true, });
8 | urlData.value = new URL(urlData.value);
9 | const requestContext = {
10 | url: urlData.value,
11 | flags: urlData.flags,
12 | body: null,
13 | headers: { ...clientRequest.headers },
14 | method: clientRequest.method,
15 | rewrite: ctx,
16 | agent: new ((urlData.value.protocol == 'https:' || ctx.config.forceHttps) ? https : http).Agent({
17 | rejectUnauthorized: false,
18 | }),
19 | address: null,
20 | clientRequest,
21 | clientSocket,
22 | clientHead,
23 | };
24 | ctx.config.requestMiddleware.forEach(fn => fn(requestContext));
25 | ((requestContext.url.protocol == 'https:' || ctx.config.forceHttps) ? https : http).request({
26 | headers: requestContext.headers,
27 | method: requestContext.method,
28 | hostname: requestContext.url.hostname,
29 | port: requestContext.url.port,
30 | path: requestContext.url.pathname + requestContext.url.search,
31 | agent: requestContext.agent,
32 | localAddress: requestContext.address,
33 | }).on('upgrade', (remoteResponse, remoteSocket, remoteHead) => {
34 | let handshake = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n';
35 | for (let key in remoteResponse.headers) {
36 | handshake += `${key}: ${remoteResponse.headers[key]}\r\n`;
37 | };
38 | handshake += '\r\n';
39 | clientSocket.write(handshake);
40 | clientSocket.write(remoteHead);
41 | remoteSocket.on('close', () => clientSocket.end());
42 | clientSocket.on('close', () => remoteSocket.end());
43 | remoteSocket.on('error', () => clientSocket.end());
44 | clientSocket.on('error', () => remoteSocket.end());
45 | remoteSocket.pipe(clientSocket);
46 | clientSocket.pipe(remoteSocket);
47 | }).on('error', () => {
48 | clientSocket.end()
49 | }).end();
50 | } catch(err) {
51 | clientSocket.end();
52 | };
53 | };
54 | };
55 |
56 | module.exports = createWebSocketProxy;
--------------------------------------------------------------------------------
/lib/url.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------------------
2 | // WARNING: this file is used by both the client and the server.
3 | // Do not use any browser or node-specific API!
4 | // -------------------------------------------------------------
5 | const codec = require('./codec');
6 | const defaultConfig = {
7 | prefix: '/service/',
8 | codec: 'plain'
9 | };
10 |
11 | class URLWrapper {
12 | constructor(config = defaultConfig) {
13 | this.prefix = config.prefix || defaultConfig.prefix;
14 | this.codec = codec[config.codec || 'plain'] || codec['plain'];
15 | this.regex = /^(#|about:|data:|blob:|mailto:|javascript:)/;
16 | };
17 | wrap(val, config = {}) {
18 | if (!val || this.regex.test(val)) return val;
19 | let flags = '';
20 | (config.flags || []).forEach(flag => flags += `${flag}_/`);
21 | if (config.base) try {
22 | if (!['http:', 'https:', 'ws:', 'wss:'].includes(new URL(val, config.base).protocol)) return val;
23 | } catch(e) {
24 | return val;
25 | };
26 | return (config.origin || '') + this.prefix + flags + this.codec.encode(config.base ? new URL(val, config.base) : val) + '/';
27 | };
28 | unwrap(val, config = {}) {
29 | if (!val || this.regex.test(val)) return val;
30 | let processed = val.slice((config.origin || '').length + this.prefix.length);
31 | const flags = ('/' + processed).match(/(?<=\/)(.*?)(?=_\/)/g) || [];
32 | flags.forEach(flag => processed = processed.slice(`${flag}_/`.length));
33 | let [ url, leftovers ] = processed.split(/\/(.+)?/);
34 | return config.flags ? { value: this.codec.decode((url || '')) + (config.leftovers && leftovers ? leftovers : ''), flags } : this.codec.decode((url || '')) + (config.leftovers && leftovers ? leftovers : '');
35 | };
36 | };
37 |
38 | module.exports = URLWrapper;
39 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Shadow",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/eslint": {
8 | "version": "7.28.2",
9 | "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz",
10 | "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==",
11 | "requires": {
12 | "@types/estree": "*",
13 | "@types/json-schema": "*"
14 | }
15 | },
16 | "@types/eslint-scope": {
17 | "version": "3.7.1",
18 | "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz",
19 | "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==",
20 | "requires": {
21 | "@types/eslint": "*",
22 | "@types/estree": "*"
23 | }
24 | },
25 | "@types/estree": {
26 | "version": "0.0.46",
27 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz",
28 | "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg=="
29 | },
30 | "@types/json-schema": {
31 | "version": "7.0.9",
32 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
33 | "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
34 | },
35 | "@types/node": {
36 | "version": "16.11.6",
37 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz",
38 | "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w=="
39 | },
40 | "@webassemblyjs/ast": {
41 | "version": "1.11.1",
42 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
43 | "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
44 | "requires": {
45 | "@webassemblyjs/helper-numbers": "1.11.1",
46 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
47 | }
48 | },
49 | "@webassemblyjs/floating-point-hex-parser": {
50 | "version": "1.11.1",
51 | "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
52 | "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
53 | },
54 | "@webassemblyjs/helper-api-error": {
55 | "version": "1.11.1",
56 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
57 | "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
58 | },
59 | "@webassemblyjs/helper-buffer": {
60 | "version": "1.11.1",
61 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
62 | "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
63 | },
64 | "@webassemblyjs/helper-numbers": {
65 | "version": "1.11.1",
66 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
67 | "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
68 | "requires": {
69 | "@webassemblyjs/floating-point-hex-parser": "1.11.1",
70 | "@webassemblyjs/helper-api-error": "1.11.1",
71 | "@xtuc/long": "4.2.2"
72 | }
73 | },
74 | "@webassemblyjs/helper-wasm-bytecode": {
75 | "version": "1.11.1",
76 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
77 | "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
78 | },
79 | "@webassemblyjs/helper-wasm-section": {
80 | "version": "1.11.1",
81 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
82 | "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
83 | "requires": {
84 | "@webassemblyjs/ast": "1.11.1",
85 | "@webassemblyjs/helper-buffer": "1.11.1",
86 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
87 | "@webassemblyjs/wasm-gen": "1.11.1"
88 | }
89 | },
90 | "@webassemblyjs/ieee754": {
91 | "version": "1.11.1",
92 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
93 | "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
94 | "requires": {
95 | "@xtuc/ieee754": "^1.2.0"
96 | }
97 | },
98 | "@webassemblyjs/leb128": {
99 | "version": "1.11.1",
100 | "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
101 | "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
102 | "requires": {
103 | "@xtuc/long": "4.2.2"
104 | }
105 | },
106 | "@webassemblyjs/utf8": {
107 | "version": "1.11.1",
108 | "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
109 | "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
110 | },
111 | "@webassemblyjs/wasm-edit": {
112 | "version": "1.11.1",
113 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
114 | "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
115 | "requires": {
116 | "@webassemblyjs/ast": "1.11.1",
117 | "@webassemblyjs/helper-buffer": "1.11.1",
118 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
119 | "@webassemblyjs/helper-wasm-section": "1.11.1",
120 | "@webassemblyjs/wasm-gen": "1.11.1",
121 | "@webassemblyjs/wasm-opt": "1.11.1",
122 | "@webassemblyjs/wasm-parser": "1.11.1",
123 | "@webassemblyjs/wast-printer": "1.11.1"
124 | }
125 | },
126 | "@webassemblyjs/wasm-gen": {
127 | "version": "1.11.1",
128 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
129 | "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
130 | "requires": {
131 | "@webassemblyjs/ast": "1.11.1",
132 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
133 | "@webassemblyjs/ieee754": "1.11.1",
134 | "@webassemblyjs/leb128": "1.11.1",
135 | "@webassemblyjs/utf8": "1.11.1"
136 | }
137 | },
138 | "@webassemblyjs/wasm-opt": {
139 | "version": "1.11.1",
140 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
141 | "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
142 | "requires": {
143 | "@webassemblyjs/ast": "1.11.1",
144 | "@webassemblyjs/helper-buffer": "1.11.1",
145 | "@webassemblyjs/wasm-gen": "1.11.1",
146 | "@webassemblyjs/wasm-parser": "1.11.1"
147 | }
148 | },
149 | "@webassemblyjs/wasm-parser": {
150 | "version": "1.11.1",
151 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
152 | "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
153 | "requires": {
154 | "@webassemblyjs/ast": "1.11.1",
155 | "@webassemblyjs/helper-api-error": "1.11.1",
156 | "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
157 | "@webassemblyjs/ieee754": "1.11.1",
158 | "@webassemblyjs/leb128": "1.11.1",
159 | "@webassemblyjs/utf8": "1.11.1"
160 | }
161 | },
162 | "@webassemblyjs/wast-printer": {
163 | "version": "1.11.1",
164 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
165 | "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
166 | "requires": {
167 | "@webassemblyjs/ast": "1.11.1",
168 | "@xtuc/long": "4.2.2"
169 | }
170 | },
171 | "@xtuc/ieee754": {
172 | "version": "1.2.0",
173 | "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
174 | "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
175 | },
176 | "@xtuc/long": {
177 | "version": "4.2.2",
178 | "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
179 | "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
180 | },
181 | "accepts": {
182 | "version": "1.3.7",
183 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
184 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
185 | "requires": {
186 | "mime-types": "~2.1.24",
187 | "negotiator": "0.6.2"
188 | }
189 | },
190 | "acorn": {
191 | "version": "8.5.0",
192 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
193 | "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q=="
194 | },
195 | "acorn-hammerhead": {
196 | "version": "0.5.0",
197 | "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.5.0.tgz",
198 | "integrity": "sha512-TI9TFfJBfduhcM2GggayNhdYvdJ3UgS/Bu3sB7FB2AUmNCmCJ+TSOT6GXu+bodG5/xL74D5zE4XRaqyjgjsYVQ==",
199 | "requires": {
200 | "@types/estree": "0.0.46"
201 | }
202 | },
203 | "acorn-import-assertions": {
204 | "version": "1.8.0",
205 | "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
206 | "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw=="
207 | },
208 | "ajv": {
209 | "version": "6.12.6",
210 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
211 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
212 | "requires": {
213 | "fast-deep-equal": "^3.1.1",
214 | "fast-json-stable-stringify": "^2.0.0",
215 | "json-schema-traverse": "^0.4.1",
216 | "uri-js": "^4.2.2"
217 | }
218 | },
219 | "ajv-keywords": {
220 | "version": "3.5.2",
221 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
222 | "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
223 | },
224 | "array-flatten": {
225 | "version": "1.1.1",
226 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
227 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
228 | },
229 | "body-parser": {
230 | "version": "1.19.0",
231 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
232 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
233 | "requires": {
234 | "bytes": "3.1.0",
235 | "content-type": "~1.0.4",
236 | "debug": "2.6.9",
237 | "depd": "~1.1.2",
238 | "http-errors": "1.7.2",
239 | "iconv-lite": "0.4.24",
240 | "on-finished": "~2.3.0",
241 | "qs": "6.7.0",
242 | "raw-body": "2.4.0",
243 | "type-is": "~1.6.17"
244 | }
245 | },
246 | "browserslist": {
247 | "version": "4.17.6",
248 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz",
249 | "integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==",
250 | "requires": {
251 | "caniuse-lite": "^1.0.30001274",
252 | "electron-to-chromium": "^1.3.886",
253 | "escalade": "^3.1.1",
254 | "node-releases": "^2.0.1",
255 | "picocolors": "^1.0.0"
256 | }
257 | },
258 | "buffer-from": {
259 | "version": "1.1.2",
260 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
261 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
262 | },
263 | "bytes": {
264 | "version": "3.1.0",
265 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
266 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
267 | },
268 | "caniuse-lite": {
269 | "version": "1.0.30001274",
270 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz",
271 | "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew=="
272 | },
273 | "chrome-trace-event": {
274 | "version": "1.0.3",
275 | "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
276 | "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
277 | },
278 | "commander": {
279 | "version": "2.20.3",
280 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
281 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
282 | },
283 | "content-disposition": {
284 | "version": "0.5.3",
285 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
286 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
287 | "requires": {
288 | "safe-buffer": "5.1.2"
289 | }
290 | },
291 | "content-type": {
292 | "version": "1.0.4",
293 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
294 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
295 | },
296 | "cookie": {
297 | "version": "0.4.0",
298 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
299 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
300 | },
301 | "cookie-signature": {
302 | "version": "1.0.6",
303 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
304 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
305 | },
306 | "css-tree": {
307 | "version": "1.1.3",
308 | "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
309 | "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
310 | "requires": {
311 | "mdn-data": "2.0.14",
312 | "source-map": "^0.6.1"
313 | }
314 | },
315 | "debug": {
316 | "version": "2.6.9",
317 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
318 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
319 | "requires": {
320 | "ms": "2.0.0"
321 | }
322 | },
323 | "depd": {
324 | "version": "1.1.2",
325 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
326 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
327 | },
328 | "destroy": {
329 | "version": "1.0.4",
330 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
331 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
332 | },
333 | "ee-first": {
334 | "version": "1.1.1",
335 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
336 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
337 | },
338 | "electron-to-chromium": {
339 | "version": "1.3.887",
340 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.887.tgz",
341 | "integrity": "sha512-QQUumrEjFDKSVYVdaeBmFdyQGoaV+fCSMyWHvfx/u22bRHSTeBQYt6P4jMY+gFd4kgKB9nqk7RMtWkDB49OYPA=="
342 | },
343 | "encodeurl": {
344 | "version": "1.0.2",
345 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
346 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
347 | },
348 | "enhanced-resolve": {
349 | "version": "5.8.3",
350 | "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
351 | "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
352 | "requires": {
353 | "graceful-fs": "^4.2.4",
354 | "tapable": "^2.2.0"
355 | }
356 | },
357 | "es-module-lexer": {
358 | "version": "0.9.3",
359 | "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
360 | "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
361 | },
362 | "escalade": {
363 | "version": "3.1.1",
364 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
365 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
366 | },
367 | "escape-html": {
368 | "version": "1.0.3",
369 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
370 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
371 | },
372 | "eslint-scope": {
373 | "version": "5.1.1",
374 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
375 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
376 | "requires": {
377 | "esrecurse": "^4.3.0",
378 | "estraverse": "^4.1.1"
379 | }
380 | },
381 | "esrecurse": {
382 | "version": "4.3.0",
383 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
384 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
385 | "requires": {
386 | "estraverse": "^5.2.0"
387 | },
388 | "dependencies": {
389 | "estraverse": {
390 | "version": "5.3.0",
391 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
392 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
393 | }
394 | }
395 | },
396 | "estraverse": {
397 | "version": "4.3.0",
398 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
399 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
400 | },
401 | "etag": {
402 | "version": "1.8.1",
403 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
404 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
405 | },
406 | "events": {
407 | "version": "3.3.0",
408 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
409 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
410 | },
411 | "express": {
412 | "version": "4.17.1",
413 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
414 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
415 | "requires": {
416 | "accepts": "~1.3.7",
417 | "array-flatten": "1.1.1",
418 | "body-parser": "1.19.0",
419 | "content-disposition": "0.5.3",
420 | "content-type": "~1.0.4",
421 | "cookie": "0.4.0",
422 | "cookie-signature": "1.0.6",
423 | "debug": "2.6.9",
424 | "depd": "~1.1.2",
425 | "encodeurl": "~1.0.2",
426 | "escape-html": "~1.0.3",
427 | "etag": "~1.8.1",
428 | "finalhandler": "~1.1.2",
429 | "fresh": "0.5.2",
430 | "merge-descriptors": "1.0.1",
431 | "methods": "~1.1.2",
432 | "on-finished": "~2.3.0",
433 | "parseurl": "~1.3.3",
434 | "path-to-regexp": "0.1.7",
435 | "proxy-addr": "~2.0.5",
436 | "qs": "6.7.0",
437 | "range-parser": "~1.2.1",
438 | "safe-buffer": "5.1.2",
439 | "send": "0.17.1",
440 | "serve-static": "1.14.1",
441 | "setprototypeof": "1.1.1",
442 | "statuses": "~1.5.0",
443 | "type-is": "~1.6.18",
444 | "utils-merge": "1.0.1",
445 | "vary": "~1.1.2"
446 | }
447 | },
448 | "fast-deep-equal": {
449 | "version": "3.1.3",
450 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
451 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
452 | },
453 | "fast-json-stable-stringify": {
454 | "version": "2.1.0",
455 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
456 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
457 | },
458 | "finalhandler": {
459 | "version": "1.1.2",
460 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
461 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
462 | "requires": {
463 | "debug": "2.6.9",
464 | "encodeurl": "~1.0.2",
465 | "escape-html": "~1.0.3",
466 | "on-finished": "~2.3.0",
467 | "parseurl": "~1.3.3",
468 | "statuses": "~1.5.0",
469 | "unpipe": "~1.0.0"
470 | }
471 | },
472 | "forwarded": {
473 | "version": "0.2.0",
474 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
475 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
476 | },
477 | "fresh": {
478 | "version": "0.5.2",
479 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
480 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
481 | },
482 | "glob-to-regexp": {
483 | "version": "0.4.1",
484 | "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
485 | "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
486 | },
487 | "graceful-fs": {
488 | "version": "4.2.8",
489 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
490 | "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
491 | },
492 | "has-flag": {
493 | "version": "4.0.0",
494 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
495 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
496 | },
497 | "http-errors": {
498 | "version": "1.7.2",
499 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
500 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
501 | "requires": {
502 | "depd": "~1.1.2",
503 | "inherits": "2.0.3",
504 | "setprototypeof": "1.1.1",
505 | "statuses": ">= 1.5.0 < 2",
506 | "toidentifier": "1.0.0"
507 | }
508 | },
509 | "iconv-lite": {
510 | "version": "0.4.24",
511 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
512 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
513 | "requires": {
514 | "safer-buffer": ">= 2.1.2 < 3"
515 | }
516 | },
517 | "inherits": {
518 | "version": "2.0.3",
519 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
520 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
521 | },
522 | "ipaddr.js": {
523 | "version": "1.9.1",
524 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
525 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
526 | },
527 | "jest-worker": {
528 | "version": "27.3.1",
529 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz",
530 | "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==",
531 | "requires": {
532 | "@types/node": "*",
533 | "merge-stream": "^2.0.0",
534 | "supports-color": "^8.0.0"
535 | }
536 | },
537 | "json-parse-better-errors": {
538 | "version": "1.0.2",
539 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
540 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
541 | },
542 | "json-schema-traverse": {
543 | "version": "0.4.1",
544 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
545 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
546 | },
547 | "loader-runner": {
548 | "version": "4.2.0",
549 | "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
550 | "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw=="
551 | },
552 | "mdn-data": {
553 | "version": "2.0.14",
554 | "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
555 | "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
556 | },
557 | "media-typer": {
558 | "version": "0.3.0",
559 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
560 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
561 | },
562 | "merge-descriptors": {
563 | "version": "1.0.1",
564 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
565 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
566 | },
567 | "merge-stream": {
568 | "version": "2.0.0",
569 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
570 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
571 | },
572 | "methods": {
573 | "version": "1.1.2",
574 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
575 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
576 | },
577 | "mime": {
578 | "version": "1.6.0",
579 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
580 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
581 | },
582 | "mime-db": {
583 | "version": "1.50.0",
584 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz",
585 | "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A=="
586 | },
587 | "mime-types": {
588 | "version": "2.1.33",
589 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz",
590 | "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==",
591 | "requires": {
592 | "mime-db": "1.50.0"
593 | }
594 | },
595 | "ms": {
596 | "version": "2.0.0",
597 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
598 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
599 | },
600 | "negotiator": {
601 | "version": "0.6.2",
602 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
603 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
604 | },
605 | "neo-async": {
606 | "version": "2.6.2",
607 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
608 | "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
609 | },
610 | "node-fetch": {
611 | "version": "2.1.0",
612 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.0.tgz",
613 | "integrity": "sha1-nZbd6f4fzvbkCBImuklrjVlQtkc="
614 | },
615 | "node-releases": {
616 | "version": "2.0.1",
617 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
618 | "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA=="
619 | },
620 | "on-finished": {
621 | "version": "2.3.0",
622 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
623 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
624 | "requires": {
625 | "ee-first": "1.1.1"
626 | }
627 | },
628 | "p-limit": {
629 | "version": "3.1.0",
630 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
631 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
632 | "requires": {
633 | "yocto-queue": "^0.1.0"
634 | }
635 | },
636 | "parse5": {
637 | "version": "6.0.1",
638 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
639 | "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
640 | },
641 | "parseurl": {
642 | "version": "1.3.3",
643 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
644 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
645 | },
646 | "path-to-regexp": {
647 | "version": "0.1.7",
648 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
649 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
650 | },
651 | "picocolors": {
652 | "version": "1.0.0",
653 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
654 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
655 | },
656 | "proxy-addr": {
657 | "version": "2.0.7",
658 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
659 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
660 | "requires": {
661 | "forwarded": "0.2.0",
662 | "ipaddr.js": "1.9.1"
663 | }
664 | },
665 | "punycode": {
666 | "version": "2.1.1",
667 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
668 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
669 | },
670 | "qs": {
671 | "version": "6.7.0",
672 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
673 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
674 | },
675 | "randombytes": {
676 | "version": "2.1.0",
677 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
678 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
679 | "requires": {
680 | "safe-buffer": "^5.1.0"
681 | }
682 | },
683 | "range-parser": {
684 | "version": "1.2.1",
685 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
686 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
687 | },
688 | "raw-body": {
689 | "version": "2.4.0",
690 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
691 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
692 | "requires": {
693 | "bytes": "3.1.0",
694 | "http-errors": "1.7.2",
695 | "iconv-lite": "0.4.24",
696 | "unpipe": "1.0.0"
697 | }
698 | },
699 | "safe-buffer": {
700 | "version": "5.1.2",
701 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
702 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
703 | },
704 | "safer-buffer": {
705 | "version": "2.1.2",
706 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
707 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
708 | },
709 | "schema-utils": {
710 | "version": "3.1.1",
711 | "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
712 | "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
713 | "requires": {
714 | "@types/json-schema": "^7.0.8",
715 | "ajv": "^6.12.5",
716 | "ajv-keywords": "^3.5.2"
717 | }
718 | },
719 | "send": {
720 | "version": "0.17.1",
721 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
722 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
723 | "requires": {
724 | "debug": "2.6.9",
725 | "depd": "~1.1.2",
726 | "destroy": "~1.0.4",
727 | "encodeurl": "~1.0.2",
728 | "escape-html": "~1.0.3",
729 | "etag": "~1.8.1",
730 | "fresh": "0.5.2",
731 | "http-errors": "~1.7.2",
732 | "mime": "1.6.0",
733 | "ms": "2.1.1",
734 | "on-finished": "~2.3.0",
735 | "range-parser": "~1.2.1",
736 | "statuses": "~1.5.0"
737 | },
738 | "dependencies": {
739 | "ms": {
740 | "version": "2.1.1",
741 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
742 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
743 | }
744 | }
745 | },
746 | "serialize-javascript": {
747 | "version": "6.0.0",
748 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
749 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
750 | "requires": {
751 | "randombytes": "^2.1.0"
752 | }
753 | },
754 | "serve-static": {
755 | "version": "1.14.1",
756 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
757 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
758 | "requires": {
759 | "encodeurl": "~1.0.2",
760 | "escape-html": "~1.0.3",
761 | "parseurl": "~1.3.3",
762 | "send": "0.17.1"
763 | }
764 | },
765 | "setprototypeof": {
766 | "version": "1.1.1",
767 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
768 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
769 | },
770 | "source-map": {
771 | "version": "0.6.1",
772 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
773 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
774 | },
775 | "source-map-support": {
776 | "version": "0.5.20",
777 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz",
778 | "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==",
779 | "requires": {
780 | "buffer-from": "^1.0.0",
781 | "source-map": "^0.6.0"
782 | }
783 | },
784 | "statuses": {
785 | "version": "1.5.0",
786 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
787 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
788 | },
789 | "supports-color": {
790 | "version": "8.1.1",
791 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
792 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
793 | "requires": {
794 | "has-flag": "^4.0.0"
795 | }
796 | },
797 | "tapable": {
798 | "version": "2.2.1",
799 | "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
800 | "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
801 | },
802 | "terser": {
803 | "version": "5.9.0",
804 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz",
805 | "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==",
806 | "requires": {
807 | "commander": "^2.20.0",
808 | "source-map": "~0.7.2",
809 | "source-map-support": "~0.5.20"
810 | },
811 | "dependencies": {
812 | "source-map": {
813 | "version": "0.7.3",
814 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
815 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
816 | }
817 | }
818 | },
819 | "terser-webpack-plugin": {
820 | "version": "5.2.4",
821 | "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz",
822 | "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==",
823 | "requires": {
824 | "jest-worker": "^27.0.6",
825 | "p-limit": "^3.1.0",
826 | "schema-utils": "^3.1.1",
827 | "serialize-javascript": "^6.0.0",
828 | "source-map": "^0.6.1",
829 | "terser": "^5.7.2"
830 | }
831 | },
832 | "toidentifier": {
833 | "version": "1.0.0",
834 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
835 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
836 | },
837 | "type-is": {
838 | "version": "1.6.18",
839 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
840 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
841 | "requires": {
842 | "media-typer": "0.3.0",
843 | "mime-types": "~2.1.24"
844 | }
845 | },
846 | "unpipe": {
847 | "version": "1.0.0",
848 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
849 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
850 | },
851 | "uri-js": {
852 | "version": "4.4.1",
853 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
854 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
855 | "requires": {
856 | "punycode": "^2.1.0"
857 | }
858 | },
859 | "utils-merge": {
860 | "version": "1.0.1",
861 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
862 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
863 | },
864 | "vary": {
865 | "version": "1.1.2",
866 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
867 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
868 | },
869 | "watchpack": {
870 | "version": "2.2.0",
871 | "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
872 | "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==",
873 | "requires": {
874 | "glob-to-regexp": "^0.4.1",
875 | "graceful-fs": "^4.1.2"
876 | }
877 | },
878 | "webpack": {
879 | "version": "5.61.0",
880 | "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.61.0.tgz",
881 | "integrity": "sha512-fPdTuaYZ/GMGFm4WrPi2KRCqS1vDp773kj9S0iI5Uc//5cszsFEDgHNaX4Rj1vobUiU1dFIV3mA9k1eHeluFpw==",
882 | "requires": {
883 | "@types/eslint-scope": "^3.7.0",
884 | "@types/estree": "^0.0.50",
885 | "@webassemblyjs/ast": "1.11.1",
886 | "@webassemblyjs/wasm-edit": "1.11.1",
887 | "@webassemblyjs/wasm-parser": "1.11.1",
888 | "acorn": "^8.4.1",
889 | "acorn-import-assertions": "^1.7.6",
890 | "browserslist": "^4.14.5",
891 | "chrome-trace-event": "^1.0.2",
892 | "enhanced-resolve": "^5.8.3",
893 | "es-module-lexer": "^0.9.0",
894 | "eslint-scope": "5.1.1",
895 | "events": "^3.2.0",
896 | "glob-to-regexp": "^0.4.1",
897 | "graceful-fs": "^4.2.4",
898 | "json-parse-better-errors": "^1.0.2",
899 | "loader-runner": "^4.2.0",
900 | "mime-types": "^2.1.27",
901 | "neo-async": "^2.6.2",
902 | "schema-utils": "^3.1.0",
903 | "tapable": "^2.1.1",
904 | "terser-webpack-plugin": "^5.1.3",
905 | "watchpack": "^2.2.0",
906 | "webpack-sources": "^3.2.0"
907 | },
908 | "dependencies": {
909 | "@types/estree": {
910 | "version": "0.0.50",
911 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz",
912 | "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw=="
913 | }
914 | }
915 | },
916 | "webpack-sources": {
917 | "version": "3.2.1",
918 | "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz",
919 | "integrity": "sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA=="
920 | },
921 | "yocto-queue": {
922 | "version": "0.1.0",
923 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
924 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
925 | }
926 | }
927 | }
928 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Shadow",
3 | "version": "1.0.0",
4 | "repository": "https://github.com/FogNetwork/Shadow",
5 | "bugs": {
6 | "url": "https://github.com/FogNetwork/Shadow/issues"
7 | },
8 | "description": "Shadow is a simple yet stunning service built to access any website",
9 | "main": "app.js",
10 | "scripts": {
11 | "start": "node app.js"
12 | },
13 | "author": "Fog Network",
14 | "license": "MIT",
15 | "dependencies": {
16 | "acorn-hammerhead": "^0.5.0",
17 | "css-tree": "^1.1.3",
18 | "express": "^4.17.1",
19 | "parse5": "^6.0.1",
20 | "webpack": "^5.61.0",
21 | "node-fetch": "2.1.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |