Как избежать загрязнения глобалов с помощью загрузчика сценариев iframe? - PullRequest
7 голосов
/ 30 сентября 2010

Проблема ...

Существуют плохо закодированные сценарии, которые необходимо включить на веб-страницу.

Эти сценарии загрязняют глобальную область видимости, выполняя такие действия:

  • Присвоение значений необъявленным идентификаторам
  • Добавление свойств во встроенные функции конструктора (например, Object и Array) и их прототипы
  • Другие неприятные вещи.

Решение

Я хочу включить сценарии без побочных эффектов. Я думаю, что это может быть достигнуто путем загрузки сценария в iframe и экспорта объектов в качестве свойств родительского окна. Вот что я получил до сих пор:

<script>

(function(){
  var g=this, frameIndex=frames.length, f=document.createElement('iframe');

  // hide it like this instead of display:none, because some old browser ignores
  // iframes with display:none, or is this an ancient habit I can drop?
  f.style.width='0px'; f.style.height='0px'; 
  f.style.border='none'; f.style.position='absolute';

  // append it to document.body or document.documentElement?
  // documentElement seems to work before body is loaded,
  // but is it cross-browser safe?  
  document.body.appendChild(f);

  // window object for our iframe
  var w=frames[frameIndex];

  // callback function pulls the object into the current window when script loads
  w.cb=function(){ g.SomeObject=w.SomeObject };

  // will this work on IE, or do I need to use document.createElement?
  // wanted to avoid document.createElement in this case because I'm not sure 
  // whether to call it from window.document or frames[frameIndex].document
  w.document.innerHTML='<script onload="cb()" src="myscript.js"><\/script>';

}());

</script>

Вопросы:

  • Будет ли потенциальный хаос, если скрипт модифицирует встроенные прототипы и я перенесу его в другое окно, или встроенные модули моего родительского окна останутся чистыми, и все будет "просто работать?" *

  • Эта идея сработает в «большинстве» браузеров, или есть стоп-шоу? До сих пор не тестировал ничего, кроме хрома и моз.

  • Я бы хотел удалить iframe после вытягивания объекта в текущее окно, но moz потеряет ссылку на объект, если iframe будет удален. Кто-нибудь знает способ обойти это?

  • Это уже сделано или есть лучший способ достичь моей цели? Если да, то как называется сценарий или техника, которую я должен искать?

(вопрос перенесен с здесь )

Ответы [ 4 ]

1 голос
/ 07 октября 2010

Это может быть возможным решением:

  • обернуть весь иностранный код в класс
  • сделать все необъявленные идентификаторы членами этого класса
  • перед вызовом грязного кода сделайте копию встроенных классов и назовите их по-разному
    (это возможно ??).

Я думаю, что это должно решить все проблемы.

С моим предложением ваш образец

var badA = "hahaha";
this.badB = "hehehe";
badC = "hohoho";

String.prototype.star = function(){ return '***' + this + '***' }

var somethingUseful = {
  doStuff: function () {
    alert((badA + badB + badC).star());
  }
}

должно получиться так

// Identifies the added properties to prototypes (ie String and Function)
// for later filtering if you need a for-in loop.
var stringAddons = [];
var functionAddons = []
var _string = new String();
var _function = function() {};
for (var property in _string) { if (!_string.hasOwnProperty(property)) { stringAddons.push(property); }}
for (var property in _function) { if (!_function.hasOwnProperty(property)) { functionAddons.push(property); }}

// Wraps the undeclared identifiers
var global = function()
{
  this.badA = "hahaha";
  this.badB = "hehehe";
  this.badC = "hohoho";

  String.prototype.star = function(){ return '***' + this + '***' }

  this.somethingUseful = {
    doStuff: function () {
      alert((global.badA + global.badB + global.badC).star());
    }
  }
}
var global = new Global();
global.somethingUseful.doStuff();

Сложная задача - сделать ВСЕ необъявленные идентификаторы глобальными свойствами. Может быть, хороший сценарий регулярных выражений может сделать это. Я не так хорош в регулярных выражениях :) 1021 *

1 голос
/ 07 октября 2010

код комментария под ответом Габриэля.

