CVE-2021-23407: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: 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.
CWE 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.
CVSS v3 base score: 7.5
Published: 2021-07-14T17:15Z
Repository: https://github.com/trannamtrung1st/elFinder.Net.Core
Language: C#
Files changed: elFinder.Net.Core/Demos/elFinder.Net.AdvancedDemo/Startup.cs, elFinder.Net.Core/Demos/elFinder.Net.Demo31/Startup.cs, elFinder.Net.Core/elFinder.Net.Core/Connector.cs, elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidDirNameException.cs, elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidFileNameException.cs, elFinder.Net.Core/elFinder.Net.Core/Models/Command/PutCommand.cs, elFinder.Net.Core/elFinder.Net.Core/Models/PathInfo.cs, elFinder.Net.Core/elFinder.Net.Core/Models/Response/ErrorResponse.cs, elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IDirectoryExtensions.cs, elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IFileExtensions.cs, elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDirectory.cs, elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDriver.cs, elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemFile.cs, elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Helpers/PathHelper.cs, elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/IZipDownloadPathProvider.cs
Commit date: 2021-07-01 18:12:30 +0700
Commit message: Finish security patch 1.2.4

Diff: diff --git a/elFinder.Net.Core/Demos/elFinder.Net.AdvancedDemo/Startup.cs b/elFinder.Net.Core/Demos/elFinder.Net.AdvancedDemo/Startup.cs
index 008ae77..d09d56d 100644
--- a/elFinder.Net.Core/Demos/elFinder.Net.AdvancedDemo/Startup.cs
+++ b/elFinder.Net.Core/Demos/elFinder.Net.AdvancedDemo/Startup.cs
@@ -37,11 +37,11 @@ namespace elFinder.Net.AdvancedDemo
             {
                 basePath = WebRootPath;
             }
 
             path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
-            return PathHelper.GetFullPath(basePath, path);
+            return PathHelper.GetFullPath(Path.Combine(basePath, path));
         }
 
         public IConfiguration Configuration { get; }
 
         // This method gets called by the runtime. Use this method to add services to the container.
diff --git a/elFinder.Net.Core/Demos/elFinder.Net.Demo31/Startup.cs b/elFinder.Net.Core/Demos/elFinder.Net.Demo31/Startup.cs
index 3605b02..b4e3154 100644
--- a/elFinder.Net.Core/Demos/elFinder.Net.Demo31/Startup.cs
+++ b/elFinder.Net.Core/Demos/elFinder.Net.Demo31/Startup.cs
@@ -7,10 +7,11 @@ using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.ResponseCompression;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using System.Collections.Generic;
+using System.IO;
 
 namespace elFinder.Net.Demo31
 {
     public class Startup
     {
@@ -28,11 +29,11 @@ namespace elFinder.Net.Demo31
             {
                 basePath = WebRootPath;
             }
 
             path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
-            return PathHelper.GetFullPath(basePath, path);
+            return PathHelper.GetFullPath(Path.Combine(basePath, path));
         }
 
         public IConfiguration Configuration { get; }
 
         // This method gets called by the runtime. Use this method to add services to the container.
diff --git a/elFinder.Net.Core/elFinder.Net.Core/Connector.cs b/elFinder.Net.Core/elFinder.Net.Core/Connector.cs
index a406205..5c247ed 100644
--- a/elFinder.Net.Core/elFinder.Net.Core/Connector.cs
+++ b/elFinder.Net.Core/elFinder.Net.Core/Connector.cs
@@ -372,10 +372,17 @@ namespace elFinder.Net.Core
                             var targetPath = putCmd.TargetPath;
 
                             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);
                         }
                     case ConnectorCommand.Cmd_Size:
                         {
diff --git a/elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidDirNameException.cs b/elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidDirNameException.cs
new file mode 100644
index 0000000..9306fb9
--- /dev/null
+++ b/elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidDirNameException.cs
@@ -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
+            };
+        }
+    }
+}
diff --git a/elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidFileNameException.cs b/elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidFileNameException.cs
new file mode 100644
index 0000000..7c1080d
--- /dev/null
+++ b/elFinder.Net.Core/elFinder.Net.Core/Exceptions/InvalidFileNameException.cs
@@ -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
+            };
+        }
+    }
+}
diff --git a/elFinder.Net.Core/elFinder.Net.Core/Models/Command/PutCommand.cs b/elFinder.Net.Core/elFinder.Net.Core/Models/Command/PutCommand.cs
index 579f4f1..af5b907 100644
--- a/elFinder.Net.Core/elFinder.Net.Core/Models/Command/PutCommand.cs
+++ b/elFinder.Net.Core/elFinder.Net.Core/Models/Command/PutCommand.cs
@@ -2,7 +2,9 @@
 {
     public class PutCommand : TargetCommand
     {
         public string Content { get; set; }
         public string Encoding { get; set; }
+
+        public PathInfo ContentPath { get; set; }
     }
 }
