in pkg/sdk/fsutil/targzfs/builders.go [46:142]
func FromReader(r io.Reader) (fs.FS, error) {
gz, err := gzip.NewReader(r)
if err != nil {
return nil, fmt.Errorf("unable to open .tar.gz file: %w", err)
}
// Retrieve TAR content from GZIP
var (
tarContents bytes.Buffer
tarContentLength = int64(0)
)
// Chunked read with hard limit to prevent/reduce zipbomb vulnerability
// exploitation.
for {
written, err := io.CopyN(&tarContents, gz, 1024)
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return nil, err
}
// Add to length
tarContentLength += written
// Check max size
if tarContentLength > maxDecompressedSize {
return nil, errors.New("the archive contains a too large content (>25MB)")
}
}
// Close the gzip decompressor
if err := gz.Close(); err != nil {
return nil, fmt.Errorf("unable to close gzip reader: %w", err)
}
// TAR format reader
tarReader := tar.NewReader(bytes.NewBuffer(tarContents.Bytes()))
// Prepare in-memory filesystem.
ret := &tarGzFs{
files: make(map[string]*tarEntry),
rootEntries: make([]fs.DirEntry, 0, 10),
rootEntry: nil,
}
for {
// Iterate on each file entry
hdr, err := tarReader.Next()
if errors.Is(err, io.EOF) {
break
} else if err != nil {
return nil, fmt.Errorf("unable to read .tar.gz entry: %w", err)
}
// Clean file path.
name := path.Clean(hdr.Name)
if name == "." {
continue
}
// Load content in memory
var fileContents bytes.Buffer
if _, err := io.CopyN(&fileContents, tarReader, maxDecompressedSize); err != nil {
return nil, fmt.Errorf("unable to read .tar.gz entry: %w", err)
}
// Register file
e := &tarEntry{
h: hdr,
b: fileContents.Bytes(),
entries: nil,
}
// Add as file entry
ret.files[name] = e
// Create directories
dir := path.Dir(name)
if dir == "." {
ret.rootEntries = append(ret.rootEntries, e)
} else {
if parent, ok := ret.files[dir]; ok {
parent.entries = append(parent.entries, e)
}
}
// Check file count limit
if len(ret.files) > maxFileCount {
return nil, errors.New("interrupted extraction, too many files in the archive")
}
}
// No error
return ret, nil
}