mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
131 lines
4.2 KiB
Go
131 lines
4.2 KiB
Go
package cataloger
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/anchore/syft/internal/bus"
|
|
"github.com/anchore/syft/internal/log"
|
|
"github.com/anchore/syft/syft/artifact"
|
|
"github.com/anchore/syft/syft/distro"
|
|
"github.com/anchore/syft/syft/event"
|
|
"github.com/anchore/syft/syft/pkg"
|
|
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
|
|
"github.com/anchore/syft/syft/source"
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/wagoodman/go-partybus"
|
|
"github.com/wagoodman/go-progress"
|
|
)
|
|
|
|
// Monitor provides progress-related data for observing the progress of a Catalog() call (published on the event bus).
|
|
type Monitor struct {
|
|
FilesProcessed progress.Monitorable // the number of files selected and contents analyzed from all registered catalogers
|
|
PackagesDiscovered progress.Monitorable // the number of packages discovered from all registered catalogers
|
|
}
|
|
|
|
// newMonitor creates a new Monitor object and publishes the object on the bus as a PackageCatalogerStarted event.
|
|
func newMonitor() (*progress.Manual, *progress.Manual) {
|
|
filesProcessed := progress.Manual{}
|
|
packagesDiscovered := progress.Manual{}
|
|
|
|
bus.Publish(partybus.Event{
|
|
Type: event.PackageCatalogerStarted,
|
|
Value: Monitor{
|
|
FilesProcessed: progress.Monitorable(&filesProcessed),
|
|
PackagesDiscovered: progress.Monitorable(&packagesDiscovered),
|
|
},
|
|
})
|
|
return &filesProcessed, &packagesDiscovered
|
|
}
|
|
|
|
// Catalog a given source (container image or filesystem) with the given catalogers, returning all discovered packages.
|
|
// In order to efficiently retrieve contents from a underlying container image the content fetch requests are
|
|
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
|
|
// request.
|
|
func Catalog(resolver source.FileResolver, theDistro *distro.Distro, catalogers ...Cataloger) (*pkg.Catalog, []artifact.Relationship, error) {
|
|
catalog := pkg.NewCatalog()
|
|
var allRelationships []artifact.Relationship
|
|
|
|
// TODO: update to show relationships
|
|
filesProcessed, packagesDiscovered := newMonitor()
|
|
|
|
// perform analysis, accumulating errors for each failed analysis
|
|
var errs error
|
|
for _, theCataloger := range catalogers {
|
|
// find packages from the underlying raw data
|
|
packages, relationships, err := theCataloger.Catalog(resolver)
|
|
if err != nil {
|
|
errs = multierror.Append(errs, err)
|
|
continue
|
|
}
|
|
|
|
catalogedPackages := len(packages)
|
|
|
|
// TODO: update to show relationships and files
|
|
log.Debugf("package cataloger %q discovered %d packages", theCataloger.Name(), catalogedPackages)
|
|
packagesDiscovered.N += int64(catalogedPackages)
|
|
|
|
for _, p := range packages {
|
|
// generate CPEs
|
|
p.CPEs = cpe.Generate(p)
|
|
|
|
// generate PURL
|
|
p.PURL = generatePackageURL(p, theDistro)
|
|
|
|
// TODO: break out into another function (refactor this function)
|
|
// create file-to-package relationships for files owned by the package
|
|
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
|
|
if err != nil {
|
|
log.Warnf("unable to create any package-file relationships for package name=%q: %w", p.Name, err)
|
|
} else {
|
|
allRelationships = append(allRelationships, owningRelationships...)
|
|
}
|
|
|
|
// add to catalog
|
|
catalog.Add(p)
|
|
}
|
|
|
|
allRelationships = append(allRelationships, relationships...)
|
|
}
|
|
|
|
allRelationships = append(allRelationships, pkg.NewRelationships(catalog)...)
|
|
|
|
if errs != nil {
|
|
return nil, nil, errs
|
|
}
|
|
|
|
filesProcessed.SetCompleted()
|
|
packagesDiscovered.SetCompleted()
|
|
|
|
return catalog, allRelationships, nil
|
|
}
|
|
|
|
func packageFileOwnershipRelationships(p pkg.Package, resolver source.FilePathResolver) ([]artifact.Relationship, error) {
|
|
fileOwner, ok := p.Metadata.(pkg.FileOwner)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
|
|
var relationships []artifact.Relationship
|
|
|
|
for _, path := range fileOwner.OwnedFiles() {
|
|
locations, err := resolver.FilesByPath(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to find path for path=%q: %w", path, err)
|
|
}
|
|
|
|
// if len(locations) == 0 {
|
|
// // TODO: this is notable, we should at least log it(?)... however, ideally there is something in the SBOM about this
|
|
// }
|
|
|
|
for _, l := range locations {
|
|
relationships = append(relationships, artifact.Relationship{
|
|
From: l.Coordinates,
|
|
To: p,
|
|
Type: artifact.PackageOfRelationship,
|
|
})
|
|
}
|
|
}
|
|
|
|
return relationships, nil
|
|
}
|