From 0285507ac638bc747f94c4b53529be32a28434d0 Mon Sep 17 00:00:00 2001 From: Christoph Hartmann Date: Mon, 11 Mar 2024 09:40:13 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=B9=20refactor=20config=20gathering=20?= =?UTF-8?q?for=20sbom=20cmd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cnquery/cmd/root.go | 120 +++++++++++++++++++++++++++++-- apps/cnquery/cmd/sbom.go | 94 ++----------------------- apps/cnquery/cmd/scan.go | 148 +++++---------------------------------- sbom/sbom.go | 9 +++ 4 files changed, 145 insertions(+), 226 deletions(-) diff --git a/apps/cnquery/cmd/root.go b/apps/cnquery/cmd/root.go index 8ea11b8ee4..ca08fd126c 100644 --- a/apps/cnquery/cmd/root.go +++ b/apps/cnquery/cmd/root.go @@ -4,6 +4,7 @@ package cmd import ( + "fmt" "os" "regexp" "strings" @@ -15,9 +16,16 @@ import ( "github.com/spf13/viper" "go.mondoo.com/cnquery/v10/cli/config" cli_errors "go.mondoo.com/cnquery/v10/cli/errors" - "go.mondoo.com/cnquery/v10/cli/providers" + "go.mondoo.com/cnquery/v10/cli/execruntime" + "go.mondoo.com/cnquery/v10/cli/inventoryloader" + cliproviders "go.mondoo.com/cnquery/v10/cli/providers" + "go.mondoo.com/cnquery/v10/cli/reporter" "go.mondoo.com/cnquery/v10/cli/theme" "go.mondoo.com/cnquery/v10/logger" + "go.mondoo.com/cnquery/v10/providers" + "go.mondoo.com/cnquery/v10/providers-sdk/v1/inventory" + "go.mondoo.com/cnquery/v10/providers-sdk/v1/plugin" + "go.mondoo.com/cnquery/v10/providers-sdk/v1/upstream" ) const ( @@ -39,24 +47,24 @@ var rootCmd = &cobra.Command{ } func BuildRootCmd() (*cobra.Command, error) { - err := providers.AttachCLIs( + err := cliproviders.AttachCLIs( rootCmd, - &providers.Command{ + &cliproviders.Command{ Command: shellCmd, Run: shellRun, Action: "Interactive shell with ", }, - &providers.Command{ + &cliproviders.Command{ Command: RunCmd, Run: RunCmdRun, Action: "Run a query with ", }, - &providers.Command{ + &cliproviders.Command{ Command: scanCmd, Run: scanCmdRun, Action: "Scan ", }, - &providers.Command{ + &cliproviders.Command{ Command: sbomCmd, Run: sbomCmdRun, Action: "Collect a software bill of materials (SBOM) for ", @@ -196,3 +204,103 @@ func GenerateMarkdown(dir string) error { return nil } + +func getCobraScanConfig(cmd *cobra.Command, runtime *providers.Runtime, cliRes *plugin.ParseCLIRes) (*scanConfig, error) { + opts, err := config.Read() + if err != nil { + log.Fatal().Err(err).Msg("failed to load config") + } + + config.DisplayUsedConfig() + + props := viper.GetStringMapString("props") + annotations := viper.GetStringMapString("annotation") + + // merge the config and the user-provided annotations with the latter having precedence + optAnnotations := opts.Annotations + if optAnnotations == nil { + optAnnotations = map[string]string{} + } + for k, v := range annotations { + optAnnotations[k] = v + } + + assetName := viper.GetString("asset-name") + if assetName != "" && cliRes.Asset != nil { + cliRes.Asset.Name = assetName + } + + inv, err := inventoryloader.ParseOrUse(cliRes.Asset, viper.GetBool("insecure"), optAnnotations) + if err != nil { + log.Fatal().Err(err).Msg("failed to parse inventory") + } + + // TODO: We currently deduplicate this here because it leads to errors down the line, + // if the same querypack is added more than once. Fix this properly downstream. + querypackPaths := dedupe(viper.GetStringSlice("querypack-bundle")) + + conf := scanConfig{ + Features: opts.GetFeatures(), + IsIncognito: viper.GetBool("incognito"), + Inventory: inv, + QueryPackPaths: querypackPaths, + QueryPackNames: viper.GetStringSlice("querypacks"), + Props: props, + runtime: runtime, + } + + // if users want to get more information on available output options, + // print them before executing the scan + output := viper.GetString("output") + if output == "help" { + fmt.Println("Available output formats: " + reporter.AllFormats()) + os.Exit(0) + } + + // --json takes precedence + if ok := viper.GetBool("json"); ok { + output = "json" + } + conf.Output = output + + // detect CI/CD runs and read labels from runtime and apply them to all assets in the inventory + runtimeEnv := execruntime.Detect() + if opts.AutoDetectCICDCategory && runtimeEnv.IsAutomatedEnv() || opts.Category == "cicd" { + log.Info().Msg("detected ci-cd environment") + // NOTE: we only apply those runtime environment labels for CI/CD runs to ensure other assets from the + // inventory are not touched, we may consider to add the data to the flagAsset + if runtimeEnv != nil { + runtimeLabels := runtimeEnv.Labels() + conf.Inventory.ApplyLabels(runtimeLabels) + } + conf.Inventory.ApplyCategory(inventory.AssetCategory_CATEGORY_CICD) + } + + serviceAccount := opts.GetServiceCredential() + if serviceAccount != nil { + log.Info().Msg("using service account credentials") + conf.runtime.UpstreamConfig = &upstream.UpstreamConfig{ + SpaceMrn: opts.GetParentMrn(), + ApiEndpoint: opts.UpstreamApiEndpoint(), + ApiProxy: opts.APIProxy, + Incognito: conf.IsIncognito, + Creds: serviceAccount, + } + providers.DefaultRuntime().UpstreamConfig = conf.runtime.UpstreamConfig + } else { + log.Warn().Msg("No credentials provided. Switching to --incognito mode.") + conf.IsIncognito = true + } + + if len(conf.QueryPackPaths) > 0 && !conf.IsIncognito { + log.Warn().Msg("Scanning with local bundles will switch into --incognito mode by default. Your results will not be sent upstream.") + conf.IsIncognito = true + } + + // print headline when its not printed to yaml + if output == "" { + fmt.Fprintln(os.Stdout, theme.DefaultTheme.Welcome) + } + + return &conf, nil +} diff --git a/apps/cnquery/cmd/sbom.go b/apps/cnquery/cmd/sbom.go index 70ac5a9579..a84a61faff 100644 --- a/apps/cnquery/cmd/sbom.go +++ b/apps/cnquery/cmd/sbom.go @@ -12,15 +12,10 @@ import ( "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" - "go.mondoo.com/cnquery/v10/cli/config" - "go.mondoo.com/cnquery/v10/cli/execruntime" - "go.mondoo.com/cnquery/v10/cli/inventoryloader" "go.mondoo.com/cnquery/v10/cli/reporter" "go.mondoo.com/cnquery/v10/logger" "go.mondoo.com/cnquery/v10/providers" - "go.mondoo.com/cnquery/v10/providers-sdk/v1/inventory" "go.mondoo.com/cnquery/v10/providers-sdk/v1/plugin" - "go.mondoo.com/cnquery/v10/providers-sdk/v1/upstream" sbom "go.mondoo.com/cnquery/v10/sbom" "go.mondoo.com/cnquery/v10/shared" ) @@ -77,7 +72,7 @@ var sbomCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *pl log.Fatal().Err(err).Msg("failed to load query pack") } - conf, err := getSbomScanConfig(cmd, runtime, cliRes) + conf, err := getCobraScanConfig(cmd, runtime, cliRes) if err != nil { log.Fatal().Err(err).Msg("failed to get scan config") } @@ -85,6 +80,7 @@ var sbomCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *pl conf.QueryPackNames = nil conf.QueryPackPaths = nil conf.Bundle = pb + conf.IsIncognito = true report, err := RunScan(conf) if err != nil { @@ -98,14 +94,9 @@ var sbomCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *pl logger.DebugDumpJSON("mondoo-sbom-report", buf.Bytes()) } - jr, err := sbom.NewReportCollectionJson(buf.Bytes()) + boms, err := sbom.NewBom(buf.Bytes()) if err != nil { - log.Fatal().Err(err).Msg("failed to parse report collection") - } - - boms, err := sbom.GenerateBom(jr) - if err != nil { - log.Fatal().Err(err).Msg("failed to generate SBOM") + log.Fatal().Err(err).Msg("failed to parse bom") } var exporter sbom.Exporter @@ -145,80 +136,3 @@ var sbomCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *pl } } } - -// TODO: harmonize with getCobraScanConfig -func getSbomScanConfig(cmd *cobra.Command, runtime *providers.Runtime, cliRes *plugin.ParseCLIRes) (*scanConfig, error) { - opts, err := config.Read() - if err != nil { - log.Fatal().Err(err).Msg("failed to load config") - } - - config.DisplayUsedConfig() - - annotations, err := cmd.Flags().GetStringToString("annotation") - if err != nil { - log.Fatal().Err(err).Msg("failed to parse annotations") - } - - // merge the config and the user-provided annotations with the latter having precedence - optAnnotations := opts.Annotations - if optAnnotations == nil { - optAnnotations = map[string]string{} - } - for k, v := range annotations { - optAnnotations[k] = v - } - - assetName, err := cmd.Flags().GetString("asset-name") - if err != nil { - log.Fatal().Err(err).Msg("failed to parse asset-name") - } - if assetName != "" && cliRes.Asset != nil { - cliRes.Asset.Name = assetName - } - - inv, err := inventoryloader.ParseOrUse(cliRes.Asset, viper.GetBool("insecure"), optAnnotations) - if err != nil { - log.Fatal().Err(err).Msg("failed to parse inventory") - } - - conf := scanConfig{ - Features: opts.GetFeatures(), - IsIncognito: true, - Inventory: inv, - runtime: runtime, - } - - // detect CI/CD runs and read labels from runtime and apply them to all assets in the inventory - runtimeEnv := execruntime.Detect() - if opts.AutoDetectCICDCategory && runtimeEnv.IsAutomatedEnv() || opts.Category == "cicd" { - log.Info().Msg("detected ci-cd environment") - // NOTE: we only apply those runtime environment labels for CI/CD runs to ensure other assets from the - // inventory are not touched, we may consider to add the data to the flagAsset - if runtimeEnv != nil { - runtimeLabels := runtimeEnv.Labels() - conf.Inventory.ApplyLabels(runtimeLabels) - } - conf.Inventory.ApplyCategory(inventory.AssetCategory_CATEGORY_CICD) - } - - var serviceAccount *upstream.ServiceAccountCredentials - if !conf.IsIncognito { - serviceAccount = opts.GetServiceCredential() - if serviceAccount != nil { - log.Info().Msg("using service account credentials") - conf.runtime.UpstreamConfig = &upstream.UpstreamConfig{ - SpaceMrn: opts.GetParentMrn(), - ApiEndpoint: opts.UpstreamApiEndpoint(), - ApiProxy: opts.APIProxy, - Incognito: conf.IsIncognito, - Creds: serviceAccount, - } - } else { - log.Warn().Msg("No credentials provided. Switching to --incognito mode.") - conf.IsIncognito = true - } - } - - return &conf, nil -} diff --git a/apps/cnquery/cmd/scan.go b/apps/cnquery/cmd/scan.go index 5895051671..21772852aa 100644 --- a/apps/cnquery/cmd/scan.go +++ b/apps/cnquery/cmd/scan.go @@ -14,18 +14,13 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "go.mondoo.com/cnquery/v10" - "go.mondoo.com/cnquery/v10/cli/config" - "go.mondoo.com/cnquery/v10/cli/execruntime" - "go.mondoo.com/cnquery/v10/cli/inventoryloader" "go.mondoo.com/cnquery/v10/cli/reporter" - "go.mondoo.com/cnquery/v10/cli/theme" "go.mondoo.com/cnquery/v10/explorer" "go.mondoo.com/cnquery/v10/explorer/scan" "go.mondoo.com/cnquery/v10/mqlc" "go.mondoo.com/cnquery/v10/providers" "go.mondoo.com/cnquery/v10/providers-sdk/v1/inventory" "go.mondoo.com/cnquery/v10/providers-sdk/v1/plugin" - "go.mondoo.com/cnquery/v10/providers-sdk/v1/upstream" ) func init() { @@ -81,24 +76,27 @@ To manually configure a query pack, use this: os.Exit(0) } - viper.BindPFlag("platform-id", cmd.Flags().Lookup("platform-id")) + _ = viper.BindPFlag("platform-id", cmd.Flags().Lookup("platform-id")) + _ = viper.BindPFlag("inventory-file", cmd.Flags().Lookup("inventory-file")) + _ = viper.BindPFlag("inventory-ansible", cmd.Flags().Lookup("inventory-ansible")) + _ = viper.BindPFlag("inventory-domainlist", cmd.Flags().Lookup("inventory-domainlist")) + _ = viper.BindPFlag("querypack-bundle", cmd.Flags().Lookup("querypack-bundle")) + _ = viper.BindPFlag("detect-cicd", cmd.Flags().Lookup("detect-cicd")) + _ = viper.BindPFlag("asset-name", cmd.Flags().Lookup("asset-name")) + _ = viper.BindPFlag("category", cmd.Flags().Lookup("category")) - viper.BindPFlag("inventory-file", cmd.Flags().Lookup("inventory-file")) - viper.BindPFlag("inventory-ansible", cmd.Flags().Lookup("inventory-ansible")) - viper.BindPFlag("inventory-domainlist", cmd.Flags().Lookup("inventory-domainlist")) - viper.BindPFlag("querypack-bundle", cmd.Flags().Lookup("querypack-bundle")) - viper.BindPFlag("detect-cicd", cmd.Flags().Lookup("detect-cicd")) - viper.BindPFlag("asset-name", cmd.Flags().Lookup("asset-name")) - viper.BindPFlag("category", cmd.Flags().Lookup("category")) + _ = viper.BindPFlag("annotations", cmd.Flags().Lookup("annotations")) + _ = viper.BindPFlag("props", cmd.Flags().Lookup("props")) // for all assets - viper.BindPFlag("incognito", cmd.Flags().Lookup("incognito")) - viper.BindPFlag("insecure", cmd.Flags().Lookup("insecure")) - viper.BindPFlag("querypacks", cmd.Flags().Lookup("querypack")) - viper.BindPFlag("sudo.active", cmd.Flags().Lookup("sudo")) - viper.BindPFlag("record", cmd.Flags().Lookup("record")) - - viper.BindPFlag("output", cmd.Flags().Lookup("output")) + _ = viper.BindPFlag("incognito", cmd.Flags().Lookup("incognito")) + _ = viper.BindPFlag("insecure", cmd.Flags().Lookup("insecure")) + _ = viper.BindPFlag("querypacks", cmd.Flags().Lookup("querypack")) + _ = viper.BindPFlag("sudo.active", cmd.Flags().Lookup("sudo")) + _ = viper.BindPFlag("record", cmd.Flags().Lookup("record")) + + _ = viper.BindPFlag("output", cmd.Flags().Lookup("output")) + _ = viper.BindPFlag("json", cmd.Flags().Lookup("json")) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 0 { @@ -156,116 +154,6 @@ type scanConfig struct { IsIncognito bool } -func getCobraScanConfig(cmd *cobra.Command, runtime *providers.Runtime, cliRes *plugin.ParseCLIRes) (*scanConfig, error) { - opts, err := config.Read() - if err != nil { - log.Fatal().Err(err).Msg("failed to load config") - } - - config.DisplayUsedConfig() - - props, err := cmd.Flags().GetStringToString("props") - if err != nil { - log.Fatal().Err(err).Msg("failed to parse props") - } - - annotations, err := cmd.Flags().GetStringToString("annotation") - if err != nil { - log.Fatal().Err(err).Msg("failed to parse annotations") - } - - // merge the config and the user-provided annotations with the latter having precedence - optAnnotations := opts.Annotations - if optAnnotations == nil { - optAnnotations = map[string]string{} - } - for k, v := range annotations { - optAnnotations[k] = v - } - - assetName, err := cmd.Flags().GetString("asset-name") - if err != nil { - log.Fatal().Err(err).Msg("failed to parse asset-name") - } - if assetName != "" && cliRes.Asset != nil { - cliRes.Asset.Name = assetName - } - - inv, err := inventoryloader.ParseOrUse(cliRes.Asset, viper.GetBool("insecure"), optAnnotations) - if err != nil { - log.Fatal().Err(err).Msg("failed to parse inventory") - } - - // TODO: We currently deduplicate this here because it leads to errors down the line, - // if the same querypack is added more than once. Fix this properly downstream. - querypackPaths := dedupe(viper.GetStringSlice("querypack-bundle")) - - conf := scanConfig{ - Features: opts.GetFeatures(), - IsIncognito: viper.GetBool("incognito"), - Inventory: inv, - QueryPackPaths: querypackPaths, - QueryPackNames: viper.GetStringSlice("querypacks"), - Props: props, - runtime: runtime, - } - - // if users want to get more information on available output options, - // print them before executing the scan - output, _ := cmd.Flags().GetString("output") - if output == "help" { - fmt.Println("Available output formats: " + reporter.AllFormats()) - os.Exit(0) - } - - // --json takes precedence - if ok, _ := cmd.Flags().GetBool("json"); ok { - output = "json" - } - conf.Output = output - - // detect CI/CD runs and read labels from runtime and apply them to all assets in the inventory - runtimeEnv := execruntime.Detect() - if opts.AutoDetectCICDCategory && runtimeEnv.IsAutomatedEnv() || opts.Category == "cicd" { - log.Info().Msg("detected ci-cd environment") - // NOTE: we only apply those runtime environment labels for CI/CD runs to ensure other assets from the - // inventory are not touched, we may consider to add the data to the flagAsset - if runtimeEnv != nil { - runtimeLabels := runtimeEnv.Labels() - conf.Inventory.ApplyLabels(runtimeLabels) - } - conf.Inventory.ApplyCategory(inventory.AssetCategory_CATEGORY_CICD) - } - - serviceAccount := opts.GetServiceCredential() - if serviceAccount != nil { - log.Info().Msg("using service account credentials") - conf.runtime.UpstreamConfig = &upstream.UpstreamConfig{ - SpaceMrn: opts.GetParentMrn(), - ApiEndpoint: opts.UpstreamApiEndpoint(), - ApiProxy: opts.APIProxy, - Incognito: conf.IsIncognito, - Creds: serviceAccount, - } - providers.DefaultRuntime().UpstreamConfig = conf.runtime.UpstreamConfig - } else { - log.Warn().Msg("No credentials provided. Switching to --incognito mode.") - conf.IsIncognito = true - } - - if len(conf.QueryPackPaths) > 0 && !conf.IsIncognito { - log.Warn().Msg("Scanning with local bundles will switch into --incognito mode by default. Your results will not be sent upstream.") - conf.IsIncognito = true - } - - // print headline when its not printed to yaml - if output == "" { - fmt.Fprintln(os.Stdout, theme.DefaultTheme.Welcome) - } - - return &conf, nil -} - func (c *scanConfig) loadBundles() error { if c.IsIncognito { if len(c.QueryPackPaths) == 0 { diff --git a/sbom/sbom.go b/sbom/sbom.go index 94d7956aac..7d9a91d5ad 100644 --- a/sbom/sbom.go +++ b/sbom/sbom.go @@ -27,6 +27,15 @@ func QueryPack() (*explorer.Bundle, error) { return explorer.BundleFromYAML(sbomQueryPack) } +// NewBom creates a BOM from a json report collection +func NewBom(data []byte) ([]Sbom, error) { + jr, err := NewReportCollectionJson(data) + if err != nil { + return nil, err + } + return GenerateBom(jr) +} + // GenerateBom generates a BOM from a cnspec json report collection func GenerateBom(r *ReportCollectionJson) ([]Sbom, error) { if r == nil {