From adcdef8916234d73b52bcf09d60b4d3498ad6691 Mon Sep 17 00:00:00 2001 From: mleem97 <52848568+mleem97@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:59:43 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Fi?= =?UTF-8?q?x=20path=20traversal=20in=20SetPortrait?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added independent path traversal validation to CustomEmployeeManager.SetPortrait to prevent directory traversal bypasses when loading portrait images from the filesystem. If validation fails, it safely falls back to the default UI state. --- src/API/CustomEmployeeManager.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/API/CustomEmployeeManager.cs b/src/API/CustomEmployeeManager.cs index 9b4512b6..2cdf57f1 100644 --- a/src/API/CustomEmployeeManager.cs +++ b/src/API/CustomEmployeeManager.cs @@ -889,12 +889,20 @@ private static void SetPortrait(Transform card, string employeeId) var portraitTransform = card.Find("Image"); if (portraitTransform == null) return; - string assetsDir = Path.Combine(MelonEnvironment.UserDataDirectory, "ModAssets"); string? imagePath = null; - foreach (var ext in new[] { ".jpg", ".png" }) + // [Security] Prevent path traversal bypasses by validating employeeId before file system operations + if (employeeId.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || employeeId.Contains("..")) { - string candidate = Path.Combine(assetsDir, employeeId + ext); - if (File.Exists(candidate)) { imagePath = candidate; break; } + CrashLog.Log($"[Security] SetPortrait: Path traversal detected in employeeId={employeeId}"); + } + else + { + string assetsDir = Path.Combine(MelonEnvironment.UserDataDirectory, "ModAssets"); + foreach (var ext in new[] { ".jpg", ".png" }) + { + string candidate = Path.Combine(assetsDir, employeeId + ext); + if (File.Exists(candidate)) { imagePath = candidate; break; } + } } if (imagePath != null)