>From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Pavel Sanda <sanda () lyx org>
Date: Sat, 13 Jun 2026 23:18:52 +0200
Subject: [PATCH] Hardening case 00i+00h - shell-safe document basename via makeLatexName

makeLatexName() left shell metacharacters in the document's own filename
(enters via Open/Import). That explodes in various converters (00h) and
a subset is alive even inside double quoting (00i).

Fires on export / View-PDF.

Tier 00 fix: Tighten the keep-set to shell-safe alphanumerics + "+-._,@"
Tier 04 fix for Unicode will land in master.

Assisted-by: Claude Opus 4.7
---
 src/support/filetools.cpp | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/src/support/filetools.cpp b/src/support/filetools.cpp
index e0dc4e7654..f6c316f5a3 100644
--- a/src/support/filetools.cpp
+++ b/src/support/filetools.cpp
@@ -195,18 +195,41 @@ string const latex_path(string const & original_path,
 }
 
 
-// Substitutes spaces with underscores in filename (and path)
+// Sanitises filename for downstream conversion / helper consumers.
+//
+// CONTRACT: the basename of the returned FileName is shell-safe by
+// construction. Every output byte is in the strict `keep` allow-list
+// below (alphanumerics + "+-._,@"), so the result can flow into any
+// downstream helper script in lib/scripts/ without re-escaping, even
+// when the helper invokes a subprocess with shell=True and substitutes
+// the filename unquoted. The function is idempotent: every output byte
+// is in `keep`, so a second pass is a no-op.
+//
+// Bytes excluded from the keep-set are chosen to leave NO live POSIX
+// shell metacharacter in any quoting context:
+//   - $ ` \ "             special inside "..."
+//   - ; | & < > ( ) { }   command separators / redirections / groups
+//   - * ? [ ]             globs
+//   - ! ~ # '             history / tilde / comment / quote
+//   - whitespace          (space, tab, newline)
+// Bytes preserved beyond [A-Za-z0-9]: "+-._,@" - all POSIX shell-safe
+// in every context and common in legitimate filenames. ':' is excluded
+// because it is illegal on NTFS (Windows drive separator / Alternate
+// Data Stream sigil), so preserving it would paper over a
+// cross-platform portability bug rather than helping.
+//
+// Do NOT relax this set without auditing every consumer of
+// Buffer::latexName() / makeLatexName() in lib/scripts/.
 FileName const makeLatexName(FileName const & file)
 {
 	string name = file.onlyFileName();
 	string const path = file.onlyPath().absFileName() + "/";
 
-	// ok so we scan through the string twice, but who cares.
 	// FIXME: in Unicode time this will break for sure! There is
 	// a non-latin world out there...
 	string const keep = "abcdefghijklmnopqrstuvwxyz"
 		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-		"@!'()*+,-./0123456789:;<=>?[]`|";
+		"0123456789+-._,@";
 
 	string::size_type pos = 0;
 	while ((pos = name.find_first_not_of(keep, pos)) != string::npos)
-- 
2.39.5

