Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,6 @@ internal Task ExtractRelativeToDirectoryAsync(string destinationDirectoryPath, b
// already present on disk before extraction
private static bool FilePathEscapesDirectory(string destinationDirectoryPath, string fileDestinationPath)
{
// Windows is case insensitive while Linux is case sensitive
// This ensures the comparison is consistent with how the OS would resolve the paths
StringComparison pathComparison = OperatingSystem.IsWindows()
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;

string resolvedDest = ResolvePhysicalPath(destinationDirectoryPath);

// Use the logical destination path for computing the relative path
Expand All @@ -444,8 +438,8 @@ private static bool FilePathEscapesDirectory(string destinationDirectoryPath, st
string normalizedFile = Path.GetFullPath(fileDestinationPath);

// Guard with StartsWith before computing relative path
if (!normalizedFile.StartsWith(logicalPrefix, pathComparison) &&
!normalizedFile.Equals(logicalDest, pathComparison))
if (!normalizedFile.StartsWith(logicalPrefix, StringComparison.Ordinal) &&
!normalizedFile.Equals(logicalDest, StringComparison.Ordinal))
{
return true;
}
Expand All @@ -465,8 +459,8 @@ private static bool FilePathEscapesDirectory(string destinationDirectoryPath, st
current = ResolveSymlink(current);

string normalizedCurrent = Path.GetFullPath(current);
if (!normalizedCurrent.StartsWith(destPrefix, pathComparison) &&
!normalizedCurrent.Equals(resolvedDest, pathComparison))
if (!normalizedCurrent.StartsWith(destPrefix, StringComparison.Ordinal) &&
!normalizedCurrent.Equals(resolvedDest, StringComparison.Ordinal))
{
return true;
}
Expand Down Expand Up @@ -525,7 +519,7 @@ private static string ResolvePhysicalPath(string path)

string fullPath = Path.GetFullPath(qualifiedPath); // Removes relative segments

return fullPath.StartsWith(destinationDirectoryFullPath, PathInternal.StringComparison) ? fullPath : null;
return fullPath.StartsWith(destinationDirectoryFullPath, StringComparison.Ordinal) ? fullPath : null;
}

// Extracts the current entry into the filesystem, regardless of the entry type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ public void ExtractEntry_ManySubfolderSegments_NoPrecedingDirectoryEntries()
Assert.True(File.Exists(Path.Join(root.Path, fileWithTwoSegments)));
}

[Fact]
public void ExtractToDirectory_DifferentlyCasedSiblingDirectory_Throws()
{
using TempDirectory root = new TempDirectory();
string destinationPath = Path.Join(root.Path, "Dest");
Directory.CreateDirectory(destinationPath);

using MemoryStream archive = new MemoryStream();
using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Pax, leaveOpen: true))
{
writer.WriteEntry(new PaxTarEntry(TarEntryType.RegularFile, "../dest/pwn.txt"));
}

archive.Position = 0;

Assert.Throws<IOException>(() => TarFile.ExtractToDirectory(archive, destinationPath, overwriteFiles: false));
Assert.False(File.Exists(Path.Join(root.Path, "dest", "pwn.txt")));
}

[Theory]
[InlineData(TarEntryType.SymbolicLink)]
[InlineData(TarEntryType.HardLink)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ public async Task ExtractEntry_ManySubfolderSegments_NoPrecedingDirectoryEntries
}
}

[Fact]
public async Task ExtractToDirectory_DifferentlyCasedSiblingDirectory_Throws_Async()
{
using TempDirectory root = new TempDirectory();
string destinationPath = Path.Join(root.Path, "Dest");
Directory.CreateDirectory(destinationPath);

await using MemoryStream archive = new MemoryStream();
await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Pax, leaveOpen: true))
{
await writer.WriteEntryAsync(new PaxTarEntry(TarEntryType.RegularFile, "../dest/pwn.txt"));
}

archive.Position = 0;

await Assert.ThrowsAsync<IOException>(() => TarFile.ExtractToDirectoryAsync(archive, destinationPath, overwriteFiles: false));
Assert.False(File.Exists(Path.Join(root.Path, "dest", "pwn.txt")));
}

[Fact]
public async Task ExtractEntry_DockerImageTarWithFileTypeInDirectoriesInMode_SuccessfullyExtracts_Async()
{
Expand Down
Loading