修复bug;提高沙盒稳定性;优化沙盒性能
This commit is contained in:
parent
7a8a98424c
commit
03380d1d93
|
@ -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();
|
||||
/**
|
||||
|
|
|
@ -8,8 +8,9 @@ import security from "../../util/security.js";
|
|||
export class Client {
|
||||
/**
|
||||
* @param {import('../index.js').NodeWS | InstanceType<typeof import('ws').WebSocket> | 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;
|
||||
|
|
|
@ -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"]);
|
||||
|
|
|
@ -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();
|
||||
//获取第一个 { 后的所有字符
|
||||
|
|
|
@ -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<Domain, DomainMonitors>} */
|
||||
static #domainMonitors = new WeakMap();
|
||||
|
||||
/** @type {WeakMap<Object, Set<Monitor>>} */
|
||||
#targetMonitorsMap = new WeakMap();
|
||||
/** @type {WeakMap<Domain, Object<number, Set<Monitor>>>} */
|
||||
#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<Monitor>?}
|
||||
* @param {Object} target
|
||||
* @returns {Array<Monitor>?}
|
||||
*/
|
||||
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<any>,
|
||||
* 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<any>,
|
||||
* 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<Function, string>} */
|
||||
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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue