zopefoundation / RestrictedPython

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

Async support? #207

Closed kevinpelago closed 3 years ago

kevinpelago commented 3 years ago

Hi there,

It looks as though async/await is explicitly not supported, according to this and this.

My question is twofold:

Thank you! Kevin

d-maurer commented 3 years ago

Kevin Scott wrote at 2021-3-22 15:31 -0700:

It looks as though async/await is explicitly not supported, according to this and this.

My question is twofold:

  • Are there security risks inherent in async functions?

async function have been formerly emulated via generators. Thus, the security risks should be comparable for async functions and generators.

  • Is there a way to enable async functionality?

RestrictedPython works by transforming the AST (= "Abstract Syntax Tree") by checking/rejecting/injecting policy checks. Naturally, it rejects everything, it does not yet know about (because this may introduce security risks). To "enable" async functionality, it would be necessary to change the AST transformation and tell it how to handle this language construct.

kevinpelago commented 3 years ago

Thanks for the help, @d-maurer .

Thus, the security risks should be comparable for async functions and generators.

Is there any reference material or literature available that can expand on this more? I'm not very familiar myself, and Google turns up little.

To "enable" async functionality, it would be necessary to change the AST transformation and tell it how to handle this language construct.

I believe a working example of such a policy would be the following (mostly lifted from the test example):

    class RestrictingAsyncNodeTransformer(RestrictingNodeTransformer):
        """Transformer which allows `async def` for the tests."""

        def visit_AsyncFunctionDef(self, node):
            return self.node_contents_visit(node)

        def visit_Await(self, node):
            return self.node_contents_visit(node)

        def visit_AsyncFor(self, node):
            return self.node_contents_visit(node)

        def visit_AsyncWith(self, node):
            return self.node_contents_visit(node)

    byte_code = compile_restricted(
        code policy=RestrictingAsyncNodeTransformer
    )

This appears to work for my use case, but if this code looks wrong, I'd love any feedback.

Thanks y'all!

d-maurer commented 3 years ago

Kevin Scott wrote at 2021-3-23 14:39 -0700:

Thus, the security risks should be comparable for async functions and generators.

Is there any reference material or literature available that can speak to this more? I'm not very familiar myself, and Google turns up little.

Asynchronous programming is strongly related to the coroutine concept. PEP 342 proposes the implementation of coroutines via generators. PEP 492 introduces the explicit implementation via async def (and friends).

To "enable" async functionality, it would be necessary to change the AST transformation and tell it how to handle this language construct.

I believe a working example of such a policy would be the following (mostly lifted from the test example):

   class RestrictingAsyncNodeTransformer(RestrictingNodeTransformer):
       """Transformer which allows `async def` for the tests."""

       def visit_AsyncFunctionDef(self, node):
           return self.node_contents_visit(node)

       def visit_Await(self, node):
           return self.node_contents_visit(node)

       def visit_AsyncFor(self, node):
           return self.node_contents_visit(node)

       def visit_AsyncWith(self, node):
           return self.node_contents_visit(node)

   byte_code = compile_restricted(
       code policy=RestrictingAsyncNodeTransformer
   )

This appears to work for my use case, but if this code looks wrong, I'd love any feedback.

It allows the async related commands without any possibility for policy intervention. Potentially, this is what you want.

-- Dieter