commit 2b4781366747ac4bee7c280b61424ceb29d459e9
Author: Pavel Sanda <[email protected]>
Date:   Wed Jul 8 12:26:10 2026 +0200

    Hardening case 00de (xindex) - version check + --restricted jail
    
    Stacks on the processing gate 00de: adds xindex to the processor table.
    For >= 1.07 ungated + appends --restricted to the spawned command so
    a hostile document cannot shadow xindex's own config/modules in CWD.
    
    Assisted-by: Claude Opus 4.8
---
 src/LaTeX.cpp | 35 +++++++++++++++++++++++++++++++++--
 1 file changed, 33 insertions(+), 2 deletions(-)

diff --git a/src/LaTeX.cpp b/src/LaTeX.cpp
index 77cd2d0d30..e38686f578 100644
--- a/src/LaTeX.cpp
+++ b/src/LaTeX.cpp
@@ -613,6 +613,19 @@ int LaTeX::runMakeIndex(string const & f, OutputParams 
const & rp,
            && !checkProcessorAuth(doc_fname, tmp))
                return Systemcall::KILLED;
 
+       // A --restricted-capable xindex (not gated, i.e. version >= the table
+       // threshold) is run with --restricted so a document cannot shadow
+       // xindex's own modules/configs from the current directory and get code
+       // executed. This also covers a document-supplied \index_command, which
+       // replaces the whole command string. Older xindex would reject the
+       // unknown flag, hence the !isProcessorGated guard (cached above).
+       {
+               string iprog;
+               split(tmp, iprog, ' ');
+               if (onlyFileName(iprog) == "xindex" && !isProcessorGated(tmp))
+                       tmp += " --restricted";
+       }
+
        Language const * doc_lang = languages.getLanguage(rp.document_language);
        
        if (contains(tmp, "$$x")) {
@@ -844,6 +857,11 @@ RequiredProcessor const required_processors[] = {
        { "upmendex",  true,  nullptr, nullptr, 0, 0 },
        // biber: code-capable; fixed upstream at 2.22
        { "biber", false, "--version", "version:\\s*([0-9]+)\\.([0-9]+)", 2, 22 
},
+       // xindex: code-capable (texlua); the CWD-shadowing RCE is closed by
+       // running it with --restricted (see runMakeIndex). Versions that 
support
+       // that flag are not gated. PIN this threshold to the xindex release 
that
+       // actually ships --restricted before enabling (placeholder: 1.07).
+       { "xindex", false, "--version", "xindex version ([0-9]+)\\.([0-9]+)", 
1, 7 },
 };
 
 // False only when >= required version. 
@@ -871,8 +889,21 @@ bool isProcessorGated(string const & command)
                        break;
                }
                //safe because prog was matched against the table
-               cmd_ret const r =
-                       runCommand(quoteName(prog) + ' ' + p.version_arg);
+               // Probe in a fresh empty directory, not the export dir: a
+               // texlua-based processor (xindex) executes CWD-first module 
loads
+               // before it prints its version, so probing where the document 
has
+               // written files would run planted code. 
createLyXTmpDir(FileName())
+               // makes a unique dir under the system temp; on failure we 
leave the
+               // fail-safe gated=true.
+               cmd_ret r{};
+               FileName const probe_dir = createLyXTmpDir(FileName());
+               if (!probe_dir.empty()) {
+                       FileName const saved = FileName::getcwd();
+                       probe_dir.chdir();
+                       r = runCommand(quoteName(prog) + ' ' + p.version_arg);
+                       saved.chdir();
+                       probe_dir.destroyDirectory();
+               }
                smatch m;
                regex const re(p.version_re);
                if (r.valid && regex_search(r.result, m, re)) {
-- 
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to