Script Capabilities Preview [Client Beta]

I don’t see any reason not to do so, cuz Script Capabilities environment is no longer a Swiss cheese. to my knowledge, where known vulnerabilities/VM Escapes weren’t patched

1 Like

It has been quite a moment since this, are there any updates around this?

It appears that scripts and localscripts inside of sandboxed areas cannot be reparented regardless of permissions. Is this intentional?

It kind of makes sense if doing this would break the sandbox. However it makes it so that sandboxed scripts can only run localscripts under one player at a time unless they are parented to replicatedstorage, which is undesirable

Example use case: having local effects stored in localscripts that are duplicated and placed on client’s characters to run when needed
sandbox_issue_repro4.rbxm (1.9 KB)

It could be intentional since it requires an Unassigned capability, just like AccessOutsideWrite can’t access Replicated+First and ServerScript + Storage (and other vulnerable instances) cause it requires same Unassigned capabilities, meaning it is for security reasons and not yet available as a capability

There are some APIs that are not available in Sandboxed scripts even with all Capability checkboxes enabled.
We will improve the error message to point it out more clearly.

We are still working to get almost all APIs under one capability or another.
We are also exploring the option for AccessOutsideWrite reparenting to auto-sandbox the instance, but we are still evaluating how surprising vs helpful such automatic property setup will be.

1 Like

I have been playing around with this as I have a very unique use case for it. However, even after nearly a year, it doesn’t seem like much progress has been made, at least not from a scripting/API standpoint.

The last time I looked into this, there was no way to interact with it via scripts, and from what I can tell that still appears to be the case. I recently came back after seeing mentions of an API implementation and was excited to try it out.

As a test, I attempted to apply SecurityCapabilities to a Folder through a script, but the results were confusing.

I initially tried the following, then realized it wouldn’t work because self isn’t passed:

game.ServerStorage.Folder.Capabilities.Add(Enum.SecurityCapability.Players)

I then switched to calling the method properly:

game.ServerStorage.Folder.Capabilities:Add(Enum.SecurityCapability.Players)

This didn’t throw any errors, and for a short time I assumed it worked. However, when I continued working on my project, it became clear that the capability was not actually applied.

To verify, I ran:

print(game.ServerStorage.Folder.Capabilities:Contains(Enum.SecurityCapability.Players))

This returned false, confirming that the capability was never added in the first place.

At this point, I’m wondering:

  • Is this API still a work in progress?
  • Are SecurityCapabilities not yet functional via scripts?
  • Or is access to this API limited or gated in some way?

Any clarification would be appreciated.

You need to do game.ServerStorage.Folder.Capabilities = game.ServerStorage.Folder.Capabilities:Add(Enum.SecurityCapability.Players).
The Add method is not mutating, it returns a new instance of the capability set with an added capability.

We might add a lint warning for such cases in the future, sorry for the confusion.

this is very unintuitive for game developers please make it so :Add mutates it directly without having to set Capabilities property

It looks like there isn’t a way to access StarterGui even with the UI capability.

I have this simple to toggle the backpack but it isn’t possible to run while sandboxed.

image

local StarterGui = game:GetService("StarterGui")

local button = script.Parent

local function onButtonActivated()
	StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, not StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack))
end

button.Activated:Connect(onButtonActivated)

It gives off this error that it’s lacking the Unassigned capability which I assume is because there is no capability for StarterGui

02:33:31.800 The current thread cannot access ‘StarterGui’ (lacking capability Unassigned) - Client - ToggleBackpackScript:6
02:33:31.800 Stack Begin - Studio
02:33:31.800 Script ‘Players.WinnersTakesAll.PlayerGui.ToggleGui.ToggleBackpackButton.ToggleBackpackScript’, Line 6 - function onButtonActivated - Studio - ToggleBackpackScript:6
02:33:31.800 Stack End - Studio

Edit: the release date for the breaking changes is now the 707 release (ETA 2/19/26).

Hey all, thanks for your interest in Capabilities. We’ve been working hard to improve the system and have some updates, including some upcoming breaking changes.

Expanded Support for Classes

When running Sandboxed scripts, you may have seen this error: The current thread cannot access '<Instance>' (lacking capability Unassigned). This can be due to accessing Classes no script should be accessing, but it’s oftentimes due to us not having full coverage yet. The 707 release (January 28th ETA) adds coverage for more classes. There will still be gaps, but we are aiming for 100% coverage of usable classes by March.

New Capabilities