diff --git a/elFinder.Net.Core/elFinder.Net.Core/Models/PathInfo.cs b/elFinder.Net.Core/elFinder.Net.Core/Models/PathInfo.cs
index 243f9de..f0ae1a8 100644
--- a/elFinder.Net.Core/elFinder.Net.Core/Models/PathInfo.cs
+++ b/elFinder.Net.Core/elFinder.Net.Core/Models/PathInfo.cs
@@ -1,19 +1,25 @@
-﻿namespace elFinder.Net.Core
+﻿using elFinder.Net.Core.Exceptions;
+
+namespace elFinder.Net.Core
 {
     public class PathInfo
     {
         public PathInfo(string path, IVolume volume, IFile file, string hashedTarget) : this(path, volume, hashedTarget, false)
         {
             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)
         {
             Path = path;
diff --git a/elFinder.Net.Core/elFinder.Net.Core/Models/Response/ErrorResponse.cs b/elFinder.Net.Core/elFinder.Net.Core/Models/Response/ErrorResponse.cs
index 2b9876d..2f0d554 100644
--- a/elFinder.Net.Core/elFinder.Net.Core/Models/Response/ErrorResponse.cs
+++ b/elFinder.Net.Core/elFinder.Net.Core/Models/Response/ErrorResponse.cs
@@ -82,8 +82,10 @@ namespace elFinder.Net.Core.Models.Response
         public const string PermissionDenied = "errPerm";
         public const string UploadFileSize = "errUploadFileSize";
         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
     }
 }
diff --git a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IDirectoryExtensions.cs b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IDirectoryExtensions.cs
index 25bc8a1..e461808 100644
--- a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IDirectoryExtensions.cs
+++ b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IDirectoryExtensions.cs
@@ -1,6 +1,7 @@
 ﻿using elFinder.Net.Core;
+using elFinder.Net.Drivers.FileSystem.Helpers;
 using System;
 using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -20,11 +21,11 @@ namespace elFinder.Net.Drivers.FileSystem.Extensions
             {
                 suffix = " - copy";
             }
 
             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
             {
                 var search = $"{name}*";
                 var sameNames = (await directory.Parent.GetDirectoriesAsync(search, false, _ => true, cancellationToken: cancellationToken))
diff --git a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IFileExtensions.cs b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IFileExtensions.cs
index da49df9..3150e94 100644
--- a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IFileExtensions.cs
+++ b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Extensions/IFileExtensions.cs
@@ -1,6 +1,7 @@
 ﻿using elFinder.Net.Core;
+using elFinder.Net.Drivers.FileSystem.Helpers;
 using System;
 using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -21,11 +22,11 @@ namespace elFinder.Net.Drivers.FileSystem.Extensions
             {
                 suffix = " - copy";
             }
 
             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
             {
                 var search = $"{name}*{extension}";
                 var sameNames = (await file.Parent.GetFilesAsync(search, false, _ => true, cancellationToken: cancellationToken))
diff --git a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDirectory.cs b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDirectory.cs
index ea8f376..78601e4 100644
--- a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDirectory.cs
+++ b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDirectory.cs
@@ -249,11 +249,11 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
             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));
         }
 
         public virtual async Task<IDirectory> MoveToAsync(string newDest, IVolume destVolume,
diff --git a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDriver.cs b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDriver.cs
index f229cc2..0e26ced 100644
--- a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDriver.cs
+++ b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemDriver.cs
@@ -27,10 +27,11 @@ using System.Threading.Tasks;
 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;
         protected readonly IVideoEditor videoEditor;
         protected readonly IZipDownloadPathProvider zipDownloadPathProvider;
@@ -125,20 +126,24 @@ namespace elFinder.Net.Drivers.FileSystem
 
         public virtual async Task<MkdirResponse> MkdirAsync(MkdirCommand cmd, CancellationToken cancellationToken = default)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
+            if (!IsObjectNameValid(cmd.Name))
+                throw new InvalidDirNameException();
+
             var mkdirResp = new MkdirResponse();
             var targetPath = cmd.TargetPath;
             var volume = targetPath.Volume;
             var targetHash = targetPath.HashedTarget;
 
             if (!targetPath.Directory.CanCreateObject()) throw new PermissionDeniedException();
 
             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);
                 OnAfterMakeDir?.Invoke(this, newDir);
 
