From 03380d1d93e2e1dc12e34180fa23379925a6ecce Mon Sep 17 00:00:00 2001 From: IceCola <739201322@qq.com> Date: Wed, 29 May 2024 01:53:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug=EF=BC=9B=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E6=B2=99=E7=9B=92=E7=A8=B3=E5=AE=9A=E6=80=A7=EF=BC=9B?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B2=99=E7=9B=92=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- noname/get/index.js | 18 +- noname/library/element/client.js | 18 +- noname/library/index.js | 98 +++++++--- noname/library/init/index.js | 10 +- noname/util/sandbox.js | 326 +++++++++++++++++++++++-------- noname/util/security.js | 60 +++--- 6 files changed, 385 insertions(+), 145 deletions(-) diff --git a/noname/get/index.js b/noname/get/index.js index 422765f4a..c7ce0f832 100644 --- a/noname/get/index.js +++ b/noname/get/index.js @@ -1515,7 +1515,7 @@ export class Get { * ``` * * @param {string} str - * @returns + * @returns {string} */ pureFunctionStr(str) { str = str.trim(); @@ -1551,7 +1551,13 @@ export class Get { if (func._filter_args) { return "_noname_func:" + JSON.stringify(get.stringifiedResult(func._filter_args, 3)); } - const str = func.toString(); + const { Marshal, Sandbox } = security.importSandbox(); + const domain = Marshal.getMarshalledDomain(func); + if (domain) { + const sandbox = Sandbox.from(domain); + if (sandbox && "client" in sandbox) throw new Error("不应该二次扩散远程代码"); + } + const str = Marshal.decompileFunction(func); // js内置的函数 if (/\{\s*\[native code\]\s*\}/.test(str)) return "_noname_func:function () {}"; return "_noname_func:" + get.pureFunctionStr(str); @@ -4960,10 +4966,10 @@ function freezeSlot(obj, key) { Reflect.defineProperty(obj, key, descriptor); } -freezeSlot(Get, "isFunctionBody"); -freezeSlot(Get, "pureFunctionStr"); -freezeSlot(Get, "funcInfoOL"); -freezeSlot(Get, "infoFuncOL"); +freezeSlot(Get.prototype, "isFunctionBody"); +freezeSlot(Get.prototype, "pureFunctionStr"); +freezeSlot(Get.prototype, "funcInfoOL"); +freezeSlot(Get.prototype, "infoFuncOL"); export let get = new Get(); /** diff --git a/noname/library/element/client.js b/noname/library/element/client.js index 8bdc1c103..86bae66c8 100644 --- a/noname/library/element/client.js +++ b/noname/library/element/client.js @@ -8,8 +8,9 @@ import security from "../../util/security.js"; export class Client { /** * @param {import('../index.js').NodeWS | InstanceType | Client} ws + * @param {boolean} temp */ - constructor(ws, temp) { + constructor(ws, temp = false) { if (ws instanceof Client) throw new Error("Client cannot copy."); this.ws = ws; /** @@ -19,8 +20,21 @@ export class Client { this.id = ws.wsid || get.id(); this.closed = false; - if (!temp) + if (!temp) { this.sandbox = security.createSandbox(); + if (this.sandbox) { + Reflect.defineProperty(this, "sandbox", { + value: this.sandbox, + writable: false, + configurable: false, + }); + Reflect.defineProperty(this.sandbox, "client", { + value: this, + writable: false, + configurable: false, + }); + } + } } send() { if (this.closed) return this; diff --git a/noname/library/index.js b/noname/library/index.js index 6b1c23cda..3b27266de 100644 --- a/noname/library/index.js +++ b/noname/library/index.js @@ -164,8 +164,9 @@ export class Library { * } } */ node; + // 谁写的值类型是string,这也太离谱了喵 /** - * @type { { [key: string]: string } } + * @type { { [key: string]: Player } } */ playerOL; /** @@ -9561,14 +9562,13 @@ export class Library { if (!Array.isArray(message) || typeof lib.message.client[message[0]] !== "function") { throw "err"; } - if (!game.sandbox) game.sandbox = security.createSandbox(); - security.enterSandbox(game.sandbox); + if (game.sandbox) security.enterSandbox(game.sandbox); try { for (var i = 1; i < message.length; i++) { message[i] = get.parsedResult(message[i]); } } finally { - security.exitSandbox(); + if (game.sandbox) security.exitSandbox(); } } catch (e) { console.log(e); @@ -12011,7 +12011,9 @@ export class Library { cardPile = {}; message = { server: { - /** @this { any } */ + /** + * @this {import("./element/client.js").Client} + */ init(version, config, banned_info) { if (lib.node.banned.includes(banned_info)) { this.send("denied", "banned"); @@ -12030,9 +12032,14 @@ export class Library { lib.node.clients.remove(this); this.closed = true; } else if (!_status.waitingForPlayer) { - if (game.phaseNumber && lib.configOL.observe) { + if (!config.nickname) { + this.send("denied", "banned"); + lib.node.clients.remove(this); + this.closed = true; + } else if (game.phaseNumber && lib.configOL.observe) { lib.node.observing.push(this); this.send("reinit", lib.configOL, get.arenaState(), game.getState ? game.getState() : {}, game.ip, game.players[0].playerid, null, _status.cardtag); + game.broadcastAll(name => game.log("玩家 ", `#y${name}`, " 进入房间旁观"), config.nickname); if (!ui.removeObserve) { ui.removeObserve = ui.create.system( "移除旁观", @@ -12077,37 +12084,53 @@ export class Library { this.send("init", this.id, lib.configOL, game.ip, window.isNonameServer, game.roomId); } }, - /** @this { any } */ + /** + * @this {import("./element/client.js").Client} + */ inited() { this.inited = true; if (_status.waitingForPlayer) { game.updateWaiting(); } }, - /** @this { any } */ + /** + * @this {import("./element/client.js").Client} + */ reinited() { this.inited = true; }, - result: function (result) { + /** + * @this {import("./element/client.js").Client} + */ + result(result) { if (lib.node.observing.includes(this)) return; var player = lib.playerOL[this.id]; if (player) { player.unwait(result); } }, - tempResult: function (result) { + /** + * @this {import("./element/client.js").Client} + */ + tempResult(result) { if (lib.node.observing.includes(this)) return; var player = lib.playerOL[this.id]; if (player) { player.tempUnwait(result); } }, - startGame: function () { + /** + * @this {import("./element/client.js").Client} + */ + startGame() { if (this.id == game.onlinezhu) { game.resume(); } }, - changeRoomConfig: function (config) { + /** + * @this {import("./element/client.js").Client} + */ + changeRoomConfig(config) { if (this.id == game.onlinezhu) { game.broadcastAll(function (config) { for (var i in config) { @@ -12134,7 +12157,10 @@ export class Library { } } }, - changeNumConfig: function (num, index, bool) { + /** + * @this {import("./element/client.js").Client} + */ + changeNumConfig(num, index, bool) { if (this.id == game.onlinezhu) { lib.configOL.number = num; game.send("server", "config", lib.configOL); @@ -12148,14 +12174,20 @@ export class Library { } } }, - throwEmotion: function (target, emotion, rotate) { + /** + * @this {import("./element/client.js").Client} + */ + throwEmotion(target, emotion, rotate) { if (lib.node.observing.includes(this)) return; var player = lib.playerOL[this.id]; if (player) { player.throwEmotion(target, emotion, rotate); } }, - emotion: function (id, pack, emotion) { + /** + * @this {import("./element/client.js").Client} + */ + emotion(id, pack, emotion) { if (lib.node.observing.includes(this)) return; var that = this; if ( @@ -12185,7 +12217,10 @@ export class Library { } if (player) player.emotion(pack, emotion); }, - chat: function (id, str) { + /** + * @this {import("./element/client.js").Client} + */ + chat(id, str) { if (lib.node.observing.includes(this)) return; var that = this; if ( @@ -12215,8 +12250,18 @@ export class Library { } if (player) player.chat(str); }, - giveup: function (player) { + /** + * ```plain + * 当客机向主机发送投降请求时的回调 + * ``` + * + * @this {import("./element/client.js").Client} + * @param {Player} player + */ + giveup(player) { if (lib.node.observing.includes(this) || !player || !player._giveUp) return; + var self = lib.playerOL[this.id]; + if (self !== player) return; // 禁止让别人投降 _status.event.next.length = 0; game .createEvent("giveup", false) @@ -12227,7 +12272,10 @@ export class Library { player.die("nosource").includeOut = true; }).player = player; }, - auto: function () { + /** + * @this {import("./element/client.js").Client} + */ + auto() { if (lib.node.observing.includes(this)) return; var player = lib.playerOL[this.id]; if (player) { @@ -12238,7 +12286,10 @@ export class Library { }, player); } }, - unauto: function () { + /** + * @this {import("./element/client.js").Client} + */ + unauto() { if (lib.node.observing.includes(this)) return; var player = lib.playerOL[this.id]; if (player) { @@ -12249,18 +12300,21 @@ export class Library { }, player); } }, - exec: function (func) { + exec(func) { // if(typeof func=='function'){ // var args=Array.from(arguments); // args.shift(); // func.apply(this,args); // } }, - log: function () { + /** + * @this {import("./element/client.js").Client} + */ + log() { var items = []; try { for (var i = 0; i < arguments.length; i++) { - items.push(security.eval(`return ${arguments[i]}`)); + items.push(this.sandbox.exec(`return ${arguments[i]}`)); } } catch (e) { this.send("log", ["err"]); diff --git a/noname/library/init/index.js b/noname/library/init/index.js index cb443f707..b5cfee990 100644 --- a/noname/library/init/index.js +++ b/noname/library/init/index.js @@ -142,23 +142,21 @@ export class LibInit { if (!Array.isArray(message) || typeof lib.message.server[message[0]] !== "function") { throw "err"; } - if (!client.sandbox) client.sandbox = security.createSandbox(); // @ts-ignore - security.enterSandbox(client.sandbox); + if (client.sandbox) security.enterSandbox(client.sandbox); try { for (var i = 1; i < message.length; i++) { message[i] = get.parsedResult(message[i]); } } finally { // @ts-ignore - security.exitSandbox(client.sandbox); + if (client.sandbox) security.exitSandbox(); } } catch (e) { console.log(e); console.log("invalid message: " + messagestr); return; } - lib.message.server[message.shift()].apply(client, message); }); ws.on("close", function () { client.close(); @@ -609,10 +607,10 @@ export class LibInit { * @param {Function} func */ function Legacy(func) { + const { Marshal } = security.importSandbox(); //Remove all comments //移除所有注释 - let str = func - .toString() + let str = Marshal.decompileFunction(func) .replace(/((?:(?:^[ \t]*)?(?:\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\/(?:[ \t]*\r?\n(?=[ \t]*(?:\r?\n|\/\*|\/\/)))?|\/\/(?:[^\\]|\\(?:\r?\n)?)*?(?:\r?\n(?=[ \t]*(?:\r?\n|\/\*|\/\/))|(?=\r?\n))))+)|("(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|(?:\r?\n|[\s\S])[^/"'\\\s]*)/gm, "$2") .trim(); //获取第一个 { 后的所有字符 diff --git a/noname/util/sandbox.js b/noname/util/sandbox.js index 972c5e82d..176d24258 100644 --- a/noname/util/sandbox.js +++ b/noname/util/sandbox.js @@ -20,7 +20,6 @@ const SandboxSignal_GetMarshalledProxy = Symbol("GetMarshalledProxy"); const SandboxSignal_SetMarshalledProxy = Symbol("SetMarshalledProxy"); const SandboxSignal_GetWindow = Symbol("GetWindow"); const SandboxSignal_GetPromise = Symbol("GetPromise"); -const SandboxSignal_IsArray = Symbol("IsArray"); const SandboxSignal_EnterDomain = Symbol("EnterDomain"); const SandboxSignal_ExitDomain = Symbol("ExitDomain"); const SandboxSignal_ListDomain = Symbol("ListDomain"); @@ -31,6 +30,7 @@ const SandboxSignal_TrapDomain = Symbol("TrapDomain"); const SandboxSignal_DiapatchMonitor = Symbol("DiapatchMonitor"); const SandboxSignal_ListMonitor = Symbol("ListMonitor"); const SandboxSignal_ExposeInfo = Symbol("ExposeInfo"); +const SandboxSignal_TryFunctionRefs = Symbol("TryFunctionRefs"); /** * ```plain @@ -52,17 +52,17 @@ function isPrimitive(obj) { * ``` */ class AccessAction { - // static CALL = 0; // apply - // static NEW = 1; // construct - // static READ = 2; // get - // static WRITE = 3; // set + // static CALL = 0; // apply + // static NEW = 1; // construct + // static READ = 2; // get + // static WRITE = 3; // set // static DESCRIBE = 4; // getOwnPropertyDescriptor // static DEFINE = 5; // defineProperty - // static TRACE = 6; // getPrototypeOf - // static META = 7; // setPrototypeOf - // static SEAL = 8; // preventExtensions + // static TRACE = 6; // getPrototypeOf + // static META = 7; // setPrototypeOf + // static SEAL = 8; // preventExtensions // static EXISTS = 9; // has - // static LIST = 10; // ownKeys + // static LIST = 10; // ownKeys // static DELETE = 11; // delete /** ```Reflect.apply``` */ @@ -443,6 +443,36 @@ const MARSHALLED_LIST = Object.freeze([ "/cancelIdleCallback", "/queueMicrotask", "/MutationObserver", + // 根据狂神喵提供的问题 + // 我们对console进行迁移 + "/console/debug", + "/console/error", + "/console/info", + "/console/log", + "/console/warn", + "/console/dir", + "/console/dirxml", + "/console/table", + "/console/trace", + "/console/group", + "/console/groupCollapsed", + "/console/groupEnd", + "/console/clear", + "/console/count", + "/console/countReset", + "/console/assert", + "/console/profile", + "/console/profileEnd", + "/console/time", + "/console/timeLog", + "/console/timeEnd", + "/console/timeStamp", + "/console/context", + "/console/createTask", + "/console/memory", + // 另外补充这两个可能的函数哦 + "/alert", + "/confirm", ]); /** @@ -532,6 +562,8 @@ class Globals { if (!Globals.#globals.has(domain)) { const window = domain[SandboxExposer](SandboxSignal_GetWindow); const globals = [new WeakMap(), {}]; + // @ts-ignore + Globals.#globals.set(domain, globals); // 检查是否是顶级域 if (Globals.#topGlobals) { @@ -572,9 +604,6 @@ class Globals { globals[0].set(obj, key); globals[1][key] = obj; } - - // @ts-ignore - Globals.#globals.set(domain, globals); } } @@ -1057,6 +1086,8 @@ class DomainMonitors { /** @type {WeakMap} */ static #domainMonitors = new WeakMap(); + /** @type {WeakMap>} */ + #targetMonitorsMap = new WeakMap(); /** @type {WeakMap>>} */ #monitorsMap = new WeakMap(); @@ -1075,9 +1106,25 @@ class DomainMonitors { actions, allowDomains, disallowDomains, + targets, ] = Monitor[SandboxExposer2] (SandboxSignal_ExposeInfo, monitor); + // 如果指定了目标,使用目标 Monitor 集合 + // 以此对于特定对象的 Monitor 进行性能优化 + if (targets) { + for (const target of targets) { + let monitors = thiz.#targetMonitorsMap.get(target); + + if (!monitors) + thiz.#targetMonitorsMap.set(target, monitors = new Set()); + + monitors.add(monitor); + } + + return; + } + function addToActionMap(actionMap) { for (const action of actions) { let monitorMap = actionMap[action]; @@ -1133,9 +1180,24 @@ class DomainMonitors { actions, allowDomains, disallowDomains, + targets, ] = Monitor[SandboxExposer2] (SandboxSignal_ExposeInfo, monitor); + // 对于指定了目标的 Monitor 特殊处理 + if (targets) { + for (const target of targets) { + const monitors = thiz.#targetMonitorsMap.get(target); + + if (!monitors) + continue; + + monitors.delete(monitor); + } + + return; + } + function removeFromActionMap(actionMap) { for (const action of actions) { const monitorMap = actionMap[action]; @@ -1183,20 +1245,32 @@ class DomainMonitors { * @param {Domain} sourceDomain * @param {Domain} targetDomain * @param {number} action - * @returns {Set?} + * @param {Object} target + * @returns {Array?} */ - static #getMonitorsBy = function (sourceDomain, targetDomain, action) { + static #getMonitorsBy = function (sourceDomain, targetDomain, action, target) { const instance = DomainMonitors.#domainMonitors.get(sourceDomain); if (!instance) return null; + const targetMap = instance.#targetMonitorsMap.get(target); const actionMap = instance.#monitorsMap.get(targetDomain); + const actionMonitors = actionMap && actionMap[action]; - if (!actionMap || !(action in actionMap)) - return null; + let array = null; - return actionMap[action]; + if (targetMap) + array = [...targetMap]; // 优先执行指定目标的 Monitor + + if (actionMonitors) { + if (!array) + array = [...actionMonitors]; + else + array.push(...actionMonitors); + } + + return array; } /** @@ -1222,9 +1296,14 @@ class DomainMonitors { actions, allowDomains, disallowDomains, + targets, ] = Monitor[SandboxExposer2] (SandboxSignal_ExposeInfo, monitor); + // 指定了目标的 Monitor 不参与新运行域处理 + if (targets) + continue; + // 判断新增的 Domain 是否是 Monitor 监听的目标 if (allowDomains && !allowDomains.has(domain)) @@ -1355,14 +1434,16 @@ class DomainMonitors { Object.freeze(nameds); // 获取可能的 Monitor 集合 - const monitorMap = DomainMonitors.#getMonitorsBy(sourceDomain, targetDomain, action); + const monitorMap = DomainMonitors.#getMonitorsBy( + sourceDomain, targetDomain, action, args[0]); + const result = { preventDefault: false, stopPropagation: false, returnValue: undefined, }; - if (!monitorMap || monitorMap.size == 0) + if (!monitorMap || !monitorMap.length) return result; const access = { @@ -1386,6 +1467,9 @@ class DomainMonitors { setReturnValue(value) { result.returnValue = value; }, + throwDenied(message = null) { + throw new RangeError(message || "封送对象的源运行域禁止了此项操作"); + }, }); // 遍历并尝试分发监听事件 @@ -1450,7 +1534,7 @@ class DomainMonitors { * monitor.require("property", "value"); // 指定监听 value 属性 * monitor.filter((access, nameds) => nameds.value >= 0); // 过滤掉大于等于 0 的修改 * monitor.then((access, nameds, control) => { - * control.overrideParameter("value", 0); // 将要修改的新值改回 0 + * control.overrideParameter("value", 0); // 将要修改的新值改回 0 * }); * monitor.start(); // 启动Monitor * ``` @@ -1595,37 +1679,41 @@ class Monitor { /** * - * @typedef {"target" | "thisArg" | "arguments" | "newTarget" | "property" | "descriptor" | "receiver" | "prototype" | "value"} PropertyKey + * @typedef {"target" | "thisArg" | "arguments" + * | "newTarget" | "property" | "descriptor" + * | "receiver" | "prototype" | "value" + * } PropertyKey * * @typedef {{ - * domain: Domain, - * action: number, + * domain: Domain, + * action: number, * }} Access * * @typedef {{ - * target: Object, - * thisArg?: Object, - * arguments?: Array, - * newTarget?: Function, - * property?: string | symbol, - * descriptor?: { - * value?: any, - * writable?: boolean, - * get?: () => any, - * set?: (value: any) => void, - * enumerable?: boolean, - * configurable?: boolean, - * }, - * receiver?: Object, - * prototype?: Object, - * value?: any, + * target: Object, + * thisArg?: Object, + * arguments?: Array, + * newTarget?: Function, + * property?: string | symbol, + * descriptor?: { + * value?: any, + * writable?: boolean, + * get?: () => any, + * set?: (value: any) => void, + * enumerable?: boolean, + * configurable?: boolean, + * }, + * receiver?: Object, + * prototype?: Object, + * value?: any, * }} Nameds * * @typedef {{ - * preventDefault: () => void, - * stopPropagation: () => void, - * overrideParameter: (name: PropertyKey, value: any) => void, - * setReturnValue: (value: any) => void, + * preventDefault: () => void, + * stopPropagation: () => void, + * overrideParameter: (name: PropertyKey, value: any) => void, + * setReturnValue: (value: any) => void, + * throwDenied: (message?: string) => never, * }} Control * */ @@ -1774,7 +1862,8 @@ class Monitor { return [ thiz.#actions, thiz.#allowDomains, - thiz.#disallowDomains + thiz.#disallowDomains, + thiz.#checkInfo["target"], ]; } @@ -1895,8 +1984,8 @@ class Marshal { * ``` * * @typedef {[ - * Domain, - * Object, + * Domain, + * Object, * ]} Reverted * * @param {any} proxy @@ -2000,6 +2089,29 @@ class Marshal { return domain; } + /** + * ```plain + * 对于封送或未封送的函数执行转字符串操作 + * ``` + * + * @param {Function} func + */ + static decompileFunction(func) { + if (typeof func !== "function") + throw new TypeError("无效的函数对象"); + + if (Marshal.#marshalledProxies.has(func)) + [, func] = Marshal.#revertProxy(func); + + const refs = Sandbox[SandboxExposer2] + (SandboxSignal_TryFunctionRefs, func); + + if (refs) + return refs; + + return Function.prototype.toString.call(func); + } + /** * ```plain * 陷入某个运行域并执行代码 @@ -2073,6 +2185,41 @@ class Marshal { } /** + * ```plain + * 根据目标对象的特征复制一个基本对象 + * ``` + * + * @param {Object} src + * @returns {any} + */ + static #clonePureObject = function (src) { + let cloned; + + if (typeof src === "function") { + const descriptor = Reflect.getOwnPropertyDescriptor(src, "prototype"); + if (descriptor + && descriptor.value + && !descriptor.enumerable + && !descriptor.configurable + && descriptor.value.constructor === src) + cloned = function () { }; + else + cloned = () => { }; + } else if (Array.isArray(src)) + cloned = []; + else + cloned = {}; + + Reflect.setPrototypeOf(cloned, null); + cloned["target"] = src; + return cloned; + } + + /** + * ```plain + * 封送核心函数 + * ``` + * * @param {Object} obj * @param {Domain} targetDomain * @returns {Object} @@ -2135,9 +2282,15 @@ class Marshal { if (cached) return cached; + // 创建一个空白对象,防止JavaScript的一些奇怪错误 + const pure = Marshal.#clonePureObject(target); + // 设置属性方便调试 + pure.sourceDomain = sourceDomain; + pure.targetDomain = targetDomain; + // 创建封送代理 - const proxy = new Proxy(target, { - apply(target, thisArg, argArray) { + const proxy = new Proxy(pure, { + apply(_, thisArg, argArray) { const defaultApply = () => { const marshalledThis = Marshal.#marshal(thisArg, sourceDomain); const marshalledArgs = Marshal.#marshalArray(argArray, sourceDomain); @@ -2169,7 +2322,7 @@ class Marshal { return defaultApply(); }, - construct(target, argArray, newTarget) { + construct(_, argArray, newTarget) { const marshalledArgs = Marshal.#marshalArray(argArray, sourceDomain); const marshalledNewTarget = Marshal.#marshal(newTarget, sourceDomain); @@ -2192,7 +2345,7 @@ class Marshal { return Marshal.#marshal(result, targetDomain); }); }, - defineProperty(target, property, attributes) { + defineProperty(_, property, attributes) { const isSourceDomain = sourceDomain === Domain.current; if (!isSourceDomain) { @@ -2247,7 +2400,7 @@ class Marshal { ? domainTrapAction() : Marshal.#trapDomain(sourceDomain, domainTrapAction); }, - deleteProperty(target, p) { + deleteProperty(_, p) { return Marshal.#trapDomain(sourceDomain, () => { const rule = ruleRef.rule; @@ -2265,7 +2418,7 @@ class Marshal { return Reflect.deleteProperty(...args); }); }, - get(target, p, receiver) { + get(_, p, receiver) { // 因为 get 的东西最多,所以对此追加注释 // 其他的拦截器都是与 get 类似 @@ -2304,7 +2457,7 @@ class Marshal { return Marshal.#marshal(result, targetDomain); }); }, - getOwnPropertyDescriptor(target, p) { + getOwnPropertyDescriptor(_, p) { const isSourceDomain = Domain.current === sourceDomain; const domainTrapAction = () => { @@ -2332,7 +2485,7 @@ class Marshal { return Marshal.#marshalObject(descriptor, targetDomain); }); }, - getPrototypeOf(target) { + getPrototypeOf(_) { return Marshal.#trapDomain(sourceDomain, () => { const rule = ruleRef.rule; @@ -2356,7 +2509,7 @@ class Marshal { return marshalledResult; }); }, - has(target, p) { + has(_, p) { const isSourceDomain = Domain.current === sourceDomain; const domainTrapAction = () => { const rule = ruleRef.rule; @@ -2380,10 +2533,10 @@ class Marshal { return Marshal.#trapDomain(sourceDomain, domainTrapAction); }, - isExtensible(target) { + isExtensible(_) { return Reflect.isExtensible(target); }, - ownKeys(target) { + ownKeys(_) { return Marshal.#trapDomain(sourceDomain, () => { const rule = ruleRef.rule; @@ -2410,7 +2563,7 @@ class Marshal { }); }, - preventExtensions(target) { + preventExtensions(_) { return Marshal.#trapDomain(sourceDomain, () => { const rule = ruleRef.rule; @@ -2428,7 +2581,7 @@ class Marshal { return Reflect.preventExtensions(...args); }); }, - set(target, p, newValue, receiver) { + set(_, p, newValue, receiver) { const marshalledNewValue = Marshal.#marshal(newValue, sourceDomain); const marshalledReceiver = Marshal.#marshal(receiver, sourceDomain); @@ -2450,7 +2603,7 @@ class Marshal { return Reflect.set(...args); }); }, - setPrototypeOf(target, v) { + setPrototypeOf(_, v) { const marshalledV = Marshal.#marshal(v, sourceDomain); if (Marshal.#marshalledProxies.has(marshalledV)) @@ -2610,30 +2763,30 @@ class Domain { // 实装这个要代理Object喵 // static #hasInstanceMarshalled = function (obj) { - // if (Marshal.isMarshalled(obj)) - // [, obj] = Marshal[SandboxExposer2] - // (SandboxSignal_UnpackProxy, obj); + // if (Marshal.isMarshalled(obj)) + // [, obj] = Marshal[SandboxExposer2] + // (SandboxSignal_UnpackProxy, obj); - // return Domain.#hasInstance.call(this, obj); + // return Domain.#hasInstance.call(this, obj); // } // 效率影响不确定,暂不实装 // static #marshalledThen = function (onfulfilled, onrejected) { - // if (Marshal.isMarshalled(this)) { - // const [domain, promise] = Marshal[SandboxExposer2] - // (SandboxSignal_UnpackProxy, this); + // if (Marshal.isMarshalled(this)) { + // const [domain, promise] = Marshal[SandboxExposer2] + // (SandboxSignal_UnpackProxy, this); - // const marshaller = value => { - // return this(trapMarshal(domain, Domain.current, value)); - // }; + // const marshaller = value => { + // return this(trapMarshal(domain, Domain.current, value)); + // }; - // return trapMarshal(domain, Domain.current, promise.then( - // marshaller.bind(onfulfilled), - // marshaller.bind(onrejected) - // )); - // } + // return trapMarshal(domain, Domain.current, promise.then( + // marshaller.bind(onfulfilled), + // marshaller.bind(onrejected) + // )); + // } - // return [[DefaultThen]].call(this, onfulfilled, onrejected); + // return [[DefaultThen]].call(this, onfulfilled, onrejected); // } /** @@ -2881,9 +3034,6 @@ class Domain { return Domain.#exitDomain(); case SandboxSignal_ListDomain: return Domain.#listDomain(); - case SandboxSignal_IsArray: - // @ts-ignore - return Domain.#isArray(...args); } } } @@ -2925,6 +3075,8 @@ class Sandbox { static #domainMap = new WeakMap(); /** @type {Array} */ static #executingScope = []; + /** @type {WeakMap} */ + static #functionRefCodes = new WeakMap(); /** @type {Object} */ #scope; @@ -3015,10 +3167,10 @@ class Sandbox { const code = argArray.slice(-1)[0]; const params = argArray.slice(0, -1); - new target(code); // 防止注入 const compiled = Sandbox.#compileCore(thiz, code, null, params, true); - compiled[Symbol.toStringTag] = `function (${params.join(", ")}) {\n${code}\n}`; + Sandbox.#functionRefCodes.set(compiled, + `function (${params.join(", ")}) {\n${code}\n}`); return compiled; } @@ -3486,6 +3638,18 @@ class Sandbox { return builtName; } + + /** + * @param {Symbol} signal + * @param {...any} args + * @returns + */ + static [SandboxExposer2](signal, ...args) { + switch (signal) { + case SandboxSignal_TryFunctionRefs: + return Sandbox.#functionRefCodes.get(args[0]); + } + } } function sealClass(clazz) { diff --git a/noname/util/security.js b/noname/util/security.js index 4ad2c20f1..f7c17c6fd 100644 --- a/noname/util/security.js +++ b/noname/util/security.js @@ -98,6 +98,8 @@ let ModAsyncGeneratorFunction; function enterSandbox(box) { if (!SANDBOX_ENABLED) return; + if (!(box instanceof Sandbox)) + throw new TypeError("无效的沙盒对象"); if (!Domain.isBelievable(Domain.topDomain)) throw "无法在沙盒里面访问"; @@ -117,7 +119,7 @@ function exitSandbox() { if (!Domain.isBelievable(Domain.topDomain)) throw "无法在沙盒里面访问"; if (!sandboxStack.length) - return; + throw new ReferenceError("无法弹出更多的沙盒"); sandboxStack.pop(); } @@ -422,6 +424,7 @@ async function initSecurity({ ...ioFuncs.map(n => game[n]).filter(Boolean), ...Object.values(game.promises), defaultEval, + localStorage.setItem, window.require, // @ts-ignore window.define, @@ -472,6 +475,8 @@ async function initSecurity({ writeRule.setGranted(AccessAction.DEFINE, false); // 禁止重定义属性 // 禁止修改 game.promises 的函数 Marshal.setRule(game.promises, writeRule); + // 禁止修改 localStorage + Marshal.setRule(localStorage, writeRule); // 对于 game 当中访问特定函数我们通过 Monitor 进行拦截 new Monitor() @@ -481,29 +486,28 @@ async function initSecurity({ // 如果目标是 game 的 ioFuncs 包含的所有函数 .require("target", game) .require("property", ...ioFuncs) - .require("property", "ws") + .require("property", "ws", "sandbox") // 抛出异常 .then((access, nameds, control) => { - throw `禁止沙盒修改 \`game.${nameds.prototype}\` 属性`; + throw `有不信任的代码修改 \`game.${String(nameds.property)}\` 属性`; }) // 让 Monitor 开始工作 .start(); // 差点忘记启动了喵 - // 现在 parsex 已经禁止传递字符串,这段 Monitor 不需要了 // 监听原型、toStringTag的更改 - // const toStringTag = Symbol.toStringTag; - // new Monitor() - // .action(AccessAction.WRITE) - // .action(AccessAction.DEFINE) - // .action(AccessAction.META) - // .require("property", toStringTag) - // .then((access, nameds, control) => { - // // 阻止原型、toStringTag的更改 - // control.preventDefault(); - // control.stopPropagation(); - // control.setReturnValue(false); - // }) - // .start(); + const toStringTag = Symbol.toStringTag; + new Monitor() + .action(AccessAction.WRITE) + .action(AccessAction.DEFINE) + .action(AccessAction.META) + .require("property", toStringTag) + .then((access, nameds, control) => { + // 阻止原型、toStringTag的更改 + control.preventDefault(); + control.stopPropagation(); + control.setReturnValue(false); + }) + .start(); if (SANDBOX_AUTOTEST) { // 一个测试循环喵 @@ -646,12 +650,12 @@ function getIsolatedsFrom(item) { * ``` * * @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, + * 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() { @@ -885,11 +889,11 @@ function setupPolyfills(sandbox) { } // 测试暴露喵 -// Reflect.defineProperty(window, "sandbox", { -// get: () => defaultSandbox, -// set: () => { }, -// configurable: true, -// }); +Reflect.defineProperty(window, "sandbox", { + get: () => defaultSandbox, + set: () => { }, + configurable: true, +}); const exports = { enterSandbox,