Related to above, we’re adding 18 new Capabilities in the 706 release:

  • AssetRead: Access to engine APIs related to getting information about assets
  • AssetCreateUpdate: Access to engine APIs related to creating or updating assets
  • AssetManagement: Access to engine APIs related to operations on assets that are not read, create, or update
  • DynamicGeneration: Access to engine APIs related to dynamically modifying assets
  • PlatformAvatarEditing: Access to engine APIs related to creating or updating Avatars, or API’s to aid with that.
  • Capture: Access to engine APIs related to capturing screenshots or videos on the user’s screen
  • SensitiveInput: Access to engine APIs related to capturing sensitive input from the user
  • PromptExternalPurchase: Access to engine APIs related to prompting purchase of arbitrary assets
  • Monetization: Access to engine APIs related to monetizing an experience, excluding most PromptPurchases
  • LoadOwnedAsset: Access to the engine APIs for loading assets that are owned by you, shared to you, or owned by Roblox, or are a benign asset type.
  • Social: Access to engine APIs related to social features, such as friends and invites
  • ServerCommunication: Access to engine APIs related to cross-server communication, such as MessagingService
  • Logging: Access to Logs API
  • Groups: Access to engine APIs relating to groups/communities
  • Teleport: Access to engine APIs related to teleporting
  • Consequences: Access to engine APIs related to punishing users via kicks or bans
  • Material: Access to engine APIs related to Materials
  • AvatarBehavior: Allows access to engine API’s changing the behavior of the humanoid

As this expands the Capability list, we’ve also made a QOL change to alphabetize the Capabilities list in the Properties widget, which will go live in 706.

Cloning Fix

Cloning inside sandboxed containers would sometimes give the error: The current thread cannot reparent 'Script' (lacking capability Unassigned). This has been fixed to work as expected. Note that you still cannot clone and reparent outside of the container. This is live.

Breaking Changes

As mentioned above, we’re adding 18 new Capabilities. Of those, two of note are Material and AvatarBehavior. While the other 16 groups will have at least oneclass assigned to them, these two will not. That is intentional, to give developers time to assign these new Capabilities to scripts if desired prior to the breaking changes.

Coming in the 707 release:

  • The following four properties will go from being freely available when sandboxed to requiring the Material capability enabled:
  • The following two classes will go from freely available in sandboxes to requiring Basic
  • The following members of Humanoid will go from requiring the Avatar Capability to requiring AvatarBehavior
    • Properties: AutoJumpEnabled, AutoRotate, BreakJointsOnDeath, CameraOffset, EvaluateStateMachine, FloorMaterial, Health, MaxHealth, Jump, JumpHeight, JumpPower, UseJumpPower, MaxSlopeAngle, MoveDirection, PlatformStand, RequiresNeck, RootPart, SeatPart, Sit, TargetPoint, WalkSpeed, WalkToPart, WalkToPoint
    • Methods: BuildRigFromAttachments, ChangeState, EquipTool, GetBodyPartR15, GetLimb, GetMoveVelocity, GetState, GetStateEnabled, Move, MoveTo, PlayEmote, SetStateEnabled, TakeDamage, UnequipTools
    • Events: Climbing, Died, FallingDown, FreeFalling, GettingUp, HealthChanged, Jumping, MoveToFinished, PlatformStanding, Ragdoll, Running, Seated, StateChanged, StateEnabledChanged, Strafing, Swimming, Touched

Material will also include access to MaterialService, but that is currently Unassigned Security so doesn’t work today in sandboxes. To avoid disruption, we recommend assigning Basic, Material , and AvatarBehavior to your sandboxed instances if you are OK with the aforementioned members being accessed.

As this is a beta, it’s possible there will be similar breaking changes in the future. We’ll do our best to give you all a heads up.


@WinnersTakesAll to answer your question, we hope to support StarterGui next month.

4 Likes

Another quick update on potentially breaking changes. We’ll be reassigning the following classes from Avatar (to be renamed AvatarAppearance in the next release) to AvatarBehavior. To avoid breaking changes, if your scripts use these API’s and don’t already have AvatarBehavior, assign them that Capability.

These changes will go live in the 711 release (ETA March 5th). Sorry for any trouble this causes.

Additionally, we’ve been continuously making changes to assign more API’s, ultimately aiming for developers to never run into lacking Capability Unassigned errors in all practical usecases.

Will script capabilities be displayed on the Creator Store for models?

Just a suggestion, for once this is released, since I think it’s important to let people know which stuff models have access to. I’ve been dealing with a ton of cloned models of my library using require for external scripts, which I can’t limit or visually convey to people.

Another issue I found is that if you have a BindableEvent without a parent, it will error even if it has the AccessOutsideWrite capability.

local event = Instance.new("BindableEvent")

event.Event:Connect(function(msg)
	print(msg)
end)

event:Fire("I fired!")

