Evaluación de Python (comstackr (…), caja de arena), los globales van en la caja de arena a menos que en la definición, ¿por qué?

Considera lo siguiente:

def test(s): globals()['a'] = s sandbox = {'test': test} py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"' eval(compile(py_str, '', '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 

Ni siquiera estoy seguro de cómo preguntar, pero quiero que el scope global de una función sea el entorno en el que pretendo ejecutarlo sin tener que comstackr la función durante la evaluación. es posible?

Gracias por cualquier entrada

Solución

 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, '', 'exec'), sandbox) 'a' in sandbox # returns True 'b' in sandbox # returns True 'a' in globals() # returns False 'b' in globals() # returns False 

Cuando llama a una función en Python, las variables globales que ve son siempre las variables globales del módulo en el que se definió. (Si esto no fuera cierto, la función podría no funcionar; en realidad podría necesitar algunos valores globales, y no necesariamente sabemos cuáles son.) Especificar un diccionario de globales con exec o eval() solo afecta a los globales que el código que está exec o eval() ‘ve.

Si desea que una función vea otras variables globales, entonces sí debe incluir la definición de la función en la cadena que pasa a exec o eval() . Cuando lo hace, el “módulo” de la función es la cadena desde la que se compiló, con sus propios elementos globales (es decir, aquellos que proporcionó).

Puede func_globals esto creando una nueva función con el mismo objeto de código que está llamando, pero con un atributo func_globals diferente que apunta a su func_globals pero es una func_globals bastante avanzada y probablemente no valga la pena. Aún así, así es como lo harías:

 # 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 

Los contextos de ejecución externa se definen de forma estática en Python ( f.func_globals es de solo lectura), por lo que diría que lo que quieres no es posible. El motivo es que la función podría volverse inválida en Python si su contexto de definición se cambia en tiempo de ejecución. Si el idioma lo permitiera, sería una ruta extremadamente fácil para inyectar código malicioso en las llamadas a la biblioteca.

 def mycheck(s): return True exec priviledged_code in {'check_password':mycheck} 

El código de caja de arena para exec al proporcionar alternativas globales / locales tiene muchas advertencias:

  • Los globales / locales alternativos solo aplican para el código en la caja de arena. No afectan nada fuera de él, no pueden afectar nada fuera de él, y no tendría sentido si pudieran.

    Para decirlo de otra manera, su llamada “caja de arena” pasa la test objeto al código ejecutado por exec. Para cambiar los globales que la test ve, también tendría que modificar el objeto, no pasarlo como está. Eso no es realmente posible de ninguna manera que lo mantendría funcionando, mucho menos de una manera en que el objeto continuaría haciendo algo significativo.

  • Al usar los globales alternativos, cualquier cosa en la sandbox todavía vería los elementos integrados. Si desea ocultar algunos o todos los elementos incorporados del código dentro de la caja de arena, debe agregar una clave "__builtins__" a su diccionario que apunta a None (desactiva todos los elementos incorporados) o a su versión de ellos. Esto también restringe ciertos atributos de los objetos, por ejemplo, el acceso func_globals atributo func_globals de una función estará deshabilitado.

  • Incluso si elimina los elementos incorporados, la caja de arena no será segura. El código de Sandbox solo es de confianza en primer lugar.

Aquí hay una simple prueba de concepto:

 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)