Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 load flags via viper/cnquery providers #1508

Merged
merged 3 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions apps/cnspec/cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ To manually configure a policy, use this:
$ cnspec scan local -f bundle.mql.yaml --incognito

`,
PreRunE: func(cmd *cobra.Command, args []string) error {
PreRun: func(cmd *cobra.Command, _ []string) {
// Special handling for users that want to see what output options are
// available. We have to do this before printing the help because we
// don't have a target connection or provider.
Expand Down Expand Up @@ -131,18 +131,16 @@ To manually configure a policy, use this:

_ = viper.BindPFlag("json", cmd.Flags().Lookup("json"))
_ = viper.BindPFlag("output", cmd.Flags().Lookup("output"))
if err := viper.BindPFlag("output-target", cmd.Flags().Lookup("output-target")); err != nil {
return err
}

return nil
_ = viper.BindPFlag("output-target", cmd.Flags().Lookup("output-target"))
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
return []string{"yml", "yaml", "json"}, cobra.ShellCompDirectiveFilterFileExt
}
return []string{}, cobra.ShellCompDirectiveNoFileComp
},
// we have to initialize an empty run so it shows up as a runnable command in --help
Run: func(cmd *cobra.Command, args []string) {},
}

var scanCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *plugin.ParseCLIRes) {
Expand Down
4 changes: 2 additions & 2 deletions cli/reporter/cnspec_report.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion policy/cnspec_policy.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions policy/scan/scan.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/cli/testdata/cnspec_scan.ct
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ To manually configure a policy, use this:
$ cnspec scan local -f bundle.mql.yaml --incognito

Usage:
cnspec scan [flags]
cnspec scan [command]

Available Commands:
Expand Down
226 changes: 226 additions & 0 deletions test/providers/os_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package providers

import (
"os"
"sync"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mondoo.com/cnquery/v11/test"
)

var once sync.Once

const mqlPackagesQuery = "packages"

type mqlPackages []struct {
Packages []struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
} `json:"packages.list,omitempty"`
}

const mqlPlatformQuery = "asset.platform"

type mqlPlatform []struct {
Platform string `json:"asset.platform,omitempty"`
}

type connections []struct {
name string
binary string
args []string
tests []mqlTest
}

type mqlTest struct {
query string
expected func(*testing.T, test.Runner)
}

func TestOsProviderSharedTests(t *testing.T) {
once.Do(setup)

connections := connections{
{
name: "local",
binary: "./cnspec",
args: []string{"run", "local"},
tests: []mqlTest{
{
mqlPackagesQuery,
func(t *testing.T, r test.Runner) {
var c mqlPackages
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
},
},
{
mqlPlatformQuery,
func(t *testing.T, r test.Runner) {
var c mqlPlatform
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.True(t, len(x.Platform) > 0)
},
},
},
},
{
name: "fs",
binary: "./cnspec",
args: []string{"run", "fs", "--path", "./testdata/fs"},
tests: []mqlTest{
{
mqlPackagesQuery,
func(t *testing.T, r test.Runner) {
var c mqlPackages
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
},
},
{
mqlPlatformQuery,
func(t *testing.T, r test.Runner) {
var c mqlPlatform
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.Equal(t, "debian", x.Platform)
},
},
},
},
{
name: "docker",
binary: "./cnspec",
args: []string{"run", "docker", "alpine:latest"},
tests: []mqlTest{
{
mqlPackagesQuery,
func(t *testing.T, r test.Runner) {
var c mqlPackages
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
},
},
{
mqlPlatformQuery,
func(t *testing.T, r test.Runner) {
var c mqlPlatform
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.Equal(t, "alpine", x.Platform)
},
},
},
},
}

// iterate over all tests for all connections
for _, cc := range connections {
for _, tt := range cc.tests {

t.Run(cc.name+"/"+tt.query, func(t *testing.T) {
r := test.NewCliTestRunner(cc.binary, append(cc.args, "-c", tt.query, "-j")...)
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

tt.expected(t, r)
})
}
}
}

func TestProvidersEnvVarsLoading(t *testing.T) {
once.Do(setup)

t.Run("command WITHOUT path should not find any package", func(t *testing.T) {
r := test.NewCliTestRunner("./cnspec", "run", "fs", "-c", mqlPackagesQuery, "-j")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

var c mqlPackages
err = r.Json(&c)
assert.NoError(t, err)

// No packages
assert.Empty(t, c)
})
t.Run("command WITH path should find packages", func(t *testing.T) {
os.Setenv("MONDOO_PATH", "./testdata/fs")
defer os.Unsetenv("MONDOO_PATH")
// Note we are not passing the flag "--path ./testdata/fs"
r := test.NewCliTestRunner("./cnspec", "run", "fs", "-c", mqlPackagesQuery, "-j")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

var c mqlPackages
err = r.Json(&c)
assert.NoError(t, err)

// Should have packages
if assert.NotEmpty(t, c) {
x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
}
})

t.Run("command with flags set to not bind to config (ConfigEntry=\"-\")", func(t *testing.T) {
t.Run("should work via direct flag", func(t *testing.T) {
r := test.NewCliTestRunner("./cnspec", "run", "ssh", "localhost", "-c", "ls", "-p", "test", "-v")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
if assert.NotNil(t, r.Stderr()) {
assert.Contains(t, string(r.Stderr()), "skipping config binding for password")
assert.Contains(t, string(r.Stderr()), "enabled ssh password authentication")
}
})
t.Run("should NOT work via config/env-vars", func(t *testing.T) {
os.Setenv("MONDOO_PASSWORD", "test")
defer os.Unsetenv("MONDOO_PASSWORD")
r := test.NewCliTestRunner("./cnspec", "run", "ssh", "localhost", "-c", "ls", "-v")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
if assert.NotNil(t, r.Stderr()) {
assert.Contains(t, string(r.Stderr()), "skipping config binding for password")
assert.NotContains(t, string(r.Stderr()), "enabled ssh password authentication")
}
})
})
}
76 changes: 76 additions & 0 deletions test/providers/scan_flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package providers

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mondoo.com/cnquery/v11/test"
"go.mondoo.com/cnspec/v11/policy"
)

func TestScanFlags(t *testing.T) {
once.Do(setup)

t.Run("successful scan without flags", func(t *testing.T) {
r := test.NewCliTestRunner("./cnspec", "scan", "docker", "alpine:latest", "--json")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

var c policy.ReportCollection
err = r.Json(&c)
assert.NoError(t, err)

// Assest must be found
assert.NotEmpty(t, c.Assets)
})
t.Run("github scan WITHOUT flags", func(t *testing.T) {
// NOTE this will fail but, it will load the flags and fail with the right message
r := test.NewCliTestRunner("./cnspec", "scan", "github", "repo", "foo")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

assert.Contains(t, string(r.Stderr()),
"a valid GitHub authentication is required",
)
})
t.Run("github scan WITH flags but missing app auth key", func(t *testing.T) {
// NOTE this will fail but, it will load the flags and fail with the right message
r := test.NewCliTestRunner("./cnspec", "scan", "github", "repo", "foo",
"--app-id", "123", "--app-installation-id", "456",
)
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 1, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

assert.Contains(t, string(r.Stderr()),
"app-private-key is required for GitHub App authentication", // expected! it means we loaded the flags
)
})
t.Run("github scan WITH all required flags for app auth", func(t *testing.T) {
// NOTE this will fail but, it will load the flags and fail with the right message
r := test.NewCliTestRunner("./cnspec", "scan", "github", "repo", "foo",
"--app-id", "123", "--app-installation-id", "456", "--app-private-key", "private-key.pem",
)
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 1, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

assert.Contains(t, string(r.Stderr()),
"could not read private key", // expected! it means we loaded the flags
)
})
}
17 changes: 17 additions & 0 deletions test/providers/setup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package providers

import (
"log"
"os/exec"
)

// setup builds cnspec locally
func setup() {
// build cnspec
if err := exec.Command("go", "build", "../../apps/cnspec/cnspec.go").Run(); err != nil {
log.Fatalf("building cnspec: %v", err)
}
}
1 change: 1 addition & 0 deletions test/providers/testdata/fs/etc/debian_version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
12.5
1 change: 1 addition & 0 deletions test/providers/testdata/fs/etc/hostname
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
debianfs
Loading
Loading