Skip to content

Commit

Permalink
initial top-level API refactors
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
  • Loading branch information
wagoodman committed Dec 16, 2022
1 parent 23a3173 commit 81f6983
Show file tree
Hide file tree
Showing 14 changed files with 641 additions and 90 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ bin/
# attestation
cosign.key
cosign.pub

# go workspaces
go.work
go.work.sum
5 changes: 1 addition & 4 deletions cmd/syft/cli/eventloop/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,7 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) {
hashes = append(hashes, hashObj)
}

digestsCataloger, err := file.NewDigestsCataloger(hashes)
if err != nil {
return nil, err
}
digestsCataloger := file.NewDigestsCataloger(hashes)

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
Expand Down
67 changes: 30 additions & 37 deletions cmd/syft/cli/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ package packages
import (
"context"
"fmt"
"strings"

"github.com/wagoodman/go-partybus"

"github.com/anchore/stereoscope"
"github.com/anchore/syft/cmd/syft/cli/eventloop"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/formats/template"
"github.com/anchore/syft/syft/pkg/cataloger"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
Expand Down Expand Up @@ -94,44 +94,37 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
}

func GenerateSBOM(src *source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
tasks, err := eventloop.Tasks(app)
hashers, err := file.Hashers(app.FileMetadata.Digests...)
if err != nil {
// TODO: this is awkward, fix this
err = fmt.Errorf("unable to create file hashers: %w", err)
errs <- err
return nil, err
}

s := sbom.SBOM{
Source: src.Metadata,
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
Configuration: app,
},
}

buildRelationships(&s, src, tasks, errs)

return &s, nil
}

func buildRelationships(s *sbom.SBOM, src *source.Source, tasks []eventloop.Task, errs chan error) {
var relationships []<-chan artifact.Relationship
for _, task := range tasks {
c := make(chan artifact.Relationship)
relationships = append(relationships, c)
go eventloop.RunTask(task, &s.Artifacts, src, c, errs)
}

s.Relationships = append(s.Relationships, MergeRelationships(relationships...)...)
}

func MergeRelationships(cs ...<-chan artifact.Relationship) (relationships []artifact.Relationship) {
for _, c := range cs {
for n := range c {
relationships = append(relationships, n)
}
}

return relationships
cfg := syft.DefaultSBOMBuilderConfig().
WithCatalogers(src.Metadata,
cataloger.Config{
Search: cataloger.SearchConfig{
IncludeIndexedArchives: app.Package.SearchIndexedArchives,
IncludeUnindexedArchives: app.Package.SearchUnindexedArchives,
Scope: source.ParseScope(app.Package.Cataloger.Scope),
},
Relationships: cataloger.RelationshipsConfig{
FileOwnership: true, // TODO: tie to app config
FileOwnershipOverlap: false, // TODO: tie to app config
},
SyntheticData: cataloger.SyntheticConfig{
GenerateCPEs: true, // TODO: tie to app config
GuessLanguageFromPURL: true, // TODO: tie to app config
},
FileCatalogingSelection: cataloger.OwnedFilesSelection, // TODO: tie to app config
FileHashers: hashers,
},
strings.Join(app.Catalogers, ","), // TODO: update app config to just be a string?
)

return syft.BuildSBOM(src, cfg)
}

