zopefoundation / RestrictedPython

A restricted execution environment for Python to run untrusted code.
http://restrictedpython.readthedocs.io/
Other
456 stars 38 forks source link

'printed' question #277

Open jsmith173 opened 5 months ago

jsmith173 commented 5 months ago

My protection function starts this way, so I'm appended the results = printed line to my code and returning with safe_globals["results"] at the end of this function. Is this the correct way to collect the print messages? Sometimes I have got error messages like this

"Line 2: SyntaxError: invalid syntax at statement: 'results = printed'" However results = printed is not in the original code.

For the follwing input for example @__builtins__

def interpret(code, my_dict, enable_exec=True):
    """Interprets the given python code inside a safe execution environment"""
    warnings.filterwarnings('ignore') 

    code += "\nresults = printed"
    byte_code = compile_restricted(
        code,
        filename="<string>",
        mode="exec",
    )
d-maurer commented 5 months ago

jsmith173 wrote at 2024-3-26 01:36 -0700:

My protection function starts this way, so I'm appended the results = printed line to my code and returning with safe_globals["results"] at the end of this function.

The safe_globals["results"] looks strange.

RestrictedPython's "print" support works as follows:

  1. If a module (outside a function definition) or function definition uses either print or printed for the first time, code for the assignment _print = _print_(_getattr_) is generated. Usually, this would assign a PrintCollector to _print.

  2. Calls to print are translated into calls to _print._call_print.

    With RestrictedPython's PrintCollector, this will collect the "print"ed output

  3. Accesses to printed are translated into _print().

    With RestrictedPython's PrintCollector, this will return the collected output

Thus, if you want to use the collected output, you use printed in an expession - e.g. the right hand side of an assignment.

The signature of exec is (code, [globals, [locals]]). If you pass both globals and locals, you will find the result of assignments in locals, otherwise in globals.

Thus your use of RestrictedPython might look like:

     code = compile_restricted(...)

     gbls = ... create_my_safe_globals ...
     exec(code, gbls)
     gbls["..."]

or

     my_safe_globals = ...
     code = compile_restricted(...)

     lcls = {}
     exec(code, my_safe_globals, lcls)
     lcls["..."]

Is this the correct way to collect the print messages? Sometimes I have got error messages like this

"Line 2: SyntaxError: invalid syntax at statement: 'results = printed'" However results = printed is not in the original code.

You might have forgotten something (e.g. a closing parenthesis).

I suggest you output the complete code should the compilation raise an exception. You then check this output to understand the exception.

jsmith173 commented 5 months ago

I think I did it previously as you suggests. I have a function that decides the input is vulnerable or not. This function also should return the printed output (if has). In case of vulnerability it raises an exception. To return the printed output I have added a right hand access to the source code results = printed. However in some cases the compile or the exec report problem at this line however this line is not the part of the original code. This is a syntax error in the input of course but the problem is that the reported line is the extra added line.

So is there any other way to access the printed output? For example 'PrintCollector.txt'?

d-maurer commented 5 months ago

jsmith173 wrote at 2024-3-26 02:54 -0700:

I think I did it previously as you suggests. I have a function that decides the input is vulnerable or not. This function also should return the printed output (if has). In case of vulnerability it raises an exception. To return the printed output I have added a right hand access to the source code results = printed. However in some cases the compile or the exec report problem at this line however this line is not the part of the original code. This is a syntax error in the input of course but the problem is that the reported line is the extra added line.

Syntax errors are often only recognized after the error point. I have given you an example (missing parethesis in function call). Thus, the syntax error may indicate your added line even though the actual error is in the "original code".

So is there any other way to access the printed output? For example 'PrintCollector.txt'?

Sure. But, it is unlikely to solve your problem (which likely is a syntax error in the "original code"). The syntax error will not disapear when you do not add your assignment.

In my previous comment, I detailed how RestrictedPython implements the print functionality. If you reread it, you will find that via an appropriate definition of _print you can control (almost) everything.

One possibility would be to let the _print call return a PrintCollector instance preinstantiated for an exec. Something like:

  code = compile_restricted(...)
  ...

  my_collector = PrintCollector()
  gbls = get_safe_globals().copy()
  gbls["_print"] = lambda *args: my_collector
  lcls = {}
  exec(code, gbls, lcls)
  printed = my_collector()