移除顶级await以适配<chrome89版本

This commit is contained in:
IceCola 2024-05-26 18:28:53 +08:00
parent 11b971c0c2
commit 0ab098cf42
6 changed files with 380 additions and 254 deletions

View File

@ -10,7 +10,7 @@ import * as config from "../util/config.js";
import { promiseErrorHandlerMap } from "../util/browser.js";
import { importCardPack, importCharacterPack, importExtension, importMode } from "./import.js";
import { onload } from "./onload.js";
import security from "../util/security.js";
import { initializeSandboxRealms } from "../util/initRealms.js";
// 判断是否从file协议切换到http/s协议
export function canUseHttpProtocol() {
@ -125,7 +125,12 @@ export async function boot() {
// 加载polyfill内容
await import("./polyfill.js");
// 初始化沙盒的Realms
await initializeSandboxRealms();
// 初始化security
const securityModule = await import("../util/security.js");
const security = securityModule.default;
security.initSecurity({
lib, game, ui, get, ai, _status, gnc,
});

View File

@ -12,7 +12,6 @@ import { GameEventPromise } from "../element/gameEventPromise.js";
import { rootURL } from "../../../noname.js";
import security from "../../util/security.js";
import { Domain, Marshal, Sandbox } from "../../util/sandbox.js";
export class LibInit {
/**
@ -596,32 +595,15 @@ export class LibInit {
* @returns
*/
parsex(item, scope) {
let ModFunction = Function;
let ModGeneratorFunction = GeneratorFunction;
// let ModAsyncFunction = AsyncFunction;
// let ModAsyncGeneratorFunction = AsyncGeneratorFunction;
// 虽然现在 parsex 被控制到了沙盒,
// 但是因为默认沙盒还是可以额外操作东西,
// 故而对不同的运行域做了区分
if (security.SANDBOX_ENABLED) {
const domain = Marshal.getMarshalledDomain(item) || Domain.caller;
// 非顶级域调用情况下我们替换掉Function类型
if (domain && domain !== Domain.topDomain) {
const sandbox = Sandbox.from(domain);
if (!sandbox)
throw "意外的运行域: 运行域没有绑定沙盒";
[
let [
ModFunction,
ModGeneratorFunction,
// ModAsyncFunction,
// ModAsyncGeneratorFunction,
] = security.getIsolateds(sandbox);
}
}
] = security.getIsolatedsFrom(item);
//by 诗笺、Tipx-L
/**

View File

@ -21,7 +21,6 @@ import {
getTreesFromGithub,
} from "../../../../library/update.js";
import security from "../../../../util/security.js";
import { AccessAction, Marshal, Monitor } from "../../../../util/sandbox.js";
export const otherMenu = function (/** @type { boolean | undefined } */ connectMenu) {
if (connectMenu) return;
@ -1215,6 +1214,7 @@ export const otherMenu = function (/** @type { boolean | undefined } */ connectM
cheat: lib.cheat,
});
if (security.isSandboxRequired()) {
const { Monitor, AccessAction } = security.importSandbox();
new Monitor()
.action(AccessAction.DEFINE)
.action(AccessAction.WRITE)

121
noname/util/initRealms.js Normal file
View File

@ -0,0 +1,121 @@
// 为了兼容无法使用顶级await的版本
const iframe = document.createElement("iframe");
// 执行上下文传递函数,请勿动喵
// 用于传递顶级execute context
/** @type {(target: Function, thiz: Object, args: Array) => any} */
// @ts-ignore
const ContextInvoker1 = (function (apply, target, thiz, args) {
return apply(target, thiz, args);
}).bind(null, Reflect.apply);
/** @type {(target: Function, args: Array, newTarget: Function) => any} */
// @ts-ignore
const ContextInvoker2 = (function (construct, target, args, newTarget) {
return construct(target, args, newTarget);
}).bind(null, Reflect.construct);
/** @type {(closure: Object, target: Function) => ((...args: any[]) => any)} */
// @ts-ignore
const ContextInvokerCreator = (function (apply, closure, target) {
return function (...args) {
return apply(target, closure,
// @ts-ignore
[this === window ? null : this, args, new.target]);
};
}).bind(null, Reflect.apply);
/**
* @param {string} path
* @param {string} name
*/
function replaceName(path, name) {
const index = path.lastIndexOf("/");
return path.slice(0, index + 1) + name;
}
const TARGET_URL = replaceName(import.meta.url, "sandbox.js");
const SANDBOX_EXPORT = {};
async function initializeSandboxRealms() {
const document = window.document;
const createElement = document.createElement.bind(document);
const appendChild = document.body.appendChild.bind(document.body);
// 通过构造 iframe 来创建新的变量域
// 我们需要确保顶级运行域的原型链不暴露
// 为此我们从新的变量域重新载入当前脚本
// 然后就可以直接冻结当前变量域的原型链
const iframe = createElement("iframe");
iframe.style.display = "none";
appendChild(iframe);
if (!iframe.contentWindow)
throw new ReferenceError("无法载入运行域");
// 定义 createRealms 函数
Reflect.defineProperty(iframe.contentWindow, "createRealms", {
value() {
// 通过构造 iframe 来创建新的变量域
const iframe = createElement("iframe");
iframe.style.display = "none";
appendChild(iframe);
const window = iframe.contentWindow;
if (!window)
throw new ReferenceError("顶级域已经被卸载");
iframe.remove();
return window;
},
});
// 传递顶级变量域、上下文执行器
// @ts-ignore
iframe.contentWindow.replacedGlobal = window;
// @ts-ignore
iframe.contentWindow.replacedCI1 = ContextInvoker1;
// @ts-ignore
iframe.contentWindow.replacedCI2 = ContextInvoker2;
// @ts-ignore
iframe.contentWindow.replacedCIC = ContextInvokerCreator;
// 重新以新的变量域载入当前脚本
const script = iframe.contentWindow.document.createElement("script");
script.src = TARGET_URL;
script.type = "module";
// 无法同步载入
// script.async = false;
// script.defer = false;
const promise = new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
});
iframe.contentWindow.document.head.appendChild(script);
await promise; // Top Await Required Chrome 89
// @ts-ignore
if (!iframe.contentWindow.SANDBOX_EXPORT)
throw new ReferenceError("无法同步载入运行域");
// @ts-ignore
delete iframe.contentWindow.replacedGlobal;
// @ts-ignore
delete iframe.contentWindow.replacedCI1;
// @ts-ignore
delete iframe.contentWindow.replacedCI2;
// @ts-ignore
delete iframe.contentWindow.replacedCIC;
// @ts-ignore
Object.assign(SANDBOX_EXPORT, iframe.contentWindow.SANDBOX_EXPORT);
iframe.remove();
}
export {
initializeSandboxRealms,
SANDBOX_EXPORT,
};

View File

@ -1,4 +1,9 @@
const FILE_URL = import.meta.url;
import { SANDBOX_EXPORT } from "./initRealms.js";
// 很重要的事情!
// 请不要在在其他文件中import sandbox.js
// 如果需要沙盒相关的类请用security.importSandbox()导入!!!
// 什么时候支持顶级await(Chrome 89)就可以改回去了,现在好麻烦哦
/** @typedef {any} Window */
@ -1030,28 +1035,15 @@ class NativeWrapper {
/** @type {(target: Function, thiz: Object, args: Array) => any} */
// @ts-ignore
const ContextInvoker1 = window.replacedCI1
|| (function (apply, target, thiz, args) {
return apply(target, thiz, args);
}).bind(null, Reflect.apply);
const ContextInvoker1 = window.replacedCI1;
/** @type {(target: Function, args: Array, newTarget: Function) => any} */
// @ts-ignore
const ContextInvoker2 = window.replacedCI2
|| (function (construct, target, args, newTarget) {
return construct(target, args, newTarget);
}).bind(null, Reflect.construct);
const ContextInvoker2 = window.replacedCI2;
/** @type {(closure: Object, target: Function) => ((...args: any[]) => any)} */
// @ts-ignore
const ContextInvokerCreator = window.replacedCIC
|| (function (apply, closure, target) {
return function (...args) {
return apply(target, closure,
// @ts-ignore
[this === window ? null : this, args, new.target]);
};
}).bind(null, Reflect.apply);
const ContextInvokerCreator = window.replacedCIC;
/**
* ```plain
@ -3521,89 +3513,9 @@ function sealObject(obj, freeze = Object.freeze) {
}
}
const SANDBOX_EXPORT = {
AccessAction,
Rule,
Monitor,
Marshal,
Domain,
Sandbox,
};
// TODO: 其他扩展的全局变量
if (SANDBOX_ENABLED) {
// 确保顶级运行域的原型链不暴露
if (window.top === window) {
// 如果当前是顶级运行域
const document = window.document;
const createElement = document.createElement.bind(document);
const appendChild = document.body.appendChild.bind(document.body);
// 通过构造 iframe 来创建新的变量域
// 我们需要确保顶级运行域的原型链不暴露
// 为此我们从新的变量域重新载入当前脚本
// 然后就可以直接冻结当前变量域的原型链
const iframe = createElement("iframe");
iframe.style.display = "none";
appendChild(iframe);
if (!iframe.contentWindow)
throw new ReferenceError("无法载入运行域");
// 定义 createRealms 函数
Reflect.defineProperty(iframe.contentWindow, "createRealms", {
value() {
// 通过构造 iframe 来创建新的变量域
const iframe = createElement("iframe");
iframe.style.display = "none";
appendChild(iframe);
const window = iframe.contentWindow;
if (!window)
throw new ReferenceError("顶级域已经被卸载");
iframe.remove();
return window;
},
});
// 传递顶级变量域、上下文执行器
// @ts-ignore
iframe.contentWindow.replacedGlobal = window;
// @ts-ignore
iframe.contentWindow.replacedCI1 = ContextInvoker1;
// @ts-ignore
iframe.contentWindow.replacedCI2 = ContextInvoker2;
// @ts-ignore
iframe.contentWindow.replacedCIC = ContextInvokerCreator;
// 重新以新的变量域载入当前脚本
const script = iframe.contentWindow.document.createElement("script");
script.src = FILE_URL;
script.type = "module";
const promise = new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
});
iframe.contentWindow.document.head.appendChild(script);
await promise;
// @ts-ignore
delete iframe.contentWindow.replacedGlobal;
// @ts-ignore
delete iframe.contentWindow.replacedCI1;
// @ts-ignore
delete iframe.contentWindow.replacedCI2;
// @ts-ignore
delete iframe.contentWindow.replacedCIC;
// 从新的变量域暴露的对象获取导出
// @ts-ignore
Object.assign(SANDBOX_EXPORT, iframe.contentWindow.SANDBOX_EXPORT);
iframe.remove(); // 释放 iframe 与相关的 browser context
({
// @ts-ignore
AccessAction,
@ -3658,8 +3570,14 @@ if (SANDBOX_ENABLED) {
// 向顶级运行域暴露导出
// @ts-ignore
window.SANDBOX_EXPORT =
Object.assign({}, SANDBOX_EXPORT);
window.SANDBOX_EXPORT = {
AccessAction,
Rule,
Monitor,
Marshal,
Domain,
Sandbox,
};
}
}

View File

@ -1,5 +1,3 @@
import { Sandbox, Domain, Marshal, Monitor, AccessAction, Rule, SANDBOX_ENABLED } from "./sandbox.js";
// 是否强制所有模式下使用沙盒
const SANDBOX_FORCED = true;
// 是否启用自动测试
@ -12,11 +10,37 @@ const TRUSTED_IPS = Object.freeze([
"47.99.105.222",
]);
/** @type {boolean} */
let SANDBOX_ENABLED = true;
/** @type {typeof import("./sandbox.js").AccessAction} */
let AccessAction;
/** @type {typeof import("./sandbox.js").Domain} */
let Domain;
/** @type {typeof import("./sandbox.js").Marshal} */
let Marshal;
/** @type {typeof import("./sandbox.js").Monitor} */
let Monitor;
/** @type {typeof import("./sandbox.js").Rule} */
let Rule;
/** @type {typeof import("./sandbox.js").Sandbox} */
let Sandbox;
/** @typedef {import("./sandbox.js").AccessAction} AccessAction */
/** @typedef {import("./sandbox.js").Domain} Domain */
/** @typedef {import("./sandbox.js").Marshal} Marshal */
/** @typedef {import("./sandbox.js").Monitor} Monitor */
/** @typedef {import("./sandbox.js").Rule} Rule */
/** @typedef {import("./sandbox.js").Sandbox} Sandbox */
/** @type {boolean} */
let initialized = false;
/** @type {Sandbox} */
let defaultSandbox;
/** @type {Array<Sandbox>} */
const sandboxStack = [];
// 沙盒Function类型缓存
/** @type {WeakMap<Sandbox, Array<typeof Function>>} */
const isolatedsMap = new WeakMap();
@ -46,6 +70,16 @@ const polyfills = {
namespaces: {},
};
// 被封装的Function类型
/** @type {typeof Function} */
let ModFunction;
/** @type {typeof Function} */
let ModGeneratorFunction;
/** @type {typeof Function} */
let ModAsyncFunction;
/** @type {typeof Function} */
let ModAsyncGeneratorFunction;
/**
* ```plain
* 将一个沙盒作为当前联网传输的运行沙盒
@ -283,7 +317,12 @@ function _exec2(x, scope = {}) {
return scope;
}
function initSecurity({
/**
* ```plain
* 初始化模块
* ```
*/
async function initSecurity({
lib,
game,
ui,
@ -295,6 +334,15 @@ function initSecurity({
if (initialized)
throw "security 已经被初始化过了";
const sandbox = await import("./sandbox.js");
SANDBOX_ENABLED = sandbox.SANDBOX_ENABLED;
AccessAction = sandbox.AccessAction;
Domain = sandbox.Domain;
Marshal = sandbox.Marshal;
Monitor = sandbox.Monitor;
Rule = sandbox.Rule;
Sandbox = sandbox.Sandbox;
topVariables.lib = lib;
topVariables.game = game;
topVariables.ui = ui;
@ -307,11 +355,7 @@ function initSecurity({
return;
loadPolyfills();
// @ts-ignore
Object.assign(defaultSandbox.scope, topVariables);
// @ts-ignore
setupPolyfills(defaultSandbox);
initIsolatedEnvironment();
// 不允许被远程代码访问的game函数
const ioFuncs = [
@ -510,6 +554,181 @@ function getIsolateds(sandbox) {
return isolateds.slice();
}
/**
* ```plain
* 根据传入对象的运行域获取对应的Function类型
* ```
*
* @param {Object} item
* @returns {Array<typeof Function>}
*/
function getIsolatedsFrom(item) {
const domain = Marshal.getMarshalledDomain(item) || Domain.caller;
// 非顶级域调用情况下我们替换掉Function类型
if (domain && domain !== Domain.topDomain) {
const box = Sandbox.from(domain);
if (!box)
throw "意外的运行域: 运行域没有绑定沙盒";
return getIsolateds(box);
}
return [
ModFunction,
ModGeneratorFunction,
ModAsyncFunction,
ModAsyncGeneratorFunction,
];
}
/**
* ```plain
* 导入 `sandbox.js` 的相关类
* ```
*
* @returns {{
* AccessAction: typeof import("./sandbox.js").AccessAction,
* Domain: typeof import("./sandbox.js").Domain,
* Marshal: typeof import("./sandbox.js").Marshal,
* Monitor: typeof import("./sandbox.js").Monitor,
* Rule: typeof import("./sandbox.js").Rule,
* Sandbox: typeof import("./sandbox.js").Sandbox,
* }}
*/
function importSandbox() {
if (!AccessAction)
throw new ReferenceError("sandbox.js 还没有被载入");
return {
AccessAction,
Domain,
Marshal,
Monitor,
Rule,
Sandbox,
};
}
/**
* ```plain
* 初始化顶级域的Funcion类型封装
* ```
*/
function initIsolatedEnvironment() {
/** @type {typeof Function} */
// @ts-ignore
const defaultFunction = function () { }.constructor;
/** @type {typeof Function} */
// @ts-ignore
const defaultGeneratorFunction = function* () { }.constructor;
/** @type {typeof Function} */
// @ts-ignore
const defaultAsyncFunction = async function () { }.constructor;
/** @type {typeof Function} */
// @ts-ignore
const defaultAsyncGeneratorFunction = async function* () { }.constructor;
// @ts-ignore
defaultSandbox = createSandbox(); // 所有 eval、parsex 代码全部丢进去喵
// @ts-ignore
// 对于 defaultSandbox 我们要补充一些东西喵
defaultSandbox.scope.localStorage = localStorage;
// 对Function类型进行包裹
/** @type {Array<typeof Function>} */
const [
IsolatedFunction,
IsolatedGeneratorFunction,
IsolatedAsyncFunction,
IsolatedAsyncGeneratorFunction,
]
// @ts-ignore
= getIsolateds(defaultSandbox);
// 封装Function类型
ModFunction = new Proxy(defaultFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedFunction(...argumentsList);
},
});
/** @type {typeof Function} */
ModGeneratorFunction = new Proxy(defaultGeneratorFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedGeneratorFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedGeneratorFunction(...argumentsList);
},
});
/** @type {typeof Function} */
ModAsyncFunction = new Proxy(defaultAsyncFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncFunction(...argumentsList);
},
});
/** @type {typeof Function} */
ModAsyncGeneratorFunction = new Proxy(defaultAsyncGeneratorFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncGeneratorFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncGeneratorFunction(...argumentsList);
},
});
function rewriteCtor(prototype, newCtor) {
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'constructor')
|| { configurable: true, writable: true, enumerable: false };
if (!descriptor.configurable) throw new TypeError("无法覆盖不可配置的构造函数");
descriptor.value = newCtor;
Reflect.defineProperty(prototype, 'constructor', descriptor);
}
// 覆盖所有的Function类型构造函数
window.Function = ModFunction;
rewriteCtor(defaultFunction.prototype, ModFunction);
rewriteCtor(defaultGeneratorFunction.prototype, ModGeneratorFunction);
rewriteCtor(defaultAsyncFunction.prototype, ModAsyncFunction);
rewriteCtor(defaultAsyncGeneratorFunction.prototype, ModAsyncGeneratorFunction);
}
/**
* ```plain
* 加载当前的垫片函数
@ -607,127 +826,6 @@ function setupPolyfills(sandbox) {
`, context);
}
/** @type {typeof Function} */
// @ts-ignore
const defaultFunction = function () { }.constructor;
/** @type {typeof Function} */
// @ts-ignore
const defaultGeneratorFunction = function* () { }.constructor;
/** @type {typeof Function} */
// @ts-ignore
const defaultAsyncFunction = async function () { }.constructor;
/** @type {typeof Function} */
// @ts-ignore
const defaultAsyncGeneratorFunction = async function* () { }.constructor;
const defaultSandbox = createSandbox(); // 所有 eval、parsex 代码全部丢进去喵
if (SANDBOX_ENABLED) {
// @ts-ignore
// 对于 defaultSandbox 我们要补充一些东西喵
defaultSandbox.scope.localStorage = localStorage;
// 对Function类型进行包裹
/** @type {Array<typeof Function>} */
const [
IsolatedFunction,
IsolatedGeneratorFunction,
IsolatedAsyncFunction,
IsolatedAsyncGeneratorFunction,
]
// @ts-ignore
= getIsolateds(defaultSandbox);
/** @type {typeof Function} */
let ModFunction;
/** @type {typeof Function} */
let ModGeneratorFunction;
/** @type {typeof Function} */
let ModAsyncFunction;
/** @type {typeof Function} */
let ModAsyncGeneratorFunction;
// 封装Function类型
ModFunction = new Proxy(defaultFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedFunction(...argumentsList);
},
});
/** @type {typeof Function} */
ModGeneratorFunction = new Proxy(defaultGeneratorFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedGeneratorFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedGeneratorFunction(...argumentsList);
},
});
/** @type {typeof Function} */
ModAsyncFunction = new Proxy(defaultAsyncFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncFunction(...argumentsList);
},
});
/** @type {typeof Function} */
ModAsyncGeneratorFunction = new Proxy(defaultAsyncGeneratorFunction, {
apply(target, thisArg, argumentsList) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncGeneratorFunction(...argumentsList);
},
construct(target, argumentsList, newTarget) {
if (!sandBoxRequired)
return new target(...argumentsList);
return new IsolatedAsyncGeneratorFunction(...argumentsList);
},
});
function rewriteCtor(prototype, newCtor) {
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'constructor')
|| { configurable: true, writable: true, enumerable: false };
if (!descriptor.configurable) throw new TypeError("无法覆盖不可配置的构造函数");
descriptor.value = newCtor;
Reflect.defineProperty(prototype, 'constructor', descriptor);
}
// 覆盖所有的Function类型构造函数
window.Function = ModFunction;
rewriteCtor(defaultFunction.prototype, ModFunction);
rewriteCtor(defaultGeneratorFunction.prototype, ModGeneratorFunction);
rewriteCtor(defaultAsyncFunction.prototype, ModAsyncFunction);
rewriteCtor(defaultAsyncGeneratorFunction.prototype, ModAsyncGeneratorFunction);
}
// 测试暴露喵
// window.sandbox = defaultSandbox;
@ -739,6 +837,8 @@ const exports = {
isUnsafeObject,
assertSafeObject,
getIsolateds,
getIsolatedsFrom,
importSandbox,
requireSandbox,
requireSandboxOn,
isSandboxRequired,