mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-08 16:21:14 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f140a1208 | ||
|
|
cb7d2c698d |
@@ -115,9 +115,6 @@ static const std::unordered_map<std::string, AuditEventCategory> audit_event_cat
|
|||||||
{"subprocess.Popen", AuditEventCategory::ProcessCreate},
|
{"subprocess.Popen", AuditEventCategory::ProcessCreate},
|
||||||
{"_winapi.CreateProcess", AuditEventCategory::ProcessCreate},
|
{"_winapi.CreateProcess", AuditEventCategory::ProcessCreate},
|
||||||
{"_posixsubprocess.fork_exec", AuditEventCategory::ProcessCreate},
|
{"_posixsubprocess.fork_exec", AuditEventCategory::ProcessCreate},
|
||||||
|
|
||||||
// processreplace: exec* replaces the current process image rather than spawning a child
|
|
||||||
{"os.exec", AuditEventCategory::ProcessReplace},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the category event_name belongs to, or AuditEventCategory::None when it isn't audited.
|
// Returns the category event_name belongs to, or AuditEventCategory::None when it isn't audited.
|
||||||
@@ -711,7 +708,6 @@ static const std::unordered_map<std::string, std::vector<Py_ssize_t>> audit_targ
|
|||||||
{"pty.spawn", {0}},
|
{"pty.spawn", {0}},
|
||||||
{"_winapi.CreateProcess", {1, 0}},
|
{"_winapi.CreateProcess", {1, 0}},
|
||||||
{"_posixsubprocess.fork_exec", {0}},
|
{"_posixsubprocess.fork_exec", {0}},
|
||||||
{"os.exec", {0}},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AuditEventCategory open_category(PyObject* args)
|
AuditEventCategory open_category(PyObject* args)
|
||||||
@@ -765,7 +761,6 @@ std::vector<std::string>* permission_list_for(AuditEventCategory category, Plugi
|
|||||||
case AuditEventCategory::Http: return &permissions.network_http;
|
case AuditEventCategory::Http: return &permissions.network_http;
|
||||||
case AuditEventCategory::Socket: return &permissions.network_socket;
|
case AuditEventCategory::Socket: return &permissions.network_socket;
|
||||||
case AuditEventCategory::ProcessCreate: return &permissions.process;
|
case AuditEventCategory::ProcessCreate: return &permissions.process;
|
||||||
case AuditEventCategory::ProcessReplace: return &permissions.process;
|
|
||||||
default: return nullptr;
|
default: return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -837,8 +832,6 @@ 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);
|
return wxString::Format(_L("Plugin \"%s\" is requesting to open a network connection to:\n%s"), plugin_name, target_list);
|
||||||
case AuditEventCategory::ProcessCreate:
|
case AuditEventCategory::ProcessCreate:
|
||||||
return wxString::Format(_L("Plugin \"%s\" is requesting to run the following command(s):\n%s"), plugin_name, target_list);
|
return wxString::Format(_L("Plugin \"%s\" is requesting to run the following command(s):\n%s"), plugin_name, target_list);
|
||||||
case AuditEventCategory::ProcessReplace:
|
|
||||||
return wxString::Format(_L("Plugin \"%s\" is requesting to replace the running application with:\n%s"), plugin_name, target_list);
|
|
||||||
default:
|
default:
|
||||||
return wxString::Format(_L("Plugin \"%s\" is requesting permission for the Python audit event \"%s\"."), plugin_name, event_name);
|
return wxString::Format(_L("Plugin \"%s\" is requesting permission for the Python audit event \"%s\"."), plugin_name, event_name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ enum class AuditEventCategory {
|
|||||||
Http,
|
Http,
|
||||||
Socket,
|
Socket,
|
||||||
ProcessCreate,
|
ProcessCreate,
|
||||||
ProcessReplace,
|
|
||||||
Threading,
|
Threading,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,19 @@ extern const char* const INSTALL_STATE_FILE;
|
|||||||
// Plugin config and orca.host.ui payloads both cross the boundary as plain JSON-compatible
|
// Plugin config and orca.host.ui payloads both cross the boundary as plain JSON-compatible
|
||||||
// values, so both go through these.
|
// values, so both go through these.
|
||||||
|
|
||||||
inline pybind11::object json_to_py(const nlohmann::json& j)
|
// Maximum nesting depth for JSON <-> Python conversion. A self-referential or pathologically
|
||||||
|
// deep value would otherwise recurse until the native C stack overflows, an uncatchable crash;
|
||||||
|
// past this bound we raise instead. 200 is far beyond any legitimate plugin config or UI payload.
|
||||||
|
inline constexpr int kMaxJsonConversionDepth = 200;
|
||||||
|
|
||||||
|
inline pybind11::object json_to_py(const nlohmann::json& j, int depth = 0)
|
||||||
{
|
{
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
if (depth > kMaxJsonConversionDepth)
|
||||||
|
throw py::value_error("Plugin JSON value nested too deeply");
|
||||||
|
|
||||||
switch (j.type()) {
|
switch (j.type()) {
|
||||||
case json::value_t::null: return py::none();
|
case json::value_t::null: return py::none();
|
||||||
case json::value_t::boolean: return py::bool_(j.get<bool>());
|
case json::value_t::boolean: return py::bool_(j.get<bool>());
|
||||||
@@ -40,24 +48,27 @@ inline pybind11::object json_to_py(const nlohmann::json& j)
|
|||||||
case json::value_t::array: {
|
case json::value_t::array: {
|
||||||
py::list lst;
|
py::list lst;
|
||||||
for (const auto& e : j)
|
for (const auto& e : j)
|
||||||
lst.append(json_to_py(e));
|
lst.append(json_to_py(e, depth + 1));
|
||||||
return lst;
|
return lst;
|
||||||
}
|
}
|
||||||
case json::value_t::object: {
|
case json::value_t::object: {
|
||||||
py::dict d;
|
py::dict d;
|
||||||
for (auto it = j.begin(); it != j.end(); ++it)
|
for (auto it = j.begin(); it != j.end(); ++it)
|
||||||
d[py::str(it.key())] = json_to_py(it.value());
|
d[py::str(it.key())] = json_to_py(it.value(), depth + 1);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
default: return py::none();
|
default: return py::none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline nlohmann::json py_to_json(const pybind11::handle& o)
|
inline nlohmann::json py_to_json(const pybind11::handle& o, int depth = 0)
|
||||||
{
|
{
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
if (depth > kMaxJsonConversionDepth)
|
||||||
|
throw py::value_error("Plugin value nested too deeply (possible cycle)");
|
||||||
|
|
||||||
if (o.is_none())
|
if (o.is_none())
|
||||||
return json(nullptr);
|
return json(nullptr);
|
||||||
if (py::isinstance<py::bool_>(o)) // bool before int (bool subclasses int in Python)
|
if (py::isinstance<py::bool_>(o)) // bool before int (bool subclasses int in Python)
|
||||||
@@ -73,13 +84,13 @@ inline nlohmann::json py_to_json(const pybind11::handle& o)
|
|||||||
if (py::isinstance<py::dict>(o)) {
|
if (py::isinstance<py::dict>(o)) {
|
||||||
json obj = json::object();
|
json obj = json::object();
|
||||||
for (auto item : py::reinterpret_borrow<py::dict>(o))
|
for (auto item : py::reinterpret_borrow<py::dict>(o))
|
||||||
obj[py::str(item.first).cast<std::string>()] = py_to_json(item.second);
|
obj[py::str(item.first).cast<std::string>()] = py_to_json(item.second, depth + 1);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
if (py::isinstance<py::list>(o) || py::isinstance<py::tuple>(o)) {
|
if (py::isinstance<py::list>(o) || py::isinstance<py::tuple>(o)) {
|
||||||
json arr = json::array();
|
json arr = json::array();
|
||||||
for (auto e : o)
|
for (auto e : o)
|
||||||
arr.push_back(py_to_json(e));
|
arr.push_back(py_to_json(e, depth + 1));
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
return py::str(o).cast<std::string>(); // fallback: str()
|
return py::str(o).cast<std::string>(); // fallback: str()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_plugin_sort.cpp
|
test_plugin_sort.cpp
|
||||||
test_plugin_cloud_metadata.cpp
|
test_plugin_cloud_metadata.cpp
|
||||||
test_plugin_audit.cpp
|
test_plugin_audit.cpp
|
||||||
|
test_plugin_json_depth.cpp
|
||||||
test_shortcuts.cpp
|
test_shortcuts.cpp
|
||||||
test_file_url.cpp
|
test_file_url.cpp
|
||||||
test_user_manager.cpp
|
test_user_manager.cpp
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#include <catch2/catch_all.hpp>
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include <slic3r/plugin/PluginFsUtils.hpp>
|
||||||
|
#include <slic3r/plugin/PluginManager.hpp>
|
||||||
|
#include <slic3r/plugin/PythonInterpreter.hpp>
|
||||||
|
|
||||||
|
#include "plugin_test_utils.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <pybind11/embed.h>
|
||||||
|
#include <pybind11/gil.h>
|
||||||
|
#include <pybind11/pytypes.h>
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Brings the embedded interpreter up for one test and tears it down before boost::log does,
|
||||||
|
// mirroring the ScopedPluginManager idiom in the other plugin tests.
|
||||||
|
struct ScopedPluginManager
|
||||||
|
{
|
||||||
|
ScopedDataDir python_data_dir{"plugin-json-depth"};
|
||||||
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
~ScopedPluginManager()
|
||||||
|
{
|
||||||
|
PluginManager::instance().shutdown();
|
||||||
|
PythonInterpreter::instance().shutdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("py_to_json raises instead of overflowing on pathologically deep input", "[PluginHost][Python]")
|
||||||
|
{
|
||||||
|
ScopedPluginManager manager;
|
||||||
|
REQUIRE(manager.initialized);
|
||||||
|
namespace py = pybind11;
|
||||||
|
py::gil_scoped_acquire gil;
|
||||||
|
|
||||||
|
// [[[ ... 0 ... ]]] nested 300 deep: past the 200 conversion-depth cap, but shallow enough
|
||||||
|
// that the pre-fix code returns without crashing, so a regression fails cleanly rather than
|
||||||
|
// taking the process down. Built in C++ so the test does not depend on Python builtins.
|
||||||
|
py::object deep = py::int_(0);
|
||||||
|
for (int i = 0; i < 300; ++i) {
|
||||||
|
py::list wrapper;
|
||||||
|
wrapper.append(deep);
|
||||||
|
deep = std::move(wrapper);
|
||||||
|
}
|
||||||
|
CHECK_THROWS_AS(py_to_json(deep), std::exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("py_to_json still converts reasonably nested input", "[PluginHost][Python]")
|
||||||
|
{
|
||||||
|
ScopedPluginManager manager;
|
||||||
|
REQUIRE(manager.initialized);
|
||||||
|
namespace py = pybind11;
|
||||||
|
py::gil_scoped_acquire gil;
|
||||||
|
|
||||||
|
py::dict d;
|
||||||
|
d["a"] = py::int_(1);
|
||||||
|
py::list inner;
|
||||||
|
inner.append(py::str("x"));
|
||||||
|
inner.append(py::int_(2));
|
||||||
|
d["b"] = inner;
|
||||||
|
|
||||||
|
const nlohmann::json j = py_to_json(d);
|
||||||
|
CHECK(j.at("a").get<std::int64_t>() == 1);
|
||||||
|
CHECK(j.at("b").at(0).get<std::string>() == "x");
|
||||||
|
CHECK(j.at("b").at(1).get<std::int64_t>() == 2);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user