mirror of
https://github.com/titaniumnetwork-dev/Ultraviolet.git
synced 2025-05-16 04:50:01 -04:00
t
This commit is contained in:
parent
82f5f76588
commit
2c06fed426
25 changed files with 1131 additions and 604 deletions
|
@ -3,7 +3,7 @@ import path from "path";
|
|||
|
||||
const __dirname = path.resolve(path.dirname(decodeURI(new URL(import.meta.url).pathname))).slice(3);
|
||||
|
||||
console.log(__dirname);
|
||||
console.log(path.resolve(path.dirname(decodeURI(new URL(import.meta.url).pathname))), __dirname);
|
||||
|
||||
webpack({
|
||||
mode: 'none',
|
||||
|
|
|
@ -15,6 +15,7 @@ import NavigatorApi from "./navigator.js";
|
|||
import Workers from "./worker.js";
|
||||
import URLApi from "./url.js";
|
||||
import EventEmitter from "./events.js";
|
||||
import StorageApi from "./storage.js";
|
||||
|
||||
class UVClient extends EventEmitter {
|
||||
constructor(window = self, worker = !window.window) {
|
||||
|
@ -25,9 +26,14 @@ class UVClient extends EventEmitter {
|
|||
defineProperty: this.window.Object.defineProperty,
|
||||
getOwnPropertyDescriptor: this.window.Object.getOwnPropertyDescriptor,
|
||||
getOwnPropertyDescriptors: this.window.Object.getOwnPropertyDescriptors,
|
||||
getOwnPropertyNames: this.window.Object.getOwnPropertyNames,
|
||||
keys: this.window.Object.keys,
|
||||
getOwnPropertySymbols: this.window.Object.getOwnPropertySymbols,
|
||||
isArray: this.window.Array.isArray,
|
||||
setPrototypeOf: this.window.Object.setPrototypeOf,
|
||||
isExtensible: this.window.Object.isExtensible,
|
||||
Map: this.window.Map,
|
||||
Proxy: this.window.Proxy,
|
||||
};
|
||||
this.worker = worker;
|
||||
this.fetch = new Fetch(this);
|
||||
|
@ -46,6 +52,7 @@ class UVClient extends EventEmitter {
|
|||
this.url = new URLApi(this);
|
||||
this.workers = new Workers(this);
|
||||
this.location = new LocationApi(this);
|
||||
this.storage = new StorageApi(this);
|
||||
};
|
||||
initLocation(rewriteUrl, sourceUrl) {
|
||||
this.location = new LocationApi(this, sourceUrl, rewriteUrl, this.worker);
|
||||
|
|
|
@ -95,6 +95,18 @@ class LocationApi {
|
|||
});
|
||||
};
|
||||
|
||||
if ('ancestorOrigins' in this.location) {
|
||||
this.ctx.nativeMethods.defineProperty(emulation, 'ancestorOrigins', {
|
||||
get() {
|
||||
const arr = [];
|
||||
if (that.window.DOMStringList) that.ctx.nativeMethods.setPrototypeOf(arr, that.window.DOMStringList.prototype);
|
||||
return arr;
|
||||
},
|
||||
set: undefined,
|
||||
enumerable: true,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
this.ctx.nativeMethods.defineProperty(emulation, 'toString', {
|
||||
value: this.ctx.wrap(this.location, 'toString', () => {
|
||||
|
|
155
client/storage.js
Normal file
155
client/storage.js
Normal file
|
@ -0,0 +1,155 @@
|
|||
import EventEmitter from "./events.js";
|
||||
import HookEvent from "./hook.js";
|
||||
|
||||
class StorageApi extends EventEmitter {
|
||||
constructor(ctx) {
|
||||
super();
|
||||
this.ctx = ctx;
|
||||
this.window = ctx.window;
|
||||
this.localStorage = this.window.localStorage || null;
|
||||
this.sessionStorage = this.window.sessionStorage || null;
|
||||
this.Storage = this.window.Storage || {};
|
||||
this.storeProto = this.Storage.prototype || {};
|
||||
this.getItem = this.storeProto.getItem || null;
|
||||
this.setItem = this.storeProto.setItem || null;
|
||||
this.removeItem = this.storeProto.removeItem || null;
|
||||
this.clear = this.storeProto.clear || null;
|
||||
this.key = this.storeProto.key || null;
|
||||
this.methods = ['key', 'getItem', 'setItem', 'removeItem', 'clear'];
|
||||
this.wrappers = new ctx.nativeMethods.Map();
|
||||
};
|
||||
overrideMethods() {
|
||||
this.ctx.override(this.storeProto, 'getItem', (target, that, args) => {
|
||||
if (!args.length) return target.apply((this.wrappers.get(that) || that), args);
|
||||
let [ name ] = args;
|
||||
|
||||
const event = new HookEvent({ name }, target, (this.wrappers.get(that) || that));
|
||||
this.emit('getItem', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
return event.target.call(event.that, event.data.name);
|
||||
});
|
||||
this.ctx.override(this.storeProto, 'setItem', (target, that, args) => {
|
||||
if (2 > args.length) return target.apply((this.wrappers.get(that) || that), args);
|
||||
let [ name, value ] = args;
|
||||
|
||||
const event = new HookEvent({ name, value }, target, (this.wrappers.get(that) || that));
|
||||
this.emit('setItem', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
return event.target.call(event.that, event.data.name, event.data.value);
|
||||
});
|
||||
this.ctx.override(this.storeProto, 'removeItem', (target, that, args) => {
|
||||
if (!args.length) return target.apply((this.wrappers.get(that) || that), args);
|
||||
let [ name ] = args;
|
||||
|
||||
const event = new HookEvent({ name }, target, (this.wrappers.get(that) || that));
|
||||
this.emit('removeItem', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
return event.target.call(event.that, event.data.name);
|
||||
});
|
||||
this.ctx.override(this.storeProto, 'clear', (target, that) => {
|
||||
const event = new HookEvent(null, target, (this.wrappers.get(that) || that));
|
||||
this.emit('clear', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
return event.target.call(event.that);
|
||||
});
|
||||
this.ctx.override(this.storeProto, 'key', (target, that, args) => {
|
||||
if (!args.length) return target.apply((this.wrappers.get(that) || that), args);
|
||||
let [ index ] = args;
|
||||
|
||||
const event = new HookEvent({ index }, target, (this.wrappers.get(that) || that));
|
||||
this.emit('key', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
return event.target.call(event.that, event.data.index);
|
||||
});
|
||||
};
|
||||
overrideLength() {
|
||||
this.ctx.overrideDescriptor(this.storeProto, 'length', {
|
||||
get: (target, that) => {
|
||||
const event = new HookEvent({ length: target.call((this.wrappers.get(that) || that)) }, target, (this.wrappers.get(that) || that));
|
||||
this.emit('length', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
return event.data.length;
|
||||
},
|
||||
});
|
||||
};
|
||||
emulate(storage, obj = {}) {
|
||||
this.ctx.nativeMethods.setPrototypeOf(obj, this.storeProto);
|
||||
|
||||
const proxy = new this.ctx.window.Proxy(obj, {
|
||||
get: (target, prop) => {
|
||||
if (prop in this.storeProto || typeof prop === 'symbol') return storage[prop];
|
||||
|
||||
const event = new HookEvent({ name: prop }, null, storage);
|
||||
this.emit('get', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
return storage[event.data.name];
|
||||
},
|
||||
set: (target, prop, value) => {
|
||||
if (prop in this.storeProto || typeof prop === 'symbol') return storage[prop] = value;
|
||||
|
||||
const event = new HookEvent({ name: prop, value }, null, storage);
|
||||
this.emit('set', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
|
||||
return storage[event.data.name] = event.data.value;
|
||||
},
|
||||
deleteProperty: (target, prop) => {
|
||||
if (typeof prop === 'symbol') return delete storage[prop];
|
||||
|
||||
const event = new HookEvent({ name: prop }, null, storage);
|
||||
this.emit('delete', event);
|
||||
|
||||
if (event.intercepted) return event.returnValue;
|
||||
|
||||
return delete storage[event.data.name];
|
||||
},
|
||||
});
|
||||
|
||||
this.wrappers.set(proxy, storage);
|
||||
this.ctx.nativeMethods.setPrototypeOf(proxy, this.storeProto);
|
||||
|
||||
return proxy;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
export default StorageApi;
|
||||
|
||||
|
||||
class StorageWrapper {
|
||||
constructor(api, storage, wrap, unwrap, origin) {
|
||||
this.api = api;
|
||||
this.ctx = api.ctx;
|
||||
this.storage = storage;
|
||||
this.wrap = wrap;
|
||||
this.unwrap = unwrap;
|
||||
this.origin = origin;
|
||||
this.emulation = {};
|
||||
};
|
||||
clear() {
|
||||
for (const key in this.storage) {
|
||||
const data = this.unwrap(key);
|
||||
if (!data || data.origin !== this.origin) continue;
|
||||
this.api.removeItem.call(this.storage, key);
|
||||
};
|
||||
this.emulation = {};
|
||||
this.ctx.nativeMethods.setPrototypeOf(this.emulation, this.api.storeProto);
|
||||
};
|
||||
__init() {
|
||||
for (const key in this.storage) {
|
||||
const data = this.unwrap(key);
|
||||
if (!data || data.origin !== this.origin) continue;
|
||||
|
||||
this.emulation[data.name] = this.api.getItem.call(this.storage, key);
|
||||
};
|
||||
this.ctx.nativeMethods.setPrototypeOf(this.emulation, this.api.storeProto);
|
||||
};
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"prefix": "/service/",
|
||||
"prefix": "/sw/",
|
||||
"bare": "/bare/"
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/uv.sw.js', {
|
||||
scope: '/service/'
|
||||
scope: '/sw/'
|
||||
});
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
location.reload()
|
||||
|
|
21
example/static/iframe.html
Normal file
21
example/static/iframe.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
iframe {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="https://localhost/sw/hvtrs8%2F-wuw%2Cgmoelg.aoo%2F"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -14,4 +14,4 @@
|
|||
addEventListener('message', e => console.log(e.source.postMessage !== window.postMessage));
|
||||
</script>
|
||||
</body>
|
||||
</html>__uv$get(frame, 'postMessage', __uv)
|
||||
</html>
|
2
index.js
2
index.js
|
@ -18,7 +18,7 @@ console.log(
|
|||
*/
|
||||
|
||||
console.log(
|
||||
uv.rewriteJS('window.eval(saasdsd)')
|
||||
uv.rewriteJS('top')
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
382
lib/uv.bundle.js
382
lib/uv.bundle.js
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,11 @@
|
|||
if (!self.__uv) {
|
||||
__uvHook(self, {
|
||||
prefix: '/sw/',
|
||||
encodeUrl: Ultraviolet.codec.xor.encode,
|
||||
decodeUrl: Ultraviolet.codec.xor.decode,
|
||||
});
|
||||
};
|
||||
|
||||
async function __uvHook(window, config = {}) {
|
||||
if ('__uv' in window && window.__uv instanceof Ultraviolet) return false;
|
||||
|
||||
|
@ -37,6 +45,7 @@ async function __uvHook(window, config = {}) {
|
|||
__uv.meta.origin = location.origin;
|
||||
__uv.location = client.location.emulate(
|
||||
(href) => {
|
||||
if (href === 'about:srcdoc') return new URL(href);
|
||||
if (href.startsWith('blob:')) href = href.slice('blob:'.length);
|
||||
return new URL(__uv.sourceUrl(href));
|
||||
},
|
||||
|
@ -51,6 +60,44 @@ async function __uvHook(window, config = {}) {
|
|||
__uv.blobUrls = new window.Map();
|
||||
__uv.referrer = '';
|
||||
__uv.cookies = [];
|
||||
__uv.localStorageObj = {};
|
||||
__uv.sessionStorageObj = {};
|
||||
|
||||
if (__uv.location.href === 'about:srcdoc') {
|
||||
__uv.meta = window.parent.__uv.meta;
|
||||
};
|
||||
|
||||
// Storage wrappers
|
||||
|
||||
client.nativeMethods.defineProperty(client.storage.storeProto, '__uv$storageObj', {
|
||||
get() {
|
||||
if (this === client.storage.sessionStorage) return __uv.sessionStorageObj;
|
||||
if (this === client.storage.localStorage) return __uv.localStorageObj;
|
||||
},
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
if (window.localStorage) {
|
||||
for (const key in window.localStorage) {
|
||||
if (key.startsWith(methodPrefix + __uv.location.origin + '@')) {
|
||||
__uv.localStorageObj[key.slice((methodPrefix + __uv.location.origin + '@').length)] = window.localStorage.getItem(key);
|
||||
};
|
||||
};
|
||||
|
||||
__uv.lsWrap = client.storage.emulate(client.storage.localStorage, __uv.localStorageObj);
|
||||
};
|
||||
|
||||
if (window.sessionStorage) {
|
||||
for (const key in window.sessionStorage) {
|
||||
if (key.startsWith(methodPrefix + __uv.location.origin + '@')) {
|
||||
__uv.sessionStorageObj[key.slice((methodPrefix + __uv.location.origin + '@').length)] = window.sessionStorage.getItem(key);
|
||||
};
|
||||
};
|
||||
|
||||
__uv.ssWrap = client.storage.emulate(client.storage.sessionStorage, __uv.sessionStorageObj);
|
||||
};
|
||||
|
||||
|
||||
|
||||
let rawBase = window.document ? client.node.baseURI.get.call(window.document) : window.location.href;
|
||||
let base = __uv.sourceUrl(rawBase);
|
||||
|
@ -76,6 +123,8 @@ async function __uvHook(window, config = {}) {
|
|||
function: methodPrefix + 'function',
|
||||
string: methodPrefix + 'string',
|
||||
eval: methodPrefix + 'eval',
|
||||
parent: methodPrefix + 'parent',
|
||||
top: methodPrefix + 'top',
|
||||
};
|
||||
|
||||
__uv.filterKeys = [
|
||||
|
@ -86,6 +135,9 @@ async function __uvHook(window, config = {}) {
|
|||
__uv.methods.function,
|
||||
__uv.methods.string,
|
||||
__uv.methods.eval,
|
||||
__uv.methods.parent,
|
||||
__uv.methods.top,
|
||||
methodPrefix + 'storageObj',
|
||||
'Ultraviolet',
|
||||
'__uvHook',
|
||||
];
|
||||
|
@ -343,7 +395,12 @@ async function __uvHook(window, config = {}) {
|
|||
|
||||
if (__uv.attrs.isHtml(event.data.name)) {
|
||||
event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
|
||||
event.data.value = __uv.rewriteHtml(event.data.value, { ...__uv.meta, document: true });
|
||||
event.data.value = __uv.rewriteHtml(event.data.value, {...__uv.meta, document: true, injectHead: __uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.cookieStr, window.location.href) });
|
||||
};
|
||||
|
||||
if (__uv.attrs.isSrcset(event.data.name)) {
|
||||
event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
|
||||
event.data.value = __uv.html.wrapSrcset(event.data.value);
|
||||
};
|
||||
|
||||
if (__uv.attrs.isForbidden(event.data.name)) {
|
||||
|
@ -397,6 +454,16 @@ async function __uvHook(window, config = {}) {
|
|||
},
|
||||
});
|
||||
|
||||
client.element.hookProperty([HTMLImageElement], 'srcset', {
|
||||
get: (target, that) => {
|
||||
return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-srcset') || target.call(that);
|
||||
},
|
||||
set: (target, that, [val]) => {
|
||||
client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-srcset', val)
|
||||
target.call(that, __uv.html.wrapSrcset(val));
|
||||
},
|
||||
});
|
||||
|
||||
client.element.hookProperty(HTMLScriptElement, 'integrity', {
|
||||
get: (target, that) => {
|
||||
return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-integrity');
|
||||
|
@ -419,7 +486,7 @@ async function __uvHook(window, config = {}) {
|
|||
get: (target, that) => {
|
||||
const win = target.call(that);
|
||||
try {
|
||||
if (!win.__uv) __uvHook(win);
|
||||
if (!win.__uv) __uvHook(win, config);
|
||||
return win;
|
||||
} catch (e) {
|
||||
return win;
|
||||
|
@ -432,7 +499,7 @@ async function __uvHook(window, config = {}) {
|
|||
const doc = target.call(that);
|
||||
try {
|
||||
const win = doc.defaultView
|
||||
if (!win.__uv) __uvHook(win);
|
||||
if (!win.__uv) __uvHook(win, config);
|
||||
return doc;
|
||||
} catch (e) {
|
||||
return win;
|
||||
|
@ -440,6 +507,18 @@ async function __uvHook(window, config = {}) {
|
|||
},
|
||||
});
|
||||
|
||||
client.element.hookProperty(HTMLIFrameElement, 'srcdoc', {
|
||||
get: (target, that) => {
|
||||
return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-srcdoc') || target.call(that);
|
||||
},
|
||||
set: (target, that, [val]) => {
|
||||
target.call(that, __uv.rewriteHtml(val, {
|
||||
document: true,
|
||||
injectHead: __uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.cookieStr, window.location.href)
|
||||
}))
|
||||
},
|
||||
});
|
||||
|
||||
client.node.on('getTextContent', event => {
|
||||
if (event.that.tagName === 'SCRIPT') {
|
||||
event.data.value = __uv.js.source(event.data.value);
|
||||
|
@ -498,17 +577,14 @@ async function __uvHook(window, config = {}) {
|
|||
|
||||
if (__uv.attrs.isHtml(event.data.name)) {
|
||||
client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
|
||||
event.data.value = __uv.rewriteHtml(event.data.value, { ...__uv.meta, document: true });
|
||||
event.data.value = __uv.rewriteHtml(event.data.value, {...__uv.meta, document: true, injectHead: __uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.cookieStr, window.location.href) });
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
client.override(window.JSON, 'stringify', (target, that, args) => {
|
||||
try {
|
||||
return target.apply(that, args);
|
||||
} catch (e) {
|
||||
console.log(e, 'Error', args);
|
||||
if (__uv.attrs.isSrcset(event.data.name)) {
|
||||
client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value);
|
||||
event.data.value = __uv.html.wrapSrcset(event.data.value);
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
// URL
|
||||
|
@ -532,6 +608,70 @@ async function __uvHook(window, config = {}) {
|
|||
};
|
||||
});
|
||||
|
||||
client.storage.on('refresh', event => {
|
||||
|
||||
});
|
||||
|
||||
client.storage.on('get', event => {
|
||||
event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
|
||||
});
|
||||
|
||||
client.storage.on('set', event => {
|
||||
if (event.that.__uv$storageObj) {
|
||||
event.that.__uv$storageObj[event.data.name] = event.data.value;
|
||||
};
|
||||
event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
|
||||
});
|
||||
|
||||
client.storage.on('delete', event => {
|
||||
if (event.that.__uv$storageObj) {
|
||||
delete event.that.__uv$storageObj[event.data.name];
|
||||
};
|
||||
event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
|
||||
});
|
||||
|
||||
client.storage.on('getItem', event => {
|
||||
event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
|
||||
});
|
||||
|
||||
client.storage.on('setItem', event => {
|
||||
if (event.that.__uv$storageObj) {
|
||||
event.that.__uv$storageObj[event.data.name] = event.data.value;
|
||||
};
|
||||
event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
|
||||
});
|
||||
|
||||
client.storage.on('removeItem', event => {
|
||||
if (event.that.__uv$storageObj) {
|
||||
delete event.that.__uv$storageObj[event.data.name];
|
||||
};
|
||||
event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name;
|
||||
});
|
||||
|
||||
client.storage.on('clear', event => {
|
||||
if (event.that.__uv$storageObj) {
|
||||
for (const key of client.nativeMethods.keys.call(null, event.that.__uv$storageObj)) {
|
||||
delete event.that.__uv$storageObj[key];
|
||||
client.storage.removeItem.call(event.that, methodPrefix + __uv.meta.url.origin + '@' + key);
|
||||
event.respondWith();
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
client.storage.on('length', event => {
|
||||
if (event.that.__uv$storageObj) {
|
||||
event.respondWith(client.nativeMethods.keys.call(null, event.that.__uv$storageObj).length);
|
||||
};
|
||||
});
|
||||
|
||||
client.storage.on('key', event => {
|
||||
if (event.that.__uv$storageObj) {
|
||||
event.respondWith(
|
||||
(client.nativeMethods.keys.call(null, event.that.__uv$storageObj)[event.data.index] || null)
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
client.websocket.on('websocket', event => {
|
||||
const url = new URL(event.data.url);
|
||||
|
||||
|
@ -590,8 +730,6 @@ async function __uvHook(window, config = {}) {
|
|||
});
|
||||
|
||||
client.object.on('getOwnPropertyDescriptors', event => {
|
||||
console.log(event.data.descriptors);
|
||||
|
||||
for (const forbidden of __uv.filterKeys) {
|
||||
delete event.data.descriptors[forbidden];
|
||||
};
|
||||
|
@ -617,6 +755,8 @@ async function __uvHook(window, config = {}) {
|
|||
client.document.overrideWrite();
|
||||
client.document.overrideReferrer();
|
||||
client.document.overrideParseFromString();
|
||||
client.storage.overrideMethods();
|
||||
client.storage.overrideLength();
|
||||
//client.document.overrideQuerySelector();
|
||||
client.object.overrideGetPropertyNames();
|
||||
client.object.overrideGetOwnPropertyDescriptors();
|
||||
|
@ -643,20 +783,34 @@ async function __uvHook(window, config = {}) {
|
|||
(href) => {
|
||||
return new URL(__uv.sourceUrl(href));
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
client.nativeMethods.defineProperty(__uv, '$wrap', {
|
||||
get() {
|
||||
return function(name) {
|
||||
client.overrideDescriptor(window, 'localStorage', {
|
||||
get: (target, that) => {
|
||||
return (that || window).__uv.lsWrap;
|
||||
},
|
||||
});
|
||||
client.overrideDescriptor(window, 'sessionStorage', {
|
||||
get: (target, that) => {
|
||||
return (that || window).__uv.ssWrap;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
client.override(window, 'open', (target, that, args) => {
|
||||
if (!args.length) return target.apply(that, args);
|
||||
let [url] = args;
|
||||
|
||||
url = __uv.rewriteUrl(url);
|
||||
|
||||
return target.call(that, url);
|
||||
});
|
||||
|
||||
__uv.$wrap = function(name) {
|
||||
if (name === 'location') return __uv.methods.location;
|
||||
if (name === 'eval') return __uv.methods.eval;
|
||||
return name;
|
||||
};
|
||||
},
|
||||
set: val => {
|
||||
console.log('waht the fuck', val);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__uv.$get = function(that) {
|
||||
|
@ -690,9 +844,6 @@ async function __uvHook(window, config = {}) {
|
|||
|
||||
client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.setSource, {
|
||||
value: function(source) {
|
||||
|
||||
if (!client.nativeMethods.isExtensible(this)) console.log('you suck');
|
||||
|
||||
if (!client.nativeMethods.isExtensible(this)) return this;
|
||||
|
||||
client.nativeMethods.defineProperty(this, __uv.methods.source, {
|
||||
|
@ -715,7 +866,6 @@ async function __uvHook(window, config = {}) {
|
|||
client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.location, {
|
||||
configurable: true,
|
||||
get() {
|
||||
|
||||
return (this === window.document || this === window) ? __uv.location : this.location;
|
||||
},
|
||||
set(val) {
|
||||
|
@ -727,6 +877,57 @@ async function __uvHook(window, config = {}) {
|
|||
},
|
||||
});
|
||||
|
||||
client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.parent, {
|
||||
configurable: true,
|
||||
get() {
|
||||
const val = this.parent;
|
||||
|
||||
if (this === window) {
|
||||
try {
|
||||
return '__uv' in val ? val : this;
|
||||
} catch (e) {
|
||||
return this;
|
||||
};
|
||||
};
|
||||
return val;
|
||||
},
|
||||
set(val) {
|
||||
this.parent = val;
|
||||
},
|
||||
});
|
||||
|
||||
client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.top, {
|
||||
configurable: true,
|
||||
get() {
|
||||
const val = this.top;
|
||||
|
||||
if (this === window) {
|
||||
if (val === this.parent) return this[__uv.methods.parent];
|
||||
try {
|
||||
if (!('__uv' in val)) {
|
||||
let current = this;
|
||||
|
||||
while (current.parent !== val) {
|
||||
current = current.parent
|
||||
};
|
||||
|
||||
return '__uv' in current ? current : this;
|
||||
|
||||
} else {
|
||||
return val;
|
||||
};
|
||||
} catch (e) {
|
||||
return this;
|
||||
};
|
||||
};
|
||||
return val;
|
||||
},
|
||||
set(val) {
|
||||
this.top = val;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.eval, {
|
||||
configurable: true,
|
||||
get() {
|
||||
|
@ -737,7 +938,3 @@ async function __uvHook(window, config = {}) {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (!self.__uv) {
|
||||
__uvHook(self, {});
|
||||
};
|
37
lib/uv.sw.js
37
lib/uv.sw.js
|
@ -40,7 +40,6 @@ const headers = {
|
|||
],
|
||||
forward: [
|
||||
'accept-encoding',
|
||||
'accept',
|
||||
'connection',
|
||||
'content-length',
|
||||
'content-type',
|
||||
|
@ -64,7 +63,11 @@ const statusCode = {
|
|||
],
|
||||
};
|
||||
|
||||
const handler = UVServiceWorker('/bare/v1/', {});
|
||||
const handler = UVServiceWorker('/bare/v1/', {
|
||||
prefix: '/sw/',
|
||||
encodeUrl: Ultraviolet.codec.xor.encode,
|
||||
decodeUrl: Ultraviolet.codec.xor.decode,
|
||||
});
|
||||
|
||||
addEventListener('fetch',
|
||||
async event => {
|
||||
|
@ -73,6 +76,11 @@ addEventListener('fetch',
|
|||
},
|
||||
);
|
||||
|
||||
// Immediate activation.
|
||||
addEventListener('install', () => {
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
|
||||
function UVServiceWorker(bare = '/bare/v1/', options) {
|
||||
try {
|
||||
|
@ -103,6 +111,7 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
uv.meta.origin = location.origin;
|
||||
uv.meta.base = uv.meta.url = new URL(uv.sourceUrl(request.url));
|
||||
|
||||
/*
|
||||
uv.html.on('element', (element, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (element.tagName !== 'head') return false;
|
||||
|
@ -144,6 +153,7 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
}
|
||||
);
|
||||
});
|
||||
*/
|
||||
|
||||
if (uv.meta.url.protocol === 'blob:') {
|
||||
requestCtx.blob = true;
|
||||
|
@ -153,6 +163,8 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
|
||||
requestCtx.headers = Object.fromEntries([...request.headers.entries()]);
|
||||
|
||||
requestCtx.host = uv.meta.url.host;
|
||||
|
||||
if (request.referrer && request.referrer.startsWith(location.origin)) {
|
||||
const referer = new URL(uv.sourceUrl(request.referrer));
|
||||
|
||||
|
@ -165,6 +177,14 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
|
||||
const cookies = await uv.cookie.getCookies(db) || [];
|
||||
const cookieStr = uv.cookie.serialize(cookies, uv.meta, false);
|
||||
|
||||
const browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName();
|
||||
const forward = [...headers.forward];
|
||||
|
||||
if (browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) {
|
||||
forward.shift();
|
||||
};
|
||||
|
||||
if (cookieStr) requestCtx.headers.cookie = cookieStr;
|
||||
|
||||
const bareHeaders = {
|
||||
|
@ -173,7 +193,7 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
'x-bare-path': uv.meta.url.pathname + uv.meta.url.search,
|
||||
'x-bare-port': uv.meta.url.port,
|
||||
'x-bare-headers': JSON.stringify(requestCtx.headers),
|
||||
'x-bare-forward-headers': JSON.stringify(requestCtx.forward),
|
||||
'x-bare-forward-headers': JSON.stringify(forward),
|
||||
};
|
||||
|
||||
const fetchOptions = {
|
||||
|
@ -185,7 +205,9 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
};
|
||||
if (requestCtx.body) fetchOptions.body = requestCtx.body;
|
||||
|
||||
|
||||
const response = await fetch(requestCtx.url, fetchOptions);
|
||||
|
||||
if (response.status === 500) {
|
||||
return Promise.reject('Err');
|
||||
};
|
||||
|
@ -252,7 +274,12 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
await response.text(),
|
||||
{
|
||||
document: true ,
|
||||
cookies,
|
||||
injectHead: uv.createHtmlInject(
|
||||
scripts.handler,
|
||||
scripts.package,
|
||||
uv.cookie.serialize(cookies, uv.meta, true),
|
||||
request.referrer
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -269,14 +296,12 @@ function UVServiceWorker(bare = '/bare/v1/', options) {
|
|||
statusText: responseCtx.statusText,
|
||||
});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
return new Response(e.toString(), {
|
||||
status: 500,
|
||||
});
|
||||
};
|
||||
};
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
return (event) => {
|
||||
event.respondWith(new Response(e.toString(), {
|
||||
status: 500,
|
||||
|
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -9,6 +9,7 @@
|
|||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bowser": "^2.11.0",
|
||||
"css-tree": "^2.0.4",
|
||||
"esotope-hammerhead": "^0.6.1",
|
||||
"idb": "^7.0.0",
|
||||
|
@ -333,6 +334,11 @@
|
|||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/bowser": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
|
||||
"integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
|
@ -1908,6 +1914,11 @@
|
|||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"bowser": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
|
||||
"integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"bowser": "^2.11.0",
|
||||
"css-tree": "^2.0.4",
|
||||
"esotope-hammerhead": "^0.6.1",
|
||||
"idb": "^7.0.0",
|
||||
|
|
|
@ -36,7 +36,23 @@ async function db(openDB) {
|
|||
|
||||
function serialize(cookies = [], meta, js) {
|
||||
let str = '';
|
||||
const now = new Date();
|
||||
for (const cookie of cookies) {
|
||||
|
||||
let expired = false;
|
||||
|
||||
if (cookie.set) {
|
||||
if (cookie.maxAge) {
|
||||
expired = cookie.set.getTime() + (cookie.maxAge * 1e3) < now;
|
||||
} else if (cookie.expires) {
|
||||
expired = cookie.expires > now;
|
||||
};
|
||||
};
|
||||
|
||||
if (expired) {
|
||||
|
||||
continue;
|
||||
};
|
||||
if (!validateCookie(cookie, meta, js)) continue;
|
||||
if (str.length) str += '; ';
|
||||
str += cookie.name;
|
||||
|
@ -47,11 +63,30 @@ function serialize(cookies = [], meta, js) {
|
|||
};
|
||||
|
||||
async function getCookies(db) {
|
||||
return await db.getAll('cookies');
|
||||
const now = new Date();
|
||||
return (await db.getAll('cookies')).filter(cookie => {
|
||||
|
||||
let expired = false;
|
||||
if (cookie.set) {
|
||||
if (cookie.maxAge) {
|
||||
expired = (cookie.set.getTime() + (cookie.maxAge * 1e3)) < now;
|
||||
} else if (cookie.expires) {
|
||||
expired = new Date(cookie.expires.toLocaleString()) < now;
|
||||
};
|
||||
};
|
||||
|
||||
if (expired) {
|
||||
db.delete('cookies', cookie.id);
|
||||
return false;
|
||||
};
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
function setCookies(data, db, meta) {
|
||||
if (!db) return false;
|
||||
|
||||
const cookies = setCookie(data, {
|
||||
decodeValues: false,
|
||||
})
|
||||
|
@ -67,6 +102,7 @@ function setCookies(data, db, meta) {
|
|||
db.put('cookies', {
|
||||
...cookie,
|
||||
id: `${cookie.domain}@${cookie.path}@${cookie.name}`,
|
||||
set: new Date(Date.now()),
|
||||
});
|
||||
};
|
||||
return true;
|
||||
|
|
|
@ -28,7 +28,6 @@ class CSS extends EventEmitter {
|
|||
});
|
||||
return this.generate(ast);
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
return str;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,13 +5,15 @@ import setCookie from 'set-cookie-parser';
|
|||
import { xor, base64, plain } from './codecs.js';
|
||||
import mimeTypes from './mime.js';
|
||||
import { validateCookie, db, getCookies, setCookies, serialize } from './cookie.js';
|
||||
import { attributes, isUrl, isForbidden, isHtml, isSrcset, isStyle, text } from './rewrite.html.js';
|
||||
import { attributes, isUrl, isForbidden, isHtml, isSrcset, isStyle, text, injectHead, createInjection } from './rewrite.html.js';
|
||||
import { importStyle, url } from './rewrite.css.js';
|
||||
//import { call, destructureDeclaration, dynamicImport, getProperty, importDeclaration, setProperty, sourceMethods, wrapEval, wrapIdentifier } from './rewrite.script.js';
|
||||
import { dynamicImport, identifier, importDeclaration, property, unwrap, wrapEval } from './rewrite.script.test.js';
|
||||
import { dynamicImport, identifier, importDeclaration, property, unwrap, wrapEval } from './rewrite.script.js';
|
||||
import { openDB } from 'idb';
|
||||
import parsel from './parsel.js';
|
||||
import UVClient from '../client/index.js';
|
||||
import Bowser from 'bowser';
|
||||
|
||||
|
||||
const valid_chars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
|
||||
const reserved_chars = "%";
|
||||
|
@ -29,6 +31,8 @@ class Ultraviolet {
|
|||
this.meta = options.meta || {};
|
||||
this.meta.base ||= undefined;
|
||||
this.meta.origin ||= '';
|
||||
this.bundleScript = options.bundleScript || '/uv.bundle.js';
|
||||
this.handlerScript = options.handlerScript || '/uv.handler.js';
|
||||
this.meta.url ||= this.meta.base || '';
|
||||
this.codec = Ultraviolet.codec;
|
||||
this.html = new HTML(this);
|
||||
|
@ -36,10 +40,12 @@ class Ultraviolet {
|
|||
this.js = new JS(this);
|
||||
this.parsel = parsel;
|
||||
this.openDB = this.constructor.openDB;
|
||||
this.Bowser = this.constructor.Bowser;
|
||||
this.client = typeof self !== 'undefined' ? new UVClient((options.window || self)) : null;
|
||||
this.master = '__uv';
|
||||
this.dataPrefix = '__uv$';
|
||||
this.attributePrefix = '__uv';
|
||||
this.createHtmlInject = createInjection;
|
||||
this.attrs = {
|
||||
isUrl,
|
||||
isForbidden,
|
||||
|
@ -133,31 +139,17 @@ class Ultraviolet {
|
|||
// HTML
|
||||
attributes(this);
|
||||
text(this);
|
||||
|
||||
injectHead(this);
|
||||
// CSS
|
||||
url(this);
|
||||
importStyle(this);
|
||||
|
||||
// JS
|
||||
/*
|
||||
getProperty(this);
|
||||
call(this)
|
||||
setProperty(this);
|
||||
sourceMethods(this);
|
||||
importDeclaration(this);
|
||||
dynamicImport(this);
|
||||
wrapEval(this);
|
||||
wrapIdentifier(this)
|
||||
*/
|
||||
|
||||
importDeclaration(this);
|
||||
dynamicImport(this);
|
||||
property(this);
|
||||
wrapEval(this);
|
||||
identifier(this);
|
||||
unwrap(this);
|
||||
|
||||
//destructureDeclaration(this)
|
||||
};
|
||||
get rewriteHtml() {
|
||||
return this.html.rewrite.bind(this.html);
|
||||
|
@ -181,6 +173,7 @@ class Ultraviolet {
|
|||
static mime = mimeTypes;
|
||||
static setCookie = setCookie;
|
||||
static openDB = openDB;
|
||||
static Bowser = Bowser;
|
||||
};
|
||||
|
||||
export default Ultraviolet;
|
||||
|
|
|
@ -90,33 +90,4 @@ class JS extends EventEmitter {
|
|||
};
|
||||
};
|
||||
|
||||
class NodeEvent extends EventEmitter {
|
||||
constructor(node, parent = null) {
|
||||
super();
|
||||
this._node = node;
|
||||
for (let key in node) {
|
||||
Object.defineProperty(this, key, {
|
||||
get: () => node[key],
|
||||
sel: val => node[key] = val,
|
||||
});
|
||||
};
|
||||
this.parent = parent;
|
||||
};
|
||||
iterate(handler) {
|
||||
for (const key in this._node) {
|
||||
if (key === 'parent') continue;
|
||||
if (Array.isArray(this._node[key])) {
|
||||
this._node[key].forEach(entry => {
|
||||
const child = new this.constructor(entry, this._node);
|
||||
handler(child);
|
||||
});
|
||||
} else {
|
||||
const child = new this.constructor(entry, this._node);
|
||||
walk(this._node[key], this._node, handler);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export default JS;
|
||||
export { NodeEvent };
|
|
@ -1,5 +1,5 @@
|
|||
function attributes(ctx, meta = ctx.meta) {
|
||||
const { html, js, css, attributePrefix } = ctx;
|
||||
const { html, js, css, attributePrefix, handlerScript, bundleScript } = ctx;
|
||||
const origPrefix = attributePrefix + '-attr-';
|
||||
|
||||
html.on('attr', (attr, type) => {
|
||||
|
@ -20,7 +20,11 @@ function attributes(ctx, meta = ctx.meta) {
|
|||
|
||||
if (type === 'rewrite' && isHtml(attr.name)) {
|
||||
attr.node.setAttribute(origPrefix + attr.name, attr.value);
|
||||
attr.value = html.rewrite(attr.value, { ...meta, document: true });
|
||||
attr.value = html.rewrite(attr.value, {
|
||||
...meta,
|
||||
document: true,
|
||||
injectHead: attr.options.injectHead || [],
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
@ -154,6 +158,53 @@ function isEvent(name) {
|
|||
].indexOf(name) > -1;
|
||||
};
|
||||
|
||||
function injectHead(ctx) {
|
||||
const { html, js, css, attributePrefix } = ctx;
|
||||
const origPrefix = attributePrefix + '-attr-';
|
||||
html.on('element', (element, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (element.tagName !== 'head') return false;
|
||||
if (!('injectHead' in element.options)) return false;
|
||||
|
||||
element.childNodes.unshift(
|
||||
...element.options.injectHead
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
function createInjection(handler = '/uv.handler.js', bundle = '/uv.bundle.js', cookies = '', referrer = '') {
|
||||
return [
|
||||
{
|
||||
tagName: 'script',
|
||||
nodeName: 'script',
|
||||
childNodes: [
|
||||
{
|
||||
nodeName: '#text',
|
||||
value: `window.__uv$cookies = atob("${btoa(cookies)}");\nwindow.__uv$referrer = atob("${btoa(referrer)}");`
|
||||
},
|
||||
],
|
||||
attrs: [],
|
||||
skip: true,
|
||||
},
|
||||
{
|
||||
tagName: 'script',
|
||||
nodeName: 'script',
|
||||
childNodes: [],
|
||||
attrs: [
|
||||
{ name: 'src', value: bundle, skip: true }
|
||||
],
|
||||
},
|
||||
{
|
||||
tagName: 'script',
|
||||
nodeName: 'script',
|
||||
childNodes: [],
|
||||
attrs: [
|
||||
{ name: 'src', value: handler, skip: true }
|
||||
],
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
function isForbidden(name) {
|
||||
return ['http-equiv', 'integrity', 'sandbox', 'nonce', 'crossorigin'].indexOf(name) > -1;
|
||||
};
|
||||
|
@ -171,4 +222,4 @@ function isSrcset(name) {
|
|||
};
|
||||
|
||||
|
||||
export { attributes, text, isUrl, isEvent, isForbidden, isHtml, isStyle, isSrcset };
|
||||
export { attributes, createInjection, text, isUrl, isEvent, isForbidden, isHtml, isStyle, isSrcset, injectHead };
|
|
@ -1,384 +1,92 @@
|
|||
import { Syntax } from 'esotope-hammerhead';
|
||||
const master = '__uv';
|
||||
const methodPrefix = '__uv$';
|
||||
const uvMethods = {
|
||||
get: methodPrefix + 'get',
|
||||
proxy: methodPrefix + 'proxy',
|
||||
call: methodPrefix + 'call',
|
||||
set: methodPrefix + 'set',
|
||||
script: methodPrefix + 'script',
|
||||
url: methodPrefix + 'url',
|
||||
object: methodPrefix + 'obj'
|
||||
};
|
||||
const uvMethodTypes = {
|
||||
[methodPrefix + 'get']: 'get',
|
||||
[methodPrefix + 'proxy']: 'proxy',
|
||||
[methodPrefix + 'call']: 'call',
|
||||
[methodPrefix + 'set']: 'set',
|
||||
[methodPrefix + 'script']: 'script',
|
||||
[methodPrefix + 'url']: 'url',
|
||||
[methodPrefix + 'obj']: 'object'
|
||||
};
|
||||
const shortHandAssignment = {
|
||||
'+=': '+',
|
||||
'-=': '-',
|
||||
'*=': '*',
|
||||
'/=': '/',
|
||||
'%=': '%',
|
||||
'**=': '**',
|
||||
'<<=': '<<',
|
||||
'>>=': '>>',
|
||||
'>>>=': '>>>',
|
||||
'&=': '&',
|
||||
'^=': '^',
|
||||
'|=': '|',
|
||||
};
|
||||
const assignmentOperators = ['=', '+=', '-=', '*=', '/=', '%=', '**=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
|
||||
|
||||
|
||||
function getProperty(ctx) {
|
||||
function property(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.MemberExpression, (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (node.object.type === Syntax.Super)
|
||||
return false;
|
||||
if (node.parent.type === Syntax.AssignmentExpression && node.parent.left === node)
|
||||
return false;
|
||||
if (node.parent.type === Syntax.CallExpression && node.parent.callee === node)
|
||||
return false;
|
||||
if (node.parent.type === Syntax.UnaryExpression && node.parent.operator === 'delete')
|
||||
return false;
|
||||
if (node.parent.type === Syntax.UpdateExpression && (node.parent.operator === '++' || parent.operator === '--'))
|
||||
return false;
|
||||
if (node.parent.type === Syntax.NewExpression && node.parent.callee === node)
|
||||
return false;
|
||||
if (node.parent.type === Syntax.ForInStatement && node.parent.left === node) return false;
|
||||
if (node.computed && node.property.type === Syntax.Literal && !shouldWrapProperty(node.property.value))
|
||||
return false;
|
||||
if (!node.computed && node.property.type === Syntax.Identifier && !shouldWrapProperty(node.property.name))
|
||||
return false;
|
||||
js.on('MemberExpression', (node, data, type) => {
|
||||
if (node.object.type === 'Super') return false;
|
||||
|
||||
if (type === 'rewrite' && computedProperty(node)) {
|
||||
data.changes.push({
|
||||
node: `${uvMethods.get}((`,
|
||||
start: node.start,
|
||||
end: node.object.start,
|
||||
})
|
||||
|
||||
node.object.iterateEnd = function () {
|
||||
data.changes.push({
|
||||
start: node.object.end,
|
||||
node: '__uv.$wrap((',
|
||||
start: node.property.start,
|
||||
end: node.property.start,
|
||||
});
|
||||
|
||||
data.changes.push({
|
||||
node: '), ('
|
||||
});
|
||||
|
||||
if (node.computed) {
|
||||
node.property.iterateEnd = function () {
|
||||
})
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
node: '))',
|
||||
start: node.property.end,
|
||||
end: node.end,
|
||||
node: `), ${master}, true)`
|
||||
end: node.property.end,
|
||||
});
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
if (!node.computed && node.property.name === 'location' && type === 'rewrite' || node.property.name === '__uv$location' && type === 'source') {
|
||||
data.changes.push({
|
||||
start: node.property.start,
|
||||
end: node.property.end,
|
||||
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$location' : 'location'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
if (!node.computed && node.property.name === 'top' && type === 'rewrite' || node.property.name === '__uv$top' && type === 'source') {
|
||||
data.changes.push({
|
||||
start: node.property.start,
|
||||
end: node.property.end,
|
||||
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$top' : 'top'
|
||||
});
|
||||
};
|
||||
|
||||
if (!node.computed && node.property.name === 'parent' && type === 'rewrite' || node.property.name === '__uv$parent' && type === 'source') {
|
||||
data.changes.push({
|
||||
start: node.property.start,
|
||||
end: node.property.end,
|
||||
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$parent' : 'parent'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
if (!node.computed && node.property.name === 'postMessage' && type === 'rewrite') {
|
||||
data.changes.push({
|
||||
start: node.property.start,
|
||||
end: node.property.end,
|
||||
node:'__uv$setSource(__uv).postMessage',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
if (!node.computed && node.property.name === 'eval' && type === 'rewrite' || node.property.name === '__uv$eval' && type === 'source') {
|
||||
data.changes.push({
|
||||
start: node.property.start,
|
||||
end: node.property.end,
|
||||
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$eval' : 'eval'
|
||||
});
|
||||
};
|
||||
|
||||
if (!node.computed && node.property.name === '__uv$setSource' && type === 'source' && node.parent.type === Syntax.CallExpression) {
|
||||
const { parent, property } = node;
|
||||
data.changes.push({
|
||||
start: property.start - 1,
|
||||
end: parent.end,
|
||||
});
|
||||
|
||||
};
|
||||
} else {
|
||||
data.changes.push({
|
||||
end: node.end,
|
||||
node: '"' + node.property.name + `"), ${master}, false)`
|
||||
})
|
||||
};
|
||||
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
function call(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.CallExpression, (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (node.callee.type !== Syntax.MemberExpression)
|
||||
return false;
|
||||
if (node.callee.object.type === Syntax.Super)
|
||||
return false;
|
||||
if (node.callee.computed && node.callee.property.type === Syntax.Literal && !shouldWrapProperty(node.callee.property.value))
|
||||
return false;
|
||||
if (!node.callee.computed && node.callee.property.type === Syntax.Identifier && !shouldWrapProperty(node.callee.property.name))
|
||||
return false;
|
||||
|
||||
const { callee } = node;
|
||||
|
||||
data.changes.push({
|
||||
node: `${uvMethods.call}((`,
|
||||
start: node.start,
|
||||
end: callee.object.start,
|
||||
})
|
||||
|
||||
callee.object.iterateEnd = function () {
|
||||
data.changes.push({
|
||||
start: callee.object.end,
|
||||
end: callee.property.start,
|
||||
});
|
||||
|
||||
data.changes.push({
|
||||
node: '), ('
|
||||
});
|
||||
|
||||
if (callee.computed) {
|
||||
callee.property.iterateEnd = function() {
|
||||
|
||||
data.changes.push({
|
||||
end: node.arguments.length ? node.arguments[0].start : callee.end,
|
||||
start: callee.property.end,
|
||||
node: '), ['
|
||||
})
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
end: node.end,
|
||||
start: node.arguments.length ? node.arguments[node.arguments.length - 1].end : callee.end,
|
||||
node: `], ${master}, true)`
|
||||
})
|
||||
start: property.start,
|
||||
end: parent.end,
|
||||
});
|
||||
};
|
||||
};
|
||||
} else {
|
||||
data.changes.push({
|
||||
end: node.arguments.length ? node.arguments[0].start : false,
|
||||
node: '"' + callee.property.name + '"), ['
|
||||
})
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
end: node.end,
|
||||
start: node.arguments.length ? node.arguments[node.arguments.length - 1].end : false,
|
||||
node: `], ${master}, false)`
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function setProperty(ctx) {
|
||||
function identifier(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.AssignmentExpression, (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (node.left.type !== Syntax.MemberExpression) return false;
|
||||
if (!assignmentOperators.includes(node.operator)) return false;
|
||||
if (node.left.object.type === Syntax.Super)
|
||||
return false;
|
||||
if (node.left.computed && node.left.property.type === Syntax.Literal && !shouldWrapProperty(node.left.property.value))
|
||||
return false;
|
||||
if (!node.left.computed && node.left.property.type === Syntax.Identifier && !shouldWrapProperty(node.left.property.name))
|
||||
return false;
|
||||
|
||||
const { left, right } = node;
|
||||
|
||||
data.changes.push({
|
||||
node: `${uvMethods.set}((`,
|
||||
start: left.object.start,
|
||||
end: left.object.start,
|
||||
});
|
||||
|
||||
left.object.iterateEnd = function () {
|
||||
data.changes.push({
|
||||
start: left.object.end,
|
||||
end: left.property.start,
|
||||
});
|
||||
|
||||
data.changes.push({
|
||||
node: '), ('
|
||||
});
|
||||
|
||||
if (left.computed) {
|
||||
left.property.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
end: right.start,
|
||||
node: '' + left.property.name + '), '
|
||||
})
|
||||
if (shortHandAssignment[node.operator]) {
|
||||
data.changes.push({
|
||||
node: data.input.slice(left.start, left.end) + ` ${shortHandAssignment[node.operator]} `
|
||||
})
|
||||
};
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
end: node.end,
|
||||
start: right.end,
|
||||
node: `, ${master}, true)`
|
||||
})
|
||||
};
|
||||
};
|
||||
} else {
|
||||
data.changes.push({
|
||||
end: right.start,
|
||||
node: '"' + left.property.name + '"), '
|
||||
})
|
||||
if (shortHandAssignment[node.operator]) {
|
||||
data.changes.push({
|
||||
node: data.input.slice(left.start, left.end) + ` ${shortHandAssignment[node.operator]} `
|
||||
})
|
||||
};
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
end: node.end,
|
||||
start: right.end,
|
||||
node: `, ${master}, false)`
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function wrapEval(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.CallExpression, (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (!node.arguments.length) return false;
|
||||
if (node.callee.type !== Syntax.Identifier) return false;
|
||||
if (node.callee.name !== 'eval') return false;
|
||||
|
||||
const [ script ] = node.arguments;
|
||||
|
||||
data.changes.push({
|
||||
node: uvMethods.script + '(',
|
||||
start: script.start,
|
||||
end: script.start,
|
||||
})
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
node: ')',
|
||||
start: script.end,
|
||||
end: script.end,
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function sourceMethods(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.CallExpression, (node, data, type) => {
|
||||
if (type !== 'source') return false;
|
||||
if (node.callee.type !== Syntax.Identifier) return false;
|
||||
if (!uvMethodTypes[node.callee.name]) return false;
|
||||
|
||||
const info = uvWrapperInfo(node, data);
|
||||
|
||||
switch(uvMethodTypes[node.callee.name]) {
|
||||
case 'set':
|
||||
data.changes.push({
|
||||
node: info.computed ? `${info.object}[${info.property}] = ${info.value}` : `${info.object}.${info.property} = ${info.value}`,
|
||||
start: node.start,
|
||||
end: node.end,
|
||||
});
|
||||
break;
|
||||
case 'get':
|
||||
data.changes.push({
|
||||
node: info.computed ? `${info.object}[${info.property}]` : `${info.object}.${info.property}`,
|
||||
start: node.start,
|
||||
end: node.end,
|
||||
});
|
||||
break;
|
||||
case 'call':
|
||||
data.changes.push({
|
||||
node: info.computed ? `${info.object}[${info.property}](${info.args})` : `${info.object}.${info.property}${info.args}`,
|
||||
start: node.start,
|
||||
end: node.end,
|
||||
});
|
||||
break;
|
||||
case 'script':
|
||||
data.changes.push({
|
||||
node: info.script,
|
||||
start: node.start,
|
||||
end: node.end
|
||||
});
|
||||
break;
|
||||
case 'url':
|
||||
data.changes.push({
|
||||
node: info.url,
|
||||
start: node.start,
|
||||
end: node.end
|
||||
});
|
||||
break;
|
||||
case 'proxy':
|
||||
data.changes.push({
|
||||
node: info.name,
|
||||
start: node.start,
|
||||
end: node.end,
|
||||
});
|
||||
break;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function uvWrapperInfo(node, { input }) {
|
||||
const method = uvMethodTypes[node.callee.name];
|
||||
|
||||
switch(method) {
|
||||
case 'set':
|
||||
{
|
||||
const [ object, property, value, source, computed ] = node.arguments;
|
||||
return {
|
||||
method,
|
||||
object: input.slice(object.start - 1, object.end + 1),
|
||||
property: property.type === Syntax.Literal && !computed.value ? property.value : input.slice(property.start, property.end),
|
||||
computed: !!computed.value,
|
||||
value: input.slice(value.start, value.end),
|
||||
};
|
||||
};
|
||||
case 'get':
|
||||
{
|
||||
const [ object, property, source, computed ] = node.arguments;
|
||||
return {
|
||||
method,
|
||||
object: input.slice(object.start - 1, object.end + 1),
|
||||
property: property.type === Syntax.Literal && !computed.value ? property.value : input.slice(property.start, property.end),
|
||||
computed: !!computed.value,
|
||||
}
|
||||
};
|
||||
case 'call':
|
||||
{
|
||||
const [ object, property, args, source, computed ] = node.arguments;
|
||||
return {
|
||||
method,
|
||||
object: input.slice(object.start - 1, object.end + 1),
|
||||
property: property.type === Syntax.Literal && !computed.value ? property.value : input.slice(property.start, property.end),
|
||||
args: input.slice(args.start + 1, args.end - 1),
|
||||
computed: !!computed.value,
|
||||
};
|
||||
};
|
||||
case 'script':
|
||||
{
|
||||
const [ script ] = node.arguments;
|
||||
return {
|
||||
script: input.slice(script.start, script.end),
|
||||
}
|
||||
}
|
||||
case 'url':
|
||||
{
|
||||
const [ url ] = node.arguments;
|
||||
return {
|
||||
url: input.slice(url.start, url.end),
|
||||
}
|
||||
}
|
||||
case 'proxy':
|
||||
{
|
||||
const [ name ] = node.arguments;
|
||||
return { name };
|
||||
};
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
function wrapIdentifier(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.Identifier, (node, data, type) => {
|
||||
js.on('Identifier', (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
const { parent } = node;
|
||||
if (!shouldWrapIdentifier(node.name)) return false;
|
||||
if (!['location', 'eval', 'parent', 'top'].includes(node.name)) return false;
|
||||
if (parent.type === Syntax.VariableDeclarator && parent.id === node) return false;
|
||||
if ((parent.type === Syntax.AssignmentExpression || parent.type === Syntax.AssignmentPattern) && parent.left === node) return false;
|
||||
if ((parent.type === Syntax.FunctionExpression || parent.type === Syntax.FunctionDeclaration) && parent.id === node) return false;
|
||||
|
@ -393,14 +101,40 @@ function wrapIdentifier(ctx) {
|
|||
if (parent.type === Syntax.RestElement) return false;
|
||||
if (parent.type === Syntax.ExportSpecifier) return false;
|
||||
if (parent.type === Syntax.ImportSpecifier) return false;
|
||||
|
||||
data.changes.push({
|
||||
start: node.start,
|
||||
end: node.end,
|
||||
node: `${uvMethods.proxy}(${node.name}, __uv)`
|
||||
node: '__uv.$get(' + node.name + ')'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function wrapEval(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on('CallExpression', (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (!node.arguments.length) return false;
|
||||
if (node.callee.type !== 'Identifier') return false;
|
||||
if (node.callee.name !== 'eval') return false;
|
||||
|
||||
const [ script ] = node.arguments;
|
||||
|
||||
data.changes.push({
|
||||
node: '__uv.js.rewrite(',
|
||||
start: script.start,
|
||||
end: script.start,
|
||||
})
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
node: ')',
|
||||
start: script.end,
|
||||
end: script.end,
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function importDeclaration(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.Literal, (node, data, type) => {
|
||||
|
@ -420,7 +154,7 @@ function dynamicImport(ctx) {
|
|||
js.on(Syntax.ImportExpression, (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
data.changes.push({
|
||||
node: uvMethods.url + '(',
|
||||
node: '__uv.rewriteUrl(',
|
||||
start: node.source.start,
|
||||
end: node.source.start,
|
||||
})
|
||||
|
@ -434,41 +168,76 @@ function dynamicImport(ctx) {
|
|||
});
|
||||
};
|
||||
|
||||
function destructureDeclaration(ctx) {
|
||||
function unwrap(ctx) {
|
||||
const { js } = ctx;
|
||||
js.on(Syntax.VariableDeclarator, (node, data, type) => {
|
||||
if (type !== 'rewrite') return false;
|
||||
if (node.id.type !== Syntax.ObjectPattern) return false;
|
||||
const names = [];
|
||||
js.on('CallExpression', (node, data, type) => {
|
||||
if (type !== 'source') return false;
|
||||
if (!isWrapped(node.callee)) return false;
|
||||
|
||||
for (const { key } of node.id.properties) {
|
||||
names.push(key.name);
|
||||
};
|
||||
|
||||
console.log(names);
|
||||
switch(node.callee.property.name) {
|
||||
case '$wrap':
|
||||
if (!node.arguments || node.parent.type !== Syntax.MemberExpression || node.parent.property !== node) return false;
|
||||
const [ property ] = node.arguments;
|
||||
|
||||
data.changes.push({
|
||||
node: uvMethods.object + '(',
|
||||
start: node.init.start,
|
||||
end: node.init.start,
|
||||
})
|
||||
start: node.callee.start,
|
||||
end: property.start,
|
||||
});
|
||||
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
node: ')',
|
||||
start: node.init.end,
|
||||
end: node.init.end,
|
||||
start: node.end - 2,
|
||||
end: node.end,
|
||||
});
|
||||
};
|
||||
break;
|
||||
case '$get':
|
||||
case 'rewriteUrl':
|
||||
const [ arg ] = node.arguments;
|
||||
|
||||
data.changes.push({
|
||||
start: node.callee.start,
|
||||
end: arg.start,
|
||||
});
|
||||
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
start: node.end - 1,
|
||||
end: node.end,
|
||||
});
|
||||
};
|
||||
break;
|
||||
case 'rewrite':
|
||||
const [ script ] = node.arguments;
|
||||
data.changes.push({
|
||||
start: node.callee.start,
|
||||
end: script.start,
|
||||
});
|
||||
node.iterateEnd = function() {
|
||||
data.changes.push({
|
||||
start: node.end - 1,
|
||||
end: node.end,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
function shouldWrapProperty(name) {
|
||||
return name === 'eval' || name === 'postMessage' || name === 'location' || name === 'parent' || name === 'top';
|
||||
function isWrapped(node) {
|
||||
if (node.type !== Syntax.MemberExpression) return false;
|
||||
if (node.property.name === 'rewrite' && isWrapped(node.object)) return true;
|
||||
if (node.object.type !== Syntax.Identifier || node.object.name !== '__uv') return false;
|
||||
if (!['js', '$get', '$wrap', 'rewriteUrl'].includes(node.property.name)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
function shouldWrapIdentifier(name) {
|
||||
return name === 'postMessage' || name === 'location' || name === 'parent' || name === 'top';
|
||||
function computedProperty(parent) {
|
||||
if (!parent.computed) return false;
|
||||
const { property: node } = parent;
|
||||
if (node.type === 'Literal' && !['location', 'top', 'parent']) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
export { getProperty, destructureDeclaration, setProperty, call, sourceMethods, importDeclaration, dynamicImport, wrapIdentifier, wrapEval };
|
||||
|
||||
export { property, wrapEval, dynamicImport, importDeclaration, identifier, unwrap };
|
|
@ -11,12 +11,12 @@ function request(request, response) {
|
|||
|
||||
remoteRequest.on('response', remoteResponse => {
|
||||
const send = prepareResponse(remoteResponse);
|
||||
|
||||
response.writeHead(...send);
|
||||
remoteResponse.pipe(response);
|
||||
});
|
||||
|
||||
remoteRequest.on('error', e => {
|
||||
console.log(e);
|
||||
json(response, 500, {
|
||||
error: e.toString()
|
||||
});
|
||||
|
@ -24,7 +24,6 @@ function request(request, response) {
|
|||
|
||||
request.pipe(remoteRequest);
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
json(response, 500, {
|
||||
error: e.toString()
|
||||
});
|
||||
|
|
|
@ -13,8 +13,6 @@ addEventListener('fetch', async event => {
|
|||
sendHeaders.Referer = 'https://www.google.com' + request.referrer.slice(location.origin.length);
|
||||
};
|
||||
|
||||
console.log(Object.fromEntries([...request.headers.entries()]), sendHeaders);
|
||||
|
||||
event.respondWith(
|
||||
fetch('/bare/v1/', {
|
||||
headers: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export function prepareResponse({ headers, statusCode, statusMessage }) {
|
||||
const sendHeaders = {
|
||||
'x-bare-headers': JSON.stringify(headers),
|
||||
'x-bare-status': statusCode,
|
||||
'x-bare-status': statusCode.toString(),
|
||||
'x-bare-status-text': statusMessage,
|
||||
};
|
||||
|
||||
|
|
14
worker/context.js
Normal file
14
worker/context.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
class RequestContext {
|
||||
constructor(ctx = {}) {
|
||||
this.url = ctx.url || '';
|
||||
this.method = ctx.method || 'GET';
|
||||
this.body = ctx.body || null;
|
||||
this.headers = ctx.headers || {};
|
||||
this.mode = ctx.mode || 'cors';
|
||||
this.redirect = ctx.redirect || 'manual';
|
||||
this.referrer = ctx.referrer || '';
|
||||
this.destination = ctx.destination || '';
|
||||
};
|
||||
};
|
||||
|
||||
export { RequestContext };
|
10
worker/index.js
Normal file
10
worker/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { RequestContext } from "./context";
|
||||
|
||||
class ServiceWorkerProxy extends EventTarget {
|
||||
constructor() {
|
||||
|
||||
};
|
||||
static createRequest(options = {}) {
|
||||
return new RequestContext(options);
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue