[LyX/master] Hardening case 00de (xindex) - version check + --restricted jail

Pavel Sanda <[email protected]>
Newsgroups gmane.editors.lyx.cvs
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.