Recovered orphaned WIP before switching to a new task
This commit is contained in:
+40
-59
@@ -1,10 +1,7 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
# probe5.gd — Corrected, comprehensive sandboxing probe for Godot 4.6.2.
|
|
||||||
# Fixes the API-usage errors in earlier probes (static vs instance calls).
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
print("=== SANDBOX PROBE 5: Corrected Sandboxing Mechanisms ===")
|
print("=== SANDBOX PROBE 5: Corrected Sandboxing Mechanisms (Godot 4.6.2) ===")
|
||||||
probe_script_loading()
|
probe_script_loading()
|
||||||
probe_os_apis()
|
probe_os_apis()
|
||||||
probe_file_access()
|
probe_file_access()
|
||||||
@@ -12,12 +9,11 @@ func _ready():
|
|||||||
probe_classdb()
|
probe_classdb()
|
||||||
probe_expression_engine()
|
probe_expression_engine()
|
||||||
probe_engine_singletons()
|
probe_engine_singletons()
|
||||||
probe_global_scope()
|
|
||||||
probe_worker_threads()
|
probe_worker_threads()
|
||||||
probe_project_settings()
|
probe_project_settings()
|
||||||
probe_scriptserver()
|
probe_scriptserver()
|
||||||
probe_script_restrictions()
|
probe_script_restrictions()
|
||||||
probe_subviewport_isolation()
|
probe_allowlist()
|
||||||
print("=== SANDBOX PROBE 5 END ===")
|
print("=== SANDBOX PROBE 5 END ===")
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
@@ -37,7 +33,7 @@ func probe_script_loading():
|
|||||||
# Can we get the source code back from a loaded script?
|
# Can we get the source code back from a loaded script?
|
||||||
if res:
|
if res:
|
||||||
print(" get_source_code() available: ", res.has_method("get_source_code"))
|
print(" get_source_code() available: ", res.has_method("get_source_code"))
|
||||||
# Can we get the AST / parse tree?
|
# Can we get the AST / parse tree? (instance methods)
|
||||||
print(" GDScript instance has 'get_ast': ", gd.has_method("get_ast"))
|
print(" GDScript instance has 'get_ast': ", gd.has_method("get_ast"))
|
||||||
print(" GDScript instance has 'get_parser': ", gd.has_method("get_parser"))
|
print(" GDScript instance has 'get_parser': ", gd.has_method("get_parser"))
|
||||||
# Can we get the list of methods?
|
# Can we get the list of methods?
|
||||||
@@ -65,10 +61,6 @@ func probe_os_apis():
|
|||||||
print("OS.get_user_data_dir() = ", OS.get_user_data_dir())
|
print("OS.get_user_data_dir() = ", OS.get_user_data_dir())
|
||||||
# Can we get executable path?
|
# Can we get executable path?
|
||||||
print("OS.get_executable_path() = ", OS.get_executable_path())
|
print("OS.get_executable_path() = ", OS.get_executable_path())
|
||||||
# Can we get the process ID?
|
|
||||||
print("OS has 'get_process_id': ", OS.has_method("get_process_id"))
|
|
||||||
# Can we kill processes?
|
|
||||||
print("OS has 'kill': ", OS.has_method("kill"))
|
|
||||||
|
|
||||||
func probe_file_access():
|
func probe_file_access():
|
||||||
print("--- File access ---")
|
print("--- File access ---")
|
||||||
@@ -96,10 +88,6 @@ func probe_file_access():
|
|||||||
print("DirAccess.open('res://') = ", res_dir)
|
print("DirAccess.open('res://') = ", res_dir)
|
||||||
var user_dir = DirAccess.open("user://")
|
var user_dir = DirAccess.open("user://")
|
||||||
print("DirAccess.open('user://') = ", user_dir)
|
print("DirAccess.open('user://') = ", user_dir)
|
||||||
# Can we use FileAccess.get_file_as_string?
|
|
||||||
print(" FileAccess has 'get_file_as_string': ", FileAccess.has_method("get_file_as_string"))
|
|
||||||
# Can we check if a file exists?
|
|
||||||
print(" FileAccess has 'file_exists': ", FileAccess.has_method("file_exists"))
|
|
||||||
|
|
||||||
func probe_network():
|
func probe_network():
|
||||||
print("--- Network ---")
|
print("--- Network ---")
|
||||||
@@ -119,10 +107,6 @@ func probe_network():
|
|||||||
# WebSocket
|
# WebSocket
|
||||||
var ws = WebSocketPeer.new()
|
var ws = WebSocketPeer.new()
|
||||||
print("WebSocketPeer instantiated: ", ws != null)
|
print("WebSocketPeer instantiated: ", ws != null)
|
||||||
# Can we connect to a remote host?
|
|
||||||
var http2 = HTTPClient.new()
|
|
||||||
var connect_err = http2.connect_to_host("example.com", 80)
|
|
||||||
print("HTTPClient.connect_to_host('example.com', 80) err=", connect_err)
|
|
||||||
|
|
||||||
func probe_classdb():
|
func probe_classdb():
|
||||||
print("--- ClassDB ---")
|
print("--- ClassDB ---")
|
||||||
@@ -143,7 +127,7 @@ func probe_classdb():
|
|||||||
# Can we instantiate a class by name?
|
# Can we instantiate a class by name?
|
||||||
var inst = ClassDB.instantiate("Node")
|
var inst = ClassDB.instantiate("Node")
|
||||||
print("ClassDB.instantiate('Node') = ", inst)
|
print("ClassDB.instantiate('Node') = ", inst)
|
||||||
# Can we check if a class is a singleton?
|
# Can we check if a class is a singleton? (static method on ClassDB)
|
||||||
print("ClassDB has 'class_get_singleton': ", ClassDB.has_method("class_get_singleton"))
|
print("ClassDB has 'class_get_singleton': ", ClassDB.has_method("class_get_singleton"))
|
||||||
# Can we get the singleton instance of OS?
|
# Can we get the singleton instance of OS?
|
||||||
var os_singleton = ClassDB.class_get_singleton("OS")
|
var os_singleton = ClassDB.class_get_singleton("OS")
|
||||||
@@ -152,10 +136,6 @@ func probe_classdb():
|
|||||||
if os_singleton:
|
if os_singleton:
|
||||||
print(" OS singleton has 'execute': ", os_singleton.has_method("execute"))
|
print(" OS singleton has 'execute': ", os_singleton.has_method("execute"))
|
||||||
print(" OS singleton has 'get_environment': ", os_singleton.has_method("get_environment"))
|
print(" OS singleton has 'get_environment': ", os_singleton.has_method("get_environment"))
|
||||||
# Can we get the class list of a specific class?
|
|
||||||
print(" ClassDB has 'class_get_inheriters': ", ClassDB.has_method("class_get_inheriters"))
|
|
||||||
# Can we check if a class is an enum?
|
|
||||||
print(" ClassDB has 'class_get_enum_list': ", ClassDB.has_method("class_get_enum_list"))
|
|
||||||
|
|
||||||
func probe_expression_engine():
|
func probe_expression_engine():
|
||||||
print("--- Expression engine ---")
|
print("--- Expression engine ---")
|
||||||
@@ -194,17 +174,11 @@ func probe_expression_engine():
|
|||||||
if err == OK:
|
if err == OK:
|
||||||
var result5 = expr5.execute()
|
var result5 = expr5.execute()
|
||||||
print(" Expression.execute() = ", result5)
|
print(" Expression.execute() = ", result5)
|
||||||
# Can Expression access the global scope?
|
|
||||||
var expr6 = Expression.new()
|
|
||||||
err = expr6.parse("print('hello')")
|
|
||||||
print("Expression.parse('print') err=", err)
|
|
||||||
if err == OK:
|
|
||||||
var result6 = expr6.execute()
|
|
||||||
print(" Expression.execute() = ", result6)
|
|
||||||
|
|
||||||
func probe_engine_singletons():
|
func probe_engine_singletons():
|
||||||
print("--- Engine singletons ---")
|
print("--- Engine singletons ---")
|
||||||
# Engine singletons are the global objects like OS, Engine, etc.
|
# Engine singletons are the global objects like OS, Engine, etc.
|
||||||
|
# get_singletons() is a static method on Engine
|
||||||
var singletons = Engine.get_singletons()
|
var singletons = Engine.get_singletons()
|
||||||
print("Engine.get_singletons() count: ", singletons.size())
|
print("Engine.get_singletons() count: ", singletons.size())
|
||||||
for s in singletons:
|
for s in singletons:
|
||||||
@@ -215,26 +189,7 @@ func probe_engine_singletons():
|
|||||||
# Can we get the main loop?
|
# Can we get the main loop?
|
||||||
print("Engine.get_main_loop() = ", Engine.get_main_loop())
|
print("Engine.get_main_loop() = ", Engine.get_main_loop())
|
||||||
# Can we get the physics server?
|
# Can we get the physics server?
|
||||||
print("Engine has 'get_physics_server': ", Engine.has_method("get_physics_server"))
|
print("Engine.get_physics_server() = ", Engine.get_physics_server())
|
||||||
|
|
||||||
func probe_global_scope():
|
|
||||||
print("--- Global scope ---")
|
|
||||||
# Can we enumerate what's in the global scope?
|
|
||||||
var globals = []
|
|
||||||
for g in GDScript.get_global_class_list():
|
|
||||||
globals.append(g)
|
|
||||||
print("GDScript.get_global_class_list() count: ", globals.size())
|
|
||||||
# Can we check if a global is available before using it?
|
|
||||||
print("GDScript has 'get_global_class_list': ", GDScript.has_method("get_global_class_list"))
|
|
||||||
# Is there any way to restrict which globals a script sees?
|
|
||||||
print("GDScript has 'set_global_scope': ", GDScript.has_method("set_global_scope"))
|
|
||||||
print("GDScript has 'restrict_globals': ", GDScript.has_method("restrict_globals"))
|
|
||||||
# Check the GDScript class methods
|
|
||||||
var methods = GDScript.get_method_list()
|
|
||||||
var method_names = []
|
|
||||||
for m in methods:
|
|
||||||
method_names.append(m.name)
|
|
||||||
print(" GDScript methods: ", method_names)
|
|
||||||
|
|
||||||
func probe_worker_threads():
|
func probe_worker_threads():
|
||||||
print("--- Worker threads ---")
|
print("--- Worker threads ---")
|
||||||
@@ -290,12 +245,38 @@ func probe_script_restrictions():
|
|||||||
print(" evil() result: ", inst.call("evil"))
|
print(" evil() result: ", inst.call("evil"))
|
||||||
# Can we block specific global classes?
|
# Can we block specific global classes?
|
||||||
print(" GDScript has method to restrict globals: ", gd.has_method("set_global_scope"))
|
print(" GDScript has method to restrict globals: ", gd.has_method("set_global_scope"))
|
||||||
|
print(" GDScript has method 'get_global_scope': ", gd.has_method("get_global_scope"))
|
||||||
|
|
||||||
func probe_subviewport_isolation():
|
func probe_allowlist():
|
||||||
print("--- SubViewport isolation ---")
|
print("--- Allowlist / static analysis ---")
|
||||||
# SubViewports isolate rendering, but do they isolate script execution?
|
# The allowlist approach: parse GDScript source and reject disallowed patterns
|
||||||
var vp = SubViewport.new()
|
# Test: can we detect dangerous API calls via string analysis?
|
||||||
print(" SubViewport.new() = ", vp)
|
var dangerous_patterns = [
|
||||||
# SubViewports share the same process/engine - scripts still have full access
|
"OS.execute", "OS.create_process", "OS.get_environment",
|
||||||
print(" SubViewport does NOT isolate script execution (same process)")
|
"FileAccess.open", "DirAccess.open", "TCPServer", "HTTPClient",
|
||||||
vp.free()
|
"load(", "preload(", "ClassDB", "Engine.get_main_loop",
|
||||||
|
"get_tree().quit", "ProjectSettings"
|
||||||
|
]
|
||||||
|
var test_script = """extends Node
|
||||||
|
func _ready():
|
||||||
|
var f = FileAccess.open("/etc/passwd", FileAccess.READ)
|
||||||
|
OS.execute("rm", ["-rf", "/"])
|
||||||
|
"""
|
||||||
|
print(" Test script contains dangerous patterns:")
|
||||||
|
for p in dangerous_patterns:
|
||||||
|
if p in test_script:
|
||||||
|
print(" DETECTED: ", p)
|
||||||
|
# Can we use a tokenizer/lexer to parse GDScript?
|
||||||
|
# GDScript has a built-in tokenizer accessible via the language
|
||||||
|
var lang = GDScriptLanguage.new()
|
||||||
|
print(" GDScriptLanguage.new() = ", lang)
|
||||||
|
print(" GDScriptLanguage has 'get_name': ", lang.has_method("get_name"))
|
||||||
|
print(" GDScriptLanguage has 'get_extension': ", lang.has_method("get_extension"))
|
||||||
|
# Can we get the tokenizer?
|
||||||
|
print(" GDScriptLanguage has 'get_tokenizer': ", lang.has_method("get_tokenizer"))
|
||||||
|
# Can we get the parser?
|
||||||
|
print(" GDScriptLanguage has 'get_parser': ", lang.has_method("get_parser"))
|
||||||
|
# Can we get the analyzer?
|
||||||
|
print(" GDScriptLanguage has 'get_analyzer': ", lang.has_method("get_analyzer"))
|
||||||
|
# Can we get the compiler?
|
||||||
|
print(" GDScriptLanguage has 'get_compiler': ", lang.has_method("get_compiler"))
|
||||||
|
|||||||
Reference in New Issue
Block a user