github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/internal/file/zip_file_manifest.go (about)

     1  package file
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/anchore/syft/internal"
    10  	"github.com/anchore/syft/internal/log"
    11  )
    12  
    13  // ZipFileManifest is a collection of paths and their file metadata.
    14  type ZipFileManifest map[string]os.FileInfo
    15  
    16  // NewZipFileManifest creates and returns a new ZipFileManifest populated with path and metadata from the given zip archive path.
    17  func NewZipFileManifest(archivePath string) (ZipFileManifest, error) {
    18  	zipReader, err := OpenZip(archivePath)
    19  	manifest := make(ZipFileManifest)
    20  	if err != nil {
    21  		return manifest, fmt.Errorf("unable to open zip archive (%s): %w", archivePath, err)
    22  	}
    23  	defer func() {
    24  		err = zipReader.Close()
    25  		if err != nil {
    26  			log.Warnf("unable to close zip archive (%s): %+v", archivePath, err)
    27  		}
    28  	}()
    29  
    30  	for _, file := range zipReader.Reader.File {
    31  		manifest.Add(file.Name, file.FileInfo())
    32  	}
    33  	return manifest, nil
    34  }
    35  
    36  // Add a new path and it's file metadata to the collection.
    37  func (z ZipFileManifest) Add(entry string, info os.FileInfo) {
    38  	z[entry] = info
    39  }
    40  
    41  // GlobMatch returns the path keys that match the given value(s).
    42  func (z ZipFileManifest) GlobMatch(patterns ...string) []string {
    43  	uniqueMatches := internal.NewStringSet()
    44  
    45  	for _, pattern := range patterns {
    46  		for entry := range z {
    47  			// We want to match globs as if entries begin with a leading slash (akin to an absolute path)
    48  			// so that glob logic is consistent inside and outside of ZIP archives
    49  			normalizedEntry := normalizeZipEntryName(entry)
    50  
    51  			if GlobMatch(pattern, normalizedEntry) {
    52  				uniqueMatches.Add(entry)
    53  			}
    54  		}
    55  	}
    56  
    57  	results := uniqueMatches.ToSlice()
    58  	sort.Strings(results)
    59  
    60  	return results
    61  }
    62  
    63  // normalizeZipEntryName takes the given path entry and ensures it is prefixed with "/".
    64  func normalizeZipEntryName(entry string) string {
    65  	if !strings.HasPrefix(entry, "/") {
    66  		return "/" + entry
    67  	}
    68  
    69  	return entry
    70  }