2020-07-15 11:42:35 +00:00
|
|
|
package file
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"sort"
|
2020-10-28 20:50:40 +00:00
|
|
|
"strings"
|
2020-07-15 11:42:35 +00:00
|
|
|
|
2020-07-24 00:54:04 +00:00
|
|
|
"github.com/anchore/syft/internal"
|
2020-07-15 11:42:35 +00:00
|
|
|
|
2020-07-24 00:54:04 +00:00
|
|
|
"github.com/anchore/syft/internal/log"
|
2020-07-15 11:42:35 +00:00
|
|
|
)
|
|
|
|
|
2020-11-17 13:25:01 +00:00
|
|
|
// ZipFileManifest is a collection of paths and their file metadata.
|
2020-07-15 11:42:35 +00:00
|
|
|
type ZipFileManifest map[string]os.FileInfo
|
|
|
|
|
2020-11-17 13:25:01 +00:00
|
|
|
// newZipManifest creates an empty ZipFileManifest.
|
2020-07-15 11:42:35 +00:00
|
|
|
func newZipManifest() ZipFileManifest {
|
|
|
|
return make(ZipFileManifest)
|
|
|
|
}
|
|
|
|
|
2020-11-17 13:25:01 +00:00
|
|
|
// Add a new path and it's file metadata to the collection.
|
2020-07-15 11:42:35 +00:00
|
|
|
func (z ZipFileManifest) Add(entry string, info os.FileInfo) {
|
|
|
|
z[entry] = info
|
|
|
|
}
|
|
|
|
|
2020-11-17 13:25:01 +00:00
|
|
|
// GlobMatch returns the path keys that match the given value(s).
|
2020-07-15 11:42:35 +00:00
|
|
|
func (z ZipFileManifest) GlobMatch(patterns ...string) []string {
|
|
|
|
uniqueMatches := internal.NewStringSet()
|
|
|
|
|
|
|
|
for _, pattern := range patterns {
|
|
|
|
for entry := range z {
|
2020-10-28 20:50:40 +00:00
|
|
|
// We want to match globs as if entries begin with a leading slash (akin to an absolute path)
|
|
|
|
// so that glob logic is consistent inside and outside of ZIP archives
|
|
|
|
normalizedEntry := normalizeZipEntryName(entry)
|
|
|
|
|
|
|
|
if GlobMatch(pattern, normalizedEntry) {
|
2020-07-15 11:42:35 +00:00
|
|
|
uniqueMatches.Add(entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
results := uniqueMatches.ToSlice()
|
|
|
|
sort.Strings(results)
|
|
|
|
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
2020-11-17 13:25:01 +00:00
|
|
|
// NewZipFileManifest creates and returns a new ZipFileManifest populated with path and metadata from the given zip archive path.
|
2020-07-15 11:42:35 +00:00
|
|
|
func NewZipFileManifest(archivePath string) (ZipFileManifest, error) {
|
|
|
|
zipReader, err := zip.OpenReader(archivePath)
|
|
|
|
manifest := newZipManifest()
|
|
|
|
if err != nil {
|
|
|
|
return manifest, fmt.Errorf("unable to open zip archive (%s): %w", archivePath, err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
err = zipReader.Close()
|
|
|
|
if err != nil {
|
2020-09-28 21:22:17 +00:00
|
|
|
log.Errorf("unable to close zip archive (%s): %+v", archivePath, err)
|
2020-07-15 11:42:35 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
for _, file := range zipReader.Reader.File {
|
|
|
|
manifest.Add(file.Name, file.FileInfo())
|
|
|
|
}
|
|
|
|
return manifest, nil
|
|
|
|
}
|
2020-10-28 20:50:40 +00:00
|
|
|
|
2020-11-17 13:25:01 +00:00
|
|
|
// normalizeZipEntryName takes the given path entry and ensures it is prefixed with "/".
|
2020-10-28 20:50:40 +00:00
|
|
|
func normalizeZipEntryName(entry string) string {
|
|
|
|
if !strings.HasPrefix(entry, "/") {
|
|
|
|
return "/" + entry
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry
|
|
|
|
}
|