mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-30 04:11:00 +00:00
Confirm Before Opening Program Attachments and Load Only HTTPS Images
Opening a project attachment whose type runs as a program or script (executables, installers, shortcuts, shell and PowerShell scripts, macOS command files and apps, Linux desktop entries) now asks for confirmation first. The check lives in libslic3r as is_executable_file_name and ignores the trailing dots and spaces Windows strips from file names. Images in project descriptions are kept only when they load over https, so opening the Project tab no longer issues plain-http requests.
This commit is contained in:
@@ -498,6 +498,12 @@ function IsHttpUrl( strUrl )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Images load as soon as the page opens, so only https sources are kept: no plain-http requests to the local network.
|
||||||
|
function IsHttpsUrl( strUrl )
|
||||||
|
{
|
||||||
|
return IsHttpUrl(strUrl) && new URL(strUrl).protocol=='https:';
|
||||||
|
}
|
||||||
|
|
||||||
// Embedded YouTube players become a plain link to the video.
|
// Embedded YouTube players become a plain link to the video.
|
||||||
function GetYouTubeEmbedUrl( pNode )
|
function GetYouTubeEmbedUrl( pNode )
|
||||||
{
|
{
|
||||||
@@ -546,7 +552,7 @@ function CopyDescNodes( pSrc, pDst )
|
|||||||
pElem.setAttribute('href',pNode.getAttribute('href'));
|
pElem.setAttribute('href',pNode.getAttribute('href'));
|
||||||
else if( sTag=='IMG' )
|
else if( sTag=='IMG' )
|
||||||
{
|
{
|
||||||
if( !IsHttpUrl(pNode.getAttribute('src')) )
|
if( !IsHttpsUrl(pNode.getAttribute('src')) )
|
||||||
continue;
|
continue;
|
||||||
pElem.setAttribute('src',pNode.getAttribute('src'));
|
pElem.setAttribute('src',pNode.getAttribute('src'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,6 +266,8 @@ extern bool is_symlink_target_within_root(const std::string &link_rel_path, cons
|
|||||||
// True if path names an entry strictly inside root: it must be spelled with root as its prefix,
|
// True if path names an entry strictly inside root: it must be spelled with root as its prefix,
|
||||||
// and must still resolve inside root once symlinks are followed.
|
// and must still resolve inside root once symlinks are followed.
|
||||||
extern bool is_absolute_path_within_root(const boost::filesystem::path &path, const boost::filesystem::path &root);
|
extern bool is_absolute_path_within_root(const boost::filesystem::path &path, const boost::filesystem::path &root);
|
||||||
|
// True if opening a file with this name through the desktop would run it as a program or script.
|
||||||
|
extern bool is_executable_file_name(const std::string &file_name);
|
||||||
|
|
||||||
// Orca: custom protocal support utils
|
// Orca: custom protocal support utils
|
||||||
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "orcaslicer://open"); }
|
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "orcaslicer://open"); }
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/nowide/fstream.hpp>
|
#include <boost/nowide/fstream.hpp>
|
||||||
@@ -1133,6 +1134,28 @@ bool is_absolute_path_within_root(const boost::filesystem::path &path, const boo
|
|||||||
return !rel.empty() && rel != "." && is_path_within_root(rel.string(), root);
|
return !rel.empty() && rel != "." && is_path_within_root(rel.string(), root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_executable_file_name(const std::string &file_name)
|
||||||
|
{
|
||||||
|
static const std::vector<std::string> executable_extensions = {
|
||||||
|
// Windows
|
||||||
|
"exe", "com", "bat", "cmd", "scr", "pif", "msi", "msp", "msc", "cpl", "lnk", "url", "hta", "js", "jse", "vbs",
|
||||||
|
"vbe", "wsf", "wsh", "ps1", "psm1", "reg", "jar", "appref-ms", "scf", "inf", "py", "pyw",
|
||||||
|
// macOS
|
||||||
|
"app", "command", "pkg", "terminal", "workflow",
|
||||||
|
// Linux
|
||||||
|
"sh", "bash", "run", "desktop", "appimage"};
|
||||||
|
// Windows ignores trailing dots and spaces, so "setup.exe." still runs as an .exe.
|
||||||
|
const size_t end = file_name.find_last_not_of(". ");
|
||||||
|
if (end == std::string::npos)
|
||||||
|
return false;
|
||||||
|
const std::string name = file_name.substr(0, end + 1);
|
||||||
|
const size_t dot = name.find_last_of('.');
|
||||||
|
if (dot == std::string::npos || name.find_first_of("/\\", dot) != std::string::npos)
|
||||||
|
return false;
|
||||||
|
const std::string extension = boost::algorithm::to_lower_copy(name.substr(dot + 1));
|
||||||
|
return std::find(executable_extensions.begin(), executable_extensions.end(), extension) != executable_extensions.end();
|
||||||
|
}
|
||||||
|
|
||||||
bool is_img_file(const std::string &path)
|
bool is_img_file(const std::string &path)
|
||||||
{
|
{
|
||||||
return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg");
|
return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg");
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
|
#include "MsgDialog.hpp"
|
||||||
#include <slic3r/GUI/Widgets/WebView.hpp>
|
#include <slic3r/GUI/Widgets/WebView.hpp>
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
@@ -297,7 +298,18 @@ void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt)
|
|||||||
fs::path aux_root(encode_path(wxGetApp().plater()->model().get_auxiliary_file_temp_path().c_str()));
|
fs::path aux_root(encode_path(wxGetApp().plater()->model().get_auxiliary_file_temp_path().c_str()));
|
||||||
|
|
||||||
if (is_absolute_path_within_root(path, aux_root) && fs::is_regular_file(path)) {
|
if (is_absolute_path_within_root(path, aux_root) && fs::is_regular_file(path)) {
|
||||||
wxLaunchDefaultApplication(path.wstring(), 0);
|
// Attachments come with the project, so ask before running one that is a program or script.
|
||||||
|
bool open = true;
|
||||||
|
if (is_executable_file_name(path.filename().string())) {
|
||||||
|
MessageDialog dlg(this,
|
||||||
|
wxString::Format(_L("\"%s\" is a program or script. Opening it will run it on this computer.\n\n"
|
||||||
|
"Only open attachments from projects you trust. Open it anyway?"),
|
||||||
|
from_path(path.filename())),
|
||||||
|
_L("Open attachment"), wxICON_WARNING | wxYES_NO);
|
||||||
|
open = dlg.ShowModal() == wxID_YES;
|
||||||
|
}
|
||||||
|
if (open)
|
||||||
|
wxLaunchDefaultApplication(path.wstring(), 0);
|
||||||
} else {
|
} else {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "open_3mf_accessory: ignoring path outside the project auxiliary directory: " << decode_path;
|
BOOST_LOG_TRIVIAL(warning) << "open_3mf_accessory: ignoring path outside the project auxiliary directory: " << decode_path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -367,3 +367,20 @@ TEST_CASE("is_absolute_path_within_root accepts only entries inside the root", "
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("is_executable_file_name flags attachments that would run as programs", "[utils]") {
|
||||||
|
const std::string executable = GENERATE(as<std::string>{},
|
||||||
|
"setup.exe", "SETUP.EXE", "Manual.pdf.exe", "run.bat", "start.cmd", "shortcut.lnk", "site.url", "script.ps1",
|
||||||
|
"macro.vbs", "tool.jar", "script.py", "Install.command", "Tool.app", "installer.pkg", "install.sh",
|
||||||
|
"launcher.desktop", "Printer.AppImage", "setup.exe.", "setup.exe ", "setup.exe. .", ".exe");
|
||||||
|
INFO(executable);
|
||||||
|
CHECK(is_executable_file_name(executable));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("is_executable_file_name leaves documents and models alone", "[utils]") {
|
||||||
|
const std::string document = GENERATE(as<std::string>{},
|
||||||
|
"Manual.pdf", "BOM.xlsx", "notes.txt", "photo.JPG", "assembly.step", "part.stl", "project.3mf", "readme",
|
||||||
|
"exe", "setup.exe.pdf", "", "...", "dir.exe/readme");
|
||||||
|
INFO(document);
|
||||||
|
CHECK_FALSE(is_executable_file_name(document));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user