@@ -147,11 +152,12 @@ 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);
                 OnAfterMakeDir?.Invoke(this, newDir);
 
@@ -168,17 +174,21 @@ namespace elFinder.Net.Drivers.FileSystem
 
         public virtual async Task<MkfileResponse> MkfileAsync(MkfileCommand cmd, CancellationToken cancellationToken = default)
         {
             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);
             OnAfterMakeFile?.Invoke(this, newFile);
 
@@ -353,10 +363,15 @@ namespace elFinder.Net.Drivers.FileSystem
 
             var renameResp = new RenameResponse();
             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;
 
                 OnBeforeRename?.Invoke(this, (targetPath.Directory, cmd.Name));
@@ -464,10 +479,19 @@ namespace elFinder.Net.Drivers.FileSystem
 
         public virtual async Task<UploadResponse> UploadAsync(UploadCommand cmd, CancellationToken cancellationToken = default)
         {
             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;
             var warning = uploadResp.GetWarnings();
             var warningDetails = uploadResp.GetWarningDetails();
@@ -511,22 +535,22 @@ 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;
 
                     if (await uploadFileInfo.ExistsAsync)
                     {
                         if (cmd.Renames.Contains(formFile.FileName))
                         {
                             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)
                                 backupName = await bakFile.GetCopyNameAsync(cmd.Suffix, cancellationToken: cancellationToken);
 
@@ -539,11 +563,11 @@ namespace elFinder.Net.Drivers.FileSystem
                             uploadFileInfo = new FileSystemFile(uploadFullName, volume);
                         }
                         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;
                         }
                         else if (!uploadFileInfo.ObjectAttribute.Write)
                             throw new PermissionDeniedException();
@@ -713,10 +737,16 @@ namespace elFinder.Net.Drivers.FileSystem
 
         public virtual async Task<PasteResponse> PasteAsync(PasteCommand cmd, CancellationToken cancellationToken = default)
         {
             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;
             var dstVolume = dstPath.Volume;
             var copyOverwrite = dstPath.Volume.CopyOverwrite;
@@ -724,18 +754,18 @@ namespace elFinder.Net.Drivers.FileSystem
             foreach (var src in cmd.TargetPaths)
             {
                 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)
                             backupName = await bakDir.GetCopyNameAsync(cmd.Suffix, cancellationToken: cancellationToken);
 
@@ -788,20 +818,20 @@ namespace elFinder.Net.Drivers.FileSystem
                 }
                 else
                 {
                     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;
 
                     if (exists && cmd.Renames.Contains(newDestFile.Name))
                     {
                         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)
                             backupName = await bakFile.GetCopyNameAsync(cmd.Suffix, cancellationToken: cancellationToken);
 
@@ -844,11 +874,11 @@ namespace elFinder.Net.Drivers.FileSystem
                 var dstVolume = src.Volume;
 
                 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);
                     OnAfterCopy?.Invoke(this, (src.Directory, dupDir, true));
 
@@ -904,25 +934,28 @@ namespace elFinder.Net.Drivers.FileSystem
 
         public virtual async Task<ArchiveResponse> ArchiveAsync(ArchiveCommand cmd, CancellationToken cancellationToken = default)
         {
             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}";
 
             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())
                 throw new PermissionDeniedException();
 
@@ -977,11 +1010,11 @@ namespace elFinder.Net.Drivers.FileSystem
 
             if (!targetPath.File.CanExtract()) throw new PermissionDeniedException();
 
             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())
                     throw new PermissionDeniedException();
 
@@ -1003,11 +1036,12 @@ namespace elFinder.Net.Drivers.FileSystem
             OnBeforeExtract?.Invoke(this, (parentDir, fromDir, targetPath.File));
             using (var archive = ZipFile.OpenRead(targetPath.File.FullName))
             {
                 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))
                     {
                         var dir = new FileSystemDirectory(fullName, volume);
                         if (!await dir.CanExtractToAsync())
@@ -1099,10 +1133,21 @@ namespace elFinder.Net.Drivers.FileSystem
                             OnAfterWriteStream?.Invoke(this, (openFunc, targetFile));
                         }
                     }
                 }
             }
