Harden remaining path-construction sinks against traversal

This commit is contained in:
Shadowghost
2026-07-17 16:51:19 +02:00
parent 1a45fc82b5
commit 21801e8ba1
5 changed files with 17 additions and 10 deletions
@@ -5,6 +5,7 @@ using System.IO;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Helpers;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding;
@@ -63,8 +64,7 @@ public class HlsSegmentController : BaseJellyfinApiController
var file = string.Concat(segmentId, Path.GetExtension(Request.Path.Value.AsSpan()));
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture))
if (!PathHelper.IsContainedIn(transcodePath, file))
{
return BadRequest("Invalid segment.");
}
@@ -89,8 +89,7 @@ public class HlsSegmentController : BaseJellyfinApiController
var file = string.Concat(playlistId, Path.GetExtension(Request.Path.Value.AsSpan()));
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture)
if (!PathHelper.IsContainedIn(transcodePath, file)
|| Path.GetExtension(file.AsSpan()).Equals(".m3u8", StringComparison.OrdinalIgnoreCase))
{
return BadRequest("Invalid segment.");
@@ -144,8 +143,7 @@ public class HlsSegmentController : BaseJellyfinApiController
var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodeFolderPath, file));
var fileDir = Path.GetDirectoryName(file);
if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodeFolderPath, StringComparison.InvariantCulture))
if (!PathHelper.IsContainedIn(transcodeFolderPath, file))
{
return BadRequest("Invalid segment.");
}
+7 -1
View File
@@ -125,7 +125,13 @@ public class ImageController : BaseJellyfinApiController
{
// Handle image/png; charset=utf-8
var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
var userConfigurationDirectoryPath = _serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath;
var userDataPath = Path.Combine(userConfigurationDirectoryPath, user.Username);
if (!PathHelper.IsContainedIn(userConfigurationDirectoryPath, userDataPath))
{
return BadRequest("Invalid user.");
}
if (user.ProfileImage is not null)
{
await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
@@ -6,6 +6,7 @@ using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Api;
using MediaBrowser.Common.Plugins;
@@ -228,8 +229,8 @@ public class PluginsController : BaseJellyfinApiController
if (!string.IsNullOrEmpty(plugin.Manifest.ImagePath))
{
var imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath);
if (!System.IO.File.Exists(imagePath))
var imagePath = Path.GetFullPath(Path.Combine(plugin.Path, plugin.Manifest.ImagePath));
if (!PathHelper.IsContainedIn(plugin.Path, imagePath) || !System.IO.File.Exists(imagePath))
{
return NotFound();
}
@@ -893,7 +893,7 @@ namespace Jellyfin.Server.Implementations.Users
internal static void ThrowIfInvalidUsername(string name)
{
if (!string.IsNullOrWhiteSpace(name) && ValidUsernameRegex().IsMatch(name))
if (!string.IsNullOrWhiteSpace(name) && ValidUsernameRegex().IsMatch(name) && name != "." && name != "..")
{
return;
}
@@ -27,6 +27,8 @@ namespace Jellyfin.Server.Implementations.Tests.Users
[InlineData(" thishasaspaceatthestart")]
[InlineData(" thishasaspaceatbothends ")]
[InlineData(" this has a space at both ends and inbetween ")]
[InlineData(".")]
[InlineData("..")]
public void ThrowIfInvalidUsername_WhenInvalidUsername_ThrowsArgumentException(string username)
{
Assert.Throws<ArgumentException>(() => UserManager.ThrowIfInvalidUsername(username));