Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuskimmina committed Dec 15, 2023
1 parent 5499c58 commit c116e89
Show file tree
Hide file tree
Showing 2 changed files with 322 additions and 0 deletions.
285 changes: 285 additions & 0 deletions apps/cnspec/cmd/bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
// Copyright (c) Mondoo, Inc.

Check failure on line 1 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / golangci-lint

: # go.mondoo.com/cnspec/v9/apps/cnspec/cmd
// SPDX-License-Identifier: BUSL-1.1

package cmd

import (
"context"
_ "embed"
"fmt"
"os"
"path/filepath"

"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.mondoo.com/cnquery/v9/cli/config"
"go.mondoo.com/cnquery/v9/providers"
"go.mondoo.com/cnquery/v9/providers-sdk/v1/upstream"
"go.mondoo.com/cnspec/v9/internal/bundle"
"go.mondoo.com/cnspec/v9/policy"
)

func init() {
// policy init
policyBundlesCmd.AddCommand(policyInitCmd)

// validate
policyLintCmd.Flags().StringP("output", "o", "cli", "Set output format: compact, sarif")
policyLintCmd.Flags().String("output-file", "", "Set output file")
policyBundlesCmd.AddCommand(policyLintCmd)

// fmt
policyFmtCmd.Flags().Bool("sort", false, "sort the bundle.")
policyBundlesCmd.AddCommand(policyFmtCmd)

// docs
policyDocsCmd.Flags().Bool("no-code", false, "enable/disable code blocks inside of docs")
policyDocsCmd.Flags().Bool("no-ids", false, "enable/disable the printing of ID fields")
policyBundlesCmd.AddCommand(policyDocsCmd)

// publish
policyPublishCmd.Flags().Bool("no-lint", false, "Disable linting of the bundle before publishing.")
policyPublishCmd.Flags().String("policy-version", "", "Override the version of each policy in the bundle.")
policyBundlesCmd.AddCommand(policyPublishCmd)

rootCmd.AddCommand(policyBundlesCmd)
}

// ensureProviders ensures that all providers are locally installed
func ensureProviders() error {

Check failure on line 50 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / golangci-lint

other declaration of ensureProviders

Check failure on line 50 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / go-test

other declaration of ensureProviders
for _, v := range providers.DefaultProviders {
if _, err := providers.EnsureProvider(providers.ProviderLookup{ID: v.ID}, true, nil); err != nil {
return err
}
}
return nil
}

var policyBundlesCmd = &cobra.Command{
Use: "bundle",
Short: "Manage policy bundles.",
}

//go:embed policy-example.mql.yaml
var embedPolicyTemplate []byte

Check failure on line 65 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / golangci-lint

other declaration of embedPolicyTemplate

Check failure on line 65 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / go-test

other declaration of embedPolicyTemplate

var policyInitCmd = &cobra.Command{

Check failure on line 67 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / golangci-lint

other declaration of policyInitCmd

Check failure on line 67 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / go-test

other declaration of policyInitCmd
Use: "init [path]",
Short: "Create an example policy bundle that you can use as a starting point. If you don't provide a filename, cnspec uses `example-policy.mql.yml`.",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
name := "example-policy.mql.yaml"
if len(args) == 1 {
name = args[0]
}

_, err := os.Stat(name)
if err == nil {
log.Fatal().Msgf("Policy '%s' already exists", name)
}

err = os.WriteFile(name, embedPolicyTemplate, 0o640)
if err != nil {
log.Fatal().Err(err).Msgf("Could not write '%s'", name)
}
log.Info().Msgf("Example policy file written to %s", name)
},
}

var policyLintCmd = &cobra.Command{

Check failure on line 90 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / golangci-lint

other declaration of policyLintCmd

Check failure on line 90 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / go-test

other declaration of policyLintCmd
Use: "lint [path]",
Aliases: []string{"validate"},
Short: "Lint a policy bundle.",
Args: cobra.ExactArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("output", cmd.Flags().Lookup("output"))
viper.BindPFlag("output-file", cmd.Flags().Lookup("output-file"))
},
Run: func(cmd *cobra.Command, args []string) {
log.Info().Str("file", args[0]).Msg("lint policy bundle")
ensureProviders()

files, err := policy.WalkPolicyBundleFiles(args[0])
if err != nil {
log.Fatal().Err(err).Msg("could not find bundle files")
}

runtime := providers.DefaultRuntime()
result, err := bundle.Lint(runtime.Schema(), files...)
if err != nil {
log.Fatal().Err(err).Msg("could not lint bundle files")
}

out := os.Stdout
if viper.GetString("output-file") != "" {
out, err = os.Create(viper.GetString("output-file"))
if err != nil {
log.Fatal().Err(err).Msg("could not create output file")
}
defer out.Close()
}

switch viper.GetString("output") {
case "cli":
out.Write(result.ToCli())
case "sarif":
data, err := result.ToSarif(filepath.Dir(args[0]))
if err != nil {
log.Fatal().Err(err).Msg("could not generate sarif report")
}
out.Write(data)
}

if viper.GetString("output-file") == "" {
if result.HasError() {
log.Fatal().Msg("invalid policy bundle")
} else {
log.Info().Msg("valid policy bundle")
}
}
},
}

var policyFmtCmd = &cobra.Command{

Check failure on line 144 in apps/cnspec/cmd/bundle.go

View workflow job for this annotation

GitHub Actions / go-test

other declaration of policyFmtCmd
Use: "format [path]",
Aliases: []string{"fmt"},
Short: "Apply style formatting to one or more policy bundles.",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
sort, _ := cmd.Flags().GetBool("sort")
ensureProviders()
for _, path := range args {
err := bundle.FormatRecursive(path, sort)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
log.Info().Msg("completed formatting policy bundle(s)")
},
}

