1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-19 17:32:38 +01:00

New: Validate that folders in paths don't start or end with a space

(cherry picked from commit 316b5cbf75b45ef9a25f96ce1f2fbed25ad94296)

Closes #9958
This commit is contained in:
Mark McDowall 2024-04-27 18:04:50 -07:00 committed by Bogdan
parent 6c456e57d8
commit 3a4446cc8e
3 changed files with 57 additions and 5 deletions

View File

@ -3,6 +3,7 @@
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -34,7 +35,7 @@ private IAppFolderInfo GetIAppDirectoryInfo()
[TestCase(@"\\Testserver\\Test\", @"\\Testserver\Test")] [TestCase(@"\\Testserver\\Test\", @"\\Testserver\Test")]
[TestCase(@"\\Testserver\Test\file.ext", @"\\Testserver\Test\file.ext")] [TestCase(@"\\Testserver\Test\file.ext", @"\\Testserver\Test\file.ext")]
[TestCase(@"\\Testserver\Test\file.ext\\", @"\\Testserver\Test\file.ext")] [TestCase(@"\\Testserver\Test\file.ext\\", @"\\Testserver\Test\file.ext")]
[TestCase(@"\\Testserver\Test\file.ext \\", @"\\Testserver\Test\file.ext")] [TestCase(@"\\Testserver\Test\file.ext ", @"\\Testserver\Test\file.ext")]
[TestCase(@"//CAPITAL//lower// ", @"\\CAPITAL\lower")] [TestCase(@"//CAPITAL//lower// ", @"\\CAPITAL\lower")]
public void Clean_Path_Windows(string dirty, string clean) public void Clean_Path_Windows(string dirty, string clean)
{ {
@ -334,5 +335,30 @@ public void GetAncestorFolders_should_return_all_ancestors_in_path_Linux()
result[2].Should().Be(@"TV"); result[2].Should().Be(@"TV");
result[3].Should().Be(@"Series Title"); result[3].Should().Be(@"Series Title");
} }
[TestCase(@"C:\Test\")]
[TestCase(@"C:\Test")]
[TestCase(@"C:\Test\TV\")]
[TestCase(@"C:\Test\TV")]
public void IsPathValid_should_be_true(string path)
{
path.AsOsAgnostic().IsPathValid(PathValidationType.CurrentOs).Should().BeTrue();
}
[TestCase(@"C:\Test \")]
[TestCase(@"C:\Test ")]
[TestCase(@"C:\ Test\")]
[TestCase(@"C:\ Test")]
[TestCase(@"C:\Test \TV")]
[TestCase(@"C:\ Test\TV")]
[TestCase(@"C:\Test \TV\")]
[TestCase(@"C:\ Test\TV\")]
[TestCase(@" C:\Test\TV\")]
[TestCase(@" C:\Test\TV")]
public void IsPathValid_should_be_false(string path)
{
path.AsOsAgnostic().IsPathValid(PathValidationType.CurrentOs).Should().BeFalse();
}
} }
} }

View File

@ -30,6 +30,12 @@ public static class PathExtensions
public static string CleanFilePath(this string path) public static string CleanFilePath(this string path)
{ {
if (path.IsNotNullOrWhiteSpace())
{
// Trim trailing spaces before checking if the path is valid so validation doesn't fail for something we can fix.
path = path.TrimEnd(' ');
}
Ensure.That(path, () => path).IsNotNullOrWhiteSpace(); Ensure.That(path, () => path).IsNotNullOrWhiteSpace();
Ensure.That(path, () => path).IsValidPath(PathValidationType.AnyOs); Ensure.That(path, () => path).IsValidPath(PathValidationType.AnyOs);
@ -38,10 +44,10 @@ public static string CleanFilePath(this string path)
// UNC // UNC
if (!info.FullName.Contains('/') && info.FullName.StartsWith(@"\\")) if (!info.FullName.Contains('/') && info.FullName.StartsWith(@"\\"))
{ {
return info.FullName.TrimEnd('/', '\\', ' '); return info.FullName.TrimEnd('/', '\\');
} }
return info.FullName.TrimEnd('/').Trim('\\', ' '); return info.FullName.TrimEnd('/').Trim('\\');
} }
public static bool PathNotEquals(this string firstPath, string secondPath, StringComparison? comparison = null) public static bool PathNotEquals(this string firstPath, string secondPath, StringComparison? comparison = null)
@ -155,6 +161,23 @@ public static bool IsPathValid(this string path, PathValidationType validationTy
return false; return false;
} }
if (path.Trim() != path)
{
return false;
}
var directoryInfo = new DirectoryInfo(path);
while (directoryInfo != null)
{
if (directoryInfo.Name.Trim() != directoryInfo.Name)
{
return false;
}
directoryInfo = directoryInfo.Parent;
}
if (validationType == PathValidationType.AnyOs) if (validationType == PathValidationType.AnyOs)
{ {
return IsPathValidForWindows(path) || IsPathValidForNonWindows(path); return IsPathValidForWindows(path) || IsPathValidForNonWindows(path);
@ -292,6 +315,11 @@ public static string ProcessNameToExe(this string processName)
return processName; return processName;
} }
public static string CleanPath(this string path)
{
return Path.Join(path.Split(Path.DirectorySeparatorChar).Select(s => s.Trim()).ToArray());
}
public static string GetAppDataPath(this IAppFolderInfo appFolderInfo) public static string GetAppDataPath(this IAppFolderInfo appFolderInfo)
{ {
return appFolderInfo.AppDataFolder; return appFolderInfo.AppDataFolder;

View File

@ -107,8 +107,6 @@ public MovieController(IBroadcastSignalRMessage signalRBroadcaster,
.When(s => s.Path.IsNullOrWhiteSpace()); .When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.Title).NotEmpty().When(s => s.TmdbId <= 0); PostValidator.RuleFor(s => s.Title).NotEmpty().When(s => s.TmdbId <= 0);
PostValidator.RuleFor(s => s.TmdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator); PostValidator.RuleFor(s => s.TmdbId).NotNull().NotEmpty().SetValidator(moviesExistsValidator);
PutValidator.RuleFor(s => s.Path).IsValidPath();
} }
[HttpGet] [HttpGet]