var r = {
    init : null,
    _init: function(){
        var obj = new XMLHttpRequest();
        obj.onreadystatechange = function(){
            if ((this.status == 200) && this.readyState==4){
                try{
                    eval("r.init = function(){" + this.responseText + "}");
                    r.init();
                } catch(e){/*something bad in the script...*/}
            }
        }
        obj.open("GET","/jspolute_bad.js", true);
        obj.send();
    }   
}
r._init();

При добавлении методов к прототипу у вас могут возникнуть проблемы, если одна или две из экспортированных функций ожидают, что метод будет изменен во внешнем коде,Утомительное решение, которое приходит на ум, состоит в том, чтобы повторно скомандовать responseText перед его вычислением для array.prototype, string.prototype и исправить это каким-то образом.Попробуем это и дадим вам знать ... но это в основном будет обслуживать только простые сценарии.

1 голос
/ 07 октября 2010

Чтобы скопировать функцию, вы можете привести ее к строке и затем оценить ее .... Приведенный ниже код также демонстрирует, что после этого можно удалить iframe, и ваша копия останется нетронутой.

СледующееПример кода с использованием FF

Фрагмент Child.html

<script>

//
// modify the prototype
//
Object.prototype.test = function(msg)
{
        alert(msg);
};  

//
// Simply declare a function
//
var whoo_hoo = function(){alert("whoo hoo");}
</script>

Родитель с iframe:

 <iframe id="help_frame" src="http://localhost/child.html"
 onLoad="javascript:Help.import_functions(this)"></iframe>

    <script>
    var Help = {

          imported_function :null,
              import_functions : function(iframe)
   {
    this.imported_function = String(iframe.contentWindow.whoo_hoo);
    eval("this.imported_function = " + this.imported_function);
    iframe.parentNode.removeChild(iframe);

   //
   // displays 'whoo hoo' in an alert box
   //
   this.imported_function();

   try
   {
      //
      // If the Object prototype was changed in the parent
      // this would have displayed 'should not work' in an alert
      //
      this.test('should not work');
   }
   catch(e){alert('object prototype is unmodified');}

   }, 
    </script>

http://thecodeabode.blogspot.com/

0 голосов
/ 07 октября 2010

Кажется, что ни один из ответов не работает так же хорошо, как iframe.Я вполне убежден, что тюрьма iframe будет решением проблемы.Я помещаю свое текущее решение в качестве ответа, потому что оно, кажется, работает лучше, чем другие ответы, предоставленные до сих пор.Мне бы очень хотелось усовершенствовать эту технику iframe во что-то надежное, и я бы хотел понять, как работают модифицированные прототипы.Этот пример работает в chrome и moz.

test.js

var badA = "hahaha";
this.badB = "hehehe";
badC = "hohoho";

String.prototype.star = function(){ return '***' + this + '***' }

var somethingUseful = {
  doStuff: function () {
    alert((badA + badB + badC).star());
  }

}

test.html

<html>
  <head>
    <script>
    /** 
      safeLoad - load a script in an iframe jail

      @param {String} scriptPath    path to a javascript file
      @param {String} target        name of global object to import
      @param {Function} callback    function to execute after script loads
    */ 
    function safeLoad (scriptPath, target, callback) {
      var g=this, f=document.createElement('iframe'), frameIndex=frames.length;

      f.style.width='0px'; 
      f.style.height='0px'; 
      f.style.border='none'; 
      f.style.position='absolute';

      f.onload=function(){
        var w=frames[frameIndex];
        var s=w.document.createElement('script');
        s.src=scriptPath;
        s.onload=function(){
          g[target]=w[target];
          if (callback && callback.apply) callback(w);
        };
        w.document.body.appendChild(s);
      }
      document.documentElement.appendChild(f);
    }
    </script>  

    <script>

    safeLoad('test.js', 'somethingUseful', function init () {
      // next line should give ***hahahahehehehohoho***
      somethingUseful.doStuff();
      // next line should give undefinedundefinedundefinedundefined
      alert(typeof badA + typeof badB + typeof badC + String.prototype.star);
    });

    </script>   
  </head>
</html>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...