github.com/kastenhq/syft@v0.0.0-20230821225854-0710af25cdbe/cmd/syft/cli/poweruser/poweruser.go (about)

     1  package poweruser
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/gookit/color"
     9  	"github.com/wagoodman/go-partybus"
    10  
    11  	"github.com/anchore/stereoscope"
    12  	"github.com/anchore/stereoscope/pkg/image"
    13  	"github.com/kastenhq/syft/cmd/syft/cli/eventloop"
    14  	"github.com/kastenhq/syft/cmd/syft/cli/options"
    15  	"github.com/kastenhq/syft/cmd/syft/cli/packages"
    16  	"github.com/kastenhq/syft/cmd/syft/internal/ui"
    17  	"github.com/kastenhq/syft/internal"
    18  	"github.com/kastenhq/syft/internal/bus"
    19  	"github.com/kastenhq/syft/internal/config"
    20  	"github.com/kastenhq/syft/internal/version"
    21  	"github.com/kastenhq/syft/syft"
    22  	"github.com/kastenhq/syft/syft/artifact"
    23  	"github.com/kastenhq/syft/syft/formats/syftjson"
    24  	"github.com/kastenhq/syft/syft/sbom"
    25  	"github.com/kastenhq/syft/syft/source"
    26  )
    27  
    28  func Run(_ context.Context, app *config.Application, args []string) error {
    29  	f := syftjson.Format()
    30  	writer, err := options.MakeSBOMWriterForFormat(f, app.File)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	defer func() {
    35  		// inform user at end of run that command will be removed
    36  		deprecated := color.Style{color.Red, color.OpBold}.Sprint("DEPRECATED: This command will be removed in v1.0.0")
    37  		fmt.Fprintln(os.Stderr, deprecated)
    38  	}()
    39  
    40  	userInput := args[0]
    41  
    42  	eventBus := partybus.NewBus()
    43  	stereoscope.SetBus(eventBus)
    44  	syft.SetBus(eventBus)
    45  	subscription := eventBus.Subscribe()
    46  
    47  	return eventloop.EventLoop(
    48  		execWorker(app, userInput, writer),
    49  		eventloop.SetupSignals(),
    50  		subscription,
    51  		stereoscope.Cleanup,
    52  		ui.Select(options.IsVerbose(app), app.Quiet)...,
    53  	)
    54  }
    55  
    56  //nolint:funlen
    57  func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
    58  	errs := make(chan error)
    59  	go func() {
    60  		defer close(errs)
    61  		defer bus.Exit()
    62  
    63  		app.Secrets.Cataloger.Enabled = true
    64  		app.FileMetadata.Cataloger.Enabled = true
    65  		app.FileContents.Cataloger.Enabled = true
    66  		app.FileClassification.Cataloger.Enabled = true
    67  		tasks, err := eventloop.Tasks(app)
    68  		if err != nil {
    69  			errs <- err
    70  			return
    71  		}
    72  
    73  		detection, err := source.Detect(
    74  			userInput,
    75  			source.DetectConfig{
    76  				DefaultImageSource: app.DefaultImagePullSource,
    77  			},
    78  		)
    79  		if err != nil {
    80  			errs <- fmt.Errorf("could not deteremine source: %w", err)
    81  			return
    82  		}
    83  
    84  		var platform *image.Platform
    85  
    86  		if app.Platform != "" {
    87  			platform, err = image.NewPlatform(app.Platform)
    88  			if err != nil {
    89  				errs <- fmt.Errorf("invalid platform: %w", err)
    90  				return
    91  			}
    92  		}
    93  
    94  		src, err := detection.NewSource(
    95  			source.DetectionSourceConfig{
    96  				Alias: source.Alias{
    97  					Name:    app.Source.Name,
    98  					Version: app.Source.Version,
    99  				},
   100  				RegistryOptions: app.Registry.ToOptions(),
   101  				Platform:        platform,
   102  				Exclude: source.ExcludeConfig{
   103  					Paths: app.Exclusions,
   104  				},
   105  				DigestAlgorithms: nil,
   106  				BasePath:         app.BasePath,
   107  			},
   108  		)
   109  
   110  		if src != nil {
   111  			defer src.Close()
   112  		}
   113  		if err != nil {
   114  			errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
   115  			return
   116  		}
   117  
   118  		s := sbom.SBOM{
   119  			Source: src.Describe(),
   120  			Descriptor: sbom.Descriptor{
   121  				Name:          internal.ApplicationName,
   122  				Version:       version.FromBuild().Version,
   123  				Configuration: app,
   124  			},
   125  		}
   126  
   127  		var relationships []<-chan artifact.Relationship
   128  		for _, task := range tasks {
   129  			c := make(chan artifact.Relationship)
   130  			relationships = append(relationships, c)
   131  
   132  			go eventloop.RunTask(task, &s.Artifacts, src, c, errs)
   133  		}
   134  
   135  		s.Relationships = append(s.Relationships, packages.MergeRelationships(relationships...)...)
   136  
   137  		if err := writer.Write(s); err != nil {
   138  			errs <- fmt.Errorf("failed to write sbom: %w", err)
   139  			return
   140  		}
   141  	}()
   142  
   143  	return errs
   144  }