diff options
| author | Shadowghost <Shadowghost@users.noreply.github.com> | 2026-09-15 11:15:57 -0400 |
|---|---|---|
| committer | Cody Robibero <cody@robibe.ro> | 2026-09-15 11:15:57 -0400 |
| commit | 4e1c55031f1d0d63b1cbf44410f3189f0dbf5b73 (patch) | |
| tree | 6981d8f216a3ff668fb701731ae7a27601f521a5 | |
| parent | f62f3cf9b011e15e81d502f35fa9c7668a93aaa5 (diff) | |
Backport pull request #17929 from jellyfin/release-12.z
Fix ContainsSubPath check
Original-merge: b55ea60da6b9589767ba48e46f2597a5e4c8629d
Merged-by: crobibero <cody@robibe.ro>
Backported-by: Cody Robibero <cody@robibe.ro>
| -rw-r--r-- | Emby.Server.Implementations/IO/ManagedFileSystem.cs | 13 | ||||
| -rw-r--r-- | tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs | 35 |
2 files changed, 45 insertions, 3 deletions
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index ede9b27592..db743c8d31 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -489,11 +489,18 @@ namespace Emby.Server.Implementations.IO ArgumentException.ThrowIfNullOrEmpty(parentPath); ArgumentException.ThrowIfNullOrEmpty(path); - return path.Contains( - Path.TrimEndingDirectorySeparator(parentPath) + Path.DirectorySeparatorChar, - _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + var parent = Path.TrimEndingDirectorySeparator(parentPath); + + // The parent has to be an anchored prefix of the path, otherwise unrelated paths that merely + // contain the parent as a segment (e.g. /media and /data/media/tv) would be treated as related. + return path.Length > parent.Length + && path.StartsWith(parent, _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) + && (Path.EndsInDirectorySeparator(parent) || IsDirectorySeparator(path[parent.Length])); } + private static bool IsDirectorySeparator(char c) + => c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; + /// <inheritdoc /> public virtual bool AreEqual(string path1, string path2) { diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 6cadfacce8..b39ca83483 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -100,6 +100,41 @@ public partial class ManagedFileSystemTests Assert.Equal(expectedFileName, _sut.GetValidFilename(filename)); } + [Theory] + [InlineData("/media", "/media/tv", true)] + [InlineData("/media", "/media/tv/show/episode.mkv", true)] + [InlineData("/media/", "/media/tv", true)] + [InlineData("/", "/media", true)] + [InlineData("/media", "/media", false)] + [InlineData("/media", "/media/", true)] + [InlineData("/media", "/data/media/tv", false)] + [InlineData("/media", "/mediastuff/tv", false)] + [InlineData("/data/media", "/data/media/tv", true)] + [InlineData("/media/tv", "/media", false)] + [InlineData("/MEDIA", "/media/tv", false)] + public void ContainsSubPath_Unix_ReturnsExpected(string parentPath, string path, bool expected) + { + Assert.SkipWhen(OperatingSystem.IsWindows(), "Unix-only test"); + + Assert.Equal(expected, _sut.ContainsSubPath(parentPath, path)); + } + + [Theory] + [InlineData(@"C:\media", @"C:\media\tv", true)] + [InlineData(@"C:\media\", @"C:\media\tv", true)] + [InlineData(@"C:\", @"C:\media", true)] + [InlineData(@"C:\media", @"C:\media", false)] + [InlineData(@"C:\media", @"C:\data\media\tv", false)] + [InlineData(@"C:\media", @"C:\mediastuff\tv", false)] + [InlineData(@"C:\MEDIA", @"C:\media\tv", true)] + [InlineData(@"C:\media", @"C:\media/tv", true)] + public void ContainsSubPath_Windows_ReturnsExpected(string parentPath, string path, bool expected) + { + Assert.SkipUnless(OperatingSystem.IsWindows(), "Windows-only test"); + + Assert.Equal(expected, _sut.ContainsSubPath(parentPath, path)); + } + [Fact] public void GetFileInfo_DanglingSymlink_ExistsFalse() { |
