CVE-2021-23407 (published 2021-07-14T17:15Z)

This affects the package elFinder.Net.Core from 0 and before 1.2.4. The user-controlled file name is not properly sanitized before it is used to create a file system path.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/trannamtrung1st/elFinder.Net.Core
commit_details: repo_url: https://github.com/trannamtrung1st/elFinder.Net.Core
author: trung-tran-sts
author_date: 2021-07-01 18:12:30+07:00
msg: Finish security patch 1.2.4
repository: repo_url: https://github.com/trannamtrung1st/elFinder.Net.Core
repo_name: trannamtrung1st/elFinder.Net.Core
description: An elFinder backend connector with less magic code and more compatibility. This enables .NET Standard 2.0 projects to easily integrate elFinder functionalities.
repo_language: C#
owner: trannamtrung1st
file_changes: filename: Startup.cs
change_type: ModificationType.MODIFY
diff: @@ -39,7 +39,7 @@ namespace elFinder.Net.AdvancedDemo
             }
 
             path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
-            return PathHelper.GetFullPath(basePath, path);
+            return PathHelper.GetFullPath(Path.Combine(basePath, path));
         }
 
         public IConfiguration Configuration { get; }

programming_language: C#
filename: Startup.cs
change_type: ModificationType.MODIFY
diff: @@ -9,6 +9,7 @@ using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using System.Collections.Generic;
+using System.IO;
 
 namespace elFinder.Net.Demo31
 {
@@ -30,7 +31,7 @@ namespace elFinder.Net.Demo31
             }
 
             path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
-            return PathHelper.GetFullPath(basePath, path);
+            return PathHelper.GetFullPath(Path.Combine(basePath, path));
         }
 
         public IConfiguration Configuration { get; }

programming_language: C#
filename: Connector.cs
change_type: ModificationType.MODIFY
diff: @@ -374,6 +374,13 @@ namespace elFinder.Net.Core
                             if (targetPath.IsDirectory)
                                 throw new NotFileException();
 
+                            if (putCmd.Encoding == "hash")
+                            {
+                                putCmd.ContentPath = await ParsePathAsync(putCmd.Content, cancellationToken: cancellationToken);
+                                if (putCmd.ContentPath.IsDirectory)
+                                    throw new NotFileException();
+                            }
+
                             var putResp = await putCmd.TargetPath.Volume.Driver.PutAsync(putCmd, cancellationToken);
                             return ConnectorResult.Success(putResp);
                         }

programming_language: C#
filename: InvalidDirNameException.cs
change_type: ModificationType.ADD
diff: @@ -0,0 +1,15 @@
+﻿using elFinder.Net.Core.Models.Response;
+
+namespace elFinder.Net.Core.Exceptions
+{
+    public class InvalidDirNameException : ConnectorException
+    {
+        public InvalidDirNameException()
+        {
+            ErrorResponse = new ErrorResponse(this)
+            {
+                error = ErrorResponse.InvalidDirName
+            };
+        }
+    }
+}

programming_language: C#
filename: InvalidFileNameException.cs
change_type: ModificationType.ADD
diff: @@ -0,0 +1,15 @@
+﻿using elFinder.Net.Core.Models.Response;
+
+namespace elFinder.Net.Core.Exceptions
+{
+    public class InvalidFileNameException : ConnectorException
+    {
+        public InvalidFileNameException()
+        {
+            ErrorResponse = new ErrorResponse(this)
+            {
+                error = ErrorResponse.InvalidFileName
+            };
+        }
+    }
+}

programming_language: C#
filename: PutCommand.cs
change_type: ModificationType.MODIFY
diff: @@ -4,5 +4,7 @@
     {
         public string Content { get; set; }
         public string Encoding { get; set; }
+
+        public PathInfo ContentPath { get; set; }
     }
 }

