diff --git a/MapleLib/Img/ImgFileSystemManager.cs b/MapleLib/Img/ImgFileSystemManager.cs index 5f0dce8..6f5acfb 100644 --- a/MapleLib/Img/ImgFileSystemManager.cs +++ b/MapleLib/Img/ImgFileSystemManager.cs @@ -111,7 +111,7 @@ public ImgFileSystemManager( if (!Directory.Exists(versionPath)) throw new DirectoryNotFoundException($"Version directory not found: {versionPath}"); - _versionPath = versionPath; + _versionPath = Path.GetFullPath(versionPath); _config = config ?? new HaCreatorConfig(); // Initialize LRU cache with size-based eviction @@ -122,12 +122,12 @@ public ImgFileSystemManager( _imageCache = new LRUCache(maxCacheBytes, EstimateWzImageSize); // Load version manifest - string manifestPath = Path.Combine(versionPath, MANIFEST_FILENAME); + string manifestPath = Path.Combine(_versionPath, MANIFEST_FILENAME); if (File.Exists(manifestPath)) { string json = File.ReadAllText(manifestPath); _versionInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(json); - _versionInfo.DirectoryPath = versionPath; + _versionInfo.DirectoryPath = _versionPath; } else { @@ -136,7 +136,7 @@ public ImgFileSystemManager( { Version = Path.GetFileName(versionPath), DisplayName = Path.GetFileName(versionPath), - DirectoryPath = versionPath, + DirectoryPath = _versionPath, ExtractedDate = DateTime.Now }; } @@ -269,9 +269,7 @@ public WzImage LoadImage(string category, string relativePath) Interlocked.Increment(ref _cacheMisses); // Build full path - string fullPath = Path.Combine(_versionPath, category, relativePath); - if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) - fullPath += ".img"; + string fullPath = GetContainedImagePath(category, relativePath); if (!File.Exists(fullPath)) return null; @@ -489,10 +487,7 @@ public bool CategoryExists(string category) /// public bool ImageExists(string category, string relativePath) { - string fullPath = Path.Combine(_versionPath, category, relativePath); - if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) - fullPath += ".img"; - return File.Exists(fullPath); + return File.Exists(GetContainedImagePath(category, relativePath)); } /// @@ -500,9 +495,7 @@ public bool ImageExists(string category, string relativePath) /// public string GetImageDiagnostics(string category, string relativePath) { - string fullPath = Path.Combine(_versionPath, category, relativePath); - if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) - fullPath += ".img"; + string fullPath = GetContainedImagePath(category, relativePath); string categoryPath = Path.Combine(_versionPath, category); bool categoryDirExists = Directory.Exists(categoryPath); @@ -557,9 +550,7 @@ public bool SaveImage(WzImage image, string category, string relativePath) if (image == null) throw new ArgumentNullException(nameof(image)); - string fullPath = Path.Combine(_versionPath, category, relativePath); - if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) - fullPath += ".img"; + string fullPath = GetContainedImagePath(category, relativePath); return SaveImageToFile(image, fullPath); } @@ -619,6 +610,29 @@ public bool SaveImageToFile(WzImage image, string filePath) } } + private string GetContainedImagePath(string category, string relativePath) + { + if (string.IsNullOrWhiteSpace(category)) + throw new ArgumentException("Category is required.", nameof(category)); + if (string.IsNullOrWhiteSpace(relativePath)) + throw new ArgumentException("Relative path is required.", nameof(relativePath)); + + string fullPath = Path.Combine(_versionPath, category, relativePath); + if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) + fullPath += ".img"; + + fullPath = Path.GetFullPath(fullPath); + string root = Path.GetFullPath(_versionPath); + string rootWithSeparator = Path.EndsInDirectorySeparator(root) + ? root + : root + Path.DirectorySeparatorChar; + + if (!fullPath.StartsWith(rootWithSeparator, StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException($"Image path escapes the version directory: {fullPath}"); + + return fullPath; + } + /// /// Gets the cache key from a full file path /// @@ -644,9 +658,7 @@ private string GetCacheKeyFromPath(string filePath) /// The created WzImage or null if failed public WzImage CreateImage(string category, string relativePath) { - string fullPath = Path.Combine(_versionPath, category, relativePath); - if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) - fullPath += ".img"; + string fullPath = GetContainedImagePath(category, relativePath); // Check if file already exists if (File.Exists(fullPath)) @@ -689,9 +701,7 @@ public WzImage CreateImage(string category, string relativePath) /// True if deleted successfully public bool DeleteImage(string category, string relativePath) { - string fullPath = Path.Combine(_versionPath, category, relativePath); - if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) - fullPath += ".img"; + string fullPath = GetContainedImagePath(category, relativePath); try { @@ -724,10 +734,7 @@ public bool DeleteImage(string category, string relativePath) /// public string GetImagePath(string category, string relativePath) { - string fullPath = Path.Combine(_versionPath, category, relativePath); - if (!fullPath.EndsWith(".img", StringComparison.OrdinalIgnoreCase)) - fullPath += ".img"; - return fullPath; + return GetContainedImagePath(category, relativePath); } #endregion diff --git a/MapleLib/WzLib/Util/WzBinaryReader.cs b/MapleLib/WzLib/Util/WzBinaryReader.cs index 10d8c1b..01a08ad 100644 --- a/MapleLib/WzLib/Util/WzBinaryReader.cs +++ b/MapleLib/WzLib/Util/WzBinaryReader.cs @@ -191,7 +191,7 @@ public string ReadString(int length) ? stackalloc byte[length] : (pooledArray = s_bytePool.Rent(length)).AsSpan(0, length); - BaseStream.Read(buffer); + BaseStream.ReadExactly(buffer); return Encoding.ASCII.GetString(buffer); } finally @@ -444,4 +444,4 @@ public override void Close() } #endregion } -} \ No newline at end of file +} diff --git a/MapleLib/WzLib/WzImage.cs b/MapleLib/WzLib/WzImage.cs index 5dc4afb..8a3bcf5 100644 --- a/MapleLib/WzLib/WzImage.cs +++ b/MapleLib/WzLib/WzImage.cs @@ -247,8 +247,14 @@ public WzImageProperty GetFromPath(string path) { if (reader != null) if (!parsed) ParseImage(); + if (string.IsNullOrWhiteSpace(path)) + return null; + string[] segments = path.Split(new char[1] { '/' }, System.StringSplitOptions.RemoveEmptyEntries); + if (segments.Length == 0) + return null; + // If the first segment is "..", return null if (segments[0] == "..") return null;