[PR #988] [MERGED] Fix extraction failure on Windows due to case-sensitive path comparison #1407

Closed
opened 2026-01-29 22:20:24 +00:00 by claunia · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/adamhathcock/sharpcompress/pull/988
Author: @Copilot
Created: 10/28/2025
Status: Merged
Merged: 10/28/2025
Merged by: @adamhathcock

Base: masterHead: copilot/fix-file-write-error


📝 Commits (5)

  • ef2fee0 Initial plan
  • 5392ca9 Fix case-sensitive path comparison on Windows for file extraction
  • e111986 Address code review feedback: use RuntimeInformation for platform detection
  • 150d9c3 Complete fix for case-sensitive path comparison on Windows
  • 77d06fb Merge branch 'master' into copilot/fix-file-write-error

📊 Changes

3 files changed (+116 additions, -17 deletions)

View changed files

📝 src/SharpCompress/Common/ExtractionMethods.cs (+14 -14)
📝 src/SharpCompress/packages.lock.json (+3 -3)
tests/SharpCompress.Test/ExtractionTests.cs (+99 -0)

📄 Description

Path.GetFullPath() on Windows can return paths with different casing than what's on disk (e.g., system32 vs System32 under LocalSystem account). The security check validating extraction paths was using case-sensitive comparison, causing false positives that blocked legitimate extractions.

Changes:

  • Add PathComparison property returning StringComparison.OrdinalIgnoreCase on Windows, StringComparison.Ordinal on Unix
  • Replace hardcoded StringComparison.Ordinal in 4 path validation checks (sync + async methods)
  • Use RuntimeInformation.IsOSPlatform(OSPlatform.Windows) for platform detection
  • Add tests validating both case-insensitive extraction and path traversal protection

Example:

// Before: Would throw on Windows LocalSystem when Path.GetFullPath casing differs
using var reader = archive.ExtractAllEntries();
reader.WriteAllToDirectory(
    "C:\\Windows\\system32\\config\\systemprofile\\AppData\\Local\\temp",
    new ExtractionOptions { ExtractFullPath = true }
);
// ExtractionException: Entry is trying to write a file outside of the destination directory

// After: Succeeds with platform-appropriate comparison

Path traversal security checks remain fully functional.

Original prompt

This section details on the original issue you should resolve

<issue_title>Entry is trying to write a file outside of the destination</issue_title>
<issue_description>I get the above mentioned error with this code

        using var reader = package.ExtractAllEntries();
        reader.WriteAllToDirectory(pkgWorkDir, new SharpCompress.Common.ExtractionOptions { PreserveFileTime = true, ExtractFullPath = true, Overwrite = true, PreserveAttributes = true });

when running as Windows Service and the LocalSystem account. I use pkgWorkDir that is constructed starting with Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).

On my system, this returns C:\Windows\system32\config\systemprofile\AppData\Local.
The code throws the error

Entry is trying to write a file outside of the destination directory: C:\Windows\system32\config\systemprofile\AppData\Local

As one can see, there is a difference between System32 and system32.

The lib code here uses StringComparison.Ordinal to compare the strings.

My issue could be fixed, if - under Windows OS - we use StringComparison.OrdinalIgnoreCase.

</issue_description>

Comments on the Issue (you are @copilot in this section)

@adamhathcock This isn't an obvious fix because other OSes use case sensitive file systems. I guess the addition of an option based on OS is relevant @adamhathcock or just write your own extraction code....the write to destination is just a shortcut method. @adamhathcock look and see what those methods do....they get the entries and stream copy basically @adamhathcock Odd...doing a search and the AI says the method will "normalize" casing but haven't found anywhere saying it yet

