mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-20 14:08:20 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0c15879de |
No files matched your search
@@ -435,7 +435,7 @@ void CalibrationWizard::cache_preset_info(MachineObject *obj, float nozzle_dia,
|
||||
if (obj->is_multi_extruders()) {
|
||||
int ams_id, slot_id, tray_id;
|
||||
get_tray_ams_and_slot_id(curr_obj, result.tray_id, ams_id, slot_id, tray_id);
|
||||
result.extruder_id = preset_page->get_extruder_id_by_tray_id(result.tray_id);
|
||||
result.extruder_id = preset_page->get_extruder_id(ams_id);
|
||||
result.nozzle_diameter = preset_page->get_nozzle_diameter(result.extruder_id);
|
||||
}
|
||||
else {
|
||||
@@ -753,7 +753,7 @@ void PressureAdvanceWizard::on_cali_start()
|
||||
|
||||
X1CCalibInfos::X1CCalibInfo calib_info;
|
||||
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, calib_info.tray_id);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
|
||||
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
|
||||
@@ -792,7 +792,7 @@ void PressureAdvanceWizard::on_cali_start()
|
||||
CalibInfo calib_info;
|
||||
calib_info.dev_id = curr_obj->get_dev_id();
|
||||
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
|
||||
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
|
||||
@@ -877,7 +877,7 @@ void PressureAdvanceWizard::on_cali_start()
|
||||
calib_info.dev_id = curr_obj->get_dev_id();
|
||||
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
|
||||
calib_info.index = preset_page->get_index_by_tray_id(item.first);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
|
||||
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
|
||||
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
|
||||
@@ -1252,7 +1252,7 @@ void FlowRateWizard::on_cali_start(CaliPresetStage stage, float cali_value, Flow
|
||||
X1CCalibInfos::X1CCalibInfo calib_info;
|
||||
calib_info.tray_id = item.first;
|
||||
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, calib_info.tray_id);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
|
||||
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
|
||||
@@ -1308,15 +1308,7 @@ void FlowRateWizard::on_cali_start(CaliPresetStage stage, float cali_value, Flow
|
||||
int selected_tray_id = 0;
|
||||
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
|
||||
calib_info.select_ams = std::to_string(selected_tray_id);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
|
||||
if (from_page == FlowRatioCaliSource::FROM_COARSE_PAGE) {
|
||||
// The fine pass must run on the extruder the coarse pass used. The cache records it;
|
||||
// the preset page's selection may have changed or been reset since.
|
||||
const auto cached = get_cached_selected_filament_for_multi_extruder(curr_obj);
|
||||
const auto it = cached.find(selected_filaments.begin()->first);
|
||||
if (it != cached.end())
|
||||
calib_info.extruder_id = it->second.extruder_id;
|
||||
}
|
||||
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
|
||||
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
|
||||
@@ -1732,7 +1724,7 @@ void MaxVolumetricSpeedWizard::on_cali_start()
|
||||
int selected_tray_id = 0;
|
||||
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
|
||||
calib_info.select_ams = std::to_string(selected_tray_id);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
|
||||
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
|
||||
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
|
||||
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
|
||||
|
||||
@@ -587,23 +587,8 @@ void CalibrationPresetPage::on_sys_color_changed()
|
||||
m_ams_sync_button->msw_rescale();
|
||||
}
|
||||
|
||||
int CalibrationPresetPage::get_extruder_id_by_tray_id(int tray_id)
|
||||
int CalibrationPresetPage::get_extruder_id(int ams_id)
|
||||
{
|
||||
// An AMS behind a Filament Track Switch feeds both extruders, so the extruder is the one
|
||||
// whose panel the filament was selected in.
|
||||
if (curr_obj && curr_obj->is_multi_extruders()) {
|
||||
for (FilamentComboBox *fcb : get_selected_filament_combobox()) {
|
||||
if (fcb->get_tray_id() != tray_id)
|
||||
continue;
|
||||
const bool is_deputy = std::find(m_deputy_filament_comboBox_list.begin(), m_deputy_filament_comboBox_list.end(), fcb) !=
|
||||
m_deputy_filament_comboBox_list.end();
|
||||
return is_deputy ? DEPUTY_EXTRUDER_ID : MAIN_EXTRUDER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
int ams_id = tray_id / 4, slot_id, unused_tray_id;
|
||||
if (curr_obj)
|
||||
get_tray_ams_and_slot_id(curr_obj, tray_id, ams_id, slot_id, unused_tray_id);
|
||||
if (m_ams_id_to_extruder_id_map.find(ams_id) != m_ams_id_to_extruder_id_map.end()) {
|
||||
return m_ams_id_to_extruder_id_map[ams_id];
|
||||
}
|
||||
@@ -881,18 +866,6 @@ static void fill_nozzle_volume_combo(ComboBox *combo)
|
||||
select_nozzle_volume(combo, NozzleVolumeType::nvtStandard);
|
||||
}
|
||||
|
||||
static DynamicPrintConfig empty_tray_config()
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.set_key_value("filament_id", new ConfigOptionStrings{""});
|
||||
config.set_key_value("tag_uid", new ConfigOptionStrings{""});
|
||||
config.set_key_value("filament_type", new ConfigOptionStrings{""});
|
||||
config.set_key_value("tray_name", new ConfigOptionStrings{""});
|
||||
config.set_key_value("filament_colour", new ConfigOptionStrings{""});
|
||||
config.set_key_value("filament_exist", new ConfigOptionBools{false});
|
||||
return config;
|
||||
}
|
||||
|
||||
void CalibrationPresetPage::init_selection_values()
|
||||
{
|
||||
// init nozzle diameter and nozzle volume
|
||||
@@ -2299,36 +2272,27 @@ void CalibrationPresetPage::sync_ams_info(MachineObject* obj)
|
||||
}
|
||||
}
|
||||
|
||||
// init virtual tray info. With a Filament Track Switch installed the external spools feed
|
||||
// through the switch, so they can't be calibrated as their own trays.
|
||||
if (obj->GetFilaSwitch()->IsInstalled()) {
|
||||
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = empty_tray_config();
|
||||
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = empty_tray_config();
|
||||
} else {
|
||||
if (full_filament_ams_list.find(VIRTUAL_TRAY_MAIN_ID) != full_filament_ams_list.end()) {
|
||||
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = full_filament_ams_list[VIRTUAL_TRAY_MAIN_ID];
|
||||
}
|
||||
// init virtual tray info
|
||||
if (full_filament_ams_list.find(VIRTUAL_TRAY_MAIN_ID) != full_filament_ams_list.end()) {
|
||||
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = full_filament_ams_list[VIRTUAL_TRAY_MAIN_ID];
|
||||
}
|
||||
|
||||
if (full_filament_ams_list.find(VIRTUAL_TRAY_DEPUTY_ID) != full_filament_ams_list.end()) {
|
||||
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = full_filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID];
|
||||
}
|
||||
if (full_filament_ams_list.find(VIRTUAL_TRAY_DEPUTY_ID) != full_filament_ams_list.end()) {
|
||||
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = full_filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID];
|
||||
}
|
||||
|
||||
// update filament from panel, display only obj has ams
|
||||
// update multi ams panel, display only obj has multi ams
|
||||
if (obj->HasAms()) {
|
||||
if (obj->is_multi_extruders()) {
|
||||
// An AMS behind a Filament Track Switch is bound to both extruders and appears in both panels.
|
||||
bool main_done = false;
|
||||
bool deputy_done = false;
|
||||
for (auto &ams_item : obj->GetFilaSystem()->GetAmsList()) {
|
||||
const auto &extruder_set = ams_item.second->GetBindedExtruderSet();
|
||||
if (!main_done && extruder_set.count(MAIN_EXTRUDER_ID) != 0) {
|
||||
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), MAIN_EXTRUDER_ID);
|
||||
if (ams_item.second->GetExtruderId() == 0 && !main_done) {
|
||||
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), ams_item.second->GetExtruderId());
|
||||
main_done = true;
|
||||
}
|
||||
if (!deputy_done && extruder_set.count(DEPUTY_EXTRUDER_ID) != 0) {
|
||||
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), DEPUTY_EXTRUDER_ID);
|
||||
} else if (ams_item.second->GetExtruderId() == 1 && !deputy_done) {
|
||||
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), ams_item.second->GetExtruderId());
|
||||
deputy_done = true;
|
||||
}
|
||||
}
|
||||
@@ -2369,11 +2333,10 @@ void CalibrationPresetPage::sync_ams_info(MachineObject* obj)
|
||||
if (ams->second->IsExist()
|
||||
&& info.parse_ams_info(obj, ams->second, obj->GetFilaSystem()->IsDetectRemainEnabled(), obj->is_support_ams_humidity)) {
|
||||
ams_info.push_back(info);
|
||||
for (int extruder_id : ams->second->GetBindedExtruderSet()) {
|
||||
if (extruder_id == MAIN_EXTRUDER_ID)
|
||||
main_ams_info.push_back(info);
|
||||
else
|
||||
deputy_ams_info.push_back(info);
|
||||
if (info.nozzle_id == 0) {
|
||||
main_ams_info.push_back(info);
|
||||
} else {
|
||||
deputy_ams_info.push_back(info);
|
||||
}
|
||||
}
|
||||
m_ams_id_to_extruder_id_map[stoi(info.ams_id)] = info.nozzle_id;
|
||||
@@ -2621,7 +2584,13 @@ void CalibrationPresetPage::update_multi_extruder_filament_combobox(const std::s
|
||||
}
|
||||
}
|
||||
|
||||
DynamicPrintConfig empty_config = empty_tray_config();
|
||||
DynamicPrintConfig empty_config;
|
||||
empty_config.set_key_value("filament_id", new ConfigOptionStrings{""});
|
||||
empty_config.set_key_value("tag_uid", new ConfigOptionStrings{""});
|
||||
empty_config.set_key_value("filament_type", new ConfigOptionStrings{""});
|
||||
empty_config.set_key_value("tray_name", new ConfigOptionStrings{""});
|
||||
empty_config.set_key_value("filament_colour", new ConfigOptionStrings{""});
|
||||
empty_config.set_key_value("filament_exist", new ConfigOptionBools{false});
|
||||
|
||||
if (filament_ams_list.empty()) return;
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ public:
|
||||
void msw_rescale() override;
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
int get_extruder_id_by_tray_id(int tray_id);
|
||||
int get_extruder_id(int ams_id);
|
||||
float get_nozzle_diameter(int extruder_id) const;
|
||||
NozzleVolumeType get_nozzle_volume_type(int extruder_id) const;
|
||||
ExtruderType get_extruder_type(int extruder_id) const;
|
||||
|
||||
@@ -183,47 +183,52 @@ static const char* category_name(AuditEventCategory category)
|
||||
// Path safety
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool is_inside_allowed_root(const boost::filesystem::path& candidate, const boost::filesystem::path& allowed_root)
|
||||
namespace {
|
||||
|
||||
// Canonicalize a path for containment comparison. weakly_canonical resolves symlinks but does
|
||||
// NOT require the path to exist — it canonicalizes the prefix that exists and appends the
|
||||
// non-existing tail lexically.
|
||||
boost::filesystem::path canonical_for_comparison(const boost::filesystem::path& path)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
boost::system::error_code ec;
|
||||
|
||||
// Canonicalize both paths. weakly_canonical resolves symlinks but does
|
||||
// NOT require the path to exist — it canonicalizes the prefix that exists
|
||||
// and appends the non-existing tail lexically.
|
||||
fs::path canon_candidate = fs::weakly_canonical(candidate, ec);
|
||||
fs::path canon = fs::weakly_canonical(path, ec);
|
||||
if (ec) {
|
||||
// Fall back to lexically_normal + absolute
|
||||
canon_candidate = fs::absolute(candidate, ec).lexically_normal();
|
||||
canon = fs::absolute(path, ec).lexically_normal();
|
||||
if (ec)
|
||||
canon_candidate = candidate;
|
||||
canon = path;
|
||||
}
|
||||
return canon;
|
||||
}
|
||||
|
||||
fs::path canon_root = fs::weakly_canonical(allowed_root, ec);
|
||||
if (ec) {
|
||||
canon_root = fs::absolute(allowed_root, ec).lexically_normal();
|
||||
if (ec)
|
||||
canon_root = allowed_root;
|
||||
}
|
||||
|
||||
// Component-wise comparison: the root must be a prefix of candidate,
|
||||
// and the next component must not be ".." or missing.
|
||||
auto cand_it = canon_candidate.begin();
|
||||
auto cand_end = canon_candidate.end();
|
||||
auto root_it = canon_root.begin();
|
||||
auto root_end = canon_root.end();
|
||||
|
||||
const auto same_component = [](const fs::path& lhs, const fs::path& rhs) {
|
||||
bool same_path_component(const boost::filesystem::path& lhs, const boost::filesystem::path& rhs)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return boost::algorithm::iequals(lhs.native(), rhs.native());
|
||||
return boost::algorithm::iequals(lhs.native(), rhs.native());
|
||||
#else
|
||||
return lhs == rhs;
|
||||
return lhs == rhs;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
// Component-wise containment: the whole of canonical_root must be a prefix of
|
||||
// canonical_candidate, and the remaining components must not traverse upward. Both arguments are
|
||||
// expected to come from canonical_for_comparison(). On a match, suffix receives the candidate's
|
||||
// components below the root (empty when the two paths are equal).
|
||||
bool suffix_inside_root(const boost::filesystem::path& canonical_candidate,
|
||||
const boost::filesystem::path& canonical_root,
|
||||
boost::filesystem::path& suffix)
|
||||
{
|
||||
suffix.clear();
|
||||
|
||||
auto cand_it = canonical_candidate.begin();
|
||||
auto cand_end = canonical_candidate.end();
|
||||
auto root_it = canonical_root.begin();
|
||||
auto root_end = canonical_root.end();
|
||||
|
||||
// Consume matching components
|
||||
while (root_it != root_end && cand_it != cand_end && same_component(*root_it, *cand_it)) {
|
||||
while (root_it != root_end && cand_it != cand_end && same_path_component(*root_it, *cand_it)) {
|
||||
++root_it;
|
||||
++cand_it;
|
||||
}
|
||||
@@ -238,9 +243,19 @@ bool is_inside_allowed_root(const boost::filesystem::path& candidate, const boos
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; cand_it != cand_end; ++cand_it)
|
||||
suffix /= *cand_it;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool is_inside_allowed_root(const boost::filesystem::path& candidate, const boost::filesystem::path& allowed_root)
|
||||
{
|
||||
boost::filesystem::path suffix;
|
||||
return suffix_inside_root(canonical_for_comparison(candidate), canonical_for_comparison(allowed_root), suffix);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ScopedPluginAuditContext
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -410,7 +425,8 @@ std::vector<std::string> PluginAuditManager::default_denied_path_keywords()
|
||||
// it happens to live inside an otherwise-allowed root (e.g. the bundled TLS client cert at
|
||||
// resources_dir()/cert/..., which would become reachable the moment resources_dir() is
|
||||
// granted as a read-only allowed root). Match as whole path components, not substrings, so
|
||||
// imports such as numpy/__config__.py and stdlib configparser.py remain usable.
|
||||
// imports such as numpy/__config__.py and stdlib configparser.py remain usable. Inside
|
||||
// data_dir() they match below that root only; see is_denied_path_keyword().
|
||||
return {"secret", "secrets", "cert", "certs", "certificate", "certificates", "conf", "config"};
|
||||
}
|
||||
|
||||
@@ -429,19 +445,29 @@ bool PluginAuditManager::is_denied_path_keyword(const boost::filesystem::path& c
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
boost::system::error_code ec;
|
||||
fs::path canon = fs::weakly_canonical(candidate, ec);
|
||||
if (ec) {
|
||||
canon = fs::absolute(candidate, ec).lexically_normal();
|
||||
if (ec)
|
||||
canon = candidate;
|
||||
}
|
||||
const fs::path canon = canonical_for_comparison(candidate);
|
||||
|
||||
// The app's data folder can itself sit below a component named like one of the keywords
|
||||
// without being one: Flatpak keeps it under <home>/.var/app/<app-id>/config/OrcaSlicer, and
|
||||
// the sandbox's home layout is not ours to change. A keyword above data_dir() therefore says
|
||||
// nothing about what a plugin may reach inside it, so inside data_dir() the keywords apply to
|
||||
// the suffix below data_dir() only. Everywhere else -- resources_dir() with the bundled
|
||||
// certificates, a plugin's own folders, arbitrary files -- the whole path is still checked,
|
||||
// and so is a keyword under data_dir() itself.
|
||||
fs::path canonical_data_dir;
|
||||
if (!data_dir().empty())
|
||||
canonical_data_dir = canonical_for_comparison(data_dir());
|
||||
|
||||
fs::path scope = canon;
|
||||
fs::path suffix;
|
||||
if (!canonical_data_dir.empty() && suffix_inside_root(canon, canonical_data_dir, suffix))
|
||||
scope = suffix;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_denied_path_keywords.empty())
|
||||
return false;
|
||||
|
||||
for (const auto& component : canon) {
|
||||
for (const auto& component : scope) {
|
||||
std::string name = component.string();
|
||||
if (name.empty())
|
||||
continue;
|
||||
@@ -1295,10 +1321,12 @@ void PluginAuditManager::install_hook()
|
||||
for (const auto& name : default_denied_filenames())
|
||||
add_denied_filename(name);
|
||||
|
||||
// Categorical denies on top of the exact-name list above: no path a plugin can reach may
|
||||
// contain a "secret", "cert"(ificate), or "conf"(ig) path component, regardless of which
|
||||
// allowed root it happens to sit inside. default_denied_path_keywords() is the single
|
||||
// source of this list; the tests seed from it too.
|
||||
// Categorical denies on top of the exact-name list above: a path a plugin reaches must not
|
||||
// have a "secret", "cert"(ificate), or "conf"(ig) component below data_dir() -- or anywhere
|
||||
// in the path outside data_dir(), whose own location may legitimately contain one (Flatpak
|
||||
// keeps it under .../config/OrcaSlicer). This holds regardless of which allowed root the
|
||||
// path happens to sit inside. default_denied_path_keywords() is the single source of this
|
||||
// list; the tests seed from it too.
|
||||
for (const auto& keyword : default_denied_path_keywords())
|
||||
add_denied_path_keyword(keyword);
|
||||
|
||||
|
||||
@@ -102,12 +102,13 @@ public:
|
||||
bool is_denied_filename(const boost::filesystem::path& candidate) const;
|
||||
|
||||
// --- denied-path-keyword registry ---
|
||||
// Keywords that categorically deny a path if ANY component matches one case-insensitively --
|
||||
// Keywords that categorically deny a path when a component matches one case-insensitively --
|
||||
// e.g. a "secrets" subfolder, a "certificates" folder, a "conf"/"config" directory, or a
|
||||
// .conf/.ini file anywhere the plugin can otherwise reach, including inside an allowed root.
|
||||
// This is broader than the exact-name is_denied_filename registry, but it is not a substring
|
||||
// match: importable modules such as numpy/__config__.py, configparser.py, sysconfig.py, or
|
||||
// user folders such as "Conference" and "Concert" are unrelated names and must stay promptable.
|
||||
// The components above the app's data_dir() are not considered (see below). This is broader
|
||||
// than the exact-name is_denied_filename registry, but it is not a substring match:
|
||||
// importable modules such as numpy/__config__.py, configparser.py, sysconfig.py, or user
|
||||
// folders such as "Conference" and "Concert" are unrelated names and must stay promptable.
|
||||
void add_denied_path_keyword(const std::string& keyword);
|
||||
|
||||
// The list install_hook() seeds into the keyword registry. Exposed so tests seed the exact
|
||||
@@ -115,7 +116,10 @@ public:
|
||||
static std::vector<std::string> default_denied_path_keywords();
|
||||
|
||||
// True when any component of candidate's (canonicalized) path matches a registered keyword,
|
||||
// case-insensitively. A registered "conf" keyword also blocks .conf/.ini file components.
|
||||
// case-insensitively. Inside data_dir(), only the components below data_dir() are checked:
|
||||
// the app's own data folder may itself sit under a component named like a keyword (Flatpak
|
||||
// keeps it under .../config/OrcaSlicer), which must not deny everything inside it. A
|
||||
// registered "conf" keyword also blocks .conf/.ini file components.
|
||||
bool is_denied_path_keyword(const boost::filesystem::path& candidate) const;
|
||||
|
||||
// is_denied_filename(candidate) || is_denied_path_keyword(candidate). Convenience for
|
||||
|
||||
@@ -288,6 +288,67 @@ TEST_CASE("Plugin audit denies secret/certificate/config-like paths by keyword",
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Plugin audit checks denied keywords below data_dir, not in the directories above it", "[PluginAudit]")
|
||||
{
|
||||
// Flatpak keeps the app's data folder under <home>/.var/app/<app-id>/config/OrcaSlicer, so
|
||||
// data_dir() itself has a "config" component. That ancestor describes the sandbox's layout,
|
||||
// not what the folder holds, and must not hard-deny every path inside it.
|
||||
ScopedDataDir data_dir_guard("plugin-audit-data-dir-keyword");
|
||||
const fs::path flatpak_data_dir = data_dir_guard.dir / "config" / "OrcaSlicer";
|
||||
set_data_dir(flatpak_data_dir.string());
|
||||
|
||||
seed_denied_names();
|
||||
seed_denied_keywords();
|
||||
|
||||
PluginAuditManager& mgr = PluginAuditManager::instance();
|
||||
// Reproduce install_hook()'s grant: data_dir() is a global allowed root.
|
||||
mgr.add_global_allowed_root(data_dir());
|
||||
|
||||
ScopedPluginAuditContext ctx("test_plugin", "");
|
||||
|
||||
SECTION("the root and the paths below it that carry no keyword are allowed")
|
||||
{
|
||||
CHECK_FALSE(mgr.is_denied_path_keyword(flatpak_data_dir / "orca_plugins" / "state.json"));
|
||||
CHECK(mgr.check_open(data_dir(), "r").allowed);
|
||||
CHECK(mgr.check_open((flatpak_data_dir / "orca_plugins" / "test_plugin" / "state.json").string(), "r").allowed);
|
||||
CHECK(mgr.check_open((flatpak_data_dir / "orca_plugins" / "test_plugin" / "state.json").string(), "w").allowed);
|
||||
CHECK(mgr.check_open((flatpak_data_dir / "system" / "profiles" / "vendor.json").string(), "r").allowed);
|
||||
}
|
||||
|
||||
SECTION("a 'secrets' component below data_dir() is still denied")
|
||||
{
|
||||
AuditDecision decision = mgr.check_open((flatpak_data_dir / "secrets" / "token.json").string(), "r");
|
||||
CHECK_FALSE(decision.allowed);
|
||||
CHECK(decision.reason == "denied path keyword");
|
||||
}
|
||||
|
||||
SECTION("a 'config' component below data_dir() is still denied")
|
||||
{
|
||||
AuditDecision decision = mgr.check_open((flatpak_data_dir / "config" / "settings.json").string(), "r");
|
||||
CHECK_FALSE(decision.allowed);
|
||||
CHECK(decision.reason == "denied path keyword");
|
||||
}
|
||||
|
||||
SECTION("the app config below data_dir() is still denied by name")
|
||||
{
|
||||
AuditDecision decision = mgr.check_open((flatpak_data_dir / (SLIC3R_APP_KEY ".conf")).string(), "r");
|
||||
CHECK_FALSE(decision.allowed);
|
||||
CHECK(decision.reason == "denied filename");
|
||||
}
|
||||
|
||||
SECTION("an unrelated resources root keeps the full-path keyword check")
|
||||
{
|
||||
ScopedResourcesDir resources_dir_guard("plugin-audit-data-dir-keyword-resources");
|
||||
mgr.add_global_allowed_root(resources_dir(), /*allow_write=*/false);
|
||||
|
||||
CHECK(mgr.check_open((fs::path(resources_dir()) / "profiles" / "vendor.json").string(), "r").allowed);
|
||||
|
||||
AuditDecision decision = mgr.check_open((fs::path(resources_dir()) / "cert" / "slicer_base64.cer").string(), "r");
|
||||
CHECK_FALSE(decision.allowed);
|
||||
CHECK(decision.reason == "denied path keyword");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Plugin audit is_denied_path combines the filename and keyword registries", "[audit]")
|
||||
{
|
||||
seed_denied_names();
|
||||
|
||||
Reference in new issue
Block a user