Python eval (compile (...), песочница), глобалы идут в песочнице, если только в def, почему? - PullRequest
4 голосов
/ 30 декабря 2010

Рассмотрим следующее:

def test(s):
    globals()['a'] = s

sandbox = {'test': test}
py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"'
eval(compile(py_str, '<string>', 'exec'), sandbox)

'a' in sandbox # returns False, !What I dont want!
'b' in sandbox # returns True, What I want
'a' in globals() # returns True, !What I dont want!
'b' in globals() # returns False, What I want

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

Спасибо за любой ввод

Решение

def test(s):
    globals()['a'] = s

sandbox = {}

# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
                     test.func_closure)

# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest

py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"'
eval(compile(py_str, '<string>', 'exec'), sandbox)

'a' in sandbox # returns True
'b' in sandbox # returns True
'a' in globals() # returns False
'b' in globals() # returns False

Ответы [ 3 ]

6 голосов
/ 30 декабря 2010

Когда вы вызываете функцию в Python, глобальные переменные, которые она видит, всегда являются глобальными переменными модуля, в котором она была определена. (Если это не так, функция может не работать - на самом деле нужно некоторые глобальные значения, и вы не обязательно знаете, что это такое.) Указание словаря глобалов с exec или eval() влияет только на глобалы, код которых exec 'd или eval()' г видит.

Если вы хотите, чтобы функция видела другие глобальные переменные, вам действительно нужно включить определение функции в строку, которую вы передаете exec или eval(). Когда вы это делаете, «модуль» функции - это строка, из которой она была скомпилирована, со своими собственными глобальными переменными (т. Е. Теми, которые вы указали).

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

# create a sandbox globals dict
sandbox = {}

# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
                     test.func_closure)

# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest
2 голосов
/ 30 декабря 2010

Код песочницы для exec путем предоставления альтернативных глобальных / локальных кодов имеет множество предостережений:

  • Альтернативные глобальные / локальные параметры применяются только для кода в песочнице. Они не влияют на что-либо вне этого, они не могут повлиять на что-либо вне этого, и не имело бы смысла, если бы они могли.

    Другими словами, ваша так называемая «песочница» передает объект test в код, запускаемый exec. Чтобы изменить глобальные переменные, которые видит test, он также должен модифицировать объект, а не передавать его как есть. На самом деле это никоим образом не может привести к тому, что он будет работать, а тем более, что объект продолжит делать что-то значимое.

  • Используя альтернативные глобальные переменные, что-либо в sandbox все равно будет видеть встроенные объекты. Если вы хотите скрыть некоторые или все встроенные функции из кода внутри песочницы, вам необходимо добавить ключ "__builtins__" в свой словарь, который указывает либо на None (отключает все встроенные функции), либо на вашу их версию. Это также ограничивает некоторые атрибуты объектов, например, доступ к атрибуту func_globals функции будет отключен.

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

Вот простое доказательство концепции:

import subprocess
code = """[x for x in ().__class__.__bases__[0].__subclasses__() 
           if x.__name__ == 'Popen'][0](['ls', '-la']).wait()"""
# The following runs "ls"...
exec code in dict(__builtins__=None)
# ...even though the following raises
exec "(lambda:None).func_globals" in dict(__builtins__=None)
2 голосов
/ 30 декабря 2010

Внешние контексты исполнения статически определены в Python (f.func_globals только для чтения), поэтому я бы сказал, что то, что вы хотите, невозможно. Причина в том, что функция может стать недействительной Python, если ее контекст определения изменяется во время выполнения. Если бы язык позволял это сделать, это был бы чрезвычайно простой способ внедрения вредоносного кода в вызовы библиотеки.

def mycheck(s): 
    return True

exec priviledged_code in {'check_password':mycheck}
...