+            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));
                 using (var fileStream = await targetFile.OpenWriteAsync(cancellationToken: cancellationToken))
                 using (var writer = new StreamWriter(fileStream, Encoding.GetEncoding(cmd.Encoding)))
@@ -1266,19 +1311,23 @@ namespace elFinder.Net.Drivers.FileSystem
 
         public virtual async Task<FileResponse> ZipdlRawAsync(ZipdlCommand cmd, CancellationToken cancellationToken = default)
         {
             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;
 
             return new FileResponse
             {
@@ -1400,28 +1449,33 @@ namespace elFinder.Net.Drivers.FileSystem
         public IDirectory CreateDirectory(string fullPath, IVolume volume)
         {
             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)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
             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;
 
             if (File.Exists(newPath))
             {
                 if (!copyOverwrite)
                 {
                     var newName = await newFile.GetCopyNameAsync(suffix, cancellationToken: cancellationToken);
-                    newPath = Path.Combine(newDir, newName);
+                    newPath = PathHelper.SafelyCombine(newDir, newDir, newName);
                     isOverwrite = false;
                 }
             }
 
             OnBeforeCopy?.Invoke(this, (file, newPath, isOverwrite));
@@ -1437,19 +1491,19 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
             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)
             {
                 if (!copyOverwrite)
                 {
                     var newName = await newFile.GetCopyNameAsync(suffix, cancellationToken: cancellationToken);
-                    newPath = Path.Combine(newDir, newName);
+                    newPath = PathHelper.SafelyCombine(newDir, newDir, newName);
                 }
                 else
                 {
                     OnBeforeMove?.Invoke(this, (file, newPath, true));
 
@@ -1504,11 +1558,12 @@ namespace elFinder.Net.Drivers.FileSystem
                     OnAfterMakeDir?.Invoke(this, currentNewDest);
                 }
 
                 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));
                 }
 
                 foreach (var file in await currentDir.GetFilesAsync(cancellationToken: cancellationToken))
                 {
@@ -1548,11 +1603,12 @@ namespace elFinder.Net.Drivers.FileSystem
                     OnAfterMakeDir(this, currentNewDest);
                 }
 
                 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));
                 }
 
                 foreach (var file in await currentDir.GetFilesAsync(cancellationToken: cancellationToken))
                 {
diff --git a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemFile.cs b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemFile.cs
index 6dbbb9b..5d8d4d1 100644
--- a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemFile.cs
+++ b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/FileSystemFile.cs
@@ -207,11 +207,11 @@ namespace elFinder.Net.Drivers.FileSystem
         {
             cancellationToken.ThrowIfCancellationRequested();
 
             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));
         }
 
         public virtual async Task<IFile> CopyToAsync(string newDest, IVolume destVolume, bool copyOverwrite,
diff --git a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Helpers/PathHelper.cs b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Helpers/PathHelper.cs
index 0460432..51c8cac 100644
--- a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Helpers/PathHelper.cs
+++ b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/Helpers/PathHelper.cs
@@ -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;
         }
     }
 }
diff --git a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/IZipDownloadPathProvider.cs b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/IZipDownloadPathProvider.cs
index 6f1b080..1b3bcb1 100644
--- a/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/IZipDownloadPathProvider.cs
+++ b/elFinder.Net.Core/elFinder.Net.Drivers.FileSystem/IZipDownloadPathProvider.cs
@@ -1,6 +1,10 @@
-﻿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
 {
     public interface IZipDownloadPathProvider
@@ -9,21 +13,36 @@ namespace elFinder.Net.Drivers.FileSystem
         Task<string> ParseArchiveFileKeyAsync(string archiveFileKey);
     }
 
     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));
         }
 
         public Task<string> ParseArchiveFileKeyAsync(string archiveFileKey)
         {
             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);
         }
     }
 }

Vulnerable code:             return PathHelper.GetFullPath(basePath, path);
            return PathHelper.GetFullPath(basePath, path);
