175 lines
6.8 KiB
GDScript
175 lines
6.8 KiB
GDScript
extends Node
|
|
|
|
func _ready():
|
|
print("=== SANDBOX PROBE 6: Corrected Evidence Gathering (Godot 4.6.2) ===")
|
|
probe_script_loading()
|
|
probe_os_apis()
|
|
probe_file_access()
|
|
probe_network()
|
|
probe_classdb()
|
|
probe_expression_engine()
|
|
probe_engine_singletons()
|
|
probe_worker_threads()
|
|
probe_project_settings()
|
|
probe_allowlist()
|
|
print("=== SANDBOX PROBE 6 END ===")
|
|
get_tree().quit()
|
|
|
|
func probe_script_loading():
|
|
print("--- Script loading ---")
|
|
var gd = GDScript.new()
|
|
gd.source_code = "extends RefCounted\nfunc hello():\n\treturn 42\n"
|
|
var err = gd.reload()
|
|
print("GDScript.reload() from source_code err=", err)
|
|
if err == OK:
|
|
var inst = gd.new()
|
|
print(" instantiated, hello()=", inst.call("hello"))
|
|
var res = load("res://test_script.gd")
|
|
print("load('res://test_script.gd') = ", res)
|
|
if res:
|
|
print(" get_source_code() available: ", res.has_method("get_source_code"))
|
|
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_script_method_list': ", gd.has_method("get_script_method_list"))
|
|
print(" GDScript instance has 'get_script_property_list': ", gd.has_method("get_script_property_list"))
|
|
|
|
func probe_os_apis():
|
|
print("--- OS APIs ---")
|
|
var out = []
|
|
var exit_code = OS.execute("echo", ["hello"], out)
|
|
print("OS.execute('echo') exit=", exit_code, " out=", out)
|
|
print("OS.get_environment('HOME') = ", OS.get_environment("HOME"))
|
|
print("OS has 'create_process': ", OS.has_method("create_process"))
|
|
print("OS.get_cmdline_args() = ", OS.get_cmdline_args())
|
|
print("OS.get_user_data_dir() = ", OS.get_user_data_dir())
|
|
print("OS.get_executable_path() = ", OS.get_executable_path())
|
|
|
|
func probe_file_access():
|
|
print("--- File access ---")
|
|
var f = FileAccess.open("/etc/hostname", FileAccess.READ)
|
|
print("FileAccess.open('/etc/hostname') = ", f)
|
|
if f:
|
|
print(" content: ", f.get_as_text())
|
|
f.close()
|
|
var w = FileAccess.open("/tmp/godot_probe_write.txt", FileAccess.WRITE)
|
|
print("FileAccess.open('/tmp/...', WRITE) = ", w)
|
|
if w:
|
|
w.store_string("probe")
|
|
w.close()
|
|
print(" wrote to /tmp OK")
|
|
var dir = DirAccess.open("/")
|
|
print("DirAccess.open('/') = ", dir)
|
|
if dir:
|
|
var names = dir.get_files()
|
|
print(" root files count: ", names.size())
|
|
var res_dir = DirAccess.open("res://")
|
|
print("DirAccess.open('res://') = ", res_dir)
|
|
var user_dir = DirAccess.open("user://")
|
|
print("DirAccess.open('user://') = ", user_dir)
|
|
|
|
func probe_network():
|
|
print("--- Network ---")
|
|
var tcp = TCPServer.new()
|
|
var bind_err = tcp.listen(0)
|
|
print("TCPServer.listen(0) err=", bind_err)
|
|
var http = HTTPClient.new()
|
|
print("HTTPClient instantiated: ", http != null)
|
|
var http_req = HTTPRequest.new()
|
|
print("HTTPRequest instantiated: ", http_req != null)
|
|
var udp = PacketPeerUDP.new()
|
|
print("PacketPeerUDP instantiated: ", udp != null)
|
|
var ws = WebSocketPeer.new()
|
|
print("WebSocketPeer instantiated: ", ws != null)
|
|
|
|
func probe_classdb():
|
|
print("--- ClassDB ---")
|
|
print("ClassDB.class_exists('OS'): ", ClassDB.class_exists("OS"))
|
|
print("ClassDB.class_exists('FileAccess'): ", ClassDB.class_exists("FileAccess"))
|
|
print("ClassDB.class_exists('TCPServer'): ", ClassDB.class_exists("TCPServer"))
|
|
print("ClassDB.class_exists('HTTPClient'): ", ClassDB.class_exists("HTTPClient"))
|
|
var class_list = ClassDB.get_class_list()
|
|
print("ClassDB.get_class_list() count: ", class_list.size())
|
|
var os_methods = ClassDB.class_get_method_list("OS")
|
|
print("OS method count: ", os_methods.size())
|
|
var inst = ClassDB.instantiate("Node")
|
|
print("ClassDB.instantiate('Node') = ", inst)
|
|
print("ClassDB has 'class_get_singleton': ", ClassDB.has_method("class_get_singleton"))
|
|
|
|
func probe_expression_engine():
|
|
print("--- Expression engine ---")
|
|
var expr = Expression.new()
|
|
var err = expr.parse("1 + 2")
|
|
print("Expression.parse('1 + 2') err=", err)
|
|
if err == OK:
|
|
var result = expr.execute()
|
|
print(" Expression.execute() = ", result)
|
|
var expr2 = Expression.new()
|
|
err = expr2.parse("OS.get_environment('HOME')")
|
|
print("Expression.parse('OS.get_environment') err=", err)
|
|
if err == OK:
|
|
var result2 = expr2.execute()
|
|
print(" Expression.execute() = ", result2)
|
|
var expr3 = Expression.new()
|
|
err = expr3.parse("get_environment('HOME')", ["OS"])
|
|
print("Expression.parse with base_instance err=", err)
|
|
if err == OK:
|
|
var result3 = expr3.execute([], OS)
|
|
print(" Expression.execute with OS base = ", result3)
|
|
var expr4 = Expression.new()
|
|
err = expr4.parse("FileAccess.open('/etc/hostname', 1)")
|
|
print("Expression.parse('FileAccess.open') err=", err)
|
|
if err == OK:
|
|
var result4 = expr4.execute()
|
|
print(" Expression.execute() = ", result4)
|
|
var expr5 = Expression.new()
|
|
err = expr5.parse("get_tree().quit()")
|
|
print("Expression.parse('get_tree().quit()') err=", err)
|
|
if err == OK:
|
|
var result5 = expr5.execute()
|
|
print(" Expression.execute() = ", result5)
|
|
|
|
func probe_engine_singletons():
|
|
print("--- Engine singletons ---")
|
|
print("Engine has 'remove_singleton': ", Engine.has_method("remove_singleton"))
|
|
print("Engine has 'add_singleton': ", Engine.has_method("add_singleton"))
|
|
print("Engine.get_main_loop() = ", Engine.get_main_loop())
|
|
|
|
func probe_worker_threads():
|
|
print("--- Worker threads ---")
|
|
var thread = Thread.new()
|
|
print(" Thread.new() = ", thread)
|
|
print(" WorkerThreadPool has 'add_task': ", WorkerThreadPool.has_method("add_task"))
|
|
|
|
func probe_project_settings():
|
|
print("--- Project settings ---")
|
|
var settings = ProjectSettings.get_property_list()
|
|
var relevant = []
|
|
for s in settings:
|
|
var name = String(s.name)
|
|
if "sandbox" in name.to_lower() or "restrict" in name.to_lower() or "security" in name.to_lower():
|
|
relevant.append(name)
|
|
print(" Sandbox/security project settings: ", relevant)
|
|
print(" ProjectSettings has 'application/run/disable_os': ", ProjectSettings.has_setting("application/run/disable_os"))
|
|
print(" ProjectSettings has 'application/run/disable_file_access': ", ProjectSettings.has_setting("application/run/disable_file_access"))
|
|
print(" ProjectSettings has 'application/run/disable_network': ", ProjectSettings.has_setting("application/run/disable_network"))
|
|
print(" ProjectSettings has 'set_setting': ", ProjectSettings.has_method("set_setting"))
|
|
print(" ProjectSettings has 'save': ", ProjectSettings.has_method("save"))
|
|
|
|
func probe_allowlist():
|
|
print("--- Allowlist / static analysis ---")
|
|
var dangerous_patterns = [
|
|
"OS.execute", "OS.create_process", "OS.get_environment",
|
|
"FileAccess.open", "DirAccess.open", "TCPServer", "HTTPClient",
|
|
"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)
|