Run untrusted code with Restricted Functions

This guide page documents Restricted Functions, which can be used to safely execute untrusted code in Modal Functions.

Create a Restricted Function 

To create a Restricted Function, set restrict_modal_access=True in the Function definition:

When restrict_modal_access is enabled, the Function cannot

  • access Modal resources (Queues, Dicts, etc.)
  • call other Functions
  • access Modal’s internal APIs

Sandboxes offer an alternative interface for untrusted code 

Modal provides two primitives for running untrusted code: Restricted Functions and Sandboxes. While both can be used for running untrusted code, they provide different interfaces: Sandboxes provide a process interface, while Restricted Functions provide a function-calling interface. Process interfaces are especially useful for stateful, multi-stage communication, while function-calling interfaces are especially useful for stateless, input/output communication.

These differences are summarized in the table below.

FeatureRestricted FunctionSandbox
StateStatelessStateful
InterfaceFunction-likeContainer-like
SetupSimple decoratorRequires explicit creation/termination
Use caseQuick, isolated code executionInteractive development, long-running sessions

Best Practices 

When running untrusted code, consider these additional security measures:

  1. Use single_use_containers=True to ensure each container only handles one request. Containers that get reused could cause information leakage between users.

Note: Prior to v1.3.0, single-use containers were configured by setting max_inputs=1.

  1. Set appropriate timeouts to prevent long-running operations:
  1. Consider using block_network=True to prevent the container from making outbound network requests:
  1. Minimize the App source that’s included in the container

A restricted Modal Function will have read access to its source files in the container, so you’ll want to avoid including anything that would be harmful if exfiltrated by the untrusted process.

If deploying an App from within a larger package, the entire package source may be automatically included by default. A best practice would be to make the untrusted Function part of a standalone App that includes the minimum necessary files to run:

Example: Running LLM-generated Code 

Below is a complete example of running code generated by a language model:

This example locks down the container to ensure that the code is safe to execute by:

  • Restricting Modal access
  • Using a fresh container for each execution
  • Setting a timeout
  • Blocking network access
  • Catching and handling potential errors

Error Handling 

When a Restricted Function attempts to access Modal resources, it will raise an AuthError:

The error message will indicate that the operation is not permitted due to restricted Modal access.