Image


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/adamhathcock/sharpcompress/pull/988 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 10/28/2025 **Status:** ✅ Merged **Merged:** 10/28/2025 **Merged by:** [@adamhathcock](https://github.com/adamhathcock) **Base:** `master` ← **Head:** `copilot/fix-file-write-error` --- ### 📝 Commits (5) - [`ef2fee0`](https://github.com/adamhathcock/sharpcompress/commit/ef2fee0ee323be7338822dcdfd560cb719d04856) Initial plan - [`5392ca9`](https://github.com/adamhathcock/sharpcompress/commit/5392ca9794bfab74f53fa279a7d20a10e6850b40) Fix case-sensitive path comparison on Windows for file extraction - [`e111986`](https://github.com/adamhathcock/sharpcompress/commit/e11198616ef390b0ed73404e12a2eb130d65a55d) Address code review feedback: use RuntimeInformation for platform detection - [`150d9c3`](https://github.com/adamhathcock/sharpcompress/commit/150d9c35b755159e67b086edb751e2352751e1e2) Complete fix for case-sensitive path comparison on Windows - [`77d06fb`](https://github.com/adamhathcock/sharpcompress/commit/77d06fb60e51bad78c3516c5c4187c59b8f482c9) Merge branch 'master' into copilot/fix-file-write-error ### 📊 Changes **3 files changed** (+116 additions, -17 deletions) <details> <summary>View changed files</summary> 📝 `src/SharpCompress/Common/ExtractionMethods.cs` (+14 -14) 📝 `src/SharpCompress/packages.lock.json` (+3 -3) ➕ `tests/SharpCompress.Test/ExtractionTests.cs` (+99 -0) </details> ### 📄 Description `Path.GetFullPath()` on Windows can return paths with different casing than what's on disk (e.g., `system32` vs `System32` under LocalSystem account). The security check validating extraction paths was using case-sensitive comparison, causing false positives that blocked legitimate extractions. **Changes:** - Add `PathComparison` property returning `StringComparison.OrdinalIgnoreCase` on Windows, `StringComparison.Ordinal` on Unix - Replace hardcoded `StringComparison.Ordinal` in 4 path validation checks (sync + async methods) - Use `RuntimeInformation.IsOSPlatform(OSPlatform.Windows)` for platform detection - Add tests validating both case-insensitive extraction and path traversal protection **Example:** ```csharp // Before: Would throw on Windows LocalSystem when Path.GetFullPath casing differs using var reader = archive.ExtractAllEntries(); reader.WriteAllToDirectory( "C:\\Windows\\system32\\config\\systemprofile\\AppData\\Local\\temp", new ExtractionOptions { ExtractFullPath = true } ); // ExtractionException: Entry is trying to write a file outside of the destination directory // After: Succeeds with platform-appropriate comparison ``` Path traversal security checks remain fully functional. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Entry is trying to write a file outside of the destination</issue_title> > <issue_description>I get the above mentioned error with this code > > ```csharp > using var reader = package.ExtractAllEntries(); > reader.WriteAllToDirectory(pkgWorkDir, new SharpCompress.Common.ExtractionOptions { PreserveFileTime = true, ExtractFullPath = true, Overwrite = true, PreserveAttributes = true }); > ``` > > when running as Windows Service and the LocalSystem account. I use _pkgWorkDir_ that is constructed starting with `Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)`. > > On my system, this returns `C:\Windows\system32\config\systemprofile\AppData\Local`. > The code throws the error > > > Entry is trying to write a file outside of the destination directory: C:\Windows\system32\config\systemprofile\AppData\Local > > As one can see, there is a difference between _System32_ and _system32_. > > The lib code [here](https://github.com/adamhathcock/sharpcompress/blob/227f66f29962a2d8632a71d7c4cdf3d8631c8530/src/SharpCompress/Common/ExtractionMethods.cs#L72) uses `StringComparison.Ordinal` to compare the strings. > > My issue could be fixed, if - under Windows OS - we use `StringComparison.OrdinalIgnoreCase`. > > </issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > <comment_new><author>@adamhathcock</author><body> > This isn't an obvious fix because other OSes use case sensitive file systems. I guess the addition of an option based on OS is relevant</body></comment_new> > <comment_new><author>@adamhathcock</author><body> > or just write your own extraction code....the write to destination is just a shortcut method.</body></comment_new> > <comment_new><author>@adamhathcock</author><body> > look and see what those methods do....they get the entries and stream copy basically</body></comment_new> > <comment_new><author>@adamhathcock</author><body> > Odd...doing a search and the AI says the method will "normalize" casing but haven't found anywhere saying it yet > > ![Image](https://github.com/user-attachments/assets/4b987642-db52-4d7e-be37-71cc425e65e7) > > </body></comment_new> > </comments> > </details> - Fixes adamhathcock/sharpcompress#908 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
claunia added the pull-request label 2026-01-29 22:20:24 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/sharpcompress#1407