CVE-2023-28105 (published 2023-03-16T17:15Z)

go-used-util has commonly used utility functions for Go. Versions prior to 0.0.34 have a ZipSlip issue when using fsutil package to unzip files. When users use `zip.Unzip` to unzip zip files from a malicious attacker, they may be vulnerable to path traversal. The issue has been fixed in version 0.0.34. There are no known workarounds.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/dablelv/go-huge-util
commit_details: repo_url: https://github.com/dablelv/go-huge-util
author: R22627
author_date: 2023-03-14 16:14:22+08:00
msg: fix zip.Unzip path traversal vulnerability and add some new file utility functions
repository: repo_url: https://github.com/dablelv/go-huge-util
repo_name: dablelv/cyan
description: A modern, comprehensive and efficient utility library of Go.
repo_language: Go
owner: dablelv
file_changes: filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -21,6 +21,24 @@ gbkStr := []byte{0xC4, 0xE3, 0xBA, 0xC3}    // 你好 in gbk
 utf8Str, _ := GbkToUtf8(gbkStr)             // 你好 in utf8
 gbkStrRes, _ := Utf8ToGbk(utf8Str)          // [196 227 186 195]
 ```
+Some converting function to json.
+```go
+student := struct {
+    Hobby   string
+    Age     int32
+}{
+    Hobby: "pingpopng",
+    Age:   28,
+}
+encoding.ToIndentJSON(&student)
+/*
+output:
+{
+    "Hobby": "pingpopng",
+    "Age": 28
+}
+*/
+```
 # Net
 Some useful functions can be used to handle network. For example you can use `IPv4StrToU32()` transform ipv4 string to uint32 value.
 
@@ -329,26 +347,38 @@ math.GetRandLowerStr(3)         // lts
 math.GetRandUpperStr(3)         // YUT
 ```
 
-# JSON
-
-Some converting function to json.
+# File
 
 ```go
-student := struct {
-    Hobby   string
-    Age     int32
-}{
-    Hobby: "pingpopng",
-    Age:   28,
-}
-encoding.ToIndentJSON(&student)
-/*
-output:
-{
-    "Hobby": "pingpopng",
-    "Age": 28
-}
-*/
+// ListDir lists all the file or directory names in the specified directory.
+ListDir()
+
+// IsExist checks whether a file/dir exists.
+IsExist()
+
+// IsDir checks whether a path is a directory.
+IsDir()
+IsDirE()
+
+// IsFile checks whether a path is a file.
+IsFile()
+IsFileE()
+
+// IsSymlink checks a file whether is a symbolic link file on Linux.
+IsSymlink()
+IsSymlinkE()
+
+// IsShortcut checks a file whether is a shortcut on Windows. 
+IsShortcutFile()
+
+// Create a file.
+Create()
+CreateFile()
+
+// ClearFile clears a file content.
+ClearFile()
+
+// ...
 ```
 
 # Comparison

programming_language: Go
filename: file.go
change_type: ModificationType.MODIFY
diff: @@ -55,7 +55,7 @@ func ReadLinesV2(path string) ([]string, int, error) {
 	}
 }
 
-// ListDir lists all the file or dir names in the specified directory.
+// ListDir lists all the file or directory names in the specified directory.
 // Note that ListDir don't traverse recursively.
 func ListDir(dirname string) ([]string, error) {
 	infos, err := ioutil.ReadDir(dirname)
@@ -69,12 +69,12 @@ func ListDir(dirname string) ([]string, error) {
 	return names, nil
 }
 
-// IsPathExist checks whether a file/dir exists.
+// IsExist checks whether a file/dir exists.
 // Use os.Stat to get the info of the target file or dir to check whether exists.
 // If os.Stat returns nil err, the target exists.
 // If os.Stat returns a os.ErrNotExist err, the target does not exist.
 // If the error returned is another type, the target is uncertain whether exists.
-func IsPathExist(path string) (bool, error) {
+func IsExist(path string) (bool, error) {
 	_, err := os.Stat(path)
 	if err == nil {
 		return true, nil
@@ -123,8 +123,9 @@ func IsFileE(path string) (bool, error) {
 	return false, err
 }
 
-// IsSymlink checks a file whether is a symbolic link.
+// IsSymlink checks a file whether is a symbolic link on Linux.
 // Note that this doesn't work for the shortcut file on windows.
+// If you want to check a file whether is a shortcut file on Windows please use IsShortcut function.
 func IsSymlink(path string) bool {
 	if info, err := os.Lstat(path); err == nil && info.Mode()&os.ModeSymlink != 0 {
 		return true
@@ -132,8 +133,9 @@ func IsSymlink(path string) bool {
 	return false
 }
 
-// IsSymlinkE checks a file whether is a symbolic link.
+// IsSymlinkE checks a file whether is a symbolic link on Linux.
 // Note that this doesn't work for the shortcut file on windows.
+// If you want to check a file whether is a shortcut file on Windows please use IsShortcut function.
 func IsSymlinkE(path string) (bool, error) {
 	info, err := os.Lstat(path)
 	if err == nil && info.Mode()&os.ModeSymlink != 0 {
@@ -142,27 +144,37 @@ func IsSymlinkE(path string) (bool, error) {
 	return false, err
 }
 
+// IsShortcut checks a file whether is a shortcut on Windows.
+func IsShortcut(path string) bool {
+	ext := filepath.Ext(path)
+	if ext == ".lnk" {
+		return true
+	}
+	return false
+}
+
 // RemoveFile removes the named file or empty directory.
-// https://gist.github.com/novalagung/13c5c8f4d30e0c4bff27
+// If there is an error, it will be of type *PathError.
 func RemoveFile(path string) error {
-	err := os.Remove(path)
-	return err
+	return os.Remove(path)
 }
 
 // Create creates or truncates the target file specified by path.
-// If the parent directory does not exist, it will be created with mode os.ModePerm.is cr truncated.
+// If the parent directory does not exist, it will be created with mode os.ModePerm.
 // If the file does not exist, it is created with mode 0666.
-// If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR.
-func Create(filePath string) (*os.File, error) {
-	if exist, err := IsPathExist(filePath); err != nil {
+// If successful, methods on the returned file can be used for I/O; the associated file descriptor has mode O_RDWR.
+func Create(path string) (*os.File, error) {
+	exist, err := IsExist(path)
+	if err != nil {
 		return nil, err
-	} else if exist {
-		return os.Create(filePath)
 	}
-	if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
+	if exist {
+		return os.Create(path)
+	}
+	if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
 		return nil, err
 	}
-	return os.Create(filePath)
+	return os.Create(path)
 }
 
 // CreateFile creates a file specified by path.
@@ -181,15 +193,16 @@ func FileToBytes(path string) []byte {
 	return byteStream
 }
 
-// BytesToFile writes data to a file. If the file does not exist it will be created with permission mode 0644.
-func BytesToFile(filePath string, data []byte) error {
-	exist, _ := IsPathExist(filePath)
+// BytesToFile writes data to a file.
+// If the file does not exist it will be created with permission mode 0644.
+func BytesToFile(path string, data []byte) error {
+	exist, _ := IsExist(path)
 	if !exist {
-		if err := CreateFile(filePath); err != nil {
+		if err := CreateFile(path); err != nil {
 			return err
 		}
 	}
-	return ioutil.WriteFile(filePath, data, 0644)
+	return ioutil.WriteFile(path, data, 0644)
 }
 
 // GetDirAllEntryPaths gets all the file or dir paths in the specified directory recursively.
@@ -260,3 +273,13 @@ func GetDirAllEntryPathsFollowSymlink(dirname string, incl bool) ([]string, erro
 	}
 	return paths, nil
 }
+
+// ClearFile clears a file content.
+func ClearFile(path string) error {
+	f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	return nil
+}

programming_language: Go
filename: unzip.go
change_type: ModificationType.MODIFY
diff: @@ -6,6 +6,7 @@ import (
 	"os"
 	"path"
 	"path/filepath"
+	"strings"
 )
 
 // Unzip decompresses a zip file to specified directory.
@@ -26,8 +27,12 @@ func Unzip(zipPath, dstDir string) error {
 }
 
 func unzipFile(file *zip.File, dstDir string) error {
-	// create the directory of file
-	filePath := path.Join(dstDir, file.Name)
+	// Prevent path traversal vulnerability.
+	// Such as if the file name is "../../../path/to/file.txt" which will be cleaned to "path/to/file.txt".
+	name := strings.TrimPrefix(filepath.Join(string(filepath.Separator), file.Name), string(filepath.Separator))
+	filePath := path.Join(dstDir, name)
+
+	// Create the directory of file.
 	if file.FileInfo().IsDir() {
 		if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
 			return err
@@ -38,21 +43,21 @@ func unzipFile(file *zip.File, dstDir string) error {
 		return err
 	}
 
-	// open the file
+	// Open the file.
 	r, err := file.Open()
 	if err != nil {
 		return err
 	}
 	defer r.Close()
 
-	// create the file
+	// Create the file.
 	w, err := os.Create(filePath)
 	if err != nil {
 		return err
 	}
 	defer w.Close()
 
-	// save the decompressed file content
+	// Save the decompressed file content.
 	_, err = io.Copy(w, r)
 	return err
 }

programming_language: Go
filename: zip.go
change_type: ModificationType.MODIFY
diff: @@ -22,7 +22,8 @@ import (
 // dir
 // ├── bar.txt
 // └── foo.txt
-// Note that if a file is a symbolic link it will be skipped.
+// Note that if a file is a symbolic link on Linux it will be skipped.
+// If you want to follow a symbolic link please use the function ZipFollowSymlink.
 func Zip(zipPath string, paths ...string) error {
 	// Create zip file and it's parent dir.
 	if err := os.MkdirAll(filepath.Dir(zipPath), os.ModePerm); err != nil {

programming_language: Go