mukan-ignite/ignite/pkg/tarball/tarball.go
Mukan Erkin Törük c32551b6f7
Some checks failed
Docs Deploy / build_and_deploy (push) Has been cancelled
Generate Docs / cli (push) Has been cancelled
Generate Config Doc / cli (push) Has been cancelled
Go formatting / go-formatting (push) Has been cancelled
Check links / markdown-link-check (push) Has been cancelled
Integration / pre-test (push) Has been cancelled
Integration / test on (push) Has been cancelled
Integration / status (push) Has been cancelled
Lint / Lint Go code (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:24 +03:00

79 lines
2.1 KiB
Go

package tarball
import (
"archive/tar"
"compress/gzip"
"io"
"path/filepath"
"strings"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/errors"
)
var (
// ErrGzipFileNotFound the file not found in the gzip.
ErrGzipFileNotFound = errors.New("file not found in the gzip")
// ErrNotGzipType the file is not a gzip.
ErrNotGzipType = errors.New("file is not a gzip type")
// ErrInvalidFileName the file name is invalid.
ErrInvalidFileName = errors.New("invalid file name")
// ErrInvalidFilePath the file path is invalid.
ErrInvalidFilePath = errors.New("invalid file path")
// ErrFileTooLarge the file is too large to extract.
ErrFileTooLarge = errors.New("file too large to extract")
)
// ExtractFile founds and reads a specific file into a gzip file and folders recursively.
func ExtractFile(reader io.Reader, out io.Writer, fileName string) (string, error) {
if fileName == "" {
return "", ErrInvalidFileName
}
archive, err := gzip.NewReader(reader)
// Verify if is a GZIP file
if errors.Is(err, io.EOF) || errors.Is(err, gzip.ErrHeader) {
return "", ErrNotGzipType
} else if err != nil {
return "", err
}
defer archive.Close()
tarReader := tar.NewReader(archive)
// Read the tarball files and find only the necessary file
for {
header, err := tarReader.Next()
if errors.Is(err, io.EOF) {
return "", ErrGzipFileNotFound
} else if err != nil {
return header.Name, err
}
// Validate the file path
if !isValidPath(header.Name) {
return "", ErrInvalidFilePath
}
switch header.Typeflag {
case tar.TypeDir:
continue
case tar.TypeReg:
name := filepath.Base(header.Name)
if fileName == name {
// Limit the size of the file to extract
if header.Size > 100<<20 { // 100 MB limit
return "", ErrFileTooLarge
}
limitedReader := io.LimitReader(tarReader, 1000<<20) // 1000 MB limit
_, err := io.Copy(out, limitedReader)
return header.Name, err
}
default:
continue
}
}
}
// isValidPath checks for directory traversal attacks.
func isValidPath(filePath string) bool {
cleanPath := filepath.Clean(filePath)
return !strings.Contains(cleanPath, "..")
}