﻿namespace elFinder.Net.Core
            if (!Directory.Exists(Path.Combine(directory.Parent.FullName, newName)))
            if (!File.Exists(Path.Combine(file.DirectoryName, newName)))
            var newPath = PathHelper.GetFullPath(Parent.FullName, newName);
                var newDir = new FileSystemDirectory(Path.Combine(targetPath.Directory.FullName, cmd.Name), volume);
                var newDir = new FileSystemDirectory(Path.Combine(targetPath.Directory.FullName, dirName), volume);
            var newFile = new FileSystemFile(Path.Combine(targetPath.Directory.FullName, cmd.Name), volume);
                    string uploadFullName = Path.Combine(dest.FullName, Path.GetFileName(formFile.FileName));
                            var fullBakName = Path.Combine(uploadFileInfo.Parent.FullName, backupName);
                            uploadFullName = Path.Combine(uploadFileInfo.DirectoryName, newName);
                    var newDest = Path.Combine(dstPath.Directory.FullName, src.Directory.Name);
                        var fullBakName = Path.Combine(newDestDir.Parent.FullName, backupName);
                    var newDest = Path.Combine(dstPath.Directory.FullName, file.Name);
                        var fullBakName = Path.Combine(newDestFile.Parent.FullName, backupName);
                    var newDest = Path.Combine(src.Directory.Parent.FullName, newName);
            var directoryInfo = targetPath.Directory;
            var archivePath = Path.Combine(directoryInfo.FullName, filename);
                fromPath = Path.Combine(fromPath, Path.GetFileNameWithoutExtension(targetPath.File.Name));
                    string fullName = PathHelper.GetFullPathNormalized(fromPath, entry.FullName);
            var memStream = new MemoryStream();

            string newPath = Path.Combine(newDir, file.Name);
                    newPath = Path.Combine(newDir, newName);
            string newPath = Path.Combine(newDir, file.Name);
                    newPath = Path.Combine(newDir, newName);
                    var newDir = new FileSystemDirectory(Path.Combine(currentNewDest.FullName, dir.Name), directory.Volume);
                    var newDir = new FileSystemDirectory(Path.Combine(currentNewDest.FullName, dir.Name), srcDir.Volume);
            var newPath = PathHelper.GetFullPath(Parent.FullName, newName);
﻿using System.IO;
        public static string GetFullPath(params string[] paths)
            var fullPath = Path.GetFullPath(Path.Combine(paths));
        public static string GetFullPathNormalized(params string[] paths)
            var fullPath = Path.GetFullPath(Path.Combine(paths)).TrimEnd(Path.DirectorySeparatorChar);
            return fullPath.TrimEnd(Path.DirectorySeparatorChar);
﻿using System.IO;
            var tempFile = Path.GetTempFileName();

            return Task.FromResult(Path.Combine(tempDirPath, archiveFileKey));
Fixed code:             return PathHelper.GetFullPath(Path.Combine(basePath, path));
using System.IO;
            return PathHelper.GetFullPath(Path.Combine(basePath, path));
                            if (putCmd.Encoding == "hash")
                            {
                                putCmd.ContentPath = await ParsePathAsync(putCmd.Content, cancellationToken: cancellationToken);
                                if (putCmd.ContentPath.IsDirectory)
                                    throw new NotFileException();
                            }

﻿using elFinder.Net.Core.Models.Response;

namespace elFinder.Net.Core.Exceptions
{
    public class InvalidDirNameException : ConnectorException
    {
        public InvalidDirNameException()
        {
            ErrorResponse = new ErrorResponse(this)
            {
                error = ErrorResponse.InvalidDirName
            };
        }
    }
}
﻿using elFinder.Net.Core.Models.Response;

namespace elFinder.Net.Core.Exceptions
{
    public class InvalidFileNameException : ConnectorException
    {
        public InvalidFileNameException()
        {
            ErrorResponse = new ErrorResponse(this)
            {
                error = ErrorResponse.InvalidFileName
            };
        }
    }
}

        public PathInfo ContentPath { get; set; }
﻿using elFinder.Net.Core.Exceptions;

namespace elFinder.Net.Core

            if (!volume.Own(FileSystem)) throw new PermissionDeniedException("Volume must own this path");

            if (!volume.Own(FileSystem)) throw new PermissionDeniedException("Volume must own this path");
        public const string InvalidFileName = "errInvName";
        public const string InvalidDirName = "errInvDirname";
using elFinder.Net.Drivers.FileSystem.Helpers;
            if (!Directory.Exists(PathHelper.SafelyCombine(directory.Parent.FullName, directory.Parent.FullName, newName)))
