Skip to content

Instantly share code, notes, and snippets.

@simonw
Created May 10, 2023 17:35
Show Gist options
  • Save simonw/b9a1f080714785b7ee16c7d04db12210 to your computer and use it in GitHub Desktop.
Save simonw/b9a1f080714785b7ee16c7d04db12210 to your computer and use it in GitHub Desktop.

What I want from a run-Python-in-WASM solution

I want to be able to use WebAssembly as a sandbox to safely run Python code from untrusted sources, within my existing Python applications.

I'd like to be able to:

  • Pass in an untrusted string of Python code and have that evaluated
  • Maybe also pass in initial variables to be used in the code - though hard-coding them would work OK too
  • Have the code run in a sandboxed environment, with a timeout and memory limit
  • The sandbox disallows network access and disk access - it can only access the variables passed in
  • The result is returned back to my program

Effectively I want to be able to do something like this:

pip install python-wasm-sandbox

Then:

from python_wasm_sandbox import run_python

# This code could come from an untrusted source
code = """
result = input_string.upper()
"""

result = run_python(code, variables={
    "input_string": "Hello, World!"
}, timeout=1.0, memory_limit_in_bytes=4096)

Use-cases for this:

  • I want to build features where users can enter Python expressions which will be used to transform their data (for my Datasette project)
  • I want to let people edit code that will be run on a schedule (by a cron-like mechanism) by typing it into a textarea
  • I want my users to be able to copy-and-paste in code snippets from elsewhere - and limit the damage that can be caused if someone malicious convinces them to copy-and-paste in something harmful

Stretch goal

The ability to prepare some untrusted code once and then call it multiple times with different inputs would be neat too - something like this:

from python_wasm_sandbox import PythonSandbox

code = """
def convert(input):
    return input.upper()
"""

sandbox = PythonSandbox(code, timeout=1.0, memory_limit_in_bytes=4096)

for input in ["hello", "world"]:
    print(sandbox.execute("convert", input))

In this case, every callable defined in code becomes a thing that can be executed by the sandbox, with the result returned back to the caller.

I'd be fine with a restriction that says only basic Python types - strings, floats, integers, bytes - can be passed in and out of the sandbox. I can roll my own serialization/deserialization on top of that if I need to.

@dicej
Copy link

dicej commented May 23, 2023

Thanks, @alexcrichton. I've updated the demo to specify a memory limit. @simonw I believe the demo now satisfies all the requirements you listed, including the stretch goal. Just needs to be wrapped in a friendly API at this point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment