diff --git a/apps/cnquery/cmd/sbom.go b/apps/cnquery/cmd/sbom.go index 7eefb8bdbd..343b724d7a 100644 --- a/apps/cnquery/cmd/sbom.go +++ b/apps/cnquery/cmd/sbom.go @@ -6,6 +6,9 @@ package cmd import ( "bytes" "fmt" + "os" + "path" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -24,9 +27,11 @@ import ( func init() { rootCmd.AddCommand(sbomCmd) - sbomCmd.Flags().String("asset-name", "", "User-override for the asset name") + sbomCmd.Flags().String("asset-name", "", "User-override for the asset name.") sbomCmd.Flags().StringToString("annotation", nil, "Add an annotation to the asset.") // user-added, editable sbomCmd.Flags().StringP("output", "o", "list", "Set output format: "+sbom.AllFormats()) + sbomCmd.Flags().String("output-target", "", "Set output target to which the sbom report will be written.") + } var sbomCmd = &cobra.Command{ @@ -50,6 +55,11 @@ Note this command is experimental and may change in the future. if err != nil { log.Fatal().Err(err).Msg("failed to bind output flag") } + + err = viper.BindPFlag("output-target", cmd.Flags().Lookup("output-target")) + if err != nil { + log.Fatal().Err(err).Msg("failed to bind output-target flag") + } }, // we have to initialize an empty run so it shows up as a runnable command in --help Run: func(cmd *cobra.Command, args []string) {}, @@ -100,13 +110,27 @@ var sbomCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *pl log.Fatal().Err(err).Msg("failed to get exporter for output format: " + output) } - for _, bom := range boms { + outputTarget := viper.GetString("output-target") + for i := range boms { + bom := boms[i] output := bytes.Buffer{} err := exporter.Render(&output, &bom) if err != nil { log.Fatal().Err(err).Msg("failed to render SBOM") } - fmt.Println(output.String()) + + if outputTarget != "" { + filename := outputTarget + if len(boms) > 1 { + filename = fmt.Sprintf("%s-%d.%s", path.Base(outputTarget), i, path.Ext(outputTarget)) + } + err := os.WriteFile(filename, output.Bytes(), 0600) + if err != nil { + log.Fatal().Err(err).Msg("failed to write SBOM to file") + } + } else { + fmt.Println(output.String()) + } } } diff --git a/test/cli/cli_test.go b/test/cli/cli_test.go index 31cbc57c18..3548af8016 100644 --- a/test/cli/cli_test.go +++ b/test/cli/cli_test.go @@ -4,14 +4,14 @@ package cli import ( - "github.com/spf13/cobra" - "github.com/stretchr/testify/require" - "go.mondoo.com/cnquery/v10/apps/cnquery/cmd" "os" "sync" "testing" cmdtest "github.com/google/go-cmdtest" + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + "go.mondoo.com/cnquery/v10/apps/cnquery/cmd" ) var once sync.Once diff --git a/test/cli/testdata/cnquery_sbom.ct b/test/cli/testdata/cnquery_sbom.ct index 171451ebc7..9029b7099b 100644 --- a/test/cli/testdata/cnquery_sbom.ct +++ b/test/cli/testdata/cnquery_sbom.ct @@ -17,9 +17,10 @@ Usage: Flags: --annotation stringToString Add an annotation to the asset. (default []) - --asset-name string User-override for the asset name + --asset-name string User-override for the asset name. -h, --help help for sbom -o, --output string Set output format: json, cyclonedx-json, cyclonedx-xml, spdx-json, spdx-tag-value, table (default "list") + --output-target string Set output target to which the sbom report will be written. Global Flags: --api-proxy string Set proxy for communications with Mondoo API