Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS沙箱机制及简单实现 #196

Open
funnycoderstar opened this issue Apr 28, 2022 · 0 comments
Open

JS沙箱机制及简单实现 #196

funnycoderstar opened this issue Apr 28, 2022 · 0 comments

Comments

@funnycoderstar
Copy link
Owner

funnycoderstar commented Apr 28, 2022

沙箱使用场景

在线编辑器,比如 codepen, stackblitz,这种,代码运行都是运行在一个沙箱里的,因为不能因为使用者代码写的有问题,而导致平台直接挂掉了。

有哪些方案

  • 基于属性 diff 实现的沙箱机制(SnapshotSandbox)
  • proxy
  • iframe
  • webworker
  • 基于 ES 提案 ShadowRealm API 介绍
  • vm2,node提供的原生的

简单实现

快照沙箱

class SnapshotSandbox {
    constructor() {
        this.proxy = window;
        this.sandboxSnapshot = {}; // 记录在沙箱上的所有属性快照
        this.windowSnapshot = {}; // 存储window上的所有属性快照
        this.active();
    }
    // 激活沙箱,在沙箱里进行操作
    active() {
        for (const prop in window) {
            if (window.hasOwnProperty(prop)) {
                // 激活时,先把window上的属性保存在 windowSnapshot
                this.windowSnapshot[prop] = window[prop];
            }
        }
        // 再把沙箱之前的属性重新赋值给 window
        Object.keys(this.sandboxSnapshot).forEach((p) => {
            window[p] = this.sandboxSnapshot[p];
        });
    }
    //失活
    inActive() {
        for (const prop in window) {
            if (window.hasOwnProperty(prop)) {
                // 比较现在的window 和之前的 window对比有啥区别
                if (window[prop] !== this.windowSnapshot[prop]) {
                    // 如果不一样,就说明有变化,需要保存变化
                    // 把沙箱的属性保存在sandboxSnapshot
                    this.sandboxSnapshot[prop] = window[prop];
                    // 需要将window上之间的属性 windowSnapshot 再赋值给window
                    window[prop] = this.windowSnapshot[prop];
                }
            }
        }
    }
}

let sandBox = new SnapshotSandbox();
// 应用的运行从开始到结束,切换后不会影响全局
((window) => {
    window.a = 1;
    window.b = 2;
    console.log(window.a, window.b); // 1, 2
    // 失活
    sandBox.inActive();
    console.log(window.a, window.b); // undefined undefined
    // 激活还原
    sandBox.active();
    console.log(window.a, window.b); // 1, 2
})(sandBox.proxy); // sandBox.proxy就是window

window 环境和沙箱环境,其实都是在 window 上进行操作,所以刚开始的时候有点分不清,其实就是要理解沙箱环境和 window 环境。在这两个环境来回切换的时候,需要对应环境原来的改动找回来,就需要两个变量来存储,一个 windowSnapshot 来存储 window 上的属性,一个 sandboxSnapshot 来存储沙箱上的属性

  • 从 window 切换到沙箱环境的时候(激活沙箱),需要把 window 上当前的属性保存到一个地方,windowSnapshot 就是用来保存 window 上的属性的,然后之后再把原来沙箱上的属性再还原回来(即把沙箱上之前的属性 sandboxSnapshot 再赋值到 window 上)
  • 从沙箱环境切换到 window 的时候(失活沙箱),需要把沙箱的改动保存到一个地方,sandboxSnapshot 就会用来保存沙箱上的属性的,然后之后再把原来 window 上的属性还原回来(即把 window 上之前的属性 windowSnapshot 再赋值到 window 上)

注意上面这种简单的实现方式只能隔离变量,类似于namespace,不能起到这部分逻辑挂了,不影响全局,因为本质还是在window上,像浏览器的沙箱,是进程级别的,所以可以起到完全隔离的状态

Proxy实现的沙箱

文档

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant