Skip to content
Draft
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
8 changes: 8 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@
**Vulnerability:** The `RestoreFromBackupAsync` method used `ZipFile.ExtractToDirectory` without validating that the extracted file paths were contained within the destination directory. This could allow an attacker to write files outside the intended directory via a crafted zip archive containing `../` traversal sequences.
**Learning:** Even if modern frameworks (like .NET 6+) offer some protection, explicit path validation ("Defense in Depth") is crucial for critical file operations. Always ensure the resolved full path starts with the intended target directory *and* includes a trailing separator to prevent partial path matching bypasses.
**Prevention:** Replace convenient one-liners like `ExtractToDirectory` with manual iteration and validation loops when handling untrusted archives. Verify `!destinationPath.StartsWith(targetDir + Path.DirectorySeparatorChar)` before writing.

## 2024-05-24 - Zip Bomb Vulnerability in ImportExportService
**Vulnerability:** The `RestoreFromBackupAsync` method extracted files without checking the total size or number of entries. A malicious "Zip Bomb" (e.g., highly compressed file) could cause Denial of Service (DoS) by exhausting disk space or memory during extraction.
**Learning:** Validating individual file paths (Zip Slip) is not enough; you must also validate resource consumption. Compressed archives can expand to orders of magnitude larger than their compressed size.
**Prevention:** Implement resource limits during extraction:
1. Limit total number of entries (e.g., 10,000).
2. Limit total uncompressed size (e.g., 1GB).
3. Validate these limits incrementally inside the extraction loop.
24 changes: 23 additions & 1 deletion BookLoggerApp.Infrastructure/Services/ImportExportService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public class ImportExportService : IImportExportService
private readonly string _backupDirectory;
private readonly string _basePath;

// Security constraints for zip extraction (Zip Bomb protection)
private const long MaxTotalExtractionSize = 1024L * 1024L * 1024L; // 1 GB
private const int MaxEntryCount = 10000;

public ImportExportService(
IDbContextFactory<AppDbContext> contextFactory,
IFileSystem fileSystem,
Expand Down Expand Up @@ -422,11 +426,28 @@ public async Task RestoreFromBackupAsync(string backupPath, CancellationToken ct
try
{
// 1. Extract ZIP securely
// Sentinel: Manual extraction with path validation to prevent Zip Slip vulnerability
// Sentinel: Manual extraction with path validation to prevent Zip Slip and Zip Bomb vulnerabilities
using (var archive = ZipFile.OpenRead(backupPath))
{
long totalSize = 0;
int entryCount = 0;

foreach (var entry in archive.Entries)
{
// Check for Zip Bomb (too many entries)
entryCount++;
if (entryCount > MaxEntryCount)
{
throw new IOException("Zip Bomb detected: Too many entries in archive.");
}

// Check for Zip Bomb (total uncompressed size)
totalSize += entry.Length;
if (totalSize > MaxTotalExtractionSize)
{
throw new IOException("Zip Bomb detected: Total extraction size exceeds limit.");
}

var destinationPath = Path.GetFullPath(Path.Combine(tempExtractDir, entry.FullName));
var tempDirFullPath = Path.GetFullPath(tempExtractDir);

Expand All @@ -435,6 +456,7 @@ public async Task RestoreFromBackupAsync(string backupPath, CancellationToken ct
tempDirFullPath += Path.DirectorySeparatorChar;
}

// Check for Zip Slip
if (!destinationPath.StartsWith(tempDirFullPath, StringComparison.OrdinalIgnoreCase))
{
throw new IOException($"Zip Slip vulnerability detected: {entry.FullName}");
Expand Down
Loading