mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 08:53:15 +01:00
* initial working version Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * added build settings to pkg metadata wip - unit tests Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * handle mach-O FatFiles Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * add support to mod replace fixed golang catalger tests trying GH Actions with go 1.18rc1 Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * log error Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * use go-macholibre for extraction Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * cleaner tests Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * add version to main module Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * check macho file with macholibre Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * run golangci in its own workflow Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * wip - golangci workflow Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * fix golangci wf yml Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * fix golangci wf yml Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * wip - golangci wf Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * wip - golangci wf Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * get arch from bin file headers upgrade macholibre Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * go mod tidy Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * test new stereoscope lazy reader interface Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * go mod tidy Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * remove devel version from golang cataloger Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * go mod tidy Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * switch github workflows to go1.18 stable Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * add union reader interface in golang cataloger update stereoscope Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * go mod tidy Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * simpler golangci validation Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * fix makefile Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * get archs refactor Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * nolint for golang version Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * fix go bin tests Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * feedback changes Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * golangci nolint needs a \n before package Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * cleanup Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * move golangci-lint to its own jobs again Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * fix ci yaml Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * add support for xcoff files add arch assets to test bin file types Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * clean up golangci-lint config Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * nolint for xcoff Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * explain nolints Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * remove unused xcoff testdata assets Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * make go bin test-fixtures in docker Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * fix make clean with -f Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * update json output schema Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * update schema version in test fixture Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * feedback changes Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com> * explain possible empty main module Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>
195 lines
6.1 KiB
Go
195 lines
6.1 KiB
Go
package source
|
|
|
|
import (
|
|
"archive/tar"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/anchore/stereoscope/pkg/file"
|
|
"github.com/anchore/stereoscope/pkg/filetree"
|
|
"github.com/anchore/stereoscope/pkg/image"
|
|
)
|
|
|
|
var _ FileResolver = (*imageSquashResolver)(nil)
|
|
|
|
// imageSquashResolver implements path and content access for the Squashed source option for container image data sources.
|
|
type imageSquashResolver struct {
|
|
img *image.Image
|
|
}
|
|
|
|
// newImageSquashResolver returns a new resolver from the perspective of the squashed representation for the given image.
|
|
func newImageSquashResolver(img *image.Image) (*imageSquashResolver, error) {
|
|
if img.SquashedTree() == nil {
|
|
return nil, fmt.Errorf("the image does not have have a squashed tree")
|
|
}
|
|
|
|
return &imageSquashResolver{
|
|
img: img,
|
|
}, nil
|
|
}
|
|
|
|
// HasPath indicates if the given path exists in the underlying source.
|
|
func (r *imageSquashResolver) HasPath(path string) bool {
|
|
return r.img.SquashedTree().HasPath(file.Path(path))
|
|
}
|
|
|
|
// FilesByPath returns all file.References that match the given paths within the squashed representation of the image.
|
|
func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) {
|
|
uniqueFileIDs := file.NewFileReferenceSet()
|
|
uniqueLocations := make([]Location, 0)
|
|
|
|
for _, path := range paths {
|
|
tree := r.img.SquashedTree()
|
|
_, ref, err := tree.File(file.Path(path), filetree.FollowBasenameLinks)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ref == nil {
|
|
// no file found, keep looking through layers
|
|
continue
|
|
}
|
|
|
|
// don't consider directories (special case: there is no path information for /)
|
|
if ref.RealPath == "/" {
|
|
continue
|
|
} else if r.img.FileCatalog.Exists(*ref) {
|
|
metadata, err := r.img.FileCatalog.Get(*ref)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.RealPath, err)
|
|
}
|
|
if metadata.Metadata.IsDir {
|
|
continue
|
|
}
|
|
}
|
|
|
|
// a file may be a symlink, process it as such and resolve it
|
|
resolvedRef, err := r.img.ResolveLinkByImageSquash(*ref)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve link from img (ref=%+v): %w", ref, err)
|
|
}
|
|
|
|
if resolvedRef != nil && !uniqueFileIDs.Contains(*resolvedRef) {
|
|
uniqueFileIDs.Add(*resolvedRef)
|
|
uniqueLocations = append(uniqueLocations, NewLocationFromImage(path, *resolvedRef, r.img))
|
|
}
|
|
}
|
|
|
|
return uniqueLocations, nil
|
|
}
|
|
|
|
// FilesByGlob returns all file.References that match the given path glob pattern within the squashed representation of the image.
|
|
func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
|
uniqueFileIDs := file.NewFileReferenceSet()
|
|
uniqueLocations := make([]Location, 0)
|
|
|
|
for _, pattern := range patterns {
|
|
results, err := r.img.SquashedTree().FilesByGlob(pattern, filetree.FollowBasenameLinks)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve files by glob (%s): %w", pattern, err)
|
|
}
|
|
|
|
for _, result := range results {
|
|
// don't consider directories (special case: there is no path information for /)
|
|
if result.MatchPath == "/" {
|
|
continue
|
|
}
|
|
|
|
if r.img.FileCatalog.Exists(result.Reference) {
|
|
metadata, err := r.img.FileCatalog.Get(result.Reference)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.MatchPath, err)
|
|
}
|
|
if metadata.Metadata.IsDir {
|
|
continue
|
|
}
|
|
}
|
|
|
|
resolvedLocations, err := r.FilesByPath(string(result.MatchPath))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find files by path (result=%+v): %w", result, err)
|
|
}
|
|
for _, resolvedLocation := range resolvedLocations {
|
|
if !uniqueFileIDs.Contains(resolvedLocation.ref) {
|
|
uniqueFileIDs.Add(resolvedLocation.ref)
|
|
uniqueLocations = append(uniqueLocations, resolvedLocation)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return uniqueLocations, nil
|
|
}
|
|
|
|
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
|
// This is helpful when attempting to find a file that is in the same layer or lower as another file. For the
|
|
// imageSquashResolver, this is a simple path lookup.
|
|
func (r *imageSquashResolver) RelativeFileByPath(_ Location, path string) *Location {
|
|
paths, err := r.FilesByPath(path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if len(paths) == 0 {
|
|
return nil
|
|
}
|
|
|
|
return &paths[0]
|
|
}
|
|
|
|
// FileContentsByLocation fetches file contents for a single file reference, regardless of the source layer.
|
|
// If the path does not exist an error is returned.
|
|
func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
|
entry, err := r.img.FileCatalog.Get(location.ref)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err)
|
|
}
|
|
|
|
switch entry.Metadata.TypeFlag {
|
|
case tar.TypeSymlink, tar.TypeLink:
|
|
// the location we are searching may be a symlink, we should always work with the resolved file
|
|
locations, err := r.FilesByPath(location.RealPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve content location at location=%+v: %w", location, err)
|
|
}
|
|
|
|
switch len(locations) {
|
|
case 0:
|
|
return nil, fmt.Errorf("link resolution failed while resolving content location: %+v", location)
|
|
case 1:
|
|
location = locations[0]
|
|
default:
|
|
return nil, fmt.Errorf("link resolution resulted in multiple results while resolving content location: %+v", location)
|
|
}
|
|
}
|
|
|
|
return r.img.FileContentsByRef(location.ref)
|
|
}
|
|
|
|
func (r *imageSquashResolver) AllLocations() <-chan Location {
|
|
results := make(chan Location)
|
|
go func() {
|
|
defer close(results)
|
|
for _, ref := range r.img.SquashedTree().AllFiles(file.AllTypes...) {
|
|
results <- NewLocationFromImage(string(ref.RealPath), ref, r.img)
|
|
}
|
|
}()
|
|
return results
|
|
}
|
|
|
|
func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
|
refs, err := r.img.FilesByMIMETypeFromSquash(types...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var locations []Location
|
|
for _, ref := range refs {
|
|
locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img))
|
|
}
|
|
|
|
return locations, nil
|
|
}
|
|
|
|
func (r *imageSquashResolver) FileMetadataByLocation(location Location) (FileMetadata, error) {
|
|
return fileMetadataByLocation(r.img, location)
|
|
}
|