├── uv.png
├── uv.zip
├── sw.js
├── uv
├── uv.config.js
├── uv.sw.js
└── uv.handler.js
├── README.md
├── index.js
├── index.css
└── index.html
/uv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titaniumnetwork-dev/Ultraviolet-Static-Archive/HEAD/uv.png
--------------------------------------------------------------------------------
/uv.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titaniumnetwork-dev/Ultraviolet-Static-Archive/HEAD/uv.zip
--------------------------------------------------------------------------------
/sw.js:
--------------------------------------------------------------------------------
1 | importScripts('./uv/uv.sw.js');
2 |
3 | const sw = new UVServiceWorker();
4 |
5 | self.addEventListener('fetch', event =>
6 | event.respondWith(
7 | sw.fetch(event)
8 | )
9 | );
--------------------------------------------------------------------------------
/uv/uv.config.js:
--------------------------------------------------------------------------------
1 | self.__uv$config = {
2 | prefix: '/service/',
3 | bare: '/bare/',
4 | encodeUrl: Ultraviolet.codec.xor.encode,
5 | decodeUrl: Ultraviolet.codec.xor.decode,
6 | handler: '/uv/uv.handler.js',
7 | bundle: '/uv/uv.bundle.js',
8 | config: '/uv/uv.config.js',
9 | sw: '/uv/uv.sw.js',
10 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ultraviolet-scripts
2 | Core Ultraviolet scripts
3 |
4 | # Configuration
5 | Configure Ultraviolet for both client-hooking & service worker in `uv.config.js`
6 | ```javascript
7 | self.__uv$config = {
8 | bare: '/bare/',
9 | prefix: '/service/',
10 | encodeUrl: Ultraviolet.codec.xor.encode,
11 | decodeUrl: Ultraviolet.codec.xor.decode,
12 | handler: '/uv.handler.js',
13 | bundle: '/uv.bundle.js',
14 | config: '/uv.config.js',
15 | };
16 | ```
17 |
18 |
19 | # Example Usage
20 | ```javascript
21 | importScripts('/PATHTOSCRIPTS/uv.sw.js');
22 |
23 | const sw = new UVServiceWorker();
24 |
25 | self.addEventListener('fetch', event =>
26 | event.respondWith(
27 | sw.fetch(event)
28 | )
29 | );
30 | ```
31 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const form = document.querySelector('form');
2 | const input = document.querySelector('input');
3 |
4 | form.addEventListener('submit', async event => {
5 | event.preventDefault();
6 | window.navigator.serviceWorker.register('./sw.js', {
7 | scope: __uv$config.prefix
8 | }).then(() => {
9 | let url = input.value.trim();
10 | if (!isUrl(url)) url = 'https://www.google.com/search?q=' + url;
11 | else if (!(url.startsWith('https://') || url.startsWith('http://'))) url = 'http://' + url;
12 |
13 |
14 | window.location.href = __uv$config.prefix + __uv$config.encodeUrl(url);
15 | });
16 | });
17 |
18 | function isUrl(val = ''){
19 | if (/^http(s?):\/\//.test(val) || val.includes('.') && val.substr(0, 1) !== ' ') return true;
20 | return false;
21 | };
22 |
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;1,300;1,400&display=swap');
2 | @import url('https://unpkg.com/@fortawesome/fontawesome-free@5.15.4/css/all.min.css');
3 | @keyframes fadeInAnimation {
4 | 0% {
5 | opacity: 0;
6 | }
7 | 100% {
8 | opacity: 1;
9 | }
10 | }
11 |
12 | * {
13 | font-family: Roboto;
14 | }
15 |
16 | body {
17 | margin: 0;
18 | background: #111;
19 | display: flex;
20 | flex-direction: column;
21 | min-height: 100vh;
22 | animation: fadeInAnimation ease-in-out 0.3s;
23 | animation-iteration-count: 1;
24 | animation-fill-mode: forwards;
25 | }
26 |
27 | .fa-magnifying-glass {
28 | color: white;
29 | }
30 |
31 | .logo-wrapper {
32 | display: flex;
33 | align-items: center;
34 | justify-content: center;
35 | flex-direction: column;
36 | margin-top: 10%;
37 | }
38 |
39 | .logo {
40 | width: 150px;
41 | }
42 |
43 | .logo-wrapper .text {
44 | font-size: 75px;
45 | font-family: "Roboto";
46 | color: #FFF;
47 | }
48 |
49 | .logo-wrapper h1 {
50 | color: white;
51 | }
52 |
53 | footer {
54 | margin-top: auto;
55 | width: 93%;
56 | align-self: center;
57 | height: 80px;
58 | display: flex;
59 | justify-content: left;
60 | align-items: center;
61 | }
62 |
63 | footer a,
64 | footer span {
65 | margin: 0 15px;
66 | text-decoration: none;
67 | color: #FFF;
68 | font-size: 15px;
69 | }
70 |
71 | footer a {
72 | cursor: pointer;
73 | }
74 |
75 | footer a:hover {
76 | text-decoration: underline;
77 | }
78 |
79 | form {
80 | display: flex;
81 | justify-content: center;
82 | }
83 |
84 | .desc {
85 | display: flex;
86 | justify-content: center;
87 | }
88 |
89 | .desc p {
90 | width: 560px;
91 | color: rgba(253, 253, 253, 0.514);
92 | }
93 |
94 | form input {
95 | background: none;
96 | font-family: inherit;
97 | padding: 0px 17px;
98 | height: 48px;
99 | border: 1px solid rgb(255, 255, 255, .2);
100 | color: var(--text-color);
101 | border-radius: 3px;
102 | outline: none;
103 | width: 350px;
104 | margin-top: 5px;
105 | border-radius: 50px;
106 | color: #FFF;
107 | }
108 |
109 | form input:focus {
110 | border: 1px solid rgba(253, 253, 253, 0.514);
111 | border-radius: 6px;
112 | animation: fadeInAnimation ease-in-out 0.3s;
113 | animation-iteration-count: 1;
114 | animation-fill-mode: forwards;
115 | }
--------------------------------------------------------------------------------
/uv/uv.sw.js:
--------------------------------------------------------------------------------
1 | importScripts('/uv/uv.bundle.js');
2 | importScripts('/uv/uv.config.js');
3 |
4 | class UVServiceWorker extends EventEmitter {
5 | constructor(config = __uv$config) {
6 | super();
7 | if (!config.bare) config.bare = '/bare/';
8 | this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location));
9 | this.headers = {
10 | csp: [
11 | 'cross-origin-embedder-policy',
12 | 'cross-origin-opener-policy',
13 | 'cross-origin-resource-policy',
14 | 'content-security-policy',
15 | 'content-security-policy-report-only',
16 | 'expect-ct',
17 | 'feature-policy',
18 | 'origin-isolation',
19 | 'strict-transport-security',
20 | 'upgrade-insecure-requests',
21 | 'x-content-type-options',
22 | 'x-download-options',
23 | 'x-frame-options',
24 | 'x-permitted-cross-domain-policies',
25 | 'x-powered-by',
26 | 'x-xss-protection',
27 | ],
28 | forward: [
29 | 'accept-encoding',
30 | 'connection',
31 | 'content-length',
32 | ],
33 | };
34 | this.method = {
35 | empty: [
36 | 'GET',
37 | 'HEAD'
38 | ]
39 | };
40 | this.statusCode = {
41 | empty: [
42 | 204,
43 | 304,
44 | ],
45 | };
46 | this.config = config;
47 | this.browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName();
48 |
49 | if (this.browser === 'Firefox') {
50 | this.headers.forward.push('user-agent');
51 | this.headers.forward.push('content-type');
52 | };
53 | };
54 | async fetch({ request }) {
55 | if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) {
56 | return fetch(request);
57 | };
58 | try {
59 |
60 | const ultraviolet = new Ultraviolet(this.config);
61 |
62 | if (typeof this.config.construct === 'function') {
63 | this.config.construct(ultraviolet, 'service');
64 | };
65 |
66 | const db = await ultraviolet.cookie.db();
67 |
68 | ultraviolet.meta.origin = location.origin;
69 | ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url));
70 |
71 | const requestCtx = new RequestContext(
72 | request,
73 | this,
74 | ultraviolet,
75 | !this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null
76 | );
77 |
78 | if (ultraviolet.meta.url.protocol === 'blob:') {
79 | requestCtx.blob = true;
80 | requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname);
81 | };
82 |
83 | if (request.referrer && request.referrer.startsWith(location.origin)) {
84 | const referer = new URL(ultraviolet.sourceUrl(request.referrer));
85 |
86 | if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') {
87 | requestCtx.headers.origin = referer.origin;
88 | };
89 |
90 | requestCtx.headers.referer = referer.href;
91 | };
92 |
93 | const cookies = await ultraviolet.cookie.getCookies(db) || [];
94 | const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false);
95 |
96 | if (this.browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) {
97 | requestCtx.forward.shift();
98 | };
99 |
100 | if (cookieStr) requestCtx.headers.cookie = cookieStr;
101 | requestCtx.headers.Host = requestCtx.url.host;
102 |
103 |
104 | const reqEvent = new HookEvent(requestCtx, null, null);
105 | this.emit('request', reqEvent);
106 |
107 | if (reqEvent.intercepted) return reqEvent.returnValue;
108 |
109 | const response = await fetch(requestCtx.send);
110 |
111 | if (response.status === 500) {
112 | return Promise.reject('');
113 | };
114 |
115 | const responseCtx = new ResponseContext(requestCtx, response, this);
116 | const resEvent = new HookEvent(responseCtx, null, null);
117 |
118 | this.emit('beforemod', resEvent);
119 | if (resEvent.intercepted) return resEvent.returnValue;
120 |
121 | for (const name of this.headers.csp) {
122 | if (responseCtx.headers[name]) delete responseCtx.headers[name];
123 | };
124 |
125 | if (responseCtx.headers.location) {
126 | responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location);
127 | };
128 |
129 | if (responseCtx.headers['set-cookie']) {
130 | Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => {
131 | self.clients.matchAll().then(function (clients){
132 | clients.forEach(function(client){
133 | client.postMessage({
134 | msg: 'updateCookies',
135 | url: ultraviolet.meta.url.href,
136 | });
137 | });
138 | });
139 | });
140 | delete responseCtx.headers['set-cookie'];
141 | };
142 |
143 | if (responseCtx.body) {
144 | switch(request.destination) {
145 | case 'script':
146 | case 'worker':
147 | responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`;
148 | responseCtx.body += ultraviolet.js.rewrite(
149 | await response.text()
150 | );
151 | break;
152 | case 'style':
153 | responseCtx.body = ultraviolet.rewriteCSS(
154 | await response.text()
155 | );
156 | break;
157 | case 'iframe':
158 | case 'document':
159 | if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) {
160 | responseCtx.body = ultraviolet.rewriteHtml(
161 | await response.text(),
162 | {
163 | document: true ,
164 | injectHead: ultraviolet.createHtmlInject(
165 | this.config.handler,
166 | this.config.bundle,
167 | this.config.config,
168 | ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true),
169 | request.referrer
170 | )
171 | }
172 | );
173 | };
174 | };
175 | };
176 |
177 | if (requestCtx.headers.accept === 'text/event-stream') {
178 | responseCtx.headers['content-type'] = 'text/event-stream';
179 | };
180 |
181 | this.emit('response', resEvent);
182 | if (resEvent.intercepted) return resEvent.returnValue;
183 |
184 | return new Response(responseCtx.body, {
185 | headers: responseCtx.headers,
186 | status: responseCtx.status,
187 | statusText: responseCtx.statusText,
188 | });
189 |
190 | } catch(err) {
191 | return new Response(err.toString(), {
192 | status: 500,
193 | });
194 | };
195 | };
196 | getBarerResponse(response) {
197 | const headers = {};
198 | const raw = JSON.parse(response.headers.get('x-bare-headers'));
199 |
200 | for (const key in raw) {
201 | headers[key.toLowerCase()] = raw[key];
202 | };
203 |
204 | return {
205 | headers,
206 | status: +response.headers.get('x-bare-status'),
207 | statusText: response.headers.get('x-bare-status-text'),
208 | body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null,
209 | };
210 | };
211 | get address() {
212 | return this.addresses[Math.floor(Math.random() * this.addresses.length)];
213 | };
214 | static Ultraviolet = Ultraviolet;
215 | };
216 |
217 | self.UVServiceWorker = UVServiceWorker;
218 |
219 |
220 | class ResponseContext {
221 | constructor(request, response, worker) {
222 | const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : {
223 | status: response.status,
224 | statusText: response.statusText,
225 | headers: Object.fromEntries([...response.headers.entries()]),
226 | body: response.body,
227 | };
228 | this.request = request;
229 | this.raw = response;
230 | this.ultraviolet = request.ultraviolet;
231 | this.headers = headers;
232 | this.status = status;
233 | this.statusText = statusText;
234 | this.body = body;
235 | };
236 | get url() {
237 | return this.request.url;
238 | }
239 | get base() {
240 | return this.request.base;
241 | };
242 | set base(val) {
243 | this.request.base = val;
244 | };
245 | };
246 |
247 | class RequestContext {
248 | constructor(request, worker, ultraviolet, body = null) {
249 | this.ultraviolet = ultraviolet;
250 | this.request = request;
251 | this.headers = Object.fromEntries([...request.headers.entries()]);
252 | this.method = request.method;
253 | this.forward = [...worker.headers.forward];
254 | this.address = worker.address;
255 | this.body = body || null;
256 | this.redirect = request.redirect;
257 | this.credentials = 'omit';
258 | this.mode = request.mode === 'cors' ? request.mode : 'same-origin';
259 | this.blob = false;
260 | };
261 | get send() {
262 | return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), {
263 | method: this.method,
264 | headers: {
265 | 'x-bare-protocol': this.url.protocol,
266 | 'x-bare-host': this.url.hostname,
267 | 'x-bare-path': this.url.pathname + this.url.search,
268 | 'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'),
269 | 'x-bare-headers': JSON.stringify(this.headers),
270 | 'x-bare-forward-headers': JSON.stringify(this.forward),
271 | },
272 | redirect: this.redirect,
273 | credentials: this.credentials,
274 | mode: location.origin !== this.address.origin ? 'cors' : this.mode,
275 | body: this.body
276 | });
277 | };
278 | get url() {
279 | return this.ultraviolet.meta.url;
280 | };
281 | set url(val) {
282 | this.ultraviolet.meta.url = val;
283 | };
284 | get base() {
285 | return this.ultraviolet.meta.base;
286 | };
287 | set base(val) {
288 | this.ultraviolet.meta.base = val;
289 | };
290 | }
291 |
292 | function isHtml(url, contentType = '') {
293 | return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html';
294 | };
295 |
296 | class HookEvent {
297 | #intercepted;
298 | #returnValue;
299 | constructor(data = {}, target = null, that = null) {
300 | this.#intercepted = false;
301 | this.#returnValue = null;
302 | this.data = data;
303 | this.target = target;
304 | this.that = that;
305 | };
306 | get intercepted() {
307 | return this.#intercepted;
308 | };
309 | get returnValue() {
310 | return this.#returnValue;
311 | };
312 | respondWith(input) {
313 | this.#returnValue = input;
314 | this.#intercepted = true;
315 | };
316 | };
317 |
318 | var R = typeof Reflect === 'object' ? Reflect : null
319 | var ReflectApply = R && typeof R.apply === 'function'
320 | ? R.apply
321 | : function ReflectApply(target, receiver, args) {
322 | return Function.prototype.apply.call(target, receiver, args);
323 | }
324 |
325 | var ReflectOwnKeys
326 | if (R && typeof R.ownKeys === 'function') {
327 | ReflectOwnKeys = R.ownKeys
328 | } else if (Object.getOwnPropertySymbols) {
329 | ReflectOwnKeys = function ReflectOwnKeys(target) {
330 | return Object.getOwnPropertyNames(target)
331 | .concat(Object.getOwnPropertySymbols(target));
332 | };
333 | } else {
334 | ReflectOwnKeys = function ReflectOwnKeys(target) {
335 | return Object.getOwnPropertyNames(target);
336 | };
337 | }
338 |
339 | function ProcessEmitWarning(warning) {
340 | if (console && console.warn) console.warn(warning);
341 | }
342 |
343 | var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
344 | return value !== value;
345 | }
346 |
347 | function EventEmitter() {
348 | EventEmitter.init.call(this);
349 | }
350 |
351 | // Backwards-compat with node 0.10.x
352 | EventEmitter.EventEmitter = EventEmitter;
353 |
354 | EventEmitter.prototype._events = undefined;
355 | EventEmitter.prototype._eventsCount = 0;
356 | EventEmitter.prototype._maxListeners = undefined;
357 |
358 | // By default EventEmitters will print a warning if more than 10 listeners are
359 | // added to it. This is a useful default which helps finding memory leaks.
360 | var defaultMaxListeners = 10;
361 |
362 | function checkListener(listener) {
363 | if (typeof listener !== 'function') {
364 | throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
365 | }
366 | }
367 |
368 | Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
369 | enumerable: true,
370 | get: function() {
371 | return defaultMaxListeners;
372 | },
373 | set: function(arg) {
374 | if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
375 | throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
376 | }
377 | defaultMaxListeners = arg;
378 | }
379 | });
380 |
381 | EventEmitter.init = function() {
382 |
383 | if (this._events === undefined ||
384 | this._events === Object.getPrototypeOf(this)._events) {
385 | this._events = Object.create(null);
386 | this._eventsCount = 0;
387 | }
388 |
389 | this._maxListeners = this._maxListeners || undefined;
390 | };
391 |
392 | // Obviously not all Emitters should be limited to 10. This function allows
393 | // that to be increased. Set to zero for unlimited.
394 | EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
395 | if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
396 | throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
397 | }
398 | this._maxListeners = n;
399 | return this;
400 | };
401 |
402 | function _getMaxListeners(that) {
403 | if (that._maxListeners === undefined)
404 | return EventEmitter.defaultMaxListeners;
405 | return that._maxListeners;
406 | }
407 |
408 | EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
409 | return _getMaxListeners(this);
410 | };
411 |
412 | EventEmitter.prototype.emit = function emit(type) {
413 | var args = [];
414 | for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
415 | var doError = (type === 'error');
416 |
417 | var events = this._events;
418 | if (events !== undefined)
419 | doError = (doError && events.error === undefined);
420 | else if (!doError)
421 | return false;
422 |
423 | // If there is no 'error' event listener then throw.
424 | if (doError) {
425 | var er;
426 | if (args.length > 0)
427 | er = args[0];
428 | if (er instanceof Error) {
429 | // Note: The comments on the `throw` lines are intentional, they show
430 | // up in Node's output if this results in an unhandled exception.
431 | throw er; // Unhandled 'error' event
432 | }
433 | // At least give some kind of context to the user
434 | var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
435 | err.context = er;
436 | throw err; // Unhandled 'error' event
437 | }
438 |
439 | var handler = events[type];
440 |
441 | if (handler === undefined)
442 | return false;
443 |
444 | if (typeof handler === 'function') {
445 | ReflectApply(handler, this, args);
446 | } else {
447 | var len = handler.length;
448 | var listeners = arrayClone(handler, len);
449 | for (var i = 0; i < len; ++i)
450 | ReflectApply(listeners[i], this, args);
451 | }
452 |
453 | return true;
454 | };
455 |
456 | function _addListener(target, type, listener, prepend) {
457 | var m;
458 | var events;
459 | var existing;
460 |
461 | checkListener(listener);
462 |
463 | events = target._events;
464 | if (events === undefined) {
465 | events = target._events = Object.create(null);
466 | target._eventsCount = 0;
467 | } else {
468 | // To avoid recursion in the case that type === "newListener"! Before
469 | // adding it to the listeners, first emit "newListener".
470 | if (events.newListener !== undefined) {
471 | target.emit('newListener', type,
472 | listener.listener ? listener.listener : listener);
473 |
474 | // Re-assign `events` because a newListener handler could have caused the
475 | // this._events to be assigned to a new object
476 | events = target._events;
477 | }
478 | existing = events[type];
479 | }
480 |
481 | if (existing === undefined) {
482 | // Optimize the case of one listener. Don't need the extra array object.
483 | existing = events[type] = listener;
484 | ++target._eventsCount;
485 | } else {
486 | if (typeof existing === 'function') {
487 | // Adding the second element, need to change to array.
488 | existing = events[type] =
489 | prepend ? [listener, existing] : [existing, listener];
490 | // If we've already got an array, just append.
491 | } else if (prepend) {
492 | existing.unshift(listener);
493 | } else {
494 | existing.push(listener);
495 | }
496 |
497 | // Check for listener leak
498 | m = _getMaxListeners(target);
499 | if (m > 0 && existing.length > m && !existing.warned) {
500 | existing.warned = true;
501 | // No error code for this since it is a Warning
502 | // eslint-disable-next-line no-restricted-syntax
503 | var w = new Error('Possible EventEmitter memory leak detected. ' +
504 | existing.length + ' ' + String(type) + ' listeners ' +
505 | 'added. Use emitter.setMaxListeners() to ' +
506 | 'increase limit');
507 | w.name = 'MaxListenersExceededWarning';
508 | w.emitter = target;
509 | w.type = type;
510 | w.count = existing.length;
511 | ProcessEmitWarning(w);
512 | }
513 | }
514 |
515 | return target;
516 | }
517 |
518 | EventEmitter.prototype.addListener = function addListener(type, listener) {
519 | return _addListener(this, type, listener, false);
520 | };
521 |
522 | EventEmitter.prototype.on = EventEmitter.prototype.addListener;
523 |
524 | EventEmitter.prototype.prependListener =
525 | function prependListener(type, listener) {
526 | return _addListener(this, type, listener, true);
527 | };
528 |
529 | function onceWrapper() {
530 | if (!this.fired) {
531 | this.target.removeListener(this.type, this.wrapFn);
532 | this.fired = true;
533 | if (arguments.length === 0)
534 | return this.listener.call(this.target);
535 | return this.listener.apply(this.target, arguments);
536 | }
537 | }
538 |
539 | function _onceWrap(target, type, listener) {
540 | var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
541 | var wrapped = onceWrapper.bind(state);
542 | wrapped.listener = listener;
543 | state.wrapFn = wrapped;
544 | return wrapped;
545 | }
546 |
547 | EventEmitter.prototype.once = function once(type, listener) {
548 | checkListener(listener);
549 | this.on(type, _onceWrap(this, type, listener));
550 | return this;
551 | };
552 |
553 | EventEmitter.prototype.prependOnceListener =
554 | function prependOnceListener(type, listener) {
555 | checkListener(listener);
556 | this.prependListener(type, _onceWrap(this, type, listener));
557 | return this;
558 | };
559 |
560 | // Emits a 'removeListener' event if and only if the listener was removed.
561 | EventEmitter.prototype.removeListener =
562 | function removeListener(type, listener) {
563 | var list, events, position, i, originalListener;
564 |
565 | checkListener(listener);
566 |
567 | events = this._events;
568 | if (events === undefined)
569 | return this;
570 |
571 | list = events[type];
572 | if (list === undefined)
573 | return this;
574 |
575 | if (list === listener || list.listener === listener) {
576 | if (--this._eventsCount === 0)
577 | this._events = Object.create(null);
578 | else {
579 | delete events[type];
580 | if (events.removeListener)
581 | this.emit('removeListener', type, list.listener || listener);
582 | }
583 | } else if (typeof list !== 'function') {
584 | position = -1;
585 |
586 | for (i = list.length - 1; i >= 0; i--) {
587 | if (list[i] === listener || list[i].listener === listener) {
588 | originalListener = list[i].listener;
589 | position = i;
590 | break;
591 | }
592 | }
593 |
594 | if (position < 0)
595 | return this;
596 |
597 | if (position === 0)
598 | list.shift();
599 | else {
600 | spliceOne(list, position);
601 | }
602 |
603 | if (list.length === 1)
604 | events[type] = list[0];
605 |
606 | if (events.removeListener !== undefined)
607 | this.emit('removeListener', type, originalListener || listener);
608 | }
609 |
610 | return this;
611 | };
612 |
613 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
614 |
615 | EventEmitter.prototype.removeAllListeners =
616 | function removeAllListeners(type) {
617 | var listeners, events, i;
618 |
619 | events = this._events;
620 | if (events === undefined)
621 | return this;
622 |
623 | // not listening for removeListener, no need to emit
624 | if (events.removeListener === undefined) {
625 | if (arguments.length === 0) {
626 | this._events = Object.create(null);
627 | this._eventsCount = 0;
628 | } else if (events[type] !== undefined) {
629 | if (--this._eventsCount === 0)
630 | this._events = Object.create(null);
631 | else
632 | delete events[type];
633 | }
634 | return this;
635 | }
636 |
637 | // emit removeListener for all listeners on all events
638 | if (arguments.length === 0) {
639 | var keys = Object.keys(events);
640 | var key;
641 | for (i = 0; i < keys.length; ++i) {
642 | key = keys[i];
643 | if (key === 'removeListener') continue;
644 | this.removeAllListeners(key);
645 | }
646 | this.removeAllListeners('removeListener');
647 | this._events = Object.create(null);
648 | this._eventsCount = 0;
649 | return this;
650 | }
651 |
652 | listeners = events[type];
653 |
654 | if (typeof listeners === 'function') {
655 | this.removeListener(type, listeners);
656 | } else if (listeners !== undefined) {
657 | // LIFO order
658 | for (i = listeners.length - 1; i >= 0; i--) {
659 | this.removeListener(type, listeners[i]);
660 | }
661 | }
662 |
663 | return this;
664 | };
665 |
666 | function _listeners(target, type, unwrap) {
667 | var events = target._events;
668 |
669 | if (events === undefined)
670 | return [];
671 |
672 | var evlistener = events[type];
673 | if (evlistener === undefined)
674 | return [];
675 |
676 | if (typeof evlistener === 'function')
677 | return unwrap ? [evlistener.listener || evlistener] : [evlistener];
678 |
679 | return unwrap ?
680 | unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
681 | }
682 |
683 | EventEmitter.prototype.listeners = function listeners(type) {
684 | return _listeners(this, type, true);
685 | };
686 |
687 | EventEmitter.prototype.rawListeners = function rawListeners(type) {
688 | return _listeners(this, type, false);
689 | };
690 |
691 | EventEmitter.listenerCount = function(emitter, type) {
692 | if (typeof emitter.listenerCount === 'function') {
693 | return emitter.listenerCount(type);
694 | } else {
695 | return listenerCount.call(emitter, type);
696 | }
697 | };
698 |
699 | EventEmitter.prototype.listenerCount = listenerCount;
700 | function listenerCount(type) {
701 | var events = this._events;
702 |
703 | if (events !== undefined) {
704 | var evlistener = events[type];
705 |
706 | if (typeof evlistener === 'function') {
707 | return 1;
708 | } else if (evlistener !== undefined) {
709 | return evlistener.length;
710 | }
711 | }
712 |
713 | return 0;
714 | }
715 |
716 | EventEmitter.prototype.eventNames = function eventNames() {
717 | return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
718 | };
719 |
720 | function arrayClone(arr, n) {
721 | var copy = new Array(n);
722 | for (var i = 0; i < n; ++i)
723 | copy[i] = arr[i];
724 | return copy;
725 | }
726 |
727 | function spliceOne(list, index) {
728 | for (; index + 1 < list.length; index++)
729 | list[index] = list[index + 1];
730 | list.pop();
731 | }
732 |
733 | function unwrapListeners(arr) {
734 | var ret = new Array(arr.length);
735 | for (var i = 0; i < ret.length; ++i) {
736 | ret[i] = arr[i].listener || arr[i];
737 | }
738 | return ret;
739 | }
740 |
741 | function once(emitter, name) {
742 | return new Promise(function (resolve, reject) {
743 | function errorListener(err) {
744 | emitter.removeListener(name, resolver);
745 | reject(err);
746 | }
747 |
748 | function resolver() {
749 | if (typeof emitter.removeListener === 'function') {
750 | emitter.removeListener('error', errorListener);
751 | }
752 | resolve([].slice.call(arguments));
753 | };
754 |
755 | eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
756 | if (name !== 'error') {
757 | addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
758 | }
759 | });
760 | }
761 |
762 | function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
763 | if (typeof emitter.on === 'function') {
764 | eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
765 | }
766 | }
767 |
768 | function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
769 | if (typeof emitter.on === 'function') {
770 | if (flags.once) {
771 | emitter.once(name, listener);
772 | } else {
773 | emitter.on(name, listener);
774 | }
775 | } else if (typeof emitter.addEventListener === 'function') {
776 | // EventTarget does not have `error` event semantics like Node
777 | // EventEmitters, we do not listen for `error` events here.
778 | emitter.addEventListener(name, function wrapListener(arg) {
779 | // IE does not have builtin `{ once: true }` support so we
780 | // have to do it manually.
781 | if (flags.once) {
782 | emitter.removeEventListener(name, wrapListener);
783 | }
784 | listener(arg);
785 | });
786 | } else {
787 | throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
788 | }
789 | }
790 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Ultraviolet | Sophisticated Web Proxy
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
43 |
44 |
45 |
46 |
47 |
48 |

50 |
Ultraviolet | TN
51 |
52 |
53 |
Ultraviolet is highly sophisticated proxy used for evading internet censorship.
54 |
55 |
58 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/uv/uv.handler.js:
--------------------------------------------------------------------------------
1 | if (!self.__uv) {
2 | __uvHook(self, self.__uv$config, self.__uv$config.bare);
3 | };
4 |
5 | async function __uvHook(window, config = {}, bare = '/bare/') {
6 | if ('__uv' in window && window.__uv instanceof Ultraviolet) return false;
7 |
8 | if (window.document && !!window.window) {
9 | window.document.querySelectorAll("script[__uv-script]").forEach(node => node.remove())
10 | };
11 |
12 | const worker = !window.window;
13 | const master = '__uv';
14 | const methodPrefix = '__uv$';
15 | const __uv = new Ultraviolet({
16 | ...config,
17 | window,
18 | });
19 |
20 | if (typeof config.construct === 'function') {
21 | config.construct(__uv, worker ? 'worker' : 'window');
22 | };
23 |
24 | const { client } = __uv;
25 | const {
26 | HTMLMediaElement,
27 | HTMLScriptElement,
28 | HTMLAudioElement,
29 | HTMLVideoElement,
30 | HTMLInputElement,
31 | HTMLEmbedElement,
32 | HTMLTrackElement,
33 | HTMLAnchorElement,
34 | HTMLIFrameElement,
35 | HTMLAreaElement,
36 | HTMLLinkElement,
37 | HTMLBaseElement,
38 | HTMLFormElement,
39 | HTMLImageElement,
40 | HTMLSourceElement,
41 | } = window;
42 |
43 | client.nativeMethods.defineProperty(window, '__uv', {
44 | value: __uv,
45 | enumerable: false,
46 | });
47 |
48 |
49 | __uv.meta.origin = location.origin;
50 | __uv.location = client.location.emulate(
51 | (href) => {
52 | if (href === 'about:srcdoc') return new URL(href);
53 | if (href.startsWith('blob:')) href = href.slice('blob:'.length);
54 | return new URL(__uv.sourceUrl(href));
55 | },
56 | (href) => {
57 | return __uv.rewriteUrl(href);
58 | },
59 | );
60 |
61 | __uv.cookieStr = window.__uv$cookies || '';
62 | __uv.meta.url = __uv.location;
63 | __uv.domain = __uv.meta.url.host;
64 | __uv.blobUrls = new window.Map();
65 | __uv.referrer = '';
66 | __uv.cookies = [];
67 | __uv.localStorageObj = {};
68 | __uv.sessionStorageObj = {};
69 |
70 | try {
71 | __uv.bare = new URL(bare, window.location.href);
72 | } catch(e) {
73 | __uv.bare = window.parent.__uv.bare;
74 | };
75 |
76 | if (__uv.location.href === 'about:srcdoc') {
77 | __uv.meta = window.parent.__uv.meta;
78 | };
79 |
80 | if (window.EventTarget) {
81 | __uv.addEventListener = window.EventTarget.prototype.addEventListener;
82 | __uv.removeListener = window.EventTarget.prototype.removeListener;
83 | __uv.dispatchEvent = window.EventTarget.prototype.dispatchEvent;
84 | };
85 |
86 | // Storage wrappers
87 | client.nativeMethods.defineProperty(client.storage.storeProto, '__uv$storageObj', {
88 | get() {
89 | if (this === client.storage.sessionStorage) return __uv.sessionStorageObj;
90 | if (this === client.storage.localStorage) return __uv.localStorageObj;
91 | },
92 | enumerable: false,
93 | });
94 |
95 | if (window.localStorage) {
96 | for (const key in window.localStorage) {
97 | if (key.startsWith(methodPrefix + __uv.location.origin + '@')) {
98 | __uv.localStorageObj[key.slice((methodPrefix + __uv.location.origin + '@').length)] = window.localStorage.getItem(key);
99 | };
100 | };
101 |
102 | __uv.lsWrap = client.storage.emulate(client.storage.localStorage, __uv.localStorageObj);
103 | };
104 |
105 | if (window.sessionStorage) {
106 | for (const key in window.sessionStorage) {
107 | if (key.startsWith(methodPrefix + __uv.location.origin + '@')) {
108 | __uv.sessionStorageObj[key.slice((methodPrefix + __uv.location.origin + '@').length)] = window.sessionStorage.getItem(key);
109 | };
110 | };
111 |
112 | __uv.ssWrap = client.storage.emulate(client.storage.sessionStorage, __uv.sessionStorageObj);
113 | };
114 |
115 |
116 |
117 | let rawBase = window.document ? client.node.baseURI.get.call(window.document) : window.location.href;
118 | let base = __uv.sourceUrl(rawBase);
119 |
120 | client.nativeMethods.defineProperty(__uv.meta, 'base', {
121 | get() {
122 | if (!window.document) return __uv.meta.url.href;
123 |
124 | if (client.node.baseURI.get.call(window.document) !== rawBase) {
125 | rawBase = client.node.baseURI.get.call(window.document);
126 | base = __uv.sourceUrl(rawBase);
127 | };
128 |
129 | return base;
130 | },
131 | });
132 |
133 |
134 | __uv.methods = {
135 | setSource: methodPrefix + 'setSource',
136 | source: methodPrefix + 'source',
137 | location: methodPrefix + 'location',
138 | function: methodPrefix + 'function',
139 | string: methodPrefix + 'string',
140 | eval: methodPrefix + 'eval',
141 | parent: methodPrefix + 'parent',
142 | top: methodPrefix + 'top',
143 | };
144 |
145 | __uv.filterKeys = [
146 | master,
147 | __uv.methods.setSource,
148 | __uv.methods.source,
149 | __uv.methods.location,
150 | __uv.methods.function,
151 | __uv.methods.string,
152 | __uv.methods.eval,
153 | __uv.methods.parent,
154 | __uv.methods.top,
155 | methodPrefix + 'protocol',
156 | methodPrefix + 'storageObj',
157 | methodPrefix + 'url',
158 | methodPrefix + 'modifiedStyle',
159 | methodPrefix + 'config',
160 | methodPrefix + 'dispatched',
161 | 'Ultraviolet',
162 | '__uvHook',
163 | ];
164 |
165 |
166 | client.on('wrap', (target, wrapped) => {
167 | client.nativeMethods.defineProperty(wrapped, 'name', client.nativeMethods.getOwnPropertyDescriptor(target, 'name'));
168 | client.nativeMethods.defineProperty(wrapped, 'length', client.nativeMethods.getOwnPropertyDescriptor(target, 'length'));
169 |
170 | client.nativeMethods.defineProperty(wrapped, __uv.methods.string, {
171 | enumerable: false,
172 | value: client.nativeMethods.fnToString.call(target),
173 | });
174 |
175 | client.nativeMethods.defineProperty(wrapped, __uv.methods.function, {
176 | enumerable: false,
177 | value: target,
178 | });
179 | });
180 |
181 | client.fetch.on('request', event => {
182 | event.data.input = __uv.rewriteUrl(event.data.input);
183 | });
184 |
185 | client.fetch.on('requestUrl', event => {
186 | event.data.value = __uv.sourceUrl(event.data.value);
187 | });
188 |
189 | client.fetch.on('responseUrl', event => {
190 | event.data.value = __uv.sourceUrl(event.data.value);
191 | });
192 |
193 | // XMLHttpRequest
194 | client.xhr.on('open', event => {
195 | event.data.input = __uv.rewriteUrl(event.data.input);
196 | });
197 |
198 | client.xhr.on('responseUrl', event => {
199 | event.data.value = __uv.sourceUrl(event.data.value);
200 | });
201 |
202 |
203 | // Workers
204 | client.workers.on('worker', event => {
205 | event.data.url = __uv.rewriteUrl(event.data.url);
206 | });
207 |
208 | client.workers.on('addModule', event => {
209 | event.data.url = __uv.rewriteUrl(event.data.url);
210 | });
211 |
212 | client.workers.on('importScripts', event => {
213 | for (const i in event.data.scripts) {
214 | event.data.scripts[i] = __uv.rewriteUrl(event.data.scripts[i]);
215 | };
216 | });
217 |
218 | client.workers.on('postMessage', event => {
219 | let to = event.data.origin;
220 |
221 | event.data.origin = '*';
222 | event.data.message = {
223 | __data: event.data.message,
224 | __origin: __uv.meta.url.origin,
225 | __to: to,
226 | };
227 | });
228 |
229 | // Navigator
230 | client.navigator.on('sendBeacon', event => {
231 | event.data.url = __uv.rewriteUrl(event.data.url);
232 | });
233 |
234 | // Cookies
235 | client.document.on('getCookie', event => {
236 | event.data.value = __uv.cookieStr;
237 | });
238 |
239 | client.document.on('setCookie', event => {
240 | Promise.resolve(__uv.cookie.setCookies(event.data.value, __uv.db, __uv.meta)).then(() => {
241 | __uv.cookie.db().then(db => {
242 | __uv.cookie.getCookies(db).then(cookies => {
243 | __uv.cookieStr = __uv.cookie.serialize(cookies, __uv.meta, true);
244 | });
245 | });
246 | });
247 | const cookie = __uv.cookie.setCookie(event.data.value)[0];
248 |
249 | if (!cookie.path) cookie.path = '/';
250 | if (!cookie.domain) cookie.domain = __uv.meta.url.hostname;
251 |
252 | if (__uv.cookie.validateCookie(cookie, __uv.meta, true)) {
253 | if (__uv.cookieStr.length) __uv.cookieStr += '; ';
254 | __uv.cookieStr += `${cookie.name}=${cookie.value}`;
255 | };
256 |
257 | event.respondWith(event.data.value);
258 | });
259 |
260 | // HTML
261 | client.element.on('setInnerHTML', event => {
262 | switch (event.that.tagName) {
263 | case 'SCRIPT':
264 | event.data.value = __uv.js.rewrite(event.data.value);
265 | break;
266 | case 'STYLE':
267 | event.data.value = __uv.rewriteCSS(event.data.value);
268 | break;
269 | default:
270 | event.data.value = __uv.rewriteHtml(event.data.value);
271 | };
272 | });
273 |
274 | client.element.on('getInnerHTML', event => {
275 | switch (event.that.tagName) {
276 | case 'SCRIPT':
277 | event.data.value = __uv.js.source(event.data.value);
278 | break;
279 | default:
280 | event.data.value = __uv.sourceHtml(event.data.value);
281 | };
282 | });
283 |
284 | client.element.on('setOuterHTML', event => {
285 | event.data.value = __uv.rewriteHtml(event.data.value, { document: event.that.tagName === 'HTML' });
286 | });
287 |
288 | client.element.on('getOuterHTML', event => {
289 | switch (event.that.tagName) {
290 | case 'HEAD':
291 | event.data.value = __uv.sourceHtml(
292 | event.data.value.replace(/(.*)<\/head>/s, '$2')
293 | ).replace(/(.*)<\/op-head>/s, '$2');
294 | break;
295 | case 'BODY':
296 | event.data.value = __uv.sourceHtml(
297 | event.data.value.replace(/(.*)<\/body>/s, '$2')
298 | ).replace(/(.*)<\/op-body>/s, '$2');
299 | break;
300 | default:
301 | event.data.value = __uv.sourceHtml(event.data.value, { document: event.that.tagName === 'HTML' });
302 | break;
303 | };
304 |
305 | //event.data.value = __uv.sourceHtml(event.data.value, { document: event.that.tagName === 'HTML' });
306 | });
307 |
308 | client.document.on('write', event => {
309 | if (!event.data.html.length) return false;
310 | event.data.html = [__uv.rewriteHtml(event.data.html.join(''))];
311 | });
312 |
313 | client.document.on('writeln', event => {
314 | if (!event.data.html.length) return false;
315 | event.data.html = [__uv.rewriteHtml(event.data.html.join(''))];
316 | });
317 |
318 | client.element.on('insertAdjacentHTML', event => {
319 | event.data.html = __uv.rewriteHtml(event.data.html);
320 | });
321 |
322 | // EventSource
323 |
324 | client.eventSource.on('construct', event => {
325 | event.data.url = __uv.rewriteUrl(event.data.url);
326 | });
327 |
328 |
329 | client.eventSource.on('url', event => {
330 | event.data.url = __uv.rewriteUrl(event.data.url);
331 | });
332 |
333 | // History
334 | client.history.on('replaceState', event => {
335 | if (event.data.url) event.data.url = __uv.rewriteUrl(event.data.url, '__uv' in event.that ? event.that.__uv.meta : __uv.meta);
336 | });
337 | client.history.on('pushState', event => {
338 | if (event.data.url) event.data.url = __uv.rewriteUrl(event.data.url, '__uv' in event.that ? event.that.__uv.meta : __uv.meta);
339 | });
340 |
341 | // Element get set attribute methods
342 | client.element.on('getAttribute', event => {
343 | if (client.element.hasAttribute.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name)) {
344 | event.respondWith(
345 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name)
346 | );
347 | };
348 | });
349 |
350 | // Message
351 | client.message.on('postMessage', event => {
352 | let to = event.data.origin;
353 | let call = __uv.call;
354 |
355 |
356 | if (event.that) {
357 | call = event.that.__uv$source.call;
358 | };
359 |
360 | event.data.origin = '*';
361 | event.data.message = {
362 | __data: event.data.message,
363 | __origin: (event.that || event.target).__uv$source.location.origin,
364 | __to: to,
365 | };
366 |
367 | event.respondWith(
368 | worker ?
369 | call(event.target, [event.data.message, event.data.transfer], event.that) :
370 | call(event.target, [event.data.message, event.data.origin, event.data.transfer], event.that)
371 | );
372 |
373 | });
374 |
375 | client.message.on('data', event => {
376 | const { value: data } = event.data;
377 | if (typeof data === 'object' && '__data' in data && '__origin' in data) {
378 | event.respondWith(data.__data);
379 | };
380 | });
381 |
382 | client.message.on('origin', event => {
383 | const data = client.message.messageData.get.call(event.that);
384 | if (typeof data === 'object' && data.__data && data.__origin) {
385 | event.respondWith(data.__origin);
386 | };
387 | });
388 |
389 | client.overrideDescriptor(window, 'origin', {
390 | get: (target, that) => {
391 | return __uv.location.origin;
392 | },
393 | });
394 |
395 | client.node.on('baseURI', event => {
396 | if (event.data.value.startsWith(window.location.origin)) event.data.value = __uv.sourceUrl(event.data.value);
397 | });
398 |
399 | client.element.on('setAttribute', event => {
400 | if (event.that instanceof HTMLMediaElement && event.data.name === 'src' && event.data.value.startsWith('blob:')) {
401 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
402 | event.data.value = __uv.blobUrls.get(event.data.value);
403 | return;
404 | };
405 |
406 | if (__uv.attrs.isUrl(event.data.name)) {
407 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
408 | event.data.value = __uv.rewriteUrl(event.data.value);
409 | };
410 |
411 | if (__uv.attrs.isStyle(event.data.name)) {
412 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
413 | event.data.value = __uv.rewriteCSS(event.data.value, { context: 'declarationList' });
414 | };
415 |
416 | if (__uv.attrs.isHtml(event.data.name)) {
417 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
418 | event.data.value = __uv.rewriteHtml(event.data.value, {...__uv.meta, document: true, injectHead:__uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.configScript, __uv.cookieStr, window.location.href) });
419 | };
420 |
421 | if (__uv.attrs.isSrcset(event.data.name)) {
422 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
423 | event.data.value = __uv.html.wrapSrcset(event.data.value);
424 | };
425 |
426 | if (__uv.attrs.isForbidden(event.data.name)) {
427 | event.data.name = __uv.attributePrefix + '-attr-' + event.data.name;
428 | };
429 | });
430 |
431 | client.element.on('audio', event => {
432 | event.data.url = __uv.rewriteUrl(event.data.url);
433 | });
434 |
435 | // Element Property Attributes
436 | client.element.hookProperty([HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement, HTMLBaseElement], 'href', {
437 | get: (target, that) => {
438 | return __uv.sourceUrl(
439 | target.call(that)
440 | );
441 | },
442 | set: (target, that, [val]) => {
443 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-href', val)
444 | target.call(that, __uv.rewriteUrl(val));
445 | },
446 | });
447 |
448 | client.element.hookProperty([HTMLScriptElement, HTMLAudioElement, HTMLVideoElement, HTMLMediaElement, HTMLImageElement, HTMLInputElement, HTMLEmbedElement, HTMLIFrameElement, HTMLTrackElement, HTMLSourceElement], 'src', {
449 | get: (target, that) => {
450 | return __uv.sourceUrl(
451 | target.call(that)
452 | );
453 | },
454 | set: (target, that, [val]) => {
455 | if (new String(val).toString().trim().startsWith('blob:') && that instanceof HTMLMediaElement) {
456 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-src', val)
457 | return target.call(that, __uv.blobUrls.get(val) || val);
458 | };
459 |
460 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-src', val)
461 | target.call(that, __uv.rewriteUrl(val));
462 | },
463 | });
464 |
465 | client.element.hookProperty([HTMLFormElement], 'action', {
466 | get: (target, that) => {
467 | return __uv.sourceUrl(
468 | target.call(that)
469 | );
470 | },
471 | set: (target, that, [val]) => {
472 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-action', val)
473 | target.call(that, __uv.rewriteUrl(val));
474 | },
475 | });
476 |
477 | client.element.hookProperty([HTMLImageElement], 'srcset', {
478 | get: (target, that) => {
479 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-srcset') || target.call(that);
480 | },
481 | set: (target, that, [val]) => {
482 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-srcset', val)
483 | target.call(that, __uv.html.wrapSrcset(val));
484 | },
485 | });
486 |
487 | client.element.hookProperty(HTMLScriptElement, 'integrity', {
488 | get: (target, that) => {
489 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-integrity');
490 | },
491 | set: (target, that, [val]) => {
492 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-integrity', val);
493 | },
494 | });
495 |
496 | client.element.hookProperty(HTMLIFrameElement, 'sandbox', {
497 | get: (target, that) => {
498 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-sandbox') || target.call(that);
499 | },
500 | set: (target, that, [val]) => {
501 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-sandbox', val);
502 | },
503 | });
504 |
505 | client.element.hookProperty(HTMLIFrameElement, 'contentWindow', {
506 | get: (target, that) => {
507 | const win = target.call(that);
508 | try {
509 | if (!win.__uv) __uvHook(win, config, bare);
510 | return win;
511 | } catch (e) {
512 | return win;
513 | };
514 | },
515 | });
516 |
517 | client.element.hookProperty(HTMLIFrameElement, 'contentDocument', {
518 | get: (target, that) => {
519 | const doc = target.call(that);
520 | try {
521 | const win = doc.defaultView
522 | if (!win.__uv) __uvHook(win, config, bare);
523 | return doc;
524 | } catch (e) {
525 | return win;
526 | };
527 | },
528 | });
529 |
530 | client.element.hookProperty(HTMLIFrameElement, 'srcdoc', {
531 | get: (target, that) => {
532 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-srcdoc') || target.call(that);
533 | },
534 | set: (target, that, [val]) => {
535 | target.call(that, __uv.rewriteHtml(val, {
536 | document: true,
537 | injectHead: __uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.configScript, __uv.cookieStr, window.location.href)
538 | }))
539 | },
540 | });
541 |
542 | client.node.on('getTextContent', event => {
543 | if (event.that.tagName === 'SCRIPT') {
544 | event.data.value = __uv.js.source(event.data.value);
545 | };
546 | });
547 |
548 | client.node.on('setTextContent', event => {
549 | if (event.that.tagName === 'SCRIPT') {
550 | event.data.value = __uv.js.rewrite(event.data.value);
551 | };
552 | });
553 |
554 | // Until proper rewriting is implemented for service workers.
555 | // Not sure atm how to implement it with the already built in service worker
556 | if ('serviceWorker' in window.navigator) {
557 | delete window.Navigator.prototype.serviceWorker;
558 | };
559 |
560 | // Document
561 | client.document.on('getDomain', event => {
562 | event.data.value = __uv.domain;
563 | });
564 | client.document.on('setDomain', event => {
565 | if (!event.data.value.toString().endsWith(__uv.meta.url.hostname.split('.').slice(-2).join('.'))) return event.respondWith('');
566 | event.respondWith(__uv.domain = event.data.value);
567 | })
568 |
569 | client.document.on('url', event => {
570 | event.data.value = __uv.location.href;
571 | });
572 |
573 | client.document.on('documentURI', event => {
574 | event.data.value = __uv.location.href;
575 | });
576 |
577 | client.document.on('referrer', event => {
578 | event.data.value = __uv.referrer || __uv.sourceUrl(event.data.value);
579 | });
580 |
581 | client.document.on('parseFromString', event => {
582 | if (event.data.type !== 'text/html') return false;
583 | event.data.string = __uv.rewriteHtml(event.data.string, {...__uv.meta, document: true, });
584 | });
585 |
586 | // Attribute (node.attributes)
587 | client.attribute.on('getValue', event => {
588 | if (client.element.hasAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name)) {
589 | event.data.value = client.element.getAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name);
590 | };
591 | });
592 |
593 | client.attribute.on('setValue', event => {
594 | if (__uv.attrs.isUrl(event.data.name)) {
595 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
596 | event.data.value = __uv.rewriteUrl(event.data.value);
597 | };
598 |
599 | if (__uv.attrs.isStyle(event.data.name)) {
600 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
601 | event.data.value = __uv.rewriteCSS(event.data.value, { context: 'declarationList' });
602 | };
603 |
604 | if (__uv.attrs.isHtml(event.data.name)) {
605 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
606 | event.data.value = __uv.rewriteHtml(event.data.value, {...__uv.meta, document: true, injectHead:__uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.configScript, __uv.cookieStr, window.location.href) });
607 | };
608 |
609 | if (__uv.attrs.isSrcset(event.data.name)) {
610 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
611 | event.data.value = __uv.html.wrapSrcset(event.data.value);
612 | };
613 |
614 | });
615 |
616 | // URL
617 | client.url.on('createObjectURL', event => {
618 | let url = event.target.call(event.that, event.data.object);
619 | if (url.startsWith('blob:' + location.origin)) {
620 | let newUrl = 'blob:' + (__uv.meta.url.href !== 'about:blank' ? __uv.meta.url.origin : window.parent.__uv.meta.url.origin) + url.slice('blob:'.length + location.origin.length);
621 | __uv.blobUrls.set(newUrl, url);
622 | event.respondWith(newUrl);
623 | } else {
624 | event.respondWith(url);
625 | };
626 | });
627 |
628 | client.url.on('revokeObjectURL', event => {
629 | if (__uv.blobUrls.has(event.data.url)) {
630 | const old = event.data.url;
631 | event.data.url = __uv.blobUrls.get(event.data.url);
632 | __uv.blobUrls.delete(old);
633 | };
634 | });
635 |
636 | client.storage.on('get', event => {
637 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
638 | });
639 |
640 | client.storage.on('set', event => {
641 | if (event.that.__uv$storageObj) {
642 | event.that.__uv$storageObj[event.data.name] = event.data.value;
643 | };
644 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
645 | });
646 |
647 | client.storage.on('delete', event => {
648 | if (event.that.__uv$storageObj) {
649 | delete event.that.__uv$storageObj[event.data.name];
650 | };
651 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
652 | });
653 |
654 | client.storage.on('getItem', event => {
655 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
656 | });
657 |
658 | client.storage.on('setItem', event => {
659 | if (event.that.__uv$storageObj) {
660 | event.that.__uv$storageObj[event.data.name] = event.data.value;
661 | };
662 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
663 | });
664 |
665 | client.storage.on('removeItem', event => {
666 | if (event.that.__uv$storageObj) {
667 | delete event.that.__uv$storageObj[event.data.name];
668 | };
669 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
670 | });
671 |
672 | client.storage.on('clear', event => {
673 | if (event.that.__uv$storageObj) {
674 | for (const key of client.nativeMethods.keys.call(null, event.that.__uv$storageObj)) {
675 | delete event.that.__uv$storageObj[key];
676 | client.storage.removeItem.call(event.that, methodPrefix + __uv.meta.url.origin + '@' + key);
677 | event.respondWith();
678 | };
679 | };
680 | });
681 |
682 | client.storage.on('length', event => {
683 | if (event.that.__uv$storageObj) {
684 | event.respondWith(client.nativeMethods.keys.call(null, event.that.__uv$storageObj).length);
685 | };
686 | });
687 |
688 | client.storage.on('key', event => {
689 | if (event.that.__uv$storageObj) {
690 | event.respondWith(
691 | (client.nativeMethods.keys.call(null, event.that.__uv$storageObj)[event.data.index] || null)
692 | );
693 | };
694 | });
695 |
696 | client.websocket.on('websocket', async event => {
697 | let url;
698 | try {
699 | url = new URL(event.data.url);
700 | } catch(e) {
701 | return;
702 | };
703 |
704 | const headers = {
705 | Host: url.host,
706 | Origin: __uv.meta.url.origin,
707 | Pragma: 'no-cache',
708 | 'Cache-Control': 'no-cache',
709 | Upgrade: 'websocket',
710 | 'User-Agent': window.navigator.userAgent,
711 | 'Connection': 'Upgrade',
712 | };
713 |
714 | const cookies = __uv.cookie.serialize(__uv.cookies, { url }, false);
715 |
716 | if (cookies) headers.Cookie = cookies;
717 | const protocols = [...event.data.protocols];
718 |
719 | const remote = {
720 | protocol: url.protocol,
721 | host: url.hostname,
722 | port: url.port || (url.protocol === 'wss:' ? '443' : '80'),
723 | path: url.pathname + url.search,
724 | };
725 |
726 | if (protocols.length) headers['Sec-WebSocket-Protocol'] = protocols.join(', ');
727 |
728 | event.data.url = (__uv.bare.protocol === 'https:' ? 'wss://' : 'ws://') + __uv.bare.host + __uv.bare.pathname + 'v1/';
729 | event.data.protocols = [
730 | 'bare',
731 | __uv.encodeProtocol(JSON.stringify({
732 | remote,
733 | headers,
734 | forward_headers: [
735 | 'accept',
736 | 'accept-encoding',
737 | 'accept-language',
738 | 'sec-websocket-extensions',
739 | 'sec-websocket-key',
740 | 'sec-websocket-version',
741 | ],
742 | })),
743 | ];
744 |
745 | const ws = new event.target(event.data.url, event.data.protocols);
746 |
747 | client.nativeMethods.defineProperty(ws, methodPrefix + 'url', {
748 | enumerable: false,
749 | value: url.href,
750 | });
751 |
752 | event.respondWith(
753 | ws
754 | );
755 | });
756 |
757 | client.websocket.on('url', event => {
758 | if ('__uv$url' in event.that) {
759 | event.data.value = event.that.__uv$url;
760 | };
761 | });
762 |
763 | client.websocket.on('protocol', event => {
764 | if ('__uv$protocol' in event.that) {
765 | event.data.value = event.that.__uv$protocol;
766 | };
767 | });
768 |
769 | client.function.on('function', event => {
770 | event.data.script = __uv.rewriteJS(event.data.script);
771 | });
772 |
773 | client.function.on('toString', event => {
774 | if (__uv.methods.string in event.that) event.respondWith(event.that[__uv.methods.string]);
775 | });
776 |
777 | client.object.on('getOwnPropertyNames', event => {
778 | event.data.names = event.data.names.filter(element => !(__uv.filterKeys.includes(element)));
779 | });
780 |
781 | client.object.on('getOwnPropertyDescriptors', event => {
782 | for (const forbidden of __uv.filterKeys) {
783 | delete event.data.descriptors[forbidden];
784 | };
785 |
786 | });
787 |
788 | client.style.on('setProperty', event => {
789 | if (client.style.dashedUrlProps.includes(event.data.property)) {
790 | event.data.value = __uv.rewriteCSS(event.data.value, {
791 | context: 'value',
792 | ...__uv.meta
793 | })
794 | };
795 | });
796 |
797 | client.style.on('getPropertyValue', event => {
798 | if (client.style.dashedUrlProps.includes(event.data.property)) {
799 | event.respondWith(
800 | __uv.sourceCSS(
801 | event.target.call(event.that, event.data.property),
802 | {
803 | context: 'value',
804 | ...__uv.meta
805 | }
806 | )
807 | );
808 | };
809 | });
810 |
811 | if ('CSS2Properties' in window) {
812 | for (const key of client.style.urlProps) {
813 | client.overrideDescriptor(window.CSS2Properties.prototype, key, {
814 | get: (target, that) => {
815 | return __uv.sourceCSS(
816 | target.call(that),
817 | {
818 | context: 'value',
819 | ...__uv.meta
820 | }
821 | )
822 | },
823 | set: (target, that, val) => {
824 | target.call(
825 | that,
826 | __uv.rewriteCSS(val, {
827 | context: 'value',
828 | ...__uv.meta
829 | })
830 | );
831 | }
832 | });
833 | };
834 | } else if ('HTMLElement' in window) {
835 |
836 | client.overrideDescriptor(
837 | window.HTMLElement.prototype,
838 | 'style',
839 | {
840 | get: (target, that) => {
841 | const value = target.call(that);
842 | if (!value[methodPrefix + 'modifiedStyle']) {
843 |
844 | for (const key of client.style.urlProps) {
845 | client.nativeMethods.defineProperty(value, key, {
846 | enumerable: true,
847 | configurable: true,
848 | get() {
849 | const value = client.style.getPropertyValue.call(this, key) || '';
850 | return __uv.sourceCSS(
851 | value,
852 | {
853 | context: 'value',
854 | ...__uv.meta
855 | }
856 | )
857 | },
858 | set(val) {
859 | client.style.setProperty.call(this,
860 | (client.style.propToDashed[key] || key),
861 | __uv.rewriteCSS(val, {
862 | context: 'value',
863 | ...__uv.meta
864 | })
865 | )
866 | }
867 | });
868 | client.nativeMethods.defineProperty(value, methodPrefix + 'modifiedStyle', {
869 | enumerable: false,
870 | value: true
871 | });
872 | };
873 | };
874 | return value;
875 | }
876 | }
877 | );
878 | };
879 |
880 | client.style.on('setCssText', event => {
881 | event.data.value = __uv.rewriteCSS(event.data.value, {
882 | context: 'declarationList',
883 | ...__uv.meta
884 | });
885 | });
886 |
887 | client.style.on('getCssText', event => {
888 | event.data.value = __uv.sourceCSS(event.data.value, {
889 | context: 'declarationList',
890 | ...__uv.meta
891 | });
892 | });
893 |
894 | // Proper hash emulation.
895 | if (!!window.window) {
896 | __uv.addEventListener.call(window, 'hashchange', event => {
897 | if (event.__uv$dispatched) return false;
898 | event.stopImmediatePropagation();
899 | const hash = window.location.hash;
900 | client.history.replaceState.call(window.history, '', '', event.oldURL);
901 | __uv.location.hash = hash;
902 | });
903 | };
904 |
905 | client.location.on('hashchange', (oldUrl, newUrl, ctx) => {
906 | if (ctx.HashChangeEvent && client.history.replaceState) {
907 | client.history.replaceState.call(window.history, '', '', __uv.rewriteUrl(newUrl));
908 |
909 | const event = new ctx.HashChangeEvent('hashchange', { newURL: newUrl, oldURL: oldUrl });
910 |
911 | client.nativeMethods.defineProperty(event, methodPrefix + 'dispatched', {
912 | value: true,
913 | enumerable: false,
914 | });
915 |
916 | __uv.dispatchEvent.call(window, event);
917 | };
918 | });
919 |
920 | // Hooking functions & descriptors
921 | client.fetch.overrideRequest();
922 | client.fetch.overrideUrl();
923 | client.xhr.overrideOpen();
924 | client.xhr.overrideResponseUrl();
925 | client.element.overrideHtml();
926 | client.element.overrideAttribute();
927 | client.element.overrideInsertAdjacentHTML();
928 | client.element.overrideAudio();
929 | // client.element.overrideQuerySelector();
930 | client.node.overrideBaseURI();
931 | client.node.overrideTextContent();
932 | client.attribute.overrideNameValue();
933 | client.document.overrideDomain();
934 | client.document.overrideURL();
935 | client.document.overrideDocumentURI();
936 | client.document.overrideWrite();
937 | client.document.overrideReferrer();
938 | client.document.overrideParseFromString();
939 | client.storage.overrideMethods();
940 | client.storage.overrideLength();
941 | //client.document.overrideQuerySelector();
942 | client.object.overrideGetPropertyNames();
943 | client.object.overrideGetOwnPropertyDescriptors();
944 | client.history.overridePushState();
945 | client.history.overrideReplaceState();
946 | client.eventSource.overrideConstruct();
947 | client.eventSource.overrideUrl();
948 | client.websocket.overrideWebSocket();
949 | client.websocket.overrideProtocol();
950 | client.websocket.overrideUrl();
951 | client.url.overrideObjectURL();
952 | client.document.overrideCookie();
953 | client.message.overridePostMessage();
954 | client.message.overrideMessageOrigin();
955 | client.message.overrideMessageData();
956 | client.workers.overrideWorker();
957 | client.workers.overrideAddModule();
958 | client.workers.overrideImportScripts();
959 | client.workers.overridePostMessage();
960 | client.style.overrideSetGetProperty();
961 | client.style.overrideCssText();
962 | client.navigator.overrideSendBeacon();
963 | client.function.overrideFunction();
964 | client.function.overrideToString();
965 | client.location.overrideWorkerLocation(
966 | (href) => {
967 | return new URL(__uv.sourceUrl(href));
968 | }
969 | );
970 |
971 | client.overrideDescriptor(window, 'localStorage', {
972 | get: (target, that) => {
973 | return (that || window).__uv.lsWrap;
974 | },
975 | });
976 | client.overrideDescriptor(window, 'sessionStorage', {
977 | get: (target, that) => {
978 | return (that || window).__uv.ssWrap;
979 | },
980 | });
981 |
982 |
983 | client.override(window, 'open', (target, that, args) => {
984 | if (!args.length) return target.apply(that, args);
985 | let [url] = args;
986 |
987 | url = __uv.rewriteUrl(url);
988 |
989 | return target.call(that, url);
990 | });
991 |
992 | __uv.$wrap = function(name) {
993 | if (name === 'location') return __uv.methods.location;
994 | if (name === 'eval') return __uv.methods.eval;
995 | return name;
996 | };
997 |
998 |
999 | __uv.$get = function(that) {
1000 | if (that === window.location) return __uv.location;
1001 | if (that === window.eval) return __uv.eval;
1002 | if (that === window.parent) {
1003 | return window.__uv$parent;
1004 | };
1005 | if (that === window.top) {
1006 | return window.__uv$top;
1007 | };
1008 | return that;
1009 | };
1010 |
1011 | __uv.eval = client.wrap(window, 'eval', (target, that, args) => {
1012 | if (!args.length || typeof args[0] !== 'string') return target.apply(that, args);
1013 | let [script] = args;
1014 |
1015 | script = __uv.rewriteJS(script);
1016 | return target.call(that, script);
1017 | });
1018 |
1019 | __uv.call = function(target, args, that) {
1020 | return that ? target.apply(that, args) : target(...args);
1021 | };
1022 |
1023 | __uv.call$ = function(obj, prop, args = []) {
1024 | return obj[prop].apply(obj, args);
1025 | };
1026 |
1027 | client.nativeMethods.defineProperty(window.Object.prototype, master, {
1028 | get: () => {
1029 | return __uv;
1030 | },
1031 | enumerable: false
1032 | });
1033 |
1034 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.setSource, {
1035 | value: function(source) {
1036 | if (!client.nativeMethods.isExtensible(this)) return this;
1037 |
1038 | client.nativeMethods.defineProperty(this, __uv.methods.source, {
1039 | value: source,
1040 | writable: true,
1041 | enumerable: false
1042 | });
1043 |
1044 | return this;
1045 | },
1046 | enumerable: false,
1047 | });
1048 |
1049 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.source, {
1050 | value: __uv,
1051 | writable: true,
1052 | enumerable: false
1053 | });
1054 |
1055 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.location, {
1056 | configurable: true,
1057 | get() {
1058 | return (this === window.document || this === window) ? __uv.location : this.location;
1059 | },
1060 | set(val) {
1061 | if (this === window.document || this === window) {
1062 | __uv.location.href = val;
1063 | } else {
1064 | this.location = val;
1065 | };
1066 | },
1067 | });
1068 |
1069 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.parent, {
1070 | configurable: true,
1071 | get() {
1072 | const val = this.parent;
1073 |
1074 | if (this === window) {
1075 | try {
1076 | return '__uv' in val ? val : this;
1077 | } catch (e) {
1078 | return this;
1079 | };
1080 | };
1081 | return val;
1082 | },
1083 | set(val) {
1084 | this.parent = val;
1085 | },
1086 | });
1087 |
1088 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.top, {
1089 | configurable: true,
1090 | get() {
1091 | const val = this.top;
1092 |
1093 | if (this === window) {
1094 | if (val === this.parent) return this[__uv.methods.parent];
1095 | try {
1096 | if (!('__uv' in val)) {
1097 | let current = this;
1098 |
1099 | while (current.parent !== val) {
1100 | current = current.parent
1101 | };
1102 |
1103 | return '__uv' in current ? current : this;
1104 |
1105 | } else {
1106 | return val;
1107 | };
1108 | } catch (e) {
1109 | return this;
1110 | };
1111 | };
1112 | return val;
1113 | },
1114 | set(val) {
1115 | this.top = val;
1116 | },
1117 | });
1118 |
1119 |
1120 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.eval, {
1121 | configurable: true,
1122 | get() {
1123 | return this === window ? __uv.eval : this.eval;
1124 | },
1125 | set(val) {
1126 | this.eval = val;
1127 | },
1128 | });
1129 | };
--------------------------------------------------------------------------------