The current thread cannot use ‘Event’ (lacking capability Unassigned)

Is this intentional behavior? I think AccessOutsideWrite gives enough permissions to use events that aren’t parented since parented events work fine.

Will this eventually be supported for scripts with a Plugin RunContext? It’d be great if plugins in the Toolbox showed required capabilities before hitting Install (and if you could tweak them, regardless of whatever the publisher set as the default).

1 Like

Thank you for the report, this issue should be fixed.

One more update on potentially breaking changes. In the next engine release next week (717), the following members of Humanoid will go from AvatarAppearance to AvatarBehavior:

  • takeDamage
  • Health_XML
  • JumpReplicate
  • LeftLeg
  • RightLeg
  • MoveDirectionInternal
  • Strafe
  • Torso
  • WalkAngleError
  • WalkDirection
  • maxHealth
  • CameraMode
  • NetworkHumanoidState

And the following members of Humanoid will go from AvatarAppearance to Animation:

  • GetPlayingAnimationTracks
  • LoadAnimation
  • loadAnimation
  • AnimationPlayed
1 Like

Will we see Humanoid:EquipTool() and Humanoid:UnequipTools() switch from requiring both AvatarAppearance and AvatarBehavior to just AvatarBehavior? I can’t think of a reason why equipping a tool is related to AvatarAppearance other than it visually shows that tool on the character.

In addition, I noticed :EquipTool() sandboxing tools that are equipped with the same capabilities as the script they are run on. I imagine this is to prevent scripts from creating tools with unsandboxed scripts and running them from the tool, but is there a way around it? My use case is that I have an inventory system I develop and distribute, and want to sandbox the inventory system’s logic, UI, etc., but not the tools. I can PM you additional details about this.

Will we see Humanoid:EquipTool() and Humanoid:UnequipTools() switch from requiring both AvatarAppearance and AvatarBehavior to just AvatarBehavior?

Good question, I only included the breaking changes in my post. There are more changes to Humanoid that only make API’s less restrictive; EquipTool and UnequipTools going to only AvatarBehavior is one of them. Humanoid itself will have None and members within Humanoid will be assigned specific Capabilities. Nothing in Humanoid will require more than one Capability in the next release, and a fair number of members have None.

I imagine this is to prevent scripts from creating tools with unsandboxed scripts and running them from the tool, but is there a way around it?

Your guess is correct. You can give your script the CapabilityControl capability and then unsandbox or change the Capabilities of the tools.

1 Like

I did further testing and found that you can bypass not being able to equip tools that are unsandboxed by reparenting the tool to the character which equips the tool.

humanoid:EquipTool(tool) -- The current thread cannot reparent 'ClientSandboxBreak' to 'Workspace.WinnersTakesAll' - target location is not Sandboxed
tool.Parent = character -- Works fine

The only caveat I’ve found is that you need RunServerScript if the tool has a server-sided script, but apart from that, you can run anything unsandboxed.

local Players = game:GetService("Players")

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local backpack = player.Backpack

task.wait(3)

local tool = backpack:FindFirstChildOfClass("Tool")
if tool then
	--humanoid:EquipTool(tool)
	tool.Parent = character
end

The above script runs using RunClientScript | RunServerScript | AccessOutsideWrite | AvatarAppearance | Input | Players | AvatarBehavior permissions. If you have no server script, then you can omit the RunServerScript capability.

Minimum reproduction

I have a place file that includes a tool with both a server and a client script that runs only when equipped. It uses the above script excerpt to bypass the sandbox.

The sky turns dark when the sandbox is broken on the server, and the chat has a system message if the sandbox is broken on the client.

SandboxedToolEquipping.rbxl (62.0 KB)


I think the behavior of :EquipTool() should be reconsidered to determine whether it should sandbox tools. I lean towards it not sandboxing tools because it breaks a lot of use cases, including mine. My use case is an inventory system that wants to equip pre-existing while being sandboxed to ensure script safety.

If a script can already create itself a tool with malicious scripts, wouldn’t it be able to run that script without using this weird tool method? I wasn’t able to find an API to edit the source of a script that has a capability that is grantable, so I don’t believe this would ever be a real scenario.

Another scenario I can think of is running a pre-existing tool with a malicious script, but that tool would already need to have existed unsandboxed. A player could just execute the script themselves by equipping the tool. And if you can somehow sneak in a tool with a malicious script, you could just skip the tool part and have the script run itself.

I was never able to figure out how to disable sandboxing through :EquipTool(), but I guess this sandbox break was cooler.

Thank you for the report.
This was an issue in some of the new functionality that we enabled a day ago.
That change has now been disabled so that the server is not affected by this trick.