In a web app I am building, I have a need to execute untrusted user code within the app (in the browser). The untrusted code is given a string from Javascript, and should do its stuff and produce a new string. So I want to make sure it can't do any IO, like modifying the DOM, making HTTP requests, or accessing the local cache. To be clear, one user would upload some untrusted code, which would be executed in other users' browsers.
I clearly don't want to run some Javascript using eval, since that could do anything. My idea is that a user could upload some WebAssembly, created with Emscripten and C or Rust or whatever. The Javascript would initialise a memory buffer containing the input string, and the WebAssembly would read this and then write its output back to the buffer, which could then be used in JS.
This is what the Webassembly spec says:
WebAssembly provides no ambient access to the computing environment in which code is executed. Any interaction with the environment, such as I/O, access to resources, or operating system calls, can only be performed by invoking functions provided by the embedder and imported into a WebAssembly module. An embedder can establish security policies suitable for a respective environment by controlling or limiting which functional capabilities it makes available for import. Such considerations are an embedder’s responsibility and the subject of API definitions for a specific environment.
My understanding is that WebAssembly is guaranteeing that it can only do dangerous things if I explicitly pass them in when initializing the module, which of course I wouldn't do.
So my question is: is there any way someone could make some WASM code that could do some IO under these circumstances? Or any other nasty things?