Merge pull request #1401 from IceCola97/sandbox-dev

补充一个sandbox的问题;同时为调试模式和苹果设备放宽;适配eruda
This commit is contained in:
Spmario233 2024-05-30 18:56:45 +08:00 committed by GitHub
commit 50083b3a07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 101 additions and 45 deletions

View File

@ -125,9 +125,6 @@ export async function boot() {
// 加载polyfill内容
await import("./polyfill.js");
// 初始化沙盒的Realms
await initializeSandboxRealms();
// 设定游戏加载时间,超过时间未加载就提醒
const configLoadTime = localStorage.getItem(lib.configprefix + "loadtime");
// 现在不暴露到全局变量里了直接传给onload
@ -248,13 +245,6 @@ export async function boot() {
}
}
// 初始化security
const securityModule = await import("../util/security.js");
const security = securityModule.default;
security.initSecurity({
lib, game, ui, get, ai, _status, gnc,
});
const loadCssPromise = loadCss();
const loadConfigPromise = loadConfig();
await loadCssPromise;
@ -303,6 +293,18 @@ export async function boot() {
}
}
const sandboxEnabled = !config.get("debug") && !get.is.safari();
// 初始化沙盒的Realms
await initializeSandboxRealms(sandboxEnabled);
// 初始化security
const securityModule = await import("../util/security.js");
const security = securityModule.default;
security.initSecurity({
lib, game, ui, get, ai, _status, gnc,
});
if (Reflect.get(window, "isNonameServer")) config.set("mode", "connect");
var pack = Reflect.get(window, "noname_package");

View File

@ -1,5 +1,6 @@
// 为了兼容无法使用顶级await的版本
const iframe = document.createElement("iframe");
// 方便开关确定沙盒的问题喵
// 当此处为true、debug模式为启用、设备非苹果时沙盒生效
let SANDBOX_ENABLED = true;
// 执行上下文传递函数,请勿动喵
// 用于传递顶级execute context
@ -38,7 +39,12 @@ function replaceName(path, name) {
const TARGET_URL = replaceName(import.meta.url, "sandbox.js");
const SANDBOX_EXPORT = {};
async function initializeSandboxRealms() {
async function initializeSandboxRealms(enabled) {
if (!enabled) {
SANDBOX_ENABLED = false;
return;
}
const document = window.document;
const createElement = document.createElement.bind(document);
const appendChild = document.body.appendChild.bind(document.body);
@ -86,10 +92,6 @@ async function initializeSandboxRealms() {
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;
@ -111,7 +113,12 @@ async function initializeSandboxRealms() {
iframe.remove();
}
function isSandboxEnabled() {
return SANDBOX_ENABLED;
}
export {
initializeSandboxRealms,
isSandboxEnabled,
SANDBOX_EXPORT,
};

View File

@ -7,7 +7,7 @@
// 最后为安全考虑,请遵守规范,尽量不要使用 `eval` 函数而是使用 `security.exec2` 来替代
import { SANDBOX_EXPORT } from "./initRealms.js";
import { SANDBOX_EXPORT, isSandboxEnabled } from "./initRealms.js";
// 很重要的事情!
// 请不要在在其他文件中import sandbox.js
@ -16,8 +16,8 @@ import { SANDBOX_EXPORT } from "./initRealms.js";
/** @typedef {any} Window */
// 方便开关确定沙盒的问题喵
const SANDBOX_ENABLED = true;
// 新的开关放到了 "./initRealms.js" 里面,请不要改动此处!
const SANDBOX_ENABLED = isSandboxEnabled();
// 暴露方法Symbol用于类之间通信
const SandboxExposer = Symbol("Sandbox.Exposer"); // 实例暴露
@ -382,6 +382,7 @@ const GLOBAL_PATHES = Object.freeze([
"/WeakRef",
"/WeakMap",
"/WeakSet",
["/Object/Symbol(Symbol.hasInstance)", "Object[Symbol.hasInstance]"],
"/Object/prototype",
"/Array/prototype",
"/Function/prototype",
@ -2220,7 +2221,6 @@ class Marshal {
cloned = {};
Reflect.setPrototypeOf(cloned, null);
cloned["target"] = src;
return cloned;
}
@ -2293,12 +2293,14 @@ class Marshal {
// 创建一个空白对象防止JavaScript的一些奇怪错误
const pure = Marshal.#clonePureObject(target);
// 设置属性方便调试
pure.sourceDomain = sourceDomain;
pure.targetDomain = targetDomain;
// 创建封送代理
const proxy = new Proxy(pure, {
// 设置属性方便调试
// @ts-ignore
$target: target,
$sourceDomain: sourceDomain,
$targetDomain: targetDomain,
apply(_, thisArg, argArray) {
const defaultApply = () => {
const marshalledThis = Marshal.#marshal(thisArg, sourceDomain);
@ -2400,7 +2402,19 @@ class Marshal {
return !!dispatched.returnValue;
// @ts-ignore
return Reflect.defineProperty(...args);
const success = Reflect.defineProperty(...args);
if (success && target === args[0]) {
// 为适配JavaScript对于代理的强制要求
// 我们要对空白对象模拟不可配置
// @ts-ignore
attributes = Reflect.getOwnPropertyDescriptor(...args);
if (!attributes.configurable)
Reflect.defineProperty(pure, args[1], attributes);
}
return success;
};
// `defineProperty`、`getOwnPropertyDescriptor`、`has` 都可能被JavaScript引擎重复调用
@ -2587,7 +2601,18 @@ class Marshal {
return !!dispatched.returnValue;
// @ts-ignore
return Reflect.preventExtensions(...args);
const success = Reflect.preventExtensions(...args);
if (success && target === args[0]) {
// 为适配JavaScript对于代理的强制要求
// 我们要对空白对象进行模拟键
Reflect.ownKeys(target).forEach(key => {
pure[key] = undefined;
});
Reflect.preventExtensions(pure);
}
return success;
});
},
set(_, p, newValue, receiver) {
@ -3212,29 +3237,50 @@ 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
* 替代原本的eval函数阻止访问原生的 window 对象
* ```
*
* @param {Window} trueWindow
* @param {(x: string) => any} _eval
* @param {Proxy} intercepter
* @param {Window} global
* @param {Sandbox} thiz
* @param {any} x
* @returns
*/
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;
static #wrappedEval = function (thiz, x) {
let code = String(x).trim();
while (code.endsWith(";"))
code = code.slice(0, -1);
if (!/[;\n\r]$/.test(code))
code = `return (${code})`;
return thiz.exec(code);
}
/**
@ -3476,7 +3522,7 @@ class Sandbox {
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}))})()}}}`);
const raw = new thiz.#domainFunction("_", `with(_){with(window){with(${contextName}){return(${applyName}(function(${parameters}){"use strict";\n// 沙盒代码起始\n${code}\n// 沙盒代码结束\n},${contextName}.this,${argsName}))}}}`);
const domain = thiz.#domain;
const domainWindow = thiz.#domainWindow;
@ -3520,8 +3566,9 @@ class Sandbox {
},
});
wrappedEval = Sandbox.#wrappedEval.bind(null,
thiz.#domainWindow, thiz.#domainEval, intercepter, scope);
// wrappedEval = Sandbox.#wrappedEval.bind(null,
// thiz.#domainWindow, thiz.#domainEval, intercepter, scope);
wrappedEval = Sandbox.#wrappedEval.bind(null, thiz);
// 构建陷入的沙盒闭包
// 同时对返回值进行封送