func validateOutputOptions(app *config.Application) error {
Expand Down
152 changes: 152 additions & 0 deletions syft/catalog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package syft

import (
"fmt"
"sync"

"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)

type SBOMBuilderConfig struct {
Parallelism uint // TODO: not hooked up yet
SearchScope source.Scope
CaptureFileOwnershipOverlap bool
ToolName string
ToolVersion string
ToolConfiguration interface{}
TaskGroups [][]Task
packageCatalogingTasks *[]Task
}

func DefaultSBOMBuilderConfig() *SBOMBuilderConfig {
return &SBOMBuilderConfig{
ToolName: internal.ApplicationName,
ToolVersion: version.FromBuild().Version,
SearchScope: source.SquashedScope,
}
}

func (c *SBOMBuilderConfig) WithParallelism(parallelism uint) *SBOMBuilderConfig {
c.Parallelism = parallelism
return c
}

func (c *SBOMBuilderConfig) AsTool(name, version string) *SBOMBuilderConfig {
c.ToolName = name
c.ToolVersion = version
return c
}

func (c *SBOMBuilderConfig) WithToolConfiguration(config interface{}) *SBOMBuilderConfig {
c.ToolConfiguration = config
return c
}

func (c *SBOMBuilderConfig) WithTasks(tasks ...Task) *SBOMBuilderConfig {
c.TaskGroups = [][]Task{tasks}
return c
}

func (c *SBOMBuilderConfig) WithAdditionalTasks(tasks ...Task) *SBOMBuilderConfig {
c.TaskGroups = append(c.TaskGroups, tasks)
return c
}

func (c *SBOMBuilderConfig) WithCatalogers(src source.Metadata, cfg cataloger.Config, expression string) *SBOMBuilderConfig {
var pkgTasks TaskDescriptors

if expression == "" {
switch src.Scheme {
case source.ImageScheme:
pkgTasks = allCatalogingTaskDescriptors(cfg).AllTags(imageTag)
case source.FileScheme, source.DirectoryScheme:
pkgTasks = allCatalogingTaskDescriptors(cfg).AllTags(directoryTag)
default:
// TODO
panic("not implemented")
}
} else {
pkgTasks = allCatalogingTaskDescriptors(cfg).Evaluate(expression)
}

var fileTasks []Task

if t := generateDigestCatalogerTask(cfg.FileCatalogingSelection, cfg.FileHashers...); t != nil {
fileTasks = append(fileTasks, t)
}
if t := generateMetadataCatalogerTask(cfg.FileCatalogingSelection); t != nil {
fileTasks = append(fileTasks, t)
}

c.SearchScope = cfg.Search.Scope
c.CaptureFileOwnershipOverlap = cfg.Relationships.FileOwnershipOverlap
c.TaskGroups = append(c.TaskGroups, pkgTasks.Tasks(), fileTasks)
c.packageCatalogingTasks = &c.TaskGroups[0]

return c
}

func (c *SBOMBuilderConfig) WithAdditionalCatalogers(cfg cataloger.Config, catalogers ...pkg.Cataloger) *SBOMBuilderConfig {
var tasks []Task
for _, cat := range catalogers {
tasks = append(tasks, newTask(cat, cfg))
}
if c.packageCatalogingTasks != nil {
// add to existing package cataloging task group
*c.packageCatalogingTasks = append(*c.packageCatalogingTasks, tasks...)
} else {
// prepend to ensure that package catalogers are always run first
c.TaskGroups = append([][]Task{tasks}, c.TaskGroups...)
}
return c
}

func BuildSBOM(src *source.Source, cfg *SBOMBuilderConfig) (*sbom.SBOM, error) {
resolver, err := src.FileResolver(cfg.SearchScope)
if err != nil {
return nil, fmt.Errorf("unable to get file resolver: %w", err)
}

s := sbom.SBOM{
Source: src.Metadata,
Descriptor: sbom.Descriptor{
Name: cfg.ToolName,
Version: cfg.ToolVersion,
Configuration: cfg.ToolConfiguration,
},
Artifacts: sbom.Artifacts{
LinuxDistribution: linux.IdentifyRelease(resolver),
},
}

lock := &sync.RWMutex{}
for _, tg := range cfg.TaskGroups {
for _, t := range tg {
if err := t(resolver, &s, lock); err != nil {
return nil, err
}
}
}

// always add package to package relationships last
if cfg.CaptureFileOwnershipOverlap {
addFileOwnershipOverlapRelationships(&s, lock)
}

return nil, nil
}

func addFileOwnershipOverlapRelationships(s *sbom.SBOM, lock *sync.RWMutex) {
lock.RLock()
relationships := pkg.RelationshipsByFileOwnershipOverlap(s.Artifacts.PackageCatalog)
lock.RUnlock()
lock.Lock()
s.Relationships = append(s.Relationships, relationships...)
lock.Unlock()
}
17 changes: 13 additions & 4 deletions syft/file/digest_cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,24 @@ type DigestsCataloger struct {
hashes []crypto.Hash
}

func NewDigestsCataloger(hashes []crypto.Hash) (*DigestsCataloger, error) {
func NewDigestsCataloger(hashes []crypto.Hash) *DigestsCataloger {
return &DigestsCataloger{
hashes: hashes,
}, nil
}
}

func (i *DigestsCataloger) Catalog(resolver source.FileResolver) (map[source.Coordinates][]Digest, error) {
func (i *DigestsCataloger) Catalog(resolver source.FileResolver, coordinates ...source.Coordinates) (map[source.Coordinates][]Digest, error) {
results := make(map[source.Coordinates][]Digest)
locations := allRegularFiles(resolver)
var locations []source.Location

if len(coordinates) == 0 {
locations = allRegularFiles(resolver)
} else {
for _, c := range coordinates {
locations = append(locations, source.NewLocationFromCoordinates(c))
}
}

stage, prog := digestsCatalogingProgress(int64(len(locations)))
for _, location := range locations {
stage.Current = location.RealPath
Expand Down
27 changes: 27 additions & 0 deletions syft/file/hashers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package file

import (
"crypto"
"fmt"
)

func Hashers(names ...string) ([]crypto.Hash, error) {
supportedHashAlgorithms := make(map[string]crypto.Hash)
for _, h := range []crypto.Hash{
crypto.MD5,
crypto.SHA1,
crypto.SHA256,
} {
supportedHashAlgorithms[DigestAlgorithmName(h)] = h
}

var hashers []crypto.Hash
for _, hashStr := range names {
hashObj, ok := supportedHashAlgorithms[CleanDigestAlgorithmName(hashStr)]
if !ok {
return nil, fmt.Errorf("unsupported hash algorithm: %s", hashStr)
}
hashers = append(hashers, hashObj)
}
return hashers, nil
}
12 changes: 9 additions & 3 deletions syft/file/metadata_cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ func NewMetadataCataloger() *MetadataCataloger {
return &MetadataCataloger{}
}

func (i *MetadataCataloger) Catalog(resolver source.FileResolver) (map[source.Coordinates]source.FileMetadata, error) {
func (i *MetadataCataloger) Catalog(resolver source.FileResolver, coordinates ...source.Coordinates) (map[source.Coordinates]source.FileMetadata, error) {
results := make(map[source.Coordinates]source.FileMetadata)
var locations []source.Location
for location := range resolver.AllLocations() {
locations = append(locations, location)

if len(coordinates) == 0 {
locations = allRegularFiles(resolver)
} else {
for _, c := range coordinates {
locations = append(locations, source.NewLocationFromCoordinates(c))
}
}

stage, prog := metadataCatalogingProgress(int64(len(locations)))
for _, location := range locations {
stage.Current = location.RealPath
Expand Down
Loading

0 comments on commit 81f6983

Please sign in to comment.