using Health.Infrastructure.Files; namespace Health.Tests; public sealed class FilePathSecurityTests { private readonly Guid _userId = Guid.Parse("11111111-1111-1111-1111-111111111111"); private readonly string _fileName = "22222222-2222-2222-2222-222222222222.jpg"; [Fact] public void ProtectedUrl_ResolvesInsideCurrentUserDirectory() { var path = UserUploadPathResolver.Resolve( _userId, $"/api/files/content/{_fileName}"); Assert.NotNull(path); Assert.Contains(Path.Combine("users", _userId.ToString("N")), path); Assert.EndsWith(_fileName, path, StringComparison.OrdinalIgnoreCase); } [Fact] public void LegacyUrl_OnlyResolvesForItsOwner() { var legacy = $"/uploads/users/{_userId:N}/{_fileName}"; Assert.NotNull(UserUploadPathResolver.Resolve(_userId, legacy)); Assert.Null(UserUploadPathResolver.Resolve(Guid.NewGuid(), legacy)); } [Theory] [InlineData("/api/files/content/../../secret.jpg")] [InlineData("/api/files/content/not-a-guid.jpg")] [InlineData("/api/files/content/22222222-2222-2222-2222-222222222222.exe")] [InlineData("/uploads/reports/22222222-2222-2222-2222-222222222222.jpg")] public void UnsafeOrUnownedPath_IsRejected(string value) { Assert.Null(UserUploadPathResolver.Resolve(_userId, value)); } }