var policyPublishCmd = &cobra.Command{
Use: "publish [path]",
Aliases: []string{"upload"},
Short: "Add a user-owned policy to the Mondoo Security Registry.",
Args: cobra.ExactArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("policy-version", cmd.Flags().Lookup("policy-version"))
viper.BindPFlag("no-lint", cmd.Flags().Lookup("no-lint"))
},
Run: func(cmd *cobra.Command, args []string) {
opts, optsErr := config.Read()
if optsErr != nil {
log.Fatal().Err(optsErr).Msg("could not load configuration")
}
config.DisplayUsedConfig()

ensureProviders()

filename := args[0]
log.Info().Str("file", filename).Msg("load policy bundle")
files, err := policy.WalkPolicyBundleFiles(args[0])
if err != nil {
log.Fatal().Err(err).Msg("could not find bundle files")
}

noLint := viper.GetBool("no-lint")
if !noLint {
runtime := providers.DefaultRuntime()
result, err := bundle.Lint(runtime.Schema(), files...)
if err != nil {
log.Fatal().Err(err).Msg("could not lint bundle files")
}

// render cli output
os.Stdout.Write(result.ToCli())

if result.HasError() {
log.Fatal().Msg("invalid policy bundle")
} else {
log.Info().Msg("valid policy bundle")
}
}

// compile manipulates the bundle, therefore we read it again
bundleLoader := policy.DefaultBundleLoader()
policyBundle, err := bundleLoader.BundleFromPaths(filename)
if err != nil {
log.Fatal().Err(err).Msg("could not load policy bundle")
}

log.Info().Str("space", opts.SpaceMrn).Msg("add policy bundle to space")
overrideVersionFlag := false
overrideVersion := viper.GetString("policy-version")
if len(overrideVersion) > 0 {
overrideVersionFlag = true
}

serviceAccount := opts.GetServiceCredential()
if serviceAccount == nil {
log.Fatal().Msg("cnspec has no credentials. Log in with `cnspec login`")
}

certAuth, err := upstream.NewServiceAccountRangerPlugin(serviceAccount)
if err != nil {
log.Error().Err(err).Msg(errorMessageServiceAccount)
os.Exit(ConfigurationErrorCode)
}

httpClient, err := opts.GetHttpClient()
if err != nil {
log.Fatal().Err(err).Msg("error while creating Mondoo API client")
}
queryHubServices, err := policy.NewPolicyHubClient(opts.UpstreamApiEndpoint(), httpClient, certAuth)
if err != nil {
log.Fatal().Err(err).Msg("could not connect to the Mondoo Security Registry")
}

// set the owner mrn for spaces
policyBundle.OwnerMrn = opts.SpaceMrn
ctx := context.Background()

// override version and/or labels
for i := range policyBundle.Policies {
p := policyBundle.Policies[i]

// override policy version
if overrideVersionFlag {
p.Version = overrideVersion
}
}

// send data upstream
_, err = queryHubServices.SetBundle(ctx, policyBundle)
if err != nil {
log.Fatal().Err(err).Msg("could not add policy bundle")
}

log.Info().Msg("successfully added policies")
},
}

var policyDocsCmd = &cobra.Command{
Use: "docs [path]",
Aliases: []string{},
Short: "Retrieve only the docs for a bundle.",
Args: cobra.MinimumNArgs(1),
Hidden: true,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("no-ids", cmd.Flags().Lookup("no-ids"))
viper.BindPFlag("no-code", cmd.Flags().Lookup("no-code"))
},
Run: func(cmd *cobra.Command, args []string) {
bundleLoader := policy.DefaultBundleLoader()
bundle, err := bundleLoader.BundleFromPaths(args...)
if err != nil {
log.Fatal().Err(err).Msg("failed to load bundle")
}

noIDs := viper.GetBool("no-ids")
noCode := viper.GetBool("no-code")
bundle.ExtractDocs(os.Stdout, noIDs, noCode)
},
}
37 changes: 37 additions & 0 deletions apps/cnspec/cmd/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,44 @@ var policyListCmd = &cobra.Command{
// Printing policy MRN in gray
fmt.Printf("\033[90m %s\033[0m\n", policy.Mrn)
}
},
}

var policyUploadCmd = &cobra.Command{
Use: "upload",
Short: "upload a policy to the connected space",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
policyFile := ""
if len(args) == 1 {
policyFile = args[0]
}

registryEndpoint := os.Getenv("REGISTRY_URL")
if registryEndpoint == "" {
registryEndpoint = defaultRegistryUrl
}

// Note, this does not use the proxy config override from the mondoo.yml since we only get here when
// it is used without upstream config
client, err := policy.NewPolicyHubClient(registryEndpoint, ranger.DefaultHttpClient())
if err != nil {
log.Fatal().Err(err)
}

bundleLoader := policy.DefaultBundleLoader()
policyBundle, err := bundleLoader.BundleFromPaths(policyFile)

_, err = client.SetBundle(context.Background(), policyBundle)
if err != nil {
log.Fatal().Err(err).Msg("failed to upload policies")
}
// Success message in green
fmt.Printf("\033[32m→ successfully uploaded %d policies to the space\033[0m", len(policyBundle.Policies))
for _, policy := range policyBundle.Policies {
fmt.Println(" policy: " + policy.Name + " " + policy.Version)
fmt.Printf("\033[90m %s\033[0m\n", policy.Mrn)
}
},
}

Expand Down

0 comments on commit c116e89

Please sign in to comment.