Skip to content

Commit

Permalink
fix: Fix path traversal when uploading a module ZIP (#2867)
Browse files Browse the repository at this point in the history
  • Loading branch information
artem-dudarev authored Dec 11, 2024
1 parent 9a29858 commit 807e039
Showing 1 changed file with 78 additions and 47 deletions.
125 changes: 78 additions & 47 deletions src/VirtoCommerce.Platform.Web/Controllers/Api/ModulesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,66 +208,97 @@ public async Task<ActionResult<ModuleDescriptor>> UploadModuleArchive()
return BadRequest($"Expected a multipart request, but got {Request.ContentType}");
}

var uploadPath = Path.GetFullPath(_platformOptions.LocalUploadFolderPath);
if (!Directory.Exists(uploadPath))
var targetFilePath = await UploadFile(Request, Path.GetFullPath(_platformOptions.LocalUploadFolderPath));
if (targetFilePath is null)
{
Directory.CreateDirectory(uploadPath);
return BadRequest("Cannot read file");
}

ModuleDescriptor result = null;
string targetFilePath = null;
var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var manifest = await LoadModuleManifestFromZipArchive(targetFilePath);
if (manifest is null)
{
return BadRequest("Cannot read module manifest");
}

var module = AbstractTypeFactory<ManifestModuleInfo>.TryCreateInstance();
module.LoadFromManifest(manifest);
var existingModule = _externalModuleCatalog.Modules.OfType<ManifestModuleInfo>().FirstOrDefault(x => x.Equals(module));

if (existingModule != null)
{
module = existingModule;
}
else
{
//Force dependency validation for new module
_externalModuleCatalog.CompleteListWithDependencies([module]).ToList().Clear();
_externalModuleCatalog.AddModule(module);
}

module.Ref = targetFilePath;
var result = new ModuleDescriptor(module);

return Ok(result);
}

private static async Task<string> UploadFile(HttpRequest request, string uploadFolderPath)
{
var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var section = await reader.ReadNextSectionAsync();

if (section != null)
if (section == null)
{
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
return null;
}

if (hasContentDispositionHeader)
{
if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
{
var fileName = contentDisposition.FileName.Value;
targetFilePath = Path.Combine(uploadPath, fileName);
if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition) ||
!MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
{
return null;
}

using (var targetStream = System.IO.File.Create(targetFilePath))
{
await section.Body.CopyToAsync(targetStream);
}
var fileName = Path.GetFileName(contentDisposition.FileName.Value);
if (string.IsNullOrEmpty(fileName))
{
return null;
}

}
}
using (var packageStream = System.IO.File.Open(targetFilePath, FileMode.Open))
using (var package = new ZipArchive(packageStream, ZipArchiveMode.Read))
if (!Directory.Exists(uploadFolderPath))
{
Directory.CreateDirectory(uploadFolderPath);
}

var targetFilePath = Path.Combine(uploadFolderPath, fileName);

await using var targetStream = System.IO.File.Create(targetFilePath);
await section.Body.CopyToAsync(targetStream);

return targetFilePath;
}

private static async Task<ModuleManifest> LoadModuleManifestFromZipArchive(string path)
{
ModuleManifest manifest = null;

try
{
await using var packageStream = System.IO.File.Open(path, FileMode.Open);
using var package = new ZipArchive(packageStream, ZipArchiveMode.Read);

var entry = package.GetEntry("module.manifest");
if (entry != null)
{
var entry = package.GetEntry("module.manifest");
if (entry != null)
{
using (var manifestStream = entry.Open())
{
var manifest = ManifestReader.Read(manifestStream);
var module = AbstractTypeFactory<ManifestModuleInfo>.TryCreateInstance();
module.LoadFromManifest(manifest);
var alreadyExistModule = _externalModuleCatalog.Modules.OfType<ManifestModuleInfo>().FirstOrDefault(x => x.Equals(module));
if (alreadyExistModule != null)
{
module = alreadyExistModule;
}
else
{
//Force dependency validation for new module
_externalModuleCatalog.CompleteListWithDependencies(new[] { module }).ToList().Clear();
_externalModuleCatalog.AddModule(module);
}
module.Ref = targetFilePath;
result = new ModuleDescriptor(module);
}
}
await using var manifestStream = entry.Open();
manifest = ManifestReader.Read(manifestStream);
}
}
catch
{
// Suppress any exceptions
}

return Ok(result);
return manifest;
}

/// <summary>
Expand Down

0 comments on commit 807e039

Please sign in to comment.