fix(asar): prevent path traversal (zip-slip) during extraction

AsarExtractor's path-traversal guard called Extensions.GetRelativePath,
whose fast path strips the destination prefix literally without resolving
".." segments. A crafted archive entry such as "a/../../evil" produced a
relative path that did not start with "..", so the guard passed and the
file was written outside the extraction directory once the OS resolved the
"..". The out-of-package symlink guard shared the same weakness.

Add Extensions.IsPathInside, which normalises both paths with
Path.GetFullPath before the containment check, and use it for both the
file/directory and symlink guards.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
dchukkapalli-dev
2026-07-13 19:59:06 +00:00
co-authored by Claude Opus 4.8
parent 3b776c52fc
commit 413c38dbcb
2 changed files with 28 additions and 4 deletions
+7 -4
View File
@@ -41,9 +41,12 @@ namespace AsarSharp
var destFilename = Path.Combine(dest, filename);
var file = filesystem.GetFile(filename, followLinks);
// Path-traversal guard.
string relativePath = Extensions.GetRelativePath(dest, destFilename);
if (relativePath.StartsWith(".."))
// Path-traversal (zip-slip) guard. Uses the normalising
// containment check: GetRelativePath's fast path strips the
// prefix literally without resolving "..", so a crafted entry
// such as "a/../../evil" would otherwise pass this check and be
// written outside "dest".
if (!Extensions.IsPathInside(dest, destFilename))
{
throw new InvalidOperationException(
$"{fullPath}: file \"{destFilename}\" writes out of the package");
@@ -164,7 +167,7 @@ namespace AsarSharp
var linkTo = Path.Combine(relativeLinkPath, Path.GetFileName(file.Link));
if (Extensions.GetRelativePath(dest, linkSrcPath).StartsWith(".."))
if (!Extensions.IsPathInside(dest, linkSrcPath))
{
throw new InvalidOperationException(
$"{fullPath}: file \"{file.Link}\" links out of the package to \"{linkSrcPath}\"");