RestrictedPython - Name Is Not Defined

349 Views Asked by At

I want to safely execute some user defined code. To accomplish this, I want to use RestrictedPython to limit which modules and variables the user has access to. There are a few guidelines to the user-submitted code:

  1. The code can use the pandas, datetime, math, and random modules. I'm open to expanding this list if it can be done safely. Further, the code will not be allowed to import any new modules
  2. The code can interact with the global variable d - which will be a dictionary of pandas dataframes
  3. The code will set the global variable x to some variable, which we must retrieve after running the user's code.

Here is my current version:

from RestrictedPython import compile_restricted
from RestrictedPython import safe_builtins
import pandas as pd
import math
import random
import datetime

d = None
x = None

def MakeDFDict():
    data1 = {"A": [1, 2, 3, 4, 5], "B": [2, 4, 6, 8, 10]}
    data2 = {"C": [3, 6, 9, 12, 15], "D": [4, 8, 12, 16, 20]}
    return {"Dataframe 1": pd.DataFrame(data1),
           "Dataframe 2": pd.DataFrame(data2)}

def run_code_safely(code):
    global d
    d = MakeDFDict()
    
    global x
    x = None
    
    globals_dict = {"d": d}

    allowed_modules = {
    "__builtins__": safe_builtins,
    "math": math,
    "random": random,
    "datetime": datetime,
    "pandas": pd
    }
    restricted_globals = {
        "__builtins__": safe_builtins,
        "__import__": lambda name, globals=None, locals=None, fromlist=(), level=0: __import__(name),
        "getattr": lambda obj, attr: getattr(obj, attr)
    }

    restricted_globals.update(allowed_modules)

    bytecode = RestrictedPython.compile_restricted(code, '<inline code>', 'exec')
    try:
        exec(bytecode, restricted_globals, globals_dict)
    except Exception as e:
        return f"Error occurred while running the code:\n{e}"

    x = globals_dict['x']
    return x

code = """
x = []
for name, df in d.items():
    x.append(name, df)
"""

run_code_safely(code)

This gives the following error:

Error occurred while running the code: name '_iter_unpack_sequence_' is not defined

What am I missing here? Is ther any way to make exec yield a more explanatory error message?

1

There are 1 best solutions below

0
Effex On

To get started, update your restricted_globals as follows:

restricted_globals = {
    "__builtins__": safe_builtins,
    "__import__": lambda name, globals=None, locals=None, fromlist=(), level=0: __import__(name),
    "getattr": lambda obj, attr: getattr(obj, attr),
    "_iter_unpack_sequence_": guarded_iter_unpack_sequence,
    "_getiter_": iter
}

Here you need to add the same _iter_unpack_sequence_ that needs to be imported like this:

from RestrictedPython.Guards import guarded_iter_unpack_sequence

And also for correct operation you will need to add the _getiter_ key, the value of which will be iter.

I also noticed one small typo in the variable code (if you're interested, you forgot the parentheses to add tuples):

code = """
x = []
for name, df in d.items():
    x.append((name, df))
"""