Skip to content

Commit

Permalink
fix: 沙盒内可访问window上对象
Browse files Browse the repository at this point in the history
  • Loading branch information
CodFrm committed Nov 8, 2021
1 parent 05021c1 commit 49a276c
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 90 deletions.
2 changes: 1 addition & 1 deletion build/scriptcat/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "ScriptCat",
"version": "0.7.2",
"version": "0.7.3",
"description": "脚本猫,一个用户脚本的框架,可编写脚本每天帮你自动处理事务.",
"background": {
"page": "background.html"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "scriptcat",
"version": "0.7.2",
"version": "0.7.3",
"description": "脚本猫,一个可以执行用户脚本的浏览器扩展,万物皆可脚本化,让你的浏览器可以做更多的事情!",
"scripts": {
"test": "jest",
Expand Down
2 changes: 1 addition & 1 deletion src/apps/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const ExtVersion = "0.7.2";
export const ExtVersion = "0.7.3";

export const Server = process.env.NODE_ENV == "production" ? "https://sc.icodef.com/" : "http://localhost:8080/";

Expand Down
2 changes: 1 addition & 1 deletion src/apps/script/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { ValueModel } from '@App/model/value';
import { App } from '../app';
import { Resource } from '@App/model/do/resource';
import { ResourceManager } from '../resource';
import { compileScriptCode } from '@App/pkg/sandbox';
import { compileScriptCode } from '@App/pkg/sandbox/compile';
import { SubscribeModel } from '@App/model/subscribe';
import { Subscribe, SUBSCRIBE_STATUS_DISABLE, SUBSCRIBE_STATUS_ENABLE } from '@App/model/do/subscribe';
import { File } from '@App/model/do/back';
Expand Down
3 changes: 2 additions & 1 deletion src/injected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { ExternalMessage, ScriptExec, ScriptValueChange } from "./apps/msg-cente
import { ScriptCache } from "./model/do/script";
import { Value } from "./model/do/value";
import { addStyle } from "./pkg/frontend";
import { buildThis, createContext } from "./pkg/sandbox";
import { createContext } from "./pkg/sandbox/compile";
import { buildThis } from "./pkg/sandbox/sandbox";

// 参考了tm的实现
function waitBody(callback: () => void) {
Expand Down
68 changes: 68 additions & 0 deletions src/pkg/sandbox/compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { SandboxContext, ScriptContext } from "@App/apps/grant/frontend";
import { ScriptCache, Script } from "@App/model/do/script";

export function compileScriptCode(script: ScriptCache): string {
let code = script.code;
let require = '';
script.metadata['require'] && script.metadata['require'].forEach((val) => {
let res = script.resource[val];
if (res) {
require = require + "\n" + res.content;
}
});
code = require + code;
return 'with (context) return (()=>{\n' + code + '\n})()'
}

export function compileScript(script: ScriptCache): Function {
return new Function('context', script.code);
}

function setDepend(context: ScriptContext, apiVal: { [key: string]: any }) {
if (apiVal.param.depend) {
for (let i = 0; i < apiVal.param.depend.length; i++) {
let value = apiVal.param.depend[i];
let dependApi = context.getApi(value);
if (!dependApi) {
return;
}
if (value.startsWith("GM.")) {
let [_, t] = value.split(".");
context["GM"][t] = dependApi.api;
} else {
context[value] = dependApi.api;
}
setDepend(context, dependApi);
}
}
}

export function createSandboxContext(script: ScriptCache): SandboxContext {
let context: SandboxContext = new SandboxContext(script);
return <SandboxContext>createContext(context, script);
}

export function createContext(context: ScriptContext, script: Script): ScriptContext {
context['postRequest'] = context.postRequest;
context['script'] = context.script;
if (script.metadata["grant"]) {
context["GM"] = context;
script.metadata["grant"].forEach((value: any) => {
let apiVal = context.getApi(value);
if (!apiVal) {
return;
}
if (value.startsWith("GM.")) {
let [_, t] = value.split(".");
context["GM"][t] = apiVal.api;
} else {
context[value] = apiVal.api;
}
setDepend(context, apiVal);
});
}
context['GM_info'] = context.GM_info();

// 去除原型链
return Object.assign({}, context);
}
96 changes: 16 additions & 80 deletions src/pkg/sandbox.ts → src/pkg/sandbox/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
import { SandboxContext, ScriptContext } from "@App/apps/grant/frontend";
import { ScriptCache, Script } from "@App/model/do/script";

export function compileScriptCode(script: ScriptCache): string {
let code = script.code;
let require = '';
script.metadata['require'] && script.metadata['require'].forEach((val) => {
let res = script.resource[val];
if (res) {
require = require + "\n" + res.content;
}
});
code = require + code;
return 'with (context) return (()=>{\n' + code + '\n})()'
}

export function compileScript(script: ScriptCache): Function {
return new Function('context', script.code);
}


export function buildWindow(): any {
return {
localStorage: global.localStorage,
}
}

let writables: any = {
let sandboxGlobal: any = {
"addEventListener": global.addEventListener,
"removeEventListener": global.removeEventListener,
"dispatchEvent": global.dispatchEvent,
Expand All @@ -38,29 +20,32 @@ export let init = new Map<string, boolean>();
let descs = Object.getOwnPropertyDescriptors(global);
for (const key in descs) {
let desc = descs[key];
if (desc && desc.writable && !writables[key]) {
writables[key] = desc.value;
if (desc && desc.writable && !sandboxGlobal[key]) {
sandboxGlobal[key] = desc.value;
} else {
init.set(key, true);
try {
sandboxGlobal[key] = (<any>global)[key];
} catch (e) {
}
}
}


// 处理有多层结构的(先只对特殊的做处理)
['console'].forEach(obj => {
let descs = Object.getOwnPropertyDescriptors((<any>global)[obj]);
writables[obj] = {};// 清零
sandboxGlobal[obj] = {};// 清零
for (const key in descs) {
let desc = descs[key];
if (desc && desc.writable) {
writables[obj][key] = desc.value;
sandboxGlobal[obj][key] = desc.value;
}
}
});

//TODO:做一些恶意操作拦截等
export function buildThis(global: any, context: any) {
let special = Object.assign({}, writables);
let special = Object.assign({}, sandboxGlobal);
// 后台脚本要不要考虑不能使用eval?
let _this: any = { eval: global.eval };
let proxy: any = new Proxy(context, {
Expand All @@ -72,7 +57,7 @@ export function buildThis(global: any, context: any) {
case 'window':
case 'global':
case 'globalThis':
return special[name] || proxy;
return _this[name] || proxy;
}
if (name !== 'undefined' && name !== Symbol.unscopables) {
if (context[name]) {
Expand All @@ -87,9 +72,8 @@ export function buildThis(global: any, context: any) {
}
return special[name];
}
if (global[name] !== undefined) {
if (init.has(<string>name)) {
if (typeof global[name] === 'function' && !global[name].prototype) {
console.log('b', name, global[name]);
return global[name].bind(global);
}
return global[name];
Expand All @@ -98,14 +82,15 @@ export function buildThis(global: any, context: any) {
return undefined;
},
has(_, name) {
return name == 'undefined' || context[name] || global.hasOwnProperty(name);
// 全返回true,走get里面,如果返回false,不会进入get,会跑出沙盒取变量
return true;
},
set(_, name: string, val) {
switch (name) {
case 'window':
case 'global':
case 'globalThis':
special[name] = val;
_this[name] = val;
return true;
}
if (special[name]) {
Expand All @@ -129,58 +114,9 @@ export function buildThis(global: any, context: any) {
if (ret) {
return ret;
}
ret = Object.getOwnPropertyDescriptor(global, name);
ret = Object.getOwnPropertyDescriptor(sandboxGlobal, name);
return ret;
}
});
return proxy;
}

function setDepend(context: ScriptContext, apiVal: { [key: string]: any }) {
if (apiVal.param.depend) {
for (let i = 0; i < apiVal.param.depend.length; i++) {
let value = apiVal.param.depend[i];
let dependApi = context.getApi(value);
if (!dependApi) {
return;
}
if (value.startsWith("GM.")) {
let [_, t] = value.split(".");
context["GM"][t] = dependApi.api;
} else {
context[value] = dependApi.api;
}
setDepend(context, dependApi);
}
}
}

export function createSandboxContext(script: ScriptCache): SandboxContext {
let context: SandboxContext = new SandboxContext(script);
return <SandboxContext>createContext(context, script);
}

export function createContext(context: ScriptContext, script: Script): ScriptContext {
context['postRequest'] = context.postRequest;
context['script'] = context.script;
if (script.metadata["grant"]) {
context["GM"] = context;
script.metadata["grant"].forEach((value: any) => {
let apiVal = context.getApi(value);
if (!apiVal) {
return;
}
if (value.startsWith("GM.")) {
let [_, t] = value.split(".");
context["GM"][t] = apiVal.api;
} else {
context[value] = apiVal.api;
}
setDepend(context, apiVal);
});
}
context['GM_info'] = context.GM_info();

// 去除原型链
return Object.assign({}, context);
}
3 changes: 2 additions & 1 deletion src/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CronJob } from "cron";
import { buildThis, compileScript, createSandboxContext } from "@App/pkg/sandbox";
import { buildThis } from "@App/pkg/sandbox/sandbox";
import { SandboxContext } from "./apps/grant/frontend";
import { SendLogger } from "./pkg/utils/utils";
import { App, ENV_FRONTEND, InitApp } from "./apps/app";
Expand All @@ -9,6 +9,7 @@ import { AppEvent, ScriptValueChange } from "./apps/msg-center/event";
import { LOGGER_LEVEL_INFO, LOGGER_LEVEL_ERROR } from "./model/do/logger";
import { Script, ScriptCache, SCRIPT_TYPE_CRONTAB } from "./model/do/script";
import { nextTime } from "./views/pages/utils";
import { compileScript, createSandboxContext } from "./pkg/sandbox/compile";

InitApp({
Log: new ConsoleLogger(),
Expand Down
8 changes: 5 additions & 3 deletions src/views/pages/Option/tabs/ScriptTab/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
}"
>
<div style="padding-left: 6px; background: #e0e0e0">
<v-menu v-for="[title, items] in Object.entries(menu)" :key="title" offset-y>
<v-menu
v-for="[title, items] in Object.entries(menu)"
:key="title"
offset-y
>
<template v-slot:activator="{ on, attrs }">
<v-btn
v-bind="attrs"
Expand Down Expand Up @@ -65,9 +69,7 @@ import { Script, SCRIPT_TYPE_NORMAL } from "@App/model/do/script";
import ResizableEditor from "@components/ResizableEditor.vue";
import EventType from "@Option/EventType";
import { scriptModule } from "../../store/script";
import eventBus from "@App/views/EventBus";
import { createSandboxContext } from "@App/pkg/sandbox";
interface IEditorMenu {
[title: string]: {
Expand Down
3 changes: 2 additions & 1 deletion tests/sandbox.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { buildThis, init } from "@App/pkg/sandbox";
import { buildThis, init } from "@App/pkg/sandbox/sandbox";


describe("sandbox", () => {
let context: any = {};
let global: any = { gbok: 'gbok', onload: null };
init.set('onload', true);
init.set('gbok', true);
let _this = buildThis(global, context);

it("set contenxt", () => {
Expand Down

0 comments on commit 49a276c

Please sign in to comment.