On Tue, Sep 01, 2026 at 08:35:31PM +0200, Pavel Sanda wrote: > On Mon, Aug 31, 2026 at 09:54:20PM +0200, Pavel Sanda wrote: > > The first batch of patches tries to sanitize runCommand() calls. > > The next one uses new startscriptArgv to close kpsewhich hole > in a less fragile way.
The third one in our march for ditching runCommand from the source code kicks it from python probes. Jose, you might want to have a look. Pavel
>From 4e1d0b76f5020afca4110579619cf4ef15af8293 Mon Sep 17 00:00:00 2001 From: Pavel Sanda <[email protected]> Date: Tue, 8 Sep 2026 21:42:02 +0200 Subject: [PATCH 05/11] Probe the python interpreter without a shell Reroute python_call() and python_info() to startscriptArgv() so os.cpp is left with no runCommand(). Adds new os::pythonArgvForm() for the argv-form callers. Tier 01 DiD / argv 03a: python argv probe Assisted-by: Claude Opus 5 --- src/support/os.cpp | 47 +++++++++++++++++++++++++++++++++++++++------- src/support/os.h | 5 +++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/support/os.cpp b/src/support/os.cpp index d34adee420..8fe6c8932a 100644 --- a/src/support/os.cpp +++ b/src/support/os.cpp @@ -19,6 +19,7 @@ #include "support/debug.h" #include "support/filetools.h" #include "support/qstring_helpers.h" +#include "support/Systemcall.h" #include <QDir> @@ -58,20 +59,33 @@ int timeout_ms() static string const python_call(string const & binary, bool verbose = false) { - const string version_info = " -c \"import sys; print(sys.version_info[:2], end='')\""; + const string version_script = "import sys; print(sys.version_info[:2], end='')"; // Default to "python3" if no binary is given. if (binary.empty()) return "python3"; if (verbose) lyxerr << "Examining " << binary << "\n"; - // Check whether this is a python 2 or 3 binary. - cmd_ret const out = runCommand(binary + version_info); + // Check whether this is a python 2 or 3 binary. The candidate may be + // a bare name ("python3"), a name plus argument ("py -3") or a quoted + // absolute path found with walking-path fallback; split it into argv + // so the probe never goes through a shell. + vector<string> argv = splitCommandTokens(binary); + if (argv.empty()) + return string(); + string const program = argv.front(); + argv.erase(argv.begin()); + argv.push_back("-c"); + argv.push_back(version_script); + + Systemcall one; + string result; + int const ret = one.startscriptArgv(program, argv, &result); smatch sm; try { static regex const python_reg("\\((\\d*), (\\d*)\\)"); - if (!out.valid || !regex_match(out.result, sm, python_reg)) + if (ret != Systemcall::OK || !regex_match(result, sm, python_reg)) return string(); } catch(regex_error const & /*e*/) { LYXERR0("Regex error! This should not happen."); @@ -84,7 +98,7 @@ static string const python_call(string const & binary, bool verbose = false) return string(); if (verbose) - lyxerr << "Found Python " << out.result << "\n"; + lyxerr << "Found Python " << result << "\n"; return binary; } @@ -158,12 +172,32 @@ bool hasPython() return !(python_call(python()).empty()); } +vector<string> pythonArgvForm() +{ + return splitCommandTokens(python()); +} + + +// consumers: About dialog & GraphicsConverter version check on "2" string const python_info() { - const string info_version = " -c \"import sys; print('{} ({})'.format(sys.version.split()[0],sys.executable), end='')\""; + const string info_script = "import sys; print('{} ({})'.format(sys.version.split()[0],sys.executable), end='')"; if (!hasPython()) return("None"); - return (runCommand(python() + info_version).result); + + vector<string> argv = pythonArgvForm(); + if (argv.empty()) + return("None"); + string const program = argv.front(); + argv.erase(argv.begin()); + argv.push_back("-c"); + argv.push_back(info_script); + + Systemcall one; + string result; + if (one.startscriptArgv(program, argv, &result) != Systemcall::OK) + return("None"); + return result; } } // namespace os diff --git a/src/support/os.h b/src/support/os.h index 1b664e4444..94c243abf3 100644 --- a/src/support/os.h +++ b/src/support/os.h @@ -16,6 +16,7 @@ #include "support/docstring.h" #include <cstddef> +#include <vector> namespace lyx { @@ -62,6 +63,10 @@ int timeout_ms(); /// @param reset True if the python path should be recomputed std::string const python(bool reset = false); +/// Take python() output and split it into program + leading arguments, for invocation +/// via Systemcall::startscriptArgv (no shell, no command-line re-parsing). +std::vector<std::string> pythonArgvForm(); + /// Check for availability of the python interpreter bool hasPython(); -- 2.39.5
-- lyx-devel mailing list [email protected] https://lists.lyx.org/mailman/listinfo/lyx-devel
