修复bug;提高沙盒稳定性;优化沙盒性能

This commit is contained in:
IceCola 2024-05-29 01:53:01 +08:00
parent 7a8a98424c
commit 03380d1d93
6 changed files with 385 additions and 145 deletions

View File

@ -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();
/**

View File

@ -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;

View File

@ -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"]);

View File

@ -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();
//获取第一个 { 后的所有字符

View File

@ -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) {

View File

@ -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,