diff --git a/Creek.FileRepository/Helpers/StringHelper.cs b/Creek.FileRepository/Helpers/StringHelper.cs new file mode 100644 index 0000000..82e235b --- /dev/null +++ b/Creek.FileRepository/Helpers/StringHelper.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Creek.FileRepository.Helpers +{ + internal class StringHelper + { + public static bool IsInvalidFileName(string filename) + { + char[] invalidCharacters = new char[] { '<', '>', ':', '\'', '/', '\\', '|', '?', '*' }; + + return Array.Exists(invalidCharacters, character => filename.Contains(character.ToString())); + } + + internal static string MakeValidFileName(string originalFileName) + { + char[] invalids = System.IO.Path.GetInvalidFileNameChars(); + string validFileName = String.Join("_", originalFileName.Split(invalids, StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.'); + + // Additionally remove Windows characters explicitly, in case we're running on Linux (e.g. in Docker) but need to access the files in Windows + string[] charsToRemove = new string[] { "<", ">", ":", "\"", "/", "\\", "|", "?", "*" }; + + validFileName = RemoveCharsFromString(charsToRemove, validFileName); + + return validFileName; + } + + internal static string RemoveCharsFromString(string[] charsToRemove, string str) + { + foreach (string c in charsToRemove) + { + str = str.Replace(c, string.Empty); + } + + return str; + } + } +} diff --git a/Creek.FileRepository/Repositories/SftpRepository.cs b/Creek.FileRepository/Repositories/SftpRepository.cs index 76dc630..0f4aeba 100644 --- a/Creek.FileRepository/Repositories/SftpRepository.cs +++ b/Creek.FileRepository/Repositories/SftpRepository.cs @@ -1,4 +1,5 @@ -using Creek.FileRepository.Models; +using Creek.FileRepository.Helpers; +using Creek.FileRepository.Models; using Microsoft.Extensions.Configuration; using Renci.SshNet; using Renci.SshNet.Sftp; @@ -40,6 +41,11 @@ namespace Creek.FileRepository.Repositories /// Returns the filename. public async Task CreateFileAsync(RepoFile file) { + if (StringHelper.IsInvalidFileName(file.Filename)) + { + throw new ArgumentException($"The filename for'{nameof(file)}' contains invalid characters for a filename.", nameof(file)); + } + bool canOverride = false; this.UploadFile(file, canOverride); @@ -57,6 +63,10 @@ namespace Creek.FileRepository.Repositories { throw new ArgumentException($"'{nameof(filename)}' cannot be null or empty.", nameof(filename)); } + else if (StringHelper.IsInvalidFileName(filename)) + { + throw new ArgumentException($"'{nameof(filename)}' contains invalid characters for a filename.", nameof(filename)); + } using (var client = new SftpClient(this.host, this.port == 0 ? 22 : this.port, this.username, this.password)) { @@ -91,6 +101,10 @@ namespace Creek.FileRepository.Repositories { throw new ArgumentException($"'{nameof(filename)}' cannot be null or empty.", nameof(filename)); } + else if (StringHelper.IsInvalidFileName(filename)) + { + throw new ArgumentException($"'{nameof(filename)}' contains invalid characters for a filename.", nameof(filename)); + } RepoFile repoFile = new RepoFile(); repoFile.Content = new System.IO.MemoryStream(); @@ -128,6 +142,11 @@ namespace Creek.FileRepository.Repositories /// Returns true if update was successful. public async Task UpdateFileAsync(RepoFile file) { + if (StringHelper.IsInvalidFileName(file.Filename)) + { + throw new ArgumentException($"The filename for'{nameof(file)}' contains invalid characters for a filename.", nameof(file)); + } + bool canOverride = true; this.UploadFile(file, canOverride); diff --git a/UnitTests/SftpRepositoryShould.cs b/UnitTests/SftpRepositoryShould.cs index 6b1f8a9..cf0b52f 100644 --- a/UnitTests/SftpRepositoryShould.cs +++ b/UnitTests/SftpRepositoryShould.cs @@ -20,6 +20,12 @@ namespace UnitTests { } + [Test] + public async Task ShouldNotCreateAFileWithAnInvalidFilename() + { + Assert.That(() => CreateTestFile("create>()); + } + [Test] public async Task ShouldCreateAFileWithoutThrowing() {