-
-
Notifications
You must be signed in to change notification settings - Fork 23.5k
[3.x] Add globals disabled feature to GDScript class #61831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.x
Are you sure you want to change the base?
Conversation
d172bd8 to
3c314d8
Compare
|
It seems to me that this is not enough to create a secure sandbox, and these restrictions can be easily circumvented. Do not give the false illusion of security to users. |
|
What makes it insecure? |
With the flag on, the following identifiers are skipped in the compiler:
what is not skipped:
The types those ancestor classes could expose access are:
From the above I don't see how a script could circumvent the limitation from inside, but if I missed something please let me know. You do have a point on giving false sense of security. If a user gives access to a node in the scene tree (like the
func player_current_position():
return ModSystem.player_body.global_transform.originand an instance of tl'dr: security flaws would be game design flaws, but adding a warning to the docs is very relevant. |
Games and apps requiring in-game code to be written and executed could use GDScript as both language and compiler, but misuse could lead to unexpected results, damage to device, security breaches and in the case of shared code (e.g. mods downloaded from other players) even attacks. This commit adds a feature allowing scripts to run without access to any globals (class names, singletons, constants), allowing GDScript to be used as a general purpose script compiler and interpreter running as standalone programs, where interfaces to the environment external to the script are explicitly given. This is done via the new set_globals_disabled(bool) method.
3c314d8 to
7dbbba7
Compare
Sandbox Escape: extends Node
var evil_user_script := """
func run():
var script = get_script().duplicate()
script.set_globals_disabled(false)
script.source_code = "func _init(): OS.alert('Escaped!')"
script.reload()
var instance = script.new()
"""
func _ready() -> void:
var script := GDScript.new()
script.set_globals_disabled(true)
script.source_code = evil_user_script
var err := script.reload()
if err:
print('Error')
return
var instance = script.new()
instance.run()List of potentially dangerous places:
But even if we close all the holes, I'm not sure if this feature is a good idea, because if the user is not very, very, very careful, then they will not even notice how they provide a path to escape from the sandbox. Plus, this feature is not a true sandbox (but needs input from a cybersecurity expert, which I am not). You can always create vulnerabilities. But this is TOO EASY way to create vulnerabilities. This is in contrast to In my opinion, we cannot provide this feature and hope that the user has the knowledge and care to use this feature correctly. Passing a I think it would be better to limit the possibility of insecure interfaces or add full-fledged sandboxes. But this is a much more difficult task than this PR. By the way, creating your own scripting is not as difficult as it seems. It may not be Turing-complete or Turing-complete, but a limited language interpreted in GDScript. You can use an |
Wow, thanks for that hint, it may be very helpful to other people wanting to implement limited scripting in Godot projects |
If it was a matter of closing the holes, it could be made more restricted e.g. whitelisting system functions (so forgetting about a certain method is harmless). But if the issue is on the social/usability side, then I agree the current approach is not ready to be included. Let's freeze this for now then - and we necromance it later when I (or someone else) come up with a solution for that (if the feature is still relevant by then) |
|
Given the above discussion, I marked this as "draft" so that we can filter it out in the backlog of PRs which are ready to merge. |
I've tried (somewhat successfully) to use this in a game about coding. However, I've come across several issues which have led me to want to use the built-in gdscript interpreter over my own:
Please let me know if you know of any way around these issues. It would be really good if I could get access to a gdscript linter and/or code completion from within the engine (rather than just the editor). Solving point 2 will require a bunch of manual interpretation I would expect Expression to already handle for me 😞 (maybe I should open an issue for that?) |
|
@Zshandi The workarounds I mentioned above are more suitable for formulas, dialog systems, command line interpreters and toy scripting languages. If you want a more "real" scripting language, I agree that supporting your own language may be too much effort. Unfortunately, GDScript is still hard to isolate, and doesn't have a sandbox mode. If you want a safe scripting language (for user scripts, mods, etc.), I'd recommend taking a look at the following project: While it may be less convenient for mod support than GDScript (you have to design a safe API, "bridge" or "interface" through which mods can interact with your game), it should be easier than supporting your own scripting language. Also, Lua is known for its wide usage and reliability for such purposes. Note that the above link is a third-party project, and the author has stopped supporting it. Also, you could consider an option where the sandboxed interpreter is not integrated into Godot. Instead, you could ship it as a separate binary and communicate with it via |
Some games and apps involve players typing their own code, which is then interpreted/executed as part of the process.
One example is games where the player is expected to write logic as part of the game mechanic (control robots, hack into fictional systems, etc). There is an entire game genre based on this (https://en.wikipedia.org/wiki/Programming_game).
Another example is games which can be customized by players (mods), including writing script code (e.g. enemy AI).
In the projects I have worked so far with this mechanic, I had to design a scripting language and write my own interpreter (I have seen others trying the same as well). Due to having to write the interpreter, the scripting was rudimentary (sequential) and my players complained they wanted usual programming features like branching, loops, functions, local variables, etc, and were frustrated by the lack of.
GDScript could be used to implement in-game programming with a very functional, complex, stable and user friendly language, and it comes with a compiler for free, zero dev effort. However, due to the access to the engine globals, this comes with severe issues. Players could:
OSand damage something in their computerResourceSaverand steal assetsAnd if players are meant to share scripts (e.g. mods), hell is unleashed with anything from "delete all hdd" pranks to trojan horse attacks.
Therefore, so far using GDScript to power in-game general purpose scripting is "a nice thing you can't have".
This PR adds a feature to the
GDScriptclass allowing to disable globals, per script. All game scripts behave as usual by default, but an individual script can have a flag set so that specific script won't have access to any engine globals, including class names, singletons and constants, becoming detached from the game system. It still can access anything if access is explicitly given, via variables or method arguments.The code below demonstrates how to use the new
set_globals_disabledmethod.Assuming a file
res://user_script.gd(or a String assigned viaGDScript.source_code), which is meant to be isolated:A node can safely run this script with globals disabled:
An example to give explicit access to something, assuming the
res://test_script.gdscript:The
inputlocal variable can be used to give the script access to theInputsingleton:Function arguments also work with external objects. The following script would work even with globals disabled, if
ai_search_playeris called externally providing theKinematicBodyarguments:For mod systems where a subset of the engine functionality should be exposed, access to an interface node can be given to the isolated script (like the
inputexample), that node having a selection of methods an properties as required in the modding system. In other words, that node would work like a master singleton for the isolated script.