2013-07-26 3 views
4

Я пишу простую реализацию REPL (Read, Evaluate, Print, Loop) в JavaScript. Я могу выделить код и призывающую контекст следующим образом:Как предотвратить случайное загрязнение глобального пространства имен с помощью javascript eval?

var sandbox = { 
    // Allow the input code to use predefined helper functions 
    // without the preceding use of the this keyword. 
    helper_fn: function() { alert("foo"); } 
}; 

var func = new Function("with (this) { " + user_inputed_code + " }"); 
func.call(sandbox); 

Теперь закрывает user_inputed_code, так что this Referers к sandbox и если введенный код обращается или мутирует this она влияет на sandbox.

Однако я заметил, что если вмененный код должен был случайно забыть предварять присвоение переменной ключевым словом var, то глобальное пространство имен будет загрязнено.

Есть ли вообще предотвратить это? Если да, то как (может быть, регулярное выражение?)? Есть ли лучший способ приблизиться к этому?

+0

Можете ли вы * Eval * код в отдельном фрейме или окне, которое имеет отдельный * глобальный * объект ? Кстати, в ECAMScript «контекст» используется в отношении [* контекста исполнения *] (http://www.ecma-international.org/ecma-262/5.1/#sec-10.3). Конкретный параметр * этого * контекста выполнения - это только одна небольшая часть «контекста», которая относится ко всем параметрам и переменным, цепочке цепей и т. Д. – RobG

+0

Я планирую использовать это в проекте Titanium. Поэтому кадры недоступны. Однако замораживание может работать. Должен увидеть, поддерживает ли среда выполнения. – Sukima

+0

Можете ли вы запустить код в строгом режиме? Таким образом, назначение необъявленным переменным вызовет ошибку. Вы не можете использовать * с *. И пользователи все равно могут создавать глобальные переменные, используя 'window.foo = ...'. – RobG

ответ

1

Оказывается, существует способ с "use strict" и без Object.freeze. Вы должны вручную заменить глобальное пространство имен с собственным объектом песочницы:

var sandbox, module, func, output; 
// Empty object or with defined methods/properties 
// you want to expose as globals. 
sandbox = {}; 
// A reference to an object you WANT to provide safe 
// access to. In this example it's just an empty object. 
module = {}; 
// A better version of eval: 
func = new Function("module", "with(this){return(function(module,global,window){\"use strict\";return eval(\"" + code + "\");})(module,this,this)}"); 
output = func.call(sandbox, module); 

Этот код позволяет глобальным и окно для обозначения объекта вместо изолированного глобального пространства имен. Он маскирует переменные global и window как объект песочницы, а использование "use strict" вызовет его исключение, если вход пропустил использование var. Он также обертывает функцию в операторе with, чтобы методы и свойства, определенные в объекте sandbox, работали так, как будто им предшествовал this.. Чтобы увидеть пример реализации (с тестовыми спецификациями), проверьте this gist.

Спасибо всем за ваши идеи. Надеюсь, этот ответ поможет другим.

+1

Что делать, если 'code' вызывает другую функцию и эта функция не находится в строгом режиме? Или выполняет 'eval' сам? –

+0

Помимо некоторых отвратительных регулярных выражений, я не знаю, как это предотвратить. – Sukima

+0

См. Мой ответ. Я предоставлю два способа предотвратить это, а также ссылки на реализации repl. –

2

Я собираюсь предоставить два совершенно разных подхода к тому, что другие обсуждали здесь. Они оба являются резкими и полезны, когда вы хотите относительно изолировать свою среду.

  • Самый простой способ, который работает во всех браузерах, - это, вероятно, создать iframe и добавить к нему теги сценария. (Заметьте, действительно чрезмерно усложненный iframe все еще может пройти мимо, если они находятся в одном домене, по крайней мере, в старых браузерах). Я обсуждаю это в this question.
  • Использование веб-работников, имеющих по умолчанию изолированную среду и не имеющих доступа к глобальному объекту основного потока выполнения. Я обсуждаю это in this question.

Более конкретно, если вы строите взглянуть на this answer РЕПЛ, где мы обсудим и я объясню, как Eval код в той же области, но за пределами глобального масштаба, используя первый подход фреймов.

(я предположил, браузер, в узле вы можете просто использовать виртуальную машину модуль и выберите контекст в runInContext)

Смежные вопросы