From 55736093a66187d7950f2073191a961c08e108e2 Mon Sep 17 00:00:00 2001 From: IceCola <739201322@qq.com> Date: Wed, 29 May 2024 19:44:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B8=B8=E6=88=8F=E5=86=85?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=8F=B0bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- noname/ui/create/menu/pages/otherMenu.js | 37 ++++++++++------- noname/util/sandbox.js | 52 ++++++++++++++++++++---- noname/util/security.js | 2 +- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/noname/ui/create/menu/pages/otherMenu.js b/noname/ui/create/menu/pages/otherMenu.js index 834d14335..da128c5e0 100644 --- a/noname/ui/create/menu/pages/otherMenu.js +++ b/noname/ui/create/menu/pages/otherMenu.js @@ -1229,6 +1229,7 @@ export const otherMenu = function (/** @type { boolean | undefined } */ connectM return; } + // control.overrideParameter("target", window); }) .start(); @@ -1260,21 +1261,27 @@ export const otherMenu = function (/** @type { boolean | undefined } */ connectM */ let fun if (security.isSandboxRequired()) { - fun = security.eval(` - const _status=window._status; - const lib=window.lib; - const game=window.game; - const ui=window.ui; - const get=window.get; - const ai=window.ai; - // const cheat=window.lib.cheat; // 不再允许使用 cheat,因为它是不允许访问的变量 - //使用正则匹配绝大多数的普通obj对象,避免解析成代码块。 - const reg=${/^\{([^{}]+:\s*([^\s,]*|'[^']*'|"[^"]*"|\{[^}]*\}|\[[^\]]*\]|null|undefined|([a-zA-Z$_][a-zA-Z0-9$_]*\s*:\s*)?[a-zA-Z$_][a-zA-Z0-9$_]*\(\)))(?:,\s*([^{}]+:\s*(?:[^\s,]*|'[^']*'|"[^"]*"|\{[^}]*\}|\[[^\]]*\]|null|undefined|([a-zA-Z$_][a-zA-Z0-9$_]*\s*:\s*)?[a-zA-Z$_][a-zA-Z0-9$_]*\(\))))*\}$/}; - return function(value){ - "use strict"; - return eval(reg.test(value)?('('+value+')'):value); - }; - `); + const reg = /^\{([^{}]+:\s*([^\s,]*|'[^']*'|"[^"]*"|\{[^}]*\}|\[[^\]]*\]|null|undefined|([a-zA-Z$_][a-zA-Z0-9$_]*\s*:\s*)?[a-zA-Z$_][a-zA-Z0-9$_]*\(\)))(?:,\s*([^{}]+:\s*(?:[^\s,]*|'[^']*'|"[^"]*"|\{[^}]*\}|\[[^\]]*\]|null|undefined|([a-zA-Z$_][a-zA-Z0-9$_]*\s*:\s*)?[a-zA-Z$_][a-zA-Z0-9$_]*\(\))))*\}$/; + fun = function (value) { + const exp = reg.test(value) ? `(${value})` : value; + const expName = "_" + Math.random().toString().slice(2); + return security.exec(`return eval(${expName})`, { window: proxyWindow, [expName]: exp }); + }; + // security.exec(` + // const _status=window._status; + // const lib=window.lib; + // const game=window.game; + // const ui=window.ui; + // const get=window.get; + // const ai=window.ai; + // // const cheat=window.lib.cheat; // 不再允许使用 cheat,因为它是不允许访问的变量 + // //使用正则匹配绝大多数的普通obj对象,避免解析成代码块。 + // const reg=${/^\{([^{}]+:\s*([^\s,]*|'[^']*'|"[^"]*"|\{[^}]*\}|\[[^\]]*\]|null|undefined|([a-zA-Z$_][a-zA-Z0-9$_]*\s*:\s*)?[a-zA-Z$_][a-zA-Z0-9$_]*\(\)))(?:,\s*([^{}]+:\s*(?:[^\s,]*|'[^']*'|"[^"]*"|\{[^}]*\}|\[[^\]]*\]|null|undefined|([a-zA-Z$_][a-zA-Z0-9$_]*\s*:\s*)?[a-zA-Z$_][a-zA-Z0-9$_]*\(\))))*\}$/}; + // return function(value){ + // "use strict"; + // return eval(reg.test(value)?('('+value+')'):value); + // }; + // `, { window: proxyWindow }); } else { fun = (new Function('window', ` const _status=window._status; diff --git a/noname/util/sandbox.js b/noname/util/sandbox.js index 28d190510..de291f8c3 100644 --- a/noname/util/sandbox.js +++ b/noname/util/sandbox.js @@ -3101,9 +3101,11 @@ class Sandbox { /** @type {Document?} */ #domainDocument; /** @type {typeof Object} */ - #domainObject = Object; + #domainObject; /** @type {typeof Function} */ - #domainFunction = Function; + #domainFunction; + /** @type {typeof Function} */ + #domainEval; /** * ```plain @@ -3128,9 +3130,10 @@ class Sandbox { this.#domainDocument = null; // 默认不开放DOM,而且我们也缺少BrowserContext this.#domainObject = this.#domainWindow.Object; this.#domainFunction = this.#domainWindow.Function; + this.#domainEval = this.#domainWindow.eval; Sandbox.#domainMap.set(this.#domain, this); - Sandbox.#createScope(this); Sandbox.#initDomainFunctions(this, this.#domainWindow); + Sandbox.#createScope(this); } /** @@ -3209,6 +3212,31 @@ class Sandbox { rewriteCtor(defaultAsyncGeneratorFunction.prototype, new Proxy(defaultAsyncGeneratorFunction, handler)); } + /** + * ```plain + * 替代原本的eval函数,阻止访问原生的 window 对象 + * ``` + * + * @param {Window} trueWindow + * @param {(x: string) => any} _eval + * @param {Proxy} intercepter + * @param {Window} global + * @param {any} x + */ + static #wrappedEval = function (trueWindow, _eval, intercepter, global, x) { + const intercepterName = Sandbox.#makeName("_", trueWindow); + const evalName = Sandbox.#makeName("_", global); + const codeName = Sandbox.#makeName("_", global); + trueWindow[intercepterName] = intercepter; + global[evalName] = _eval; + global[codeName] = x; + const result = _eval(`with(${intercepterName}){with(window){${evalName}("use strict;"+${codeName})}}`); + delete global[codeName]; + delete global[evalName]; + delete trueWindow[intercepterName]; + return result; + } + /** * ```plain * 获取当前的scope @@ -3306,8 +3334,8 @@ class Sandbox { * ``` */ const builtins = { - Object: this.#domainObject, - Function: this.#domainFunction, + Object: this.#domainWindow.Object, + Function: this.#domainWindow.Function, Array: this.#domainWindow.Array, Math: this.#domainWindow.Math, Date: this.#domainWindow.Date, @@ -3332,7 +3360,7 @@ class Sandbox { Reflect: this.#domainWindow.Reflect, BigInt: this.#domainWindow.BigInt, JSON: this.#domainWindow.JSON, - eval: this.#domainWindow.eval, + // eval: this.#domainWindow.eval, // 我们另外定义 `eval` 函数 setTimeout: this.#domainWindow.setTimeout, clearTimeout: this.#domainWindow.clearTimeout, setInterval: this.#domainWindow.setInterval, @@ -3446,6 +3474,7 @@ class Sandbox { const writeContextAction = { exists: 0, extend: 1, all: 2 }[writeContext] || 0; let argumentList; + let wrappedEval; const raw = new thiz.#domainFunction("_", `with(_){with(window){with(${contextName}){return(()=>{"use strict";return(${applyName}(function(${parameters}){\n// 沙盒代码起始\n${code}\n// 沙盒代码结束\n},${contextName}.this,${argsName}))})()}}}`); @@ -3473,8 +3502,12 @@ class Sandbox { if (p === Symbol.unscopables) return undefined; - if (!(p in target)) + if (!(p in target)) { + if (p === "eval") + return wrappedEval; // 返回我们封装的 `eval` 函数 + throw new domainWindow.ReferenceError(`${String(p)} is not defined`); + } return target[p]; }, @@ -3487,9 +3520,12 @@ class Sandbox { }, }); + wrappedEval = Sandbox.#wrappedEval.bind(null, + thiz.#domainWindow, thiz.#domainEval, intercepter, scope); + // 构建陷入的沙盒闭包 // 同时对返回值进行封送 - return ((...args) => { + return ((/** @type {any} */ ...args) => { const prevDomain = Domain.current; const domainAction = () => { // 指定执行域 diff --git a/noname/util/security.js b/noname/util/security.js index c2e713c38..7159cdd1c 100644 --- a/noname/util/security.js +++ b/noname/util/security.js @@ -2,7 +2,7 @@ // 但沙盒不会也没有办法维护恶意服务器/房主对于游戏规则的破坏,请玩家尽量选择官方或其他安全的服务器,同时选择一个受信任的玩家作为房主 // 是否强制所有模式下使用沙盒 -const SANDBOX_FORCED = false; +const SANDBOX_FORCED = true; // 是否启用自动测试 const SANDBOX_AUTOTEST = false; // 是否禁用自动测试延迟