programming_language: C#
filename: PathInfo.cs
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,6 @@
-﻿namespace elFinder.Net.Core
+﻿using elFinder.Net.Core.Exceptions;
+
+namespace elFinder.Net.Core
 {
     public class PathInfo
     {
@@ -6,12 +8,16 @@
         {
             File = file;
             FileSystem = file;
+
+            if (!volume.Own(FileSystem)) throw new PermissionDeniedException("Volume must own this path");
         }
 
         public PathInfo(string path, IVolume volume, IDirectory dir, string hashedTarget) : this(path, volume, hashedTarget, true)
         {
             Directory = dir;
             FileSystem = dir;
+
+            if (!volume.Own(FileSystem)) throw new PermissionDeniedException("Volume must own this path");
         }
 
         private PathInfo(string path, IVolume volume, string hashedTarget, bool isDirectory)

programming_language: C#
filename: ErrorResponse.cs
change_type: ModificationType.MODIFY
diff: @@ -84,6 +84,8 @@ namespace elFinder.Net.Core.Models.Response
         public const string CommandNoSupport = "errCmdNoSupport";
         public const string NotFile = "errNotFile";
         public const string UploadFile = "errUploadFile";
+        public const string InvalidFileName = "errInvName";
+        public const string InvalidDirName = "errInvDirname";
         #endregion
     }
 }

programming_language: C#
filename: IDirectoryExtensions.cs
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,5 @@
 ﻿using elFinder.Net.Core;
+using elFinder.Net.Drivers.FileSystem.Helpers;
 using System;
 using System.IO;
 using System.Linq;
@@ -22,7 +23,7 @@ namespace elFinder.Net.Drivers.FileSystem.Extensions
             }
 
             string newName = $"{name}{suffix}";
-            if (!Directory.Exists(Path.Combine(directory.Parent.FullName, newName)))
+            if (!Directory.Exists(PathHelper.SafelyCombine(directory.Parent.FullName, directory.Parent.FullName, newName)))
                 return newName;
             else
             {

programming_language: C#
filename: IFileExtensions.cs
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,5 @@
 ﻿using elFinder.Net.Core;
+using elFinder.Net.Drivers.FileSystem.Helpers;
 using System;
 using System.IO;
 using System.Linq;
@@ -23,7 +24,7 @@ namespace elFinder.Net.Drivers.FileSystem.Extensions
             }
 
             string newName = $"{name}{suffix}{extension}";
-            if (!File.Exists(Path.Combine(file.DirectoryName, newName)))
+            if (!File.Exists(PathHelper.SafelyCombine(file.DirectoryName, file.DirectoryName, newName)))
                 return newName;
             else
             {

programming_language: C#
filename: FileSystemDirectory.cs
change_type: ModificationType.MODIFY
diff: @@ -251,7 +251,7 @@ namespace elFinder.Net.Drivers.FileSystem
 
             if (verify && !this.CanRename()) throw new PermissionDeniedException();
 
-            var newPath = PathHelper.GetFullPath(Parent.FullName, newName);
+            var newPath = PathHelper.GetFullPath(PathHelper.SafelyCombine(Parent.FullName, Parent.FullName, newName));
             directoryInfo.MoveTo(newPath);
             return Task.FromResult<IDirectory>(new FileSystemDirectory(newPath, volume));
         }

