>From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Pavel Sanda <sanda () lyx org>
Date: Mon, 8 Jun 2026 15:43:34 +0200
Subject: [PATCH] Hardening case 00c - arbitrary command execution via graphics filename extension

build_script() in GraphicsConverter takes the extension from the user-supplied
graphics filename and embeds it in temp paths interpolated into an os.system()
call in the generated Python conversion script. A hostile extension carrying
shell metacharacters injects arbitrary commands.

Fires on .lyx load (the referenced graphics file must exist on disk).

Tier 00 hotfix: strip everything but [A-Za-z0-9_-] from the extension.
Tier 01 DiD will land in master (argv-form Python).

Assisted-by: Claude Opus 4.7
---
 src/graphics/GraphicsConverter.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/graphics/GraphicsConverter.cpp b/src/graphics/GraphicsConverter.cpp
index 0436f4c634..47c1445a0d 100644
--- a/src/graphics/GraphicsConverter.cpp
+++ b/src/graphics/GraphicsConverter.cpp
@@ -25,6 +25,8 @@
 #include "support/TempFile.h"
 
 #include <sstream>
+#include <algorithm>
+#include <cctype>
 #include <fstream>
 
 using namespace std;
@@ -301,7 +303,12 @@ static void build_script(string const & doc_fname,
 		theConverters().getPath(from_format, to_format);
 
 	// Create a temporary base file-name for all intermediate steps.
-	string const from_ext = getExtension(from_file);
+	// The extension string is user-controlled. Avoid metacharacters
+	// to prevent havoc down the pipeline.
+	string from_ext = getExtension(from_file);
+	from_ext.erase(remove_if(from_ext.begin(), from_ext.end(),
+		[](unsigned char c){ return !(isalnum(c) || c == '_' || c == '-'); }),
+		from_ext.end());
 	TempFile tempfile(addExtension("gconvertXXXXXX", from_ext));
 	tempfile.setAutoRemove(false);
 	string outfile = tempfile.name().toFilesystemEncoding();
