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 }