github.com/anchore/syft@v1.38.2/cmd/syft/internal/commands/convert.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "github.com/spf13/cobra" 9 10 "github.com/anchore/clio" 11 "github.com/anchore/syft/cmd/syft/internal/options" 12 "github.com/anchore/syft/cmd/syft/internal/ui" 13 "github.com/anchore/syft/internal" 14 "github.com/anchore/syft/internal/log" 15 "github.com/anchore/syft/syft/format" 16 ) 17 18 const ( 19 convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout 20 {{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json" 21 {{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json 22 ` 23 ) 24 25 type ConvertOptions struct { 26 options.Config `yaml:",inline" mapstructure:",squash"` 27 options.Output `yaml:",inline" mapstructure:",squash"` 28 options.UpdateCheck `yaml:",inline" mapstructure:",squash"` 29 } 30 31 func Convert(app clio.Application) *cobra.Command { 32 id := app.ID() 33 34 opts := &ConvertOptions{ 35 UpdateCheck: options.DefaultUpdateCheck(), 36 Output: options.DefaultOutput(), 37 } 38 39 return app.SetupCommand(&cobra.Command{ 40 Use: "convert [SOURCE-SBOM] -o [FORMAT]", 41 Short: "Convert between SBOM formats", 42 Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft/wiki/format-conversion", 43 Example: internal.Tprintf(convertExample, map[string]interface{}{ 44 "appName": id.Name, 45 "command": "convert", 46 }), 47 Args: validateConvertArgs, 48 PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), 49 RunE: func(_ *cobra.Command, args []string) error { 50 restoreStdout := ui.CaptureStdoutToTraceLog() 51 defer restoreStdout() 52 53 return RunConvert(opts, args[0]) 54 }, 55 }, opts) 56 } 57 58 func validateConvertArgs(cmd *cobra.Command, args []string) error { 59 return validateArgs(cmd, args, "an SBOM argument is required") 60 } 61 62 func RunConvert(opts *ConvertOptions, userInput string) error { 63 log.Warn("convert is an experimental feature, run `syft convert -h` for help") 64 65 writer, err := opts.SBOMWriter() 66 if err != nil { 67 return err 68 } 69 70 var reader io.ReadSeekCloser 71 72 if userInput == "-" { 73 // though os.Stdin is an os.File, it does not support seeking 74 // you will get errors such as "seek /dev/stdin: illegal seek". 75 // We need to buffer what we read. 76 reader = internal.NewBufferedSeeker(os.Stdin) 77 } else { 78 f, err := os.Open(userInput) 79 if err != nil { 80 return fmt.Errorf("failed to open SBOM file: %w", err) 81 } 82 defer func() { 83 _ = f.Close() 84 }() 85 reader = f 86 } 87 88 s, _, _, err := format.Decode(reader) 89 if err != nil { 90 return fmt.Errorf("failed to decode SBOM: %w", err) 91 } 92 93 if s == nil { 94 return fmt.Errorf("no SBOM produced") 95 } 96 97 if err := writer.Write(*s); err != nil { 98 return fmt.Errorf("failed to write SBOM: %w", err) 99 } 100 101 return nil 102 }