diff --git a/docs/standard/io/snippets/zip-tar-best-practices/csharp/Program.cs b/docs/standard/io/snippets/zip-tar-best-practices/csharp/Program.cs index 2cd9524693b12..b66771fd2e228 100644 --- a/docs/standard/io/snippets/zip-tar-best-practices/csharp/Program.cs +++ b/docs/standard/io/snippets/zip-tar-best-practices/csharp/Program.cs @@ -301,18 +301,23 @@ void TarStreamingRead(Stream archiveStream, string destDir) TarEntry? entry; while ((entry = reader.GetNextEntry()) is not null) { - // DataStream is only valid until the next GetNextEntry() call, - // so consume or copy the data before advancing. if (entry.DataStream is not null) { + // DataStream is only valid until the next GetNextEntry() call, + // so consume string destPath = Path.Join(destDir, entry.Name); using var fileStream = File.Create(destPath); entry.DataStream.CopyTo(fileStream); + + // Alternatively, you can copy the entry contents into + // in a separate MemoryStream that remains valid after advancing: + if (entry.Length < 1_000_000) // Example limit + { + MemoryStream memoryStream = new MemoryStream(); + entry.DataStream.CopyTo(memoryStream); + // memoryStream can be used after GetNextEntry() is called again + } } } - - // Alternatively, pass copyContents: true to retain entry data - // in a separate MemoryStream that remains valid after advancing: - // entry = reader.GetNextEntry(copyContents: true); } // diff --git a/docs/standard/io/zip-tar-best-practices.md b/docs/standard/io/zip-tar-best-practices.md index 7dc7ec013e8ee..465c862246d6c 100644 --- a/docs/standard/io/zip-tar-best-practices.md +++ b/docs/standard/io/zip-tar-best-practices.md @@ -54,7 +54,7 @@ Best for: simple workflows with trusted input, quick scripts, and build tooling. Use these APIs for full control over each archive entry. They're essential for large archives or untrusted input. -- **ZIP:** Use to open an archive and iterate, read, or write entries selectively. Use to extract individual entries, or to extract all entries from an already-opened archive. +- **ZIP:** Use to open an archive and iterate, read, or write entries selectively. Use to extract individual entries. - **TAR:** Use and for sequential entry-by-entry access. Use to extract individual entries. @@ -75,14 +75,13 @@ When the archive source is known and trusted, the [convenience methods](#conveni - TAR extraction handles overwriting differently: it deletes the existing file before writing the replacement. If extraction fails after deletion (for example, due to an I/O error or process interruption), the original file is lost and the replacement might be incomplete. Consider backing up critical files before overwriting with TAR extraction. -> [!NOTE] -> The convenience methods don't enforce size limits, entry count limits, or other policies needed for safe extraction of untrusted archives. If that matters even for trusted input (for example, very large archives), use the streaming approach described in [Handle untrusted archives safely](#handle-untrusted-archives-safely). +> [!WARNING] +> The `ExtractToDirectory` convenience methods must only be used on trusted inputs. These helpers don't enforce size limits, entry count limits, or other policies needed for safe extraction of untrusted archives. If that matters even for trusted input (for example, very large archives), use the streaming approach described in [Handle untrusted archives safely](#handle-untrusted-archives-safely). ## Handle untrusted archives safely For untrusted input—user uploads, third-party downloads, or network transfers—iterate over entries manually and enforce your own safety checks. The following subsections describe what you need to enforce and why. -- [What the convenience methods don't protect you from](#what-the-convenience-methods-dont-protect-you-from) - [Enforce size and entry count limits](#enforce-size-and-entry-count-limits) - [Validate file names](#validate-file-names) - [Validate destination paths](#validate-destination-paths) @@ -90,10 +89,6 @@ For untrusted input—user uploads, third-party downloads, or network transfers - [Entry permission bits (Unix only)](#entry-permission-bits-unix-only) - [Complete safe extraction examples](#complete-safe-extraction-examples) -### What the convenience methods don't protect you from - -`ExtractToDirectory` protects against *path traversal*—an attack where a malicious entry name like `../../etc/passwd` tries to write outside the destination directory. The method resolves each entry's full path and rejects any that fall outside the target directory (for TAR, this check also covers symbolic link targets). However, `ExtractToDirectory` doesn't enforce size limits or entry count limits. - ### Enforce size and entry count limits Neither nor limits the total uncompressed size or the number of entries extracted, and neither do the `ExtractToDirectory` convenience methods. You must enforce these limits yourself. @@ -211,7 +206,10 @@ Additionally, when you open a in reads entries one at a time and doesn't buffer the entire archive. However, for unseekable streams, each entry's is only valid until the next call. If you need to retain entry data, either copy it immediately or pass `copyContents: true` to , which copies the entry data into a separate that remains valid after advancing. Like , `copyContents: true` loads the full entry into memory, so check entry sizes before using it with untrusted archives. + reads entries one at a time and doesn't buffer the entire archive. However, for unseekable streams, each entry's is only valid until the next call. If you need to retain entry data, copy it immediately to a separate that remains valid after advancing. + +> [!WARNING] +> Avoid using with `copyContents: true` on untrusted archives, as it allocates a potentially large amount of memory for the to hold the entry contents. Pass `copyContents: false` and validate the entry size before materializing the contents manually. :::code language="csharp" source="./snippets/zip-tar-best-practices/csharp/Program.cs" id="TarStreaming":::