Skip to content

Commit

Permalink
refactor source API and syft json source block
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 Jun 16, 2023
1 parent badb957 commit 869d879
Show file tree
Hide file tree
Showing 101 changed files with 6,479 additions and 2,615 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.bin
CHANGELOG.md
VERSION
/test/results
Expand Down
48 changes: 28 additions & 20 deletions cmd/syft/cli/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,47 @@ func Run(_ context.Context, app *config.Application, args []string) error {
}
}()

// could be an image or a directory, with or without a scheme
// TODO: validate that source is image
// note: must be a container image
userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}

if si.Scheme != source.ImageScheme {
return fmt.Errorf("attestations are only supported for oci images at this time")
}

eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()

return eventloop.EventLoop(
execWorker(app, *si, writer),
execWorker(app, userInput, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}

func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, errs chan error) ([]byte, error) {
src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
if cleanup != nil {
defer cleanup()
func buildSBOM(app *config.Application, userInput string, writer sbom.Writer, errs chan error) ([]byte, error) {
detection, err := source.Detect(userInput, app.DefaultImagePullSource)
if err != nil {
return nil, fmt.Errorf("could not deteremine source: %w", err)
}

if detection.Type != source.ContainerImageType {
return nil, fmt.Errorf("attestations are only supported for oci images at this time")
}

src, err := detection.NewSource(
&source.Alias{
Name: app.SourceName,
Version: app.SourceVersion,
},
app.Registry.ToOptions(),
app.Platform,
app.Exclusions,
)
if src != nil {
defer src.Close()
}
if err != nil {
return nil, fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
}

s, err := packages.GenerateSBOM(src, errs, app)
Expand All @@ -85,7 +93,7 @@ func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, err
}

if s == nil {
return nil, fmt.Errorf("no SBOM produced for %q", si.UserInput)
return nil, fmt.Errorf("no SBOM produced for %q", userInput)
}

// note: only works for single format no multi writer support
Expand All @@ -98,11 +106,11 @@ func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, err
}

//nolint:funlen
func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
sBytes, err := buildSBOM(app, si, writer, errs)
sBytes, err := buildSBOM(app, userInput, writer, errs)
if err != nil {
errs <- fmt.Errorf("unable to build SBOM: %w", err)
return
Expand Down Expand Up @@ -145,7 +153,7 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
predicateType = "custom"
}

args := []string{"attest", si.UserInput, "--predicate", f.Name(), "--type", predicateType}
args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
if app.Attest.Key != "" {
args = append(args, "--key", app.Attest.Key)
}
Expand Down
14 changes: 7 additions & 7 deletions cmd/syft/cli/eventloop/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"github.com/anchore/syft/syft/source"
)

type Task func(*sbom.Artifacts, *source.Source) ([]artifact.Relationship, error)
type Task func(*sbom.Artifacts, source.Source) ([]artifact.Relationship, error)

func Tasks(app *config.Application) ([]Task, error) {
var tasks []Task
Expand Down Expand Up @@ -48,7 +48,7 @@ func generateCatalogPackagesTask(app *config.Application) (Task, error) {
return nil, nil
}

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, app.ToCatalogerConfig())

results.Packages = packageCatalog
Expand All @@ -67,7 +67,7 @@ func generateCatalogFileMetadataTask(app *config.Application) (Task, error) {

metadataCataloger := filemetadata.NewCataloger()

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand Down Expand Up @@ -110,7 +110,7 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) {

digestsCataloger := filedigest.NewCataloger(hashes)

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand Down Expand Up @@ -142,7 +142,7 @@ func generateCatalogSecretsTask(app *config.Application) (Task, error) {
return nil, err
}

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.Secrets.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand All @@ -169,7 +169,7 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) {
return nil, err
}

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileContents.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand All @@ -186,7 +186,7 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) {
return task, nil
}

func RunTask(t Task, a *sbom.Artifacts, src *source.Source, c chan<- artifact.Relationship, errs chan<- error) {
func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship, errs chan<- error) {
defer close(c)

relationships, err := t(a, src)
Expand Down
38 changes: 24 additions & 14 deletions cmd/syft/cli/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,36 +42,46 @@ func Run(_ context.Context, app *config.Application, args []string) error {

// could be an image or a directory, with or without a scheme
userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}

eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()

return eventloop.EventLoop(
execWorker(app, *si, writer),
execWorker(app, userInput, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}

func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)

src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
if cleanup != nil {
defer cleanup()
detection, err := source.Detect(userInput, app.DefaultImagePullSource)
if err != nil {
errs <- fmt.Errorf("could not deteremine source: %w", err)
return
}

src, err := detection.NewSource(
&source.Alias{
Name: app.SourceName,
Version: app.SourceVersion,
},
app.Registry.ToOptions(),
app.Platform,
app.Exclusions,
)
if src != nil {
defer src.Close()
}
if err != nil {
errs <- fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
return
}

Expand All @@ -82,7 +92,7 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
}

if s == nil {
errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput)
errs <- fmt.Errorf("no SBOM produced for %q", userInput)
}

bus.Publish(partybus.Event{
Expand All @@ -93,14 +103,14 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
return errs
}

func GenerateSBOM(src *source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
func GenerateSBOM(src source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
tasks, err := eventloop.Tasks(app)
if err != nil {
return nil, err
}

s := sbom.SBOM{
Source: src.Metadata,
Source: src.Describe(),
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
Expand All @@ -113,7 +123,7 @@ func GenerateSBOM(src *source.Source, errs chan error, app *config.Application)
return &s, nil
}

func buildRelationships(s *sbom.SBOM, src *source.Source, tasks []eventloop.Task, errs chan error) {
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)
Expand Down
32 changes: 21 additions & 11 deletions cmd/syft/cli/poweruser/poweruser.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,22 @@ func Run(_ context.Context, app *config.Application, args []string) error {
}()

userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}

eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()

return eventloop.EventLoop(
execWorker(app, *si, writer),
execWorker(app, userInput, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}

func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
Expand All @@ -81,17 +77,31 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
return
}

src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
detection, err := source.Detect(userInput, app.DefaultImagePullSource)
if err != nil {
errs <- err
errs <- fmt.Errorf("could not deteremine source: %w", err)
return
}
if cleanup != nil {
defer cleanup()

src, err := detection.NewSource(
&source.Alias{
Name: app.SourceName,
Version: app.SourceVersion,
},
app.Registry.ToOptions(),
app.Platform,
app.Exclusions,
)
if src != nil {
defer src.Close()
}
if err != nil {
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
return
}

s := sbom.SBOM{
Source: src.Metadata,
Source: src.Describe(),
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
Expand Down
2 changes: 1 addition & 1 deletion internal/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ const (

// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "8.0.1"
JSONSchemaVersion = "9.0.0"
)
Loading

0 comments on commit 869d879

Please sign in to comment.