programming_language: C#
filename: FileSystemDriver.cs
change_type: ModificationType.MODIFY
diff: @@ -29,6 +29,7 @@ namespace elFinder.Net.Drivers.FileSystem
     public class FileSystemDriver : IDriver
     {
         public const string DefaultThumbExt = ".png";
+        private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars();
 
         protected readonly IPathParser pathParser;
         protected readonly IPictureEditor pictureEditor;
@@ -127,6 +128,9 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
+            if (!IsObjectNameValid(cmd.Name))
+                throw new InvalidDirNameException();
+
             var mkdirResp = new MkdirResponse();
             var targetPath = cmd.TargetPath;
             var volume = targetPath.Volume;
@@ -136,7 +140,8 @@ namespace elFinder.Net.Drivers.FileSystem
 
             if (!string.IsNullOrEmpty(cmd.Name))
             {
-                var newDir = new FileSystemDirectory(Path.Combine(targetPath.Directory.FullName, cmd.Name), volume);
+                var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(targetPath.Directory.FullName,
+                    targetPath.Directory.FullName, cmd.Name), volume);
 
                 OnBeforeMakeDir?.Invoke(this, newDir);
                 await newDir.CreateAsync(cancellationToken: cancellationToken);
@@ -149,7 +154,8 @@ namespace elFinder.Net.Drivers.FileSystem
             foreach (string dir in cmd.Dirs)
             {
                 string dirName = dir.StartsWith("/") ? dir.Substring(1) : dir;
-                var newDir = new FileSystemDirectory(Path.Combine(targetPath.Directory.FullName, dirName), volume);
+                var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(targetPath.Directory.FullName,
+                    targetPath.Directory.FullName, dirName), volume);
 
                 OnBeforeMakeDir?.Invoke(this, newDir);
                 await newDir.CreateAsync(cancellationToken: cancellationToken);
@@ -170,13 +176,17 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
+            if (!IsObjectNameValid(cmd.Name))
+                throw new InvalidFileNameException();
+
             var targetPath = cmd.TargetPath;
             var volume = targetPath.Volume;
             var targetHash = targetPath.HashedTarget;
 
             if (!targetPath.Directory.CanCreateObject()) throw new PermissionDeniedException();
 
-            var newFile = new FileSystemFile(Path.Combine(targetPath.Directory.FullName, cmd.Name), volume);
+            var newFile = new FileSystemFile(PathHelper.SafelyCombine(targetPath.Directory.FullName,
+                targetPath.Directory.FullName, cmd.Name), volume);
 
             OnBeforeMakeFile?.Invoke(this, newFile);
             await newFile.CreateAsync(cancellationToken: cancellationToken);
@@ -355,6 +365,11 @@ namespace elFinder.Net.Drivers.FileSystem
             var targetPath = cmd.TargetPath;
             var volume = targetPath.Volume;
 
+            if (!IsObjectNameValid(cmd.Name))
+                if (targetPath.IsDirectory)
+                    throw new InvalidDirNameException();
+                else throw new InvalidFileNameException();
+
             if (targetPath.IsDirectory)
             {
                 var prevName = targetPath.Directory.Name;
@@ -466,6 +481,15 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
+            if (cmd.Name.Any(name => !IsObjectNameValid(name)))
+                throw new InvalidFileNameException();
+
+            if (cmd.Renames.Any(name => !IsObjectNameValid(name)))
+                throw new InvalidFileNameException();
+
+            if (!IsObjectNameValid(cmd.Suffix))
+                throw new InvalidFileNameException();
+
             var uploadResp = new UploadResponse();
             var targetPath = cmd.TargetPath;
             var volume = targetPath.Volume;
@@ -513,7 +537,7 @@ namespace elFinder.Net.Drivers.FileSystem
                     if (!dest.CanCreateObject())
                         throw new PermissionDeniedException($"Permission denied: {volume.GetRelativePath(dest)}");
 
-                    string uploadFullName = Path.Combine(dest.FullName, Path.GetFileName(formFile.FileName));
+                    string uploadFullName = PathHelper.SafelyCombine(dest.FullName, dest.FullName, Path.GetFileName(formFile.FileName));
                     var uploadFileInfo = new FileSystemFile(uploadFullName, volume);
                     var isOverwrite = false;
 
@@ -524,7 +548,7 @@ namespace elFinder.Net.Drivers.FileSystem
                             var fileNameWithoutExt = Path.GetFileNameWithoutExtension(formFile.FileName);
                             var ext = Path.GetExtension(formFile.FileName);
                             var backupName = $"{fileNameWithoutExt}{cmd.Suffix}{ext}";
-                            var fullBakName = Path.Combine(uploadFileInfo.Parent.FullName, backupName);
+                            var fullBakName = PathHelper.SafelyCombine(uploadFileInfo.Parent.FullName, uploadFileInfo.Parent.FullName, backupName);
                             var bakFile = new FileSystemFile(fullBakName, volume);
 
                             if (await bakFile.ExistsAsync)
@@ -541,7 +565,7 @@ namespace elFinder.Net.Drivers.FileSystem
                         else if (cmd.Overwrite == 0 || (cmd.Overwrite == null && !volume.UploadOverwrite))
                         {
                             string newName = await uploadFileInfo.GetCopyNameAsync(cmd.Suffix, cancellationToken: cancellationToken);
-                            uploadFullName = Path.Combine(uploadFileInfo.DirectoryName, newName);
+                            uploadFullName = PathHelper.SafelyCombine(uploadFileInfo.DirectoryName, uploadFileInfo.DirectoryName, newName);
                             uploadFileInfo = new FileSystemFile(uploadFullName, volume);
                             isOverwrite = false;
                         }
@@ -715,6 +739,12 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
+            if (cmd.Renames.Any(name => !IsObjectNameValid(name)))
+                throw new InvalidFileNameException();
+
+            if (!IsObjectNameValid(cmd.Suffix))
+                throw new InvalidFileNameException();
+
             var pasteResp = new PasteResponse();
             var isCut = cmd.Cut == 1;
             var dstPath = cmd.DstPath;
@@ -726,14 +756,14 @@ namespace elFinder.Net.Drivers.FileSystem
                 if (src.IsDirectory)
                 {
                     IDirectory pastedDir;
-                    var newDest = Path.Combine(dstPath.Directory.FullName, src.Directory.Name);
+                    var newDest = PathHelper.SafelyCombine(dstPath.Directory.FullName, dstPath.Directory.FullName, src.Directory.Name);
                     var newDestDir = new FileSystemDirectory(newDest, dstPath.Volume);
                     var exists = await newDestDir.ExistsAsync;
 
                     if (exists && cmd.Renames.Contains(newDestDir.Name))
                     {
                         var backupName = $"{newDestDir.Name}{cmd.Suffix}";
-                        var fullBakName = Path.Combine(newDestDir.Parent.FullName, backupName);
+                        var fullBakName = PathHelper.SafelyCombine(newDestDir.Parent.FullName, newDestDir.Parent.FullName, backupName);
                         var bakDir = new FileSystemDirectory(fullBakName, newDestDir.Volume);
 
                         if (await bakDir.ExistsAsync)
@@ -790,7 +820,7 @@ namespace elFinder.Net.Drivers.FileSystem
                 {
                     IFile pastedFile;
                     var file = src.File;
-                    var newDest = Path.Combine(dstPath.Directory.FullName, file.Name);
+                    var newDest = PathHelper.SafelyCombine(dstPath.Directory.FullName, dstPath.Directory.FullName, file.Name);
                     var newDestFile = new FileSystemFile(newDest, dstPath.Volume);
                     var exists = await newDestFile.ExistsAsync;
 
@@ -799,7 +829,7 @@ namespace elFinder.Net.Drivers.FileSystem
                         var fileNameWithoutExt = Path.GetFileNameWithoutExtension(newDestFile.Name);
                         var ext = Path.GetExtension(newDestFile.Name);
                         var backupName = $"{fileNameWithoutExt}{cmd.Suffix}{ext}";
-                        var fullBakName = Path.Combine(newDestFile.Parent.FullName, backupName);
+                        var fullBakName = PathHelper.SafelyCombine(newDestFile.Parent.FullName, newDestFile.Parent.FullName, backupName);
                         var bakFile = new FileSystemFile(fullBakName, newDestFile.Volume);
 
                         if (await bakFile.ExistsAsync)
@@ -846,7 +876,7 @@ namespace elFinder.Net.Drivers.FileSystem
                 if (src.IsDirectory)
                 {
                     var newName = await src.Directory.GetCopyNameAsync(cancellationToken: cancellationToken);
-                    var newDest = Path.Combine(src.Directory.Parent.FullName, newName);
+                    var newDest = PathHelper.SafelyCombine(src.Directory.Parent.FullName, src.Directory.Parent.FullName, newName);
 
                     OnBeforeCopy?.Invoke(this, (src.Directory, newDest, true));
                     var dupDir = await CopyToAsync(src.Directory, newDest, dstVolume, copyOverwrite: false, cancellationToken: cancellationToken);
@@ -906,13 +936,16 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
+            if (!IsObjectNameValid(cmd.Name))
+                throw new InvalidFileNameException();
+
             if (cmd.Type != MediaTypeNames.Application.Zip)
                 throw new ArchiveTypeException();
 
             var archiveResp = new ArchiveResponse();
             var targetPath = cmd.TargetPath;
             var volume = cmd.TargetPath.Volume;
-            var directoryInfo = targetPath.Directory;
+            var directory = targetPath.Directory;
 
             var filename = cmd.Name ?? targetPath.Directory.Name;
             var zipExt = $".{FileExtensions.Zip}";
@@ -920,7 +953,7 @@ namespace elFinder.Net.Drivers.FileSystem
             if (!filename.EndsWith(zipExt))
                 filename += zipExt;
 
-            var archivePath = Path.Combine(directoryInfo.FullName, filename);
+            var archivePath = PathHelper.SafelyCombine(directory.FullName, directory.FullName, filename);
             var newFile = new FileSystemFile(archivePath, volume);
 
             if (!await newFile.CanArchiveToAsync())
@@ -979,7 +1012,7 @@ namespace elFinder.Net.Drivers.FileSystem
 
             if (makedir)
             {
-                fromPath = Path.Combine(fromPath, Path.GetFileNameWithoutExtension(targetPath.File.Name));
+                fromPath = PathHelper.SafelyCombine(fromPath, fromPath, Path.GetFileNameWithoutExtension(targetPath.File.Name));
                 fromDir = new FileSystemDirectory(fromPath, volume);
 
                 if (!await fromDir.CanExtractToAsync())
@@ -1005,7 +1038,8 @@ namespace elFinder.Net.Drivers.FileSystem
             {
                 foreach (ZipArchiveEntry entry in archive.Entries)
                 {
-                    string fullName = PathHelper.GetFullPathNormalized(fromPath, entry.FullName);
+                    string fullName = PathHelper.GetFullPathNormalized(
+                        PathHelper.SafelyCombine(fromPath, fromPath, entry.FullName));
 
                     if (string.IsNullOrEmpty(entry.Name))
                     {
@@ -1101,6 +1135,17 @@ namespace elFinder.Net.Drivers.FileSystem
                     }
                 }
             }
+            else if (cmd.Encoding == "hash")
+            {
+                Func<Task<Stream>> openStreamFunc = async () => await cmd.ContentPath.File.OpenReadAsync(cancellationToken: cancellationToken);
+
+                OnBeforeWriteStream?.Invoke(this, (openStreamFunc, targetFile));
+                using (var readStream = await openStreamFunc())
+                {
+                    await targetFile.OverwriteAsync(readStream, cancellationToken: cancellationToken);
+                }
+                OnAfterWriteStream?.Invoke(this, (openStreamFunc, targetFile));
+            }
             else
             {
                 OnBeforeWriteContent?.Invoke(this, (cmd.Content, cmd.Encoding, targetFile));
@@ -1268,15 +1313,19 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
+            if (!IsObjectNameValid(cmd.DownloadFileName))
+                throw new InvalidFileNameException();
+
             var archiveFile = await zipDownloadPathProvider.ParseArchiveFileKeyAsync(cmd.ArchiveFileKey);
             var tempFileInfo = new FileInfo(archiveFile);
-            var memStream = new MemoryStream();
 
+            if (!tempFileInfo.Exists) throw new PermissionDeniedException($"Malformed key");
+
+            var memStream = new MemoryStream();
             using (var fileStream = tempFileInfo.OpenRead())
             {
                 await fileStream.CopyToAsync(memStream, StreamConstants.DefaultBufferSize, cancellationToken: cancellationToken);
             }
-
             tempFileInfo.Delete();
             memStream.Position = 0;
 
@@ -1402,6 +1451,11 @@ namespace elFinder.Net.Drivers.FileSystem
             return new FileSystemDirectory(fullPath, volume);
         }
 
+        protected bool IsObjectNameValid(string name)
+        {
+            return name == null || !name.Any(ch => InvalidFileNameChars.Contains(ch));
+        }
+
         protected virtual async Task<IFile> SafeCopyToAsync(IFile file, string newDir,
             IVolume destVolume, bool copyOverwrite = true, string suffix = null,
             CancellationToken cancellationToken = default)
@@ -1410,7 +1464,7 @@ namespace elFinder.Net.Drivers.FileSystem
 
             if (!file.CanCopy()) throw new PermissionDeniedException();
 
-            string newPath = Path.Combine(newDir, file.Name);
+            string newPath = PathHelper.SafelyCombine(newDir, newDir, file.Name);
             IFile newFile = new FileSystemFile(newPath, destVolume);
             var isOverwrite = true;
 
@@ -1419,7 +1473,7 @@ namespace elFinder.Net.Drivers.FileSystem
                 if (!copyOverwrite)
                 {
                     var newName = await newFile.GetCopyNameAsync(suffix, cancellationToken: cancellationToken);
-                    newPath = Path.Combine(newDir, newName);
+                    newPath = PathHelper.SafelyCombine(newDir, newDir, newName);
                     isOverwrite = false;
                 }
             }
@@ -1439,7 +1493,7 @@ namespace elFinder.Net.Drivers.FileSystem
 
             if (!file.CanMove()) throw new PermissionDeniedException();
 
-            string newPath = Path.Combine(newDir, file.Name);
+            string newPath = PathHelper.SafelyCombine(newDir, newDir, file.Name);
             IFile newFile = new FileSystemFile(newPath, destVolume);
 
             if (await newFile.ExistsAsync)
@@ -1447,7 +1501,7 @@ namespace elFinder.Net.Drivers.FileSystem
                 if (!copyOverwrite)
                 {
                     var newName = await newFile.GetCopyNameAsync(suffix, cancellationToken: cancellationToken);
-                    newPath = Path.Combine(newDir, newName);
+                    newPath = PathHelper.SafelyCombine(newDir, newDir, newName);
                 }
                 else
                 {
@@ -1506,7 +1560,8 @@ namespace elFinder.Net.Drivers.FileSystem
 
                 foreach (var dir in await currentDir.GetDirectoriesAsync(cancellationToken: cancellationToken))
                 {
-                    var newDir = new FileSystemDirectory(Path.Combine(currentNewDest.FullName, dir.Name), directory.Volume);
+                    var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(
+                        currentNewDest.FullName, currentNewDest.FullName, dir.Name), currentNewDest.Volume);
                     queue.Enqueue((dir, newDir));
                 }
 
@@ -1550,7 +1605,8 @@ namespace elFinder.Net.Drivers.FileSystem
 
                 foreach (var dir in await currentDir.GetDirectoriesAsync(cancellationToken: cancellationToken))
                 {
-                    var newDir = new FileSystemDirectory(Path.Combine(currentNewDest.FullName, dir.Name), srcDir.Volume);
+                    var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(
+                        currentNewDest.FullName, currentNewDest.FullName, dir.Name), currentNewDest.Volume);
                     queue.Enqueue((dir, newDir));
                 }
 

programming_language: C#
filename: FileSystemFile.cs
change_type: ModificationType.MODIFY
diff: @@ -209,7 +209,7 @@ namespace elFinder.Net.Drivers.FileSystem
 
             if (verify && !this.CanRename()) throw new PermissionDeniedException();
 
-            var newPath = PathHelper.GetFullPath(Parent.FullName, newName);
+            var newPath = PathHelper.GetFullPath(PathHelper.SafelyCombine(Parent.FullName, Parent.FullName, newName));
             fileInfo.MoveTo(newPath);
             return Task.FromResult<IFile>(new FileSystemFile(newPath, volume));
         }

programming_language: C#
filename: PathHelper.cs
change_type: ModificationType.MODIFY
diff: @@ -1,24 +1,38 @@
-﻿using System.IO;
+﻿using elFinder.Net.Core.Exceptions;
+using System.IO;
 
 namespace elFinder.Net.Drivers.FileSystem.Helpers
 {
     public static class PathHelper
     {
-        public static string GetFullPath(params string[] paths)
+        private static readonly char[] SeparatorChars = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
+
+        public static string GetFullPath(string path)
         {
-            var fullPath = Path.GetFullPath(Path.Combine(paths));
+            var fullPath = Path.GetFullPath(path);
             return fullPath;
         }
 
-        public static string GetFullPathNormalized(params string[] paths)
+        public static string GetFullPathNormalized(string path)
         {
-            var fullPath = Path.GetFullPath(Path.Combine(paths)).TrimEnd(Path.DirectorySeparatorChar);
+            var fullPath = Path.GetFullPath(path).TrimEnd(SeparatorChars);
             return fullPath;
         }
 
         public static string NormalizePath(string fullPath)
         {
-            return fullPath.TrimEnd(Path.DirectorySeparatorChar);
+            return fullPath.TrimEnd(SeparatorChars);
+        }
+
+        public static string SafelyCombine(string fromParent, params string[] paths)
+        {
+            var finalPath = Path.GetFullPath(Path.Combine(paths))
+                .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
+
+            if (!finalPath.StartsWith(fromParent.TrimEnd(SeparatorChars) + Path.DirectorySeparatorChar))
+                throw new PermissionDeniedException("Path must be inside parent");
+
+            return finalPath;
         }
     }
 }

programming_language: C#
filename: IZipDownloadPathProvider.cs
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,8 @@
-﻿using System.IO;
+﻿using elFinder.Net.Core.Exceptions;
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
 using System.Threading.Tasks;
 
 namespace elFinder.Net.Drivers.FileSystem
@@ -11,11 +15,15 @@ namespace elFinder.Net.Drivers.FileSystem
 
     public class TempZipDownloadPathProvider : IZipDownloadPathProvider
     {
+        private readonly HMAC _hmac = new HMACSHA256();
+        private static readonly string Postfix = '_' + nameof(elFinder);
+
         public Task<(string ArchiveFilePath, string ArchiveFileKey)> GetFileForArchivingAsync()
         {
-            var tempFile = Path.GetTempFileName();
+            var bytes = Encoding.UTF8.GetBytes(Path.GetTempFileName() + Guid.NewGuid().ToString());
+            var tempFile = Path.Combine(Path.GetTempPath(),
+                BitConverter.ToString(_hmac.ComputeHash(bytes)).Replace("-", string.Empty) + Postfix);
             var tempFileName = Path.GetFileName(tempFile);
-
             return Task.FromResult((tempFile, tempFileName));
         }
 
@@ -23,7 +31,18 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             var tempDirPath = Path.GetTempPath();
 
-            return Task.FromResult(Path.Combine(tempDirPath, archiveFileKey));
+            if (Path.IsPathRooted(archiveFileKey)) throw new PermissionDeniedException("Malformed key");
+
+            var fullPath = Path.GetFullPath(Path.Combine(tempDirPath, archiveFileKey));
+            if (!fullPath.StartsWith(tempDirPath.EndsWith($"{Path.DirectorySeparatorChar}")
+                ? tempDirPath : (tempDirPath + Path.DirectorySeparatorChar)))
+                throw new PermissionDeniedException("Malformed key");
+
+            var fileName = Path.GetFileName(fullPath);
+            if (!fileName.EndsWith(Postfix))
+                throw new PermissionDeniedException("Malformed key");
+
+            return Task.FromResult(fullPath);
         }
     }
 }

programming_language: C#