using elFinder.Net.Drivers.FileSystem.Helpers;
            if (!File.Exists(PathHelper.SafelyCombine(file.DirectoryName, file.DirectoryName, newName)))
            var newPath = PathHelper.GetFullPath(PathHelper.SafelyCombine(Parent.FullName, Parent.FullName, newName));
        private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars();
            if (!IsObjectNameValid(cmd.Name))
                throw new InvalidDirNameException();

                var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(targetPath.Directory.FullName,
                    targetPath.Directory.FullName, cmd.Name), volume);
                var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(targetPath.Directory.FullName,
                    targetPath.Directory.FullName, dirName), volume);
            if (!IsObjectNameValid(cmd.Name))
                throw new InvalidFileNameException();

            var newFile = new FileSystemFile(PathHelper.SafelyCombine(targetPath.Directory.FullName,
                targetPath.Directory.FullName, cmd.Name), volume);
            if (!IsObjectNameValid(cmd.Name))
                if (targetPath.IsDirectory)
                    throw new InvalidDirNameException();
                else throw new InvalidFileNameException();

            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();

                    string uploadFullName = PathHelper.SafelyCombine(dest.FullName, dest.FullName, Path.GetFileName(formFile.FileName));
                            var fullBakName = PathHelper.SafelyCombine(uploadFileInfo.Parent.FullName, uploadFileInfo.Parent.FullName, backupName);
                            uploadFullName = PathHelper.SafelyCombine(uploadFileInfo.DirectoryName, uploadFileInfo.DirectoryName, newName);
            if (cmd.Renames.Any(name => !IsObjectNameValid(name)))
                throw new InvalidFileNameException();

            if (!IsObjectNameValid(cmd.Suffix))
                throw new InvalidFileNameException();

                    var newDest = PathHelper.SafelyCombine(dstPath.Directory.FullName, dstPath.Directory.FullName, src.Directory.Name);
                        var fullBakName = PathHelper.SafelyCombine(newDestDir.Parent.FullName, newDestDir.Parent.FullName, backupName);
                    var newDest = PathHelper.SafelyCombine(dstPath.Directory.FullName, dstPath.Directory.FullName, file.Name);
                        var fullBakName = PathHelper.SafelyCombine(newDestFile.Parent.FullName, newDestFile.Parent.FullName, backupName);
                    var newDest = PathHelper.SafelyCombine(src.Directory.Parent.FullName, src.Directory.Parent.FullName, newName);
            if (!IsObjectNameValid(cmd.Name))
                throw new InvalidFileNameException();

            var directory = targetPath.Directory;
            var archivePath = PathHelper.SafelyCombine(directory.FullName, directory.FullName, filename);
                fromPath = PathHelper.SafelyCombine(fromPath, fromPath, Path.GetFileNameWithoutExtension(targetPath.File.Name));
                    string fullName = PathHelper.GetFullPathNormalized(
                        PathHelper.SafelyCombine(fromPath, fromPath, entry.FullName));
            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));
            }
            if (!IsObjectNameValid(cmd.DownloadFileName))
                throw new InvalidFileNameException();

            if (!tempFileInfo.Exists) throw new PermissionDeniedException($"Malformed key");

            var memStream = new MemoryStream();
        protected bool IsObjectNameValid(string name)
        {
            return name == null || !name.Any(ch => InvalidFileNameChars.Contains(ch));
        }

            string newPath = PathHelper.SafelyCombine(newDir, newDir, file.Name);
                    newPath = PathHelper.SafelyCombine(newDir, newDir, newName);
            string newPath = PathHelper.SafelyCombine(newDir, newDir, file.Name);
                    newPath = PathHelper.SafelyCombine(newDir, newDir, newName);
                    var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(
                        currentNewDest.FullName, currentNewDest.FullName, dir.Name), currentNewDest.Volume);
                    var newDir = new FileSystemDirectory(PathHelper.SafelyCombine(
                        currentNewDest.FullName, currentNewDest.FullName, dir.Name), currentNewDest.Volume);
            var newPath = PathHelper.GetFullPath(PathHelper.SafelyCombine(Parent.FullName, Parent.FullName, newName));
﻿using elFinder.Net.Core.Exceptions;
using System.IO;
        private static readonly char[] SeparatorChars = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };

        public static string GetFullPath(string path)
            var fullPath = Path.GetFullPath(path);
        public static string GetFullPathNormalized(string path)
            var fullPath = Path.GetFullPath(path).TrimEnd(SeparatorChars);
            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;
﻿using elFinder.Net.Core.Exceptions;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
        private readonly HMAC _hmac = new HMACSHA256();
        private static readonly string Postfix = '_' + nameof(elFinder);

            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);
            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);