Fix client arbitrary file access during archive extraction zipslip

This commit is contained in:
Zeroday BYTE 2025-07-22 05:24:05 -07:00 committed by GitHub
parent b2b4b66b08
commit 5ef853a0c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -240,6 +240,8 @@ func (c *Client) Unzip(pName, pVersion string) error {
return nil
}
// Unzip as a generic archive if the module unzip fails.
// This is useful for plugins that have vendor directories or other structures.
return c.unzipArchive(pName, pVersion)
}
@ -280,24 +282,48 @@ func unzipFile(f *zipa.File, dest string) error {
defer func() { _ = rc.Close() }()
// Split to discard the first part of the path, which is [organization]-[project]-[release commit sha1] when the archive is a Yaegi go plugin with vendoring.
pathParts := strings.SplitN(f.Name, "/", 2)
p := filepath.Join(dest, pathParts[1])
if len(pathParts) < 2 {
return fmt.Errorf("no root directory: %s", f.Name)
}
// Validate and sanitize the file path.
cleanName := filepath.Clean(pathParts[1])
if strings.Contains(cleanName, "..") {
return fmt.Errorf("invalid file path in archive: %s", f.Name)
}
filePath := filepath.Join(dest, cleanName)
absFilePath, err := filepath.Abs(filePath)
if err != nil {
return fmt.Errorf("resolving file path: %w", err)
}
absDest, err := filepath.Abs(dest)
if err != nil {
return fmt.Errorf("resolving destination directory: %w", err)
}
if !strings.HasPrefix(absFilePath, absDest) {
return fmt.Errorf("file path escapes destination directory: %s", absFilePath)
}
if f.FileInfo().IsDir() {
err = os.MkdirAll(p, f.Mode())
err = os.MkdirAll(filePath, f.Mode())
if err != nil {
return fmt.Errorf("unable to create archive directory %s: %w", p, err)
return fmt.Errorf("unable to create archive directory %s: %w", filePath, err)
}
return nil
}
err = os.MkdirAll(filepath.Dir(p), 0o750)
err = os.MkdirAll(filepath.Dir(filePath), 0o750)
if err != nil {
return fmt.Errorf("unable to create archive directory %s for file %s: %w", filepath.Dir(p), p, err)
return fmt.Errorf("unable to create archive directory %s for file %s: %w", filepath.Dir(filePath), filePath, err)
}
elt, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
elt, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}