diff --git a/sandbox_probe/probe.gd b/sandbox_probe/probe.gd deleted file mode 100644 index 73ebb3d..0000000 --- a/sandbox_probe/probe.gd +++ /dev/null @@ -1,76 +0,0 @@ -extends Node - -func _ready(): - print("=== SANDBOX PROBE START ===") - probe_script_loading() - probe_os_apis() - probe_file_access() - probe_network() - probe_engine_apis() - print("=== SANDBOX PROBE END ===") - get_tree().quit() - -func probe_script_loading(): - print("--- Script loading ---") - # Can we load a GDScript from a string at runtime? - 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")) - # Can we load a script resource from a path? - var res = load("res://test_script.gd") - print("load('res://test_script.gd') = ", res) - -func probe_os_apis(): - print("--- OS APIs ---") - # Can we execute shell commands? - var out = [] - var exit_code = OS.execute("echo", ["hello"], out) - print("OS.execute('echo') exit=", exit_code, " out=", out) - # Environment access - print("OS.get_environment('HOME') = ", OS.get_environment("HOME")) - # Process spawning - print("OS.create_process available: ", OS.has_method("create_process")) - -func probe_file_access(): - print("--- File access ---") - # Can we read arbitrary files? - var f = FileAccess.open("/etc/hostname", FileAccess.READ) - print("FileAccess.open('/etc/hostname') = ", f) - if f: - print(" content: ", f.get_as_text()) - f.close() - # Can we write outside the project? - 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") - -func probe_network(): - print("--- Network ---") - # Can we open raw sockets? - var tcp = TCPServer.new() - var bind_err = tcp.listen(0) - print("TCPServer.listen(0) err=", bind_err) - # HTTP client - var http = HTTPClient.new() - print("HTTPClient instantiated: ", http != null) - -func probe_engine_apis(): - print("--- Engine APIs ---") - # Can we access the scene tree / add nodes? - var node = Node.new() - print("Node.new() = ", node) - # Can we access ProjectSettings? - print("ProjectSettings.get_setting('application/config/name') = ", ProjectSettings.get_setting("application/config/name")) - # Can we access the filesystem directory listing? - var dir = DirAccess.open("/") - print("DirAccess.open('/') = ", dir) - if dir: - var names = dir.get_files() - print(" root files count: ", names.size()) diff --git a/sandbox_probe/probe.tscn b/sandbox_probe/probe.tscn deleted file mode 100644 index 236e56a..0000000 --- a/sandbox_probe/probe.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://probe.gd" id="1"] - -[node name="Probe" type="Node"] -script = ExtResource("1") diff --git a/sandbox_probe/probe2.gd b/sandbox_probe/probe2.gd deleted file mode 100644 index b382c6b..0000000 --- a/sandbox_probe/probe2.gd +++ /dev/null @@ -1,83 +0,0 @@ -extends Node - -func _ready(): - print("=== SANDBOX PROBE 2: Sandboxing Mechanisms ===") - probe_scriptserver() - probe_script_restrictions() - probe_subviewport_isolation() - probe_engine_restrictions() - probe_allowlist_approach() - print("=== SANDBOX PROBE 2 END ===") - get_tree().quit() - -func probe_scriptserver(): - print("--- ScriptServer ---") - # Does ScriptServer exist and what can it do? - print("ScriptServer has_method('get_language_count'): ", ScriptServer.has_method("get_language_count")) - print("ScriptServer has_method('get_language'): ", ScriptServer.has_method("get_language")) - # Can we enumerate registered script languages? - if ScriptServer.has_method("get_language_count"): - var count = ScriptServer.get_language_count() - print(" language count: ", count) - for i in range(count): - var lang = ScriptServer.get_language(i) - print(" language[", i, "] = ", lang) - -func probe_script_restrictions(): - print("--- Script restrictions ---") - # Can we restrict which classes/globals a script can access? - # Test: does GDScript have any built-in allowlist mechanism? - var gd = GDScript.new() - gd.source_code = "extends RefCounted\nfunc evil():\n\treturn OS.get_environment('HOME')\n" - var err = gd.reload() - print(" GDScript with OS access reload err=", err) - if err == OK: - var inst = gd.new() - print(" evil() result: ", inst.call("evil")) - # Can we block specific global classes? - print(" GDScript has method to restrict globals: ", gd.has_method("set_global_scope")) - -func probe_subviewport_isolation(): - print("--- SubViewport isolation ---") - # SubViewports isolate rendering, but do they isolate script execution? - var vp = SubViewport.new() - print(" SubViewport.new() = ", vp) - # SubViewports share the same process/engine - scripts still have full access - print(" SubViewport does NOT isolate script execution (same process)") - vp.free() - -func probe_engine_restrictions(): - print("--- Engine restrictions ---") - # Is there a way to disable OS/File/Network access globally? - print(" Engine.has_singleton('OS'): ", Engine.has_singleton("OS")) - print(" Engine.has_singleton('FileAccess'): ", Engine.has_singleton("FileAccess")) - # Check if there's a '--disable-*' flag or project setting - print(" ProjectSettings has 'application/run/disable_*': ", ProjectSettings.has_setting("application/run/disable_os")) - # Check for any sandbox-related project settings - var settings = ProjectSettings.get_property_list() - var sandbox_settings = [] - for s in settings: - if "sandbox" in String(s.name).to_lower() or "restrict" in String(s.name).to_lower(): - sandbox_settings.append(s.name) - print(" Sandbox-related project settings: ", sandbox_settings) - -func probe_allowlist_approach(): - print("--- Allowlist approach ---") - # The allowlist approach: parse GDScript source and reject disallowed patterns - # Test: can we detect dangerous API calls via string 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) diff --git a/sandbox_probe/probe3.gd b/sandbox_probe/probe3.gd deleted file mode 100644 index 9268945..0000000 --- a/sandbox_probe/probe3.gd +++ /dev/null @@ -1,126 +0,0 @@ -extends Node - -func _ready(): - print("=== SANDBOX PROBE 3: Deep Sandboxing Mechanisms ===") - probe_global_scope_restriction() - probe_classdb_restriction() - probe_engine_singletons() - probe_script_validation() - probe_expression_engine() - probe_worker_threads() - probe_os_restriction_flags() - print("=== SANDBOX PROBE 3 END ===") - get_tree().quit() - -func probe_global_scope_restriction(): - print("--- Global scope restriction ---") - # 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_classdb_restriction(): - print("--- ClassDB restriction ---") - # ClassDB is the registry of all engine classes. Can we restrict it? - print(" ClassDB has 'get_class_list': ", ClassDB.has_method("get_class_list")) - print(" ClassDB has 'class_exists': ", ClassDB.has_method("class_exists")) - # Can we check if a specific class is available? - 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")) - # Is there a way to hide classes from scripts? - print(" ClassDB has 'set_class_enabled': ", ClassDB.has_method("set_class_enabled")) - print(" ClassDB has 'disable_class': ", ClassDB.has_method("disable_class")) - -func probe_engine_singletons(): - print("--- Engine singletons ---") - # Engine singletons are the global objects like OS, Engine, etc. - # Can we enumerate them? - var singletons = Engine.get_singletons() - print(" Engine.get_singletons() count: ", singletons.size()) - for s in singletons: - print(" singleton: ", s) - # Can we remove a singleton? - print(" Engine has 'remove_singleton': ", Engine.has_method("remove_singleton")) - print(" Engine has 'add_singleton': ", Engine.has_method("add_singleton")) - -func probe_script_validation(): - print("--- Script validation ---") - # Can we validate a script before running it? - var gd = GDScript.new() - gd.source_code = "extends RefCounted\nfunc evil():\n\treturn OS.get_environment('HOME')\n" - var err = gd.reload() - print(" GDScript.reload() err=", err) - # Can we get the list of identifiers used in a script? - print(" GDScript has 'get_script_method_list': ", gd.has_method("get_script_method_list")) - # Can we get the source code back? - print(" GDScript has 'get_source_code': ", gd.has_method("get_source_code")) - # Can we check what identifiers a script references? - print(" GDScript has 'get_script_constant_map': ", gd.has_method("get_script_constant_map")) - # Check if there's a way to get the AST - print(" GDScript has 'get_ast': ", gd.has_method("get_ast")) - print(" GDScript has 'get_parser': ", gd.has_method("get_parser")) - -func probe_expression_engine(): - print("--- Expression engine ---") - # Godot has an Expression class for evaluating expressions safely - 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) - # Can Expression access OS? - 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) - # Expression with base_instance - 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) - -func probe_worker_threads(): - print("--- Worker threads ---") - # Can we run scripts in worker threads? - print(" Thread has 'new': ", Thread.has_method("new")) - # WorkerThreadPool - print(" WorkerThreadPool has 'add_task': ", WorkerThreadPool.has_method("add_task")) - # Can we isolate a script in a thread? - print(" Thread available: ", Thread != null) - -func probe_os_restriction_flags(): - print("--- OS restriction flags ---") - # Check if there are any OS-level sandbox flags - print(" OS has 'get_environment': ", OS.has_method("get_environment")) - print(" OS has 'execute': ", OS.has_method("execute")) - print(" OS has 'create_process': ", OS.has_method("create_process")) - # Check project settings for sandbox-related options - 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) - # Check if there's a way to disable specific features - 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")) diff --git a/sandbox_probe/probe3.tscn b/sandbox_probe/probe3.tscn deleted file mode 100644 index 5b8c302..0000000 --- a/sandbox_probe/probe3.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://probe3.gd" id="1"] - -[node name="Probe3" type="Node"] -script = ExtResource("1") diff --git a/sandbox_probe/probe4.gd b/sandbox_probe/probe4.gd deleted file mode 100644 index e86bd4c..0000000 --- a/sandbox_probe/probe4.gd +++ /dev/null @@ -1,232 +0,0 @@ -extends Node - -func _ready(): - print("=== SANDBOX PROBE 4: Godot 4.6.2 Sandboxing Mechanisms ===") - probe_script_loading() - probe_os_apis() - probe_file_access() - probe_network() - probe_classdb() - probe_expression_engine() - probe_engine_singletons() - probe_global_scope() - probe_worker_threads() - probe_project_settings() - print("=== SANDBOX PROBE 4 END ===") - get_tree().quit() - -func probe_script_loading(): - print("--- Script loading ---") - # Can we load a GDScript from a string at runtime? - 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")) - # Can we load a script resource from a path? - var res = load("res://test_script.gd") - print("load('res://test_script.gd') = ", res) - # Can we get the source code back from a loaded script? - if res: - print(" get_source_code() available: ", res.has_method("get_source_code")) - # Can we get the AST / parse tree? - print(" GDScript instance has 'get_ast': ", gd.has_method("get_ast")) - print(" GDScript instance has 'get_parser': ", gd.has_method("get_parser")) - # Can we get the list of methods? - print(" GDScript instance has 'get_script_method_list': ", gd.has_method("get_script_method_list")) - # Can we get the list of member variables? - print(" GDScript instance has 'get_script_property_list': ", gd.has_method("get_script_property_list")) - -func probe_os_apis(): - print("--- OS APIs ---") - # Can we execute shell commands? - var out = [] - var exit_code = OS.execute("echo", ["hello"], out) - print("OS.execute('echo') exit=", exit_code, " out=", out) - # Environment access - print("OS.get_environment('HOME') = ", OS.get_environment("HOME")) - # Process spawning - print("OS has 'create_process': ", OS.has_method("create_process")) - # Can we get the command line? - print("OS.get_cmdline_args() = ", OS.get_cmdline_args()) - # Can we get user data dir? - print("OS.get_user_data_dir() = ", OS.get_user_data_dir()) - # Can we get executable path? - print("OS.get_executable_path() = ", OS.get_executable_path()) - -func probe_file_access(): - print("--- File access ---") - # Can we read arbitrary files? - var f = FileAccess.open("/etc/hostname", FileAccess.READ) - print("FileAccess.open('/etc/hostname') = ", f) - if f: - print(" content: ", f.get_as_text()) - f.close() - # Can we write outside the project? - 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") - # Can we list directories? - var dir = DirAccess.open("/") - print("DirAccess.open('/') = ", dir) - if dir: - var names = dir.get_files() - print(" root files count: ", names.size()) - # Can we access res:// and user:// ? - 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 ---") - # Can we open raw sockets? - var tcp = TCPServer.new() - var bind_err = tcp.listen(0) - print("TCPServer.listen(0) err=", bind_err) - # HTTP client - var http = HTTPClient.new() - print("HTTPClient instantiated: ", http != null) - # Can we make HTTP requests? - var http_req = HTTPRequest.new() - print("HTTPRequest instantiated: ", http_req != null) - # PacketPeerUDP - var udp = PacketPeerUDP.new() - print("PacketPeerUDP instantiated: ", udp != null) - # WebSocket - var ws = WebSocketPeer.new() - print("WebSocketPeer instantiated: ", ws != null) - -func probe_classdb(): - print("--- ClassDB ---") - # ClassDB is the registry of all engine classes - 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")) - # Can we enumerate all classes? - var class_list = ClassDB.get_class_list() - print("ClassDB.get_class_list() count: ", class_list.size()) - # Can we get methods of a class? - var os_methods = ClassDB.class_get_method_list("OS") - print("OS method count: ", os_methods.size()) - # Can we get properties of a class? - var os_props = ClassDB.class_get_property_list("OS") - print("OS property count: ", os_props.size()) - # Can we instantiate a class by name? - var inst = ClassDB.instantiate("Node") - print("ClassDB.instantiate('Node') = ", inst) - # Can we check if a class is a singleton? - print("ClassDB has 'class_get_singleton': ", ClassDB.has_method("class_get_singleton")) - # Can we get the singleton instance of OS? - var os_singleton = ClassDB.class_get_singleton("OS") - print("ClassDB.class_get_singleton('OS') = ", os_singleton) - # Can we check what methods are available on the singleton? - if os_singleton: - print(" OS singleton has 'execute': ", os_singleton.has_method("execute")) - print(" OS singleton has 'get_environment': ", os_singleton.has_method("get_environment")) - -func probe_expression_engine(): - print("--- Expression engine ---") - # Godot has an Expression class for evaluating expressions safely - 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) - # Can Expression access OS? - 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) - # Expression with base_instance - 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) - # Can Expression call arbitrary methods? - 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) - # Can Expression access the scene tree? - 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 ---") - # Engine singletons are the global objects like OS, Engine, etc. - var singletons = Engine.get_singletons() - print("Engine.get_singletons() count: ", singletons.size()) - for s in singletons: - print(" singleton: ", s) - # Can we remove a singleton? - print("Engine has 'remove_singleton': ", Engine.has_method("remove_singleton")) - print("Engine has 'add_singleton': ", Engine.has_method("add_singleton")) - # Can we get the main loop? - print("Engine.get_main_loop() = ", Engine.get_main_loop()) - # Can we get the 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(): - print("--- Worker threads ---") - # Can we run scripts in worker threads? - var thread = Thread.new() - print(" Thread.new() = ", thread) - # WorkerThreadPool - print(" WorkerThreadPool has 'add_task': ", WorkerThreadPool.has_method("add_task")) - # Can we isolate a script in a thread? - print(" Thread available: ", Thread != null) - -func probe_project_settings(): - print("--- Project settings ---") - # Check for any sandbox-related 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) - # Check if there's a way to disable specific features - 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")) - # Can we set project settings at runtime? - print(" ProjectSettings has 'set_setting': ", ProjectSettings.has_method("set_setting")) - print(" ProjectSettings has 'save': ", ProjectSettings.has_method("save")) diff --git a/sandbox_probe/probe4.tscn b/sandbox_probe/probe4.tscn deleted file mode 100644 index 9ed9a3d..0000000 --- a/sandbox_probe/probe4.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://probe4.gd" id="1"] - -[node name="Probe4" type="Node"] -script = ExtResource("1") diff --git a/sandbox_probe/probe5.gd b/sandbox_probe/probe5.gd deleted file mode 100644 index 436e359..0000000 --- a/sandbox_probe/probe5.gd +++ /dev/null @@ -1,221 +0,0 @@ -extends Node - -func _ready(): - print("=== SANDBOX PROBE 5b: Corrected Sandboxing Mechanisms (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_script_restrictions() - probe_allowlist() - print("=== SANDBOX PROBE 5b 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")) - print(" GDScript instance has 'get_instance_base_type': ", gd.has_method("get_instance_base_type")) - # Can we get the list of static methods on GDScript class? - var gd_methods = GDScript.get_method_list() - var gd_method_names = [] - for m in gd_methods: - gd_method_names.append(String(m.name)) - print(" GDScript static methods: ", gd_method_names) - -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) - # Can we enumerate ClassDB methods to see what's available? - var classdb_methods = ClassDB.get_method_list() - var classdb_method_names = [] - for m in classdb_methods: - classdb_method_names.append(String(m.name)) - print(" ClassDB methods: ", classdb_method_names) - -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()) - # Can we enumerate singletons via Engine instance methods? - var engine_methods = Engine.get_method_list() - var engine_method_names = [] - for m in engine_methods: - engine_method_names.append(String(m.name)) - print(" Engine instance methods: ", engine_method_names) - -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_script_restrictions(): - print("--- Script restrictions ---") - var gd = GDScript.new() - gd.source_code = "extends RefCounted\nfunc evil():\n\treturn OS.get_environment('HOME')\n" - var err = gd.reload() - print(" GDScript with OS access reload err=", err) - if err == OK: - var inst = gd.new() - print(" evil() result: ", inst.call("evil")) - 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")) - # Can we check what identifiers a script references via the analyzer? - print(" GDScript has 'get_script_constant_map': ", gd.has_method("get_script_constant_map")) - # Can we get the list of member variables? - print(" GDScript has 'get_script_property_list': ", gd.has_method("get_script_property_list")) - -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) - # Can we use a tokenizer/lexer to parse GDScript? - # GDScriptLanguage is NOT exposed to GDScript (confirmed by parse error) - print(" GDScriptLanguage NOT exposed to GDScript (parse error confirmed)") - # Can we use the Expression engine to validate syntax? - var expr = Expression.new() - var err = expr.parse("var x = 1 + 2") - print(" Expression.parse('var x = 1 + 2') err=", err) - err = expr.parse("OS.execute('rm', ['-rf', '/'])") - print(" Expression.parse('OS.execute...') err=", err) - if err == OK: - var result = expr.execute() - print(" Expression.execute() = ", result) diff --git a/sandbox_probe/probe5.tscn b/sandbox_probe/probe5.tscn deleted file mode 100644 index 4ba7895..0000000 --- a/sandbox_probe/probe5.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://probe5.gd" id="1"] - -[node name="Probe5" type="Node"] -script = ExtResource("1") diff --git a/sandbox_probe/probe_scriptserver_check.gd b/sandbox_probe/probe_scriptserver_check.gd deleted file mode 100644 index b5425c6..0000000 --- a/sandbox_probe/probe_scriptserver_check.gd +++ /dev/null @@ -1,6 +0,0 @@ -extends SceneTree -func _init(): - print("ScriptServer class exists: ", ClassDB.class_exists("ScriptServer")) - var sv = SubViewport.new() - print("SubViewport instantiated: ", sv != null) - quit() diff --git a/sandbox_probe/project.godot b/sandbox_probe/project.godot index e145707..79c5ba9 100644 --- a/sandbox_probe/project.godot +++ b/sandbox_probe/project.godot @@ -4,5 +4,5 @@ config_version=5 [application] config/name="sandbox_probe" -run/main_scene="res://probe5.tscn" +run/main_scene="res://probe6.tscn" config/features=PackedStringArray("4.6")