From 9b0a0c3af13f0d3b1c5f9a916b356da8788ca935 Mon Sep 17 00:00:00 2001 From: Daniel Kroening Date: Mon, 26 May 2025 15:29:38 +0100 Subject: [PATCH] fix shell quoting This adds missing cases to shell_quote(...), preventing potential priviledge escalation. --- src/util/run.cpp | 45 +++++++++++++++++++++++---------------------- unit/util/run.cpp | 18 ++++++++++++++++++ 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/util/run.cpp b/src/util/run.cpp index 2597dc5c14e..52a386540e9 100644 --- a/src/util/run.cpp +++ b/src/util/run.cpp @@ -27,16 +27,17 @@ Date: August 2012 // clang-format on #else -#include -#include -#include -#include - -#include -#include -#include -#include -#include +# include +# include + +# include +# include +# include +# include +# include +# include +# include +# include #endif @@ -486,18 +487,18 @@ std::string shell_quote(const std::string &src) // first check if quoting is needed at all - if(src.find(' ')==std::string::npos && - src.find('"')==std::string::npos && - src.find('*')==std::string::npos && - src.find('$')==std::string::npos && - src.find('\\')==std::string::npos && - src.find('?')==std::string::npos && - src.find('&')==std::string::npos && - src.find('|')==std::string::npos && - src.find('>')==std::string::npos && - src.find('<')==std::string::npos && - src.find('^')==std::string::npos && - src.find('\'')==std::string::npos) + bool quotes_needed = false; + + if(src.empty()) + quotes_needed = true; + else + { + for(auto &ch : src) + if(!isalnum(ch) && ch != '_' && ch != '.' && ch != '/' && ch != '-') + quotes_needed = true; + } + + if(!quotes_needed) { // seems fine -- return as is return src; diff --git a/unit/util/run.cpp b/unit/util/run.cpp index bdbc797abb7..dc93a6e7cf8 100644 --- a/unit/util/run.cpp +++ b/unit/util/run.cpp @@ -13,6 +13,24 @@ Author: Michael Tautschnig #include +SCENARIO("shell_quote() escaping", "[core][util][run]") +{ +#ifdef _WIN32 + REQUIRE(shell_quote("foo.bar") == "foo.bar"); + REQUIRE(shell_quote("foo&bar") == "\"foo&bar\""); + REQUIRE(shell_quote("foo(bar)") == "\"foo(bar)\""); + REQUIRE(shell_quote("foo\"bar") == "\"foo\"\"bar\""); +#else + REQUIRE(shell_quote("foo.bar") == "foo.bar"); + REQUIRE(shell_quote("foo/bar") == "foo/bar"); + REQUIRE(shell_quote("--foo") == "--foo"); + REQUIRE(shell_quote("") == "''"); + REQUIRE(shell_quote("foo\nbar") == "'foo\nbar'"); + REQUIRE(shell_quote("foo(bar)") == "'foo(bar)'"); + REQUIRE(shell_quote("foo'bar") == "'foo'\\'''bar'"); +#endif +} + SCENARIO("run() error reporting", "[core][util][run]") { GIVEN("A command invoking a non-existent executable")