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