包装窗口对象以隔离javascript

时间:2019-01-03 13:52:02

标签: javascript

在我当前的项目中,我试图隔离第三方js,但仍允许它进行dom访问,例如,如果js代码使用“ document.cookie”,它将得到一个空字符串,但如果它将使用“ document.getElementById”,它将从dom中获得一个真实的节点。 我不能使用常规的iframe(具有sandbox属性或其他属性)来隔离代码,因为正如我提到的那样,我需要允许dom访问。 我目前正在做的是创建一个空白的iframe并挂接其所有的window / document方法/属性,而不是注入第三方js,但是目前我正面临两个问题:

1。window对象包含一些我无法钩住的只读属性,例如window.top,因此,如果js代码调用“ window.top”,它将获得最高的访问权限窗口而不是iframe窗口,我可以通过将所有内容包装在iframe的window对象中来解决此问题,

//source: https://blog.javascripting.com/2014/05/19/wrapping-the-dom-window-object/

var wrapper = {};  
for (var prop in window) {  
  (function(prop) {
    if (typeof(window[prop]) === 'function')  {
      wrapper[prop] = function() { return window[prop].apply(window, arguments); }
    }
    else {
      Object.defineProperty(wrapper, prop, {
        'get': function() {
          if (window[prop] === window) {
            return wrapper;
          }
          else {
            return window[prop];
          }
        },
        'set': function(value) { wrapper[prop] = value; }
      });
  })(prop);
}

,并且在某些属性被引用而没有“窗口”的情况下。前缀我可以使用with语句,所以现在我有了类似的东西:

(function(window) {
  with (window) {
    //Some third-party js
  }
})(wrapper);

但是为整个窗口对象创建一个包装器来处理只读属性似乎没有必要,我只能使用“ with”语句,但是如果调用了“ window.top”没用,那你觉得呢?我上面提到的方法有更好的选择吗?

  1. 在某些情况下,主窗口对象上的某些js代码将调用由我们在iframe中运行的第三方js创建的全局属性,因此,创建的属性将存在于iframe窗口对象上,而不存在在顶部窗口对象上,例如,如果我们的第三方js将执行以下操作:

    window.SomeProp = {“非常”:function(){}; ,“有用”:function(){},“ prop”:function(){}}

我们在顶部窗口中的代码将无法使用此属性,如果我提前知道属性名称,则可以将其包装并“代理”对iframe属性的调用,但这是针对特定情况的,我正在寻找对于一个通用属性,那么您认为有一种方法可以观察新属性的创建吗?

1 个答案:

答案 0 :(得分:0)

我批评你,然后我遇到了类似的问题。因此,我对您提供的此博客中的代码进行了一些升级:

var FakeWindow = /** @class */ (function () {
    function FakeWindow() {
        return this.prepare();
    }
    FakeWindow.prototype.prepare = function () {
        var wrapper = {};
        for (var _i = 0, _a = Object.getOwnPropertyNames(window).sort(); _i < _a.length; _i++) {
            var prop = _a[_i];
            (function (prop) {
                if (typeof (window[prop]) === 'function') {
                    wrapper[prop] = function () { return window[prop].apply(window, arguments); };
                }
                else {
                    Object.defineProperty(wrapper, prop, {
                        'get': function () {
                            if (window[prop] === window) {
                                return wrapper;
                            }
                            else {
                                return window[prop];
                            }
                        },
                        'set': function (value) { wrapper[prop] = value; }
                    });
                }
            })(prop);
        }
        return wrapper;
    };
    return FakeWindow;
}());

请注意,现在改为Object.getOwnPropertyNames(window)代替了prop in window,因为使用prop in window会丢失许多道具:

let count = 0;
for(prop in window){count++} //count is 270

我的方式:

Object.getOwnPropertyNames(window).length //846

道具数量可能会因浏览器和有效插件的不同而有所不同 因此,基本上将第三方库包装在一些假窗口对象中,就像这样:

const F = new FakeWindow();
(function{const window = F;return /* here goes library content */}).bind(F)()

您可能需要将其包装在eval()中,以在将其绑定到FakeWindow之前不执行lib的内容。

对于某些库,只需要不带const window...的绑定即可。 在ThreeJS和jQuery上进行了测试。