Script Capabilities is a system that offers control over actions that scripts can perform inside the DataModel subtree. It provides better control over experience scripts rather than being an "all or nothing" system where any script can do anything that other scripts can.
- This system lets you limit what models taken from the toolbox can do and makes it easier to include user-generated content inside the experience, even those that contain scripts.
- It can also help ensure better security of experiences that allow players to run their own code, which is often executed in a restricted or emulated environment.
- It can also be used to share libraries that restrict what they can do themselves. For example, a library providing additional math methods can be restricted to the smallest set of capabilities it needs so that other developers using that library don't have to validate the entire codebase to make sure it doesn't include malicious code.
Enabling Script Capabilities
To enable this feature, change the SandboxedInstanceMode setting from Default to Experimental in the Explorer.
When the client beta test is completed, this step will no longer be required.
Sandboxed Container
This system introduces a concept of a sandboxed container. An instance of type Model, Folder, Script, or descendants of any of those classes have a Sandboxed property available in the Studio Properties window, under the Permissions section.
Enabling the Sandboxed property designates the instance as a sandboxed container inside the DataModel tree, which limits the actions that the scripts inside that container can perform based on the set of values in the Capabilities property.
Capabilities
The Capabilities property is a set of values that control different aspects of execution, split into four groups:
- Execution control - Specifies if a script can run on client or server
- Instance access control - Specifies which DataModel parts a script can interact with
- Script functionality control - Specifies which Luau functions scripts can call
- Engine API access control - Specifies which parts of the Roblox Engine API can be used
When a script attempts to perform an action that is not in the set of capabilities for the current execution state, an error is reported. Errors typically include the action being attempted, the target of an action, and the first capability that is missing, for example:
The current thread cannot modify 'Workspace' (lacking capability AccessOutsideWrite)The current thread cannot call 'Clone' (lacking capability CreateInstances)The current thread cannot call 'GetSecret' (lacking capability Network)
Execution Control
This set includes two capabilities:
- RunClientScript - LocalScript or Script with a RunContext value of Client is allowed to execute on the client
If the script is Enabled, but the capability corresponding to the location it attempts to start in is not available, a warning message is displayed in the Output window. If a script wasn't supposed to run in that context, disable or delete it.
Note that ModuleScripts are not required to have these execution capabilities to be required.
When a script fails to start because the execution control capability is missing, it is reported as a warning in the output, for example:
Cannot start server script 'Script' (lacking capability RunServerScript)
Instance Access Control
This set includes only a single capability:
- AccessOutsideWrite - Script is allowed to fetch and receive instances from outside the sandboxed container
When the capability is not available, the script can only look up instances that are inside its own sandboxed container. For example, if the script is placed directly in the sandboxed container, script.Parent.Parent returns nil.
Additionally, any Roblox API event that passes in an Instance instead passes in nil for any Instance outside the sandboxed container. For example, if the BasePart.Touched is signalled by a touch from an instance outside the sandboxed container, the event is still received, but the argument is nil.
Avoid setting this capability; sandboxing guarantees are weaker when scripts can interact with any instance in an experience.
Access to Services
Even without AccessOutsideWrite, scripts in the sandboxed container can still access to game, workspace, and services. This access is provided so that scripts can still call useful methods of those globals, like DataModel.GetService, but access to their child instances is still validated.
Internally Passed Instances
If an instance is passed through a function call that doesn't go through Roblox APIs, the reference is preserved. However, if a ModuleScript is passed in this way, it can't be required without AccessOutsideWrite. This is because the return of the ModuleScript is often mutable and can be modified by a script in a sandboxed container.
Script Functionality Control
This set of capabilities controls some general aspects of scripts:
- AssetRequire - Script is allowed to call require with an asset ID
- LoadString - Script is allowed to call loadstring
- CreateInstances - Script can create new instances using Instance.new, Instance.fromExisting, or Instance:Clone()
Keep in mind that default function restrictions still apply. Even if LoadString is enabled, the experience still has to turn enable it in ServerScriptService, and it is still only available on the server.
To create new instances, aside from CreateInstances, an additional Engine API capability providing access to that instance is required.
Engine API Access Control
This last group of capabilities controls script access to various Engine APIs:
- Basic - Access to simple instances and essential building blocks
- Audio - Access to instances related to audio APIs
- DataStore - Access to data store and memory store APIs
- Network - Access to HTTP networking APIs
- Physics - Access to instances related to physics
- UI - Access to instances related to user interfaces
- CSG: access to instances and functions related to constructive solid geometry (CSG)
- Chat: access to instances related to in-experience chat
- Animation: access to instances related to animations
- Avatar: access to instances related to avatars
- Input: access to instances related to user input
- Environment: access to instances related to the control of how the environment is displayed
- RemoteEvent: access to instances for internal networking operations
There are also instances that are available without any capabilities aside from being able to execute the scripts. These include the following HttpService methods:
If an instance property or method is accessed without a required capability, an error is reported describing the missing capability.
Finally, capabilities do not cover every instance in the Roblox engine today. Instances not listed in this section or the following one are not available for interaction from a sandboxed container and throw an error saying that an Unassigned capability is not available to the current script.
An additional limitation is that getfenv and setfenv functions are not available for scripts in a sandboxed container.
Only script access to instances is limited. The instances themselves can still exist and operate by themselves inside a sandboxed container. Lights still shine, user interfaces are still visible, and audio setups that are already wired play sounds.
Engine API Capability Assignments
Here is the list of instances and methods (if different from the instance capability) for each Engine API capability:
Basic
DataStore
Physics
UI
CSG
- IntersectOperation (also requires Basic), NegateOperation (also requires Basic), PartOperation (also requires Basic), UnionOperation (also requires Basic)
Animation
- Bone (also requires Basic)
RemoteEvent
Interactions Between Containers
Nested Containers
When one sandboxed container is nested inside another one, the instances of the inner container are accessible to the outer one.
Capabilities of the inner container are limited by the capabilities of the outer one. For example, if the outer container has capabilities of Basic, Audio and CSG, while the inner container has Basic and Network, only Basic capabilities are available to the inner container at runtime.
If there are no capabilities in common between the inner and outer containers, the resulting capability set is empty.
Bindable Functions and Events
BindableEvent and BindableFunction provide the best way to communicate with the container or allow it to run callbacks with capabilities it itself is not allowed to use directly.
When an event or a function is fired, connections are executed in the context of the function that registered them. This means that if the event or function callback is registered by the sandboxed container, it is called with the capabilities of that container. If the callback is registered by the code outside, when sandboxed container scripts invoke them, they execute your functions with capabilities available to your functions.
It is important to note that even with the AccessOutsideWrite capability, scripts in sandboxed containers cannot invoke events or functions outside their containers if they have a larger capability set than the container itself.
Module Require
Inner ModuleScripts can be required by the sandboxed container as usual. However, if the target instance is outside the container, the ModuleScript can only be required if the capability set that is available to it is smaller or equal to the capabilities available to the container.
This limitation does not apply to RunClientScript and RunServerScript capabilities. If the ModuleScript is placed in a container with only RunClientScript but is required from a script that has the RunServerScript capability, it is allowed to succeed and run those functions on the server.
Directly Called Functions
If a ModuleScript in a sandboxed container is required from outside the container, some of the protections are not available. In particular, the target function is able to access all instances available to the caller. If the caller is not in a sandboxed container, the call acts as if AccessOutsideWrite is available to it.
Other capability restrictions still apply. If you have a DataStore access capability, but the target module does not, it is unable to call DataStore methods. However, if you pass your own function working with DataStore, the target can run it during that call. If the target schedules a thread using methods like those from task, those threads lose the ability to call that function.
Instances can be passed to the target module or assigned to the module fields.
If required, it is recommended to assign table members using rawset to avoid running __index/__newindex metamethods that might be set on the table.
The overall recommendation is to communicate with BindableEvent and BindableFunction whenever possible.
Movement of Instances
Most instances do not have restrictions on movement between containers. Script instances, however, can only be moved into a container that has the same set of capabilities or a subset of those capabilities.
This means that sandboxed container with AccessOutsideWrite cannot just re-parent a script inside itself to outside and get more capabilities.