From 910eeef7b4425aac126407166e7a189bda84bd3d Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Wed, 7 Oct 2026 18:03:22 +0800 Subject: [PATCH] fix: prompt for permission when plugin tries to create a thread --- src/slic3r/plugin/PluginAuditManager.cpp | 12 ++++++++++++ src/slic3r/plugin/PluginFsUtils.cpp | 2 ++ src/slic3r/plugin/PluginFsUtils.hpp | 1 + tests/slic3rutils/test_plugin_install.cpp | 2 ++ 4 files changed, 17 insertions(+) diff --git a/src/slic3r/plugin/PluginAuditManager.cpp b/src/slic3r/plugin/PluginAuditManager.cpp index 1fe8837c5c..3361c3d8d4 100644 --- a/src/slic3r/plugin/PluginAuditManager.cpp +++ b/src/slic3r/plugin/PluginAuditManager.cpp @@ -115,6 +115,9 @@ static const std::unordered_map audit_event_cat {"subprocess.Popen", AuditEventCategory::ProcessCreate}, {"_winapi.CreateProcess", AuditEventCategory::ProcessCreate}, {"_posixsubprocess.fork_exec", AuditEventCategory::ProcessCreate}, + + // threading + {"_thread.start_new_thread", AuditEventCategory::Threading}, }; // Returns the category event_name belongs to, or AuditEventCategory::None when it isn't audited. @@ -735,6 +738,12 @@ std::vector audit_targets(const std::string& event_name, AuditEvent } return targets; } + case AuditEventCategory::Threading: + // Thread creation exposes no user-supplied target. Use a fixed sentinel so the grant + // persists per plugin: the permission list matches targets by exact string, and the + // started function's repr embeds an address that changes every run. + targets.emplace_back("thread"); + return targets; default: break; } @@ -761,6 +770,7 @@ std::vector* permission_list_for(AuditEventCategory category, Plugi case AuditEventCategory::Http: return &permissions.network_http; case AuditEventCategory::Socket: return &permissions.network_socket; case AuditEventCategory::ProcessCreate: return &permissions.process; + case AuditEventCategory::Threading: return &permissions.threading; default: return nullptr; } } @@ -832,6 +842,8 @@ wxString audit_message(AuditEventCategory category, const wxString& plugin_name, return wxString::Format(_L("Plugin \"%s\" is requesting to open a network connection to:\n%s"), plugin_name, target_list); case AuditEventCategory::ProcessCreate: return wxString::Format(_L("Plugin \"%s\" is requesting to run the following command(s):\n%s"), plugin_name, target_list); + case AuditEventCategory::Threading: + return wxString::Format(_L("Plugin \"%s\" is requesting permission to create a thread."), plugin_name); default: return wxString::Format(_L("Plugin \"%s\" is requesting permission for the Python audit event \"%s\"."), plugin_name, event_name); } diff --git a/src/slic3r/plugin/PluginFsUtils.cpp b/src/slic3r/plugin/PluginFsUtils.cpp index e3b1bc3bee..7956d01bb0 100644 --- a/src/slic3r/plugin/PluginFsUtils.cpp +++ b/src/slic3r/plugin/PluginFsUtils.cpp @@ -807,6 +807,7 @@ bool read_install_state(const boost::filesystem::path& plugin_dir, PluginInstall read_string_list("network_http", parsed.permissions.network_http); read_string_list("network_socket", parsed.permissions.network_socket); read_string_list("process", parsed.permissions.process); + read_string_list("threading", parsed.permissions.threading); } if (state.contains("enabled") && state["enabled"].is_boolean()) @@ -850,6 +851,7 @@ bool write_install_state(const boost::filesystem::path& plugin_dir, const Plugin {"network_http", state.permissions.network_http}, {"network_socket", state.permissions.network_socket}, {"process", state.permissions.process}, + {"threading", state.permissions.threading}, }; nlohmann::json capabilities = nlohmann::json::array(); diff --git a/src/slic3r/plugin/PluginFsUtils.hpp b/src/slic3r/plugin/PluginFsUtils.hpp index 7e552b7896..bd335373d0 100644 --- a/src/slic3r/plugin/PluginFsUtils.hpp +++ b/src/slic3r/plugin/PluginFsUtils.hpp @@ -92,6 +92,7 @@ struct PluginPermissions std::vector network_http; std::vector network_socket; std::vector process; + std::vector threading; }; struct PluginInstallState { diff --git a/tests/slic3rutils/test_plugin_install.cpp b/tests/slic3rutils/test_plugin_install.cpp index d79519c236..b860486979 100644 --- a/tests/slic3rutils/test_plugin_install.cpp +++ b/tests/slic3rutils/test_plugin_install.cpp @@ -122,6 +122,7 @@ TEST_CASE("install-state sidecar is the source of truth for a cloud plugin's ins state.permissions.network_http = {"https://api.example.com"}; state.permissions.network_socket = {"192.168.45.6:443"}; state.permissions.process = {"/usr/bin/curl"}; + state.permissions.threading = {"thread"}; REQUIRE(write_install_state(plugin_dir, state)); // Permission data is persisted in the same sidecar as the installation metadata. @@ -132,6 +133,7 @@ TEST_CASE("install-state sidecar is the source of truth for a cloud plugin's ins CHECK(persisted.permissions.network_http == state.permissions.network_http); CHECK(persisted.permissions.network_socket == state.permissions.network_socket); CHECK(persisted.permissions.process == state.permissions.process); + CHECK(persisted.permissions.threading == state.permissions.threading); // Reading the sidecar back onto a freshly-scanned descriptor (whose header version is still // 1.0.0) must surface the cloud-installed 1.2.0. This is what lets update_cloud_metadata compare