Skip to content

Commit

Permalink
✨ Add new server-side vulnerability report (#978)
Browse files Browse the repository at this point in the history
* ✨ Add new server-side vulnerability report

cnspec fetches the vulnerability report from upstream and displays the list of vulnerable packages from this report.

The affected commands are scan and vuln.

Also:
Fixes #977

Signed-off-by: Christian Zunker <[email protected]>
  • Loading branch information
czunker authored Dec 13, 2023
1 parent c189bb3 commit c3f93a2
Show file tree
Hide file tree
Showing 15 changed files with 1,266 additions and 1,188 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,8 @@ test/lint/govet:
test/lint/golangci-lint/run: prep/tools
golangci-lint --version
golangci-lint run

.PHONY: test/lint/golangci-lint/run/new
test/lint/golangci-lint/run/new: prep/tools
golangci-lint --version
golangci-lint run --timeout 10m --config .github/.golangci.yml --new-from-rev $(shell git log -n 1 origin/main --pretty=format:"%H")
59 changes: 39 additions & 20 deletions apps/cnspec/cmd/vuln.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
package cmd

import (
"encoding/json"
"os"

"github.com/mitchellh/mapstructure"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -15,8 +15,10 @@ import (
"go.mondoo.com/cnquery/v9/logger"
"go.mondoo.com/cnquery/v9/providers"
"go.mondoo.com/cnquery/v9/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v9/providers-sdk/v1/upstream/gql"
"go.mondoo.com/cnquery/v9/providers-sdk/v1/upstream/mvd"
"go.mondoo.com/cnspec/v9/cli/reporter"
mondoogql "go.mondoo.com/mondoo-go"
"go.mondoo.com/ranger-rpc/codes"
"go.mondoo.com/ranger-rpc/status"
)
Expand Down Expand Up @@ -87,60 +89,77 @@ var vulnCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *pl
log.Error().Err(err).Msg("failed to initialize cnspec shell")
}

vulnReportQuery := "asset.vulnerabilityReport"
vulnReportDatapointChecksum := executor.MustGetOneDatapoint(executor.MustCompile(vulnReportQuery))
_, results, err := sh.RunOnce(vulnReportQuery)
packagesQuery := "packages{ name version }"
packagesDatapointChecksum := executor.MustGetOneDatapoint(executor.MustCompile(packagesQuery))
codeBundle, results, err := sh.RunOnce(packagesQuery)
if err != nil {
log.Error().Err(err).Msg("failed to run query")
return
}

// render vulnerability report
var vulnReport mvd.VulnReport
value, ok := results[vulnReportDatapointChecksum]
value, ok := results[packagesDatapointChecksum]
if !ok {
log.Error().Msg("could not find advisory report\n\n")
log.Error().Msg("could not find packages data\n\n")
return
}

if value == nil || value.Data == nil {
log.Error().Msg("could not load advisory report\n\n")
log.Error().Msg("could not load packages data\n\n")
return
}

if value.Data.Error != nil {
err := value.Data.Error
log.Err(value.Data.Error).Msg("could not load packages data\n\n")
return
}

packagesJson := value.Data.JSON(packagesDatapointChecksum, codeBundle)

gqlPackages := []mondoogql.PackageInput{}
err = json.Unmarshal(packagesJson, &gqlPackages)
if err != nil {
log.Error().Err(err).Msg("failed to unmarshal packages")
return
}

client, err := runtime.UpstreamConfig.InitClient()
if err != nil {
if status, ok := status.FromError(err); ok {
code := status.Code()
switch code {
case codes.Unauthenticated:
log.Fatal().Msg(unauthedErrorMsg)
default:
log.Err(value.Data.Error).Msg("could not load advisory report")
log.Err(err).Msg("could not authenticate upstream")
return
}
}
}

rawData := value.Data.Value
cfg := &mapstructure.DecoderConfig{
Metadata: nil,
Result: &vulnReport,
TagName: "json",
mondooClient, err := gql.NewClient(*runtime.UpstreamConfig, client.HttpClient)
if err != nil {
log.Error().Err(err).Msg("could not initialize mondoo client")
return
}
decoder, _ := mapstructure.NewDecoder(cfg)
err = decoder.Decode(rawData)

platform := runtime.Provider.Connection.GetAsset().GetPlatform()
gqlVulnReport, err := mondooClient.GetIncognitoVulnReport(mondoogql.PlatformInput{
Name: mondoogql.NewStringPtr(mondoogql.String(platform.Name)),
Release: mondoogql.NewStringPtr(mondoogql.String(platform.Version)),
}, gqlPackages)
if err != nil {
log.Error().Msg("could not decode advisory report\n\n")
log.Error().Err(err).Msg("could not load advisory report")
return
}

vulnReport := gql.ConvertToMvdVulnReport(gqlVulnReport)

target := runtime.Provider.Connection.Asset.Name
if target == "" {
target = runtime.Provider.Connection.Asset.Mrn
}

printVulns(&vulnReport, conf, target)
printVulns(vulnReport, conf, target)
}

func printVulns(report *mvd.VulnReport, conf *scanConfig, target string) {
Expand Down
33 changes: 7 additions & 26 deletions cli/reporter/print_compact.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"strings"
"unicode/utf8"

"github.com/mitchellh/mapstructure"
"github.com/muesli/ansi"
"github.com/muesli/termenv"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -362,7 +361,7 @@ func (r *defaultReporter) printAssetSections(orderedAssets []assetMrnName) {
r.printAssetQueries(resolved, report, queries, assetMrn, asset)
r.out.Write([]byte(NewLineCharacter))
// TODO: we should re-use the report results
r.printVulns(resolved, report, report.RawResults())
r.printVulns(report, assetMrn)

}
r.out.Write([]byte(NewLineCharacter))
Expand Down Expand Up @@ -586,39 +585,21 @@ func (r *defaultReporter) printCheck(score *policy.Score, query *explorer.Mquery

// ============================= ^^ ============================================

func (r *defaultReporter) printVulns(resolved *policy.ResolvedPolicy, report *policy.Report, results map[string]*llx.RawResult) {
func (r *defaultReporter) printVulns(report *policy.Report, assetMrn string) {
print := r.Printer

value, _ := getVulnReport(results)
if value == nil || value.Data == nil {
return
}
if value.Data.Error != nil {
r.out.Write([]byte(print.Error("Could not load the vulnerability report: "+value.Data.Error.Error()) + NewLineCharacter + NewLineCharacter))
vulnReport := r.data.VulnReports[assetMrn]

if vulnReport == nil {
return
}

r.out.Write([]byte(print.Primary("Vulnerabilities:" + NewLineCharacter)))

score := report.Scores[advisoryPolicyMrn]
_ = score

rawData := value.Data.Value

var vulnReport mvd.VulnReport
cfg := &mapstructure.DecoderConfig{
Metadata: nil,
Result: &vulnReport,
TagName: "json",
}
decoder, _ := mapstructure.NewDecoder(cfg)
if err := decoder.Decode(rawData); err != nil {
r.out.Write([]byte(print.Error("could not decode advisory report" + NewLineCharacter + NewLineCharacter)))
return
}

r.printVulnList(&vulnReport)
r.printVulnSummary(&vulnReport)
r.printVulnList(vulnReport)
r.printVulnSummary(vulnReport)
}

func (r *defaultReporter) printVulnList(report *mvd.VulnReport) {
Expand Down
2 changes: 1 addition & 1 deletion cli/reporter/reporter.pb.go

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

6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ go 1.21

toolchain go1.21.3

require go.mondoo.com/cnquery/v9 v9.11.0
require (
go.mondoo.com/cnquery/v9 v9.11.0
go.mondoo.com/mondoo-go v0.0.0-20231208095824-90b6fcd58afb
)

require (
github.com/Masterminds/semver v1.5.0
Expand Down Expand Up @@ -112,7 +115,6 @@ require (
github.com/xen0n/gosmopolitan v1.2.2 // indirect
github.com/ykadowak/zerologlint v0.1.4 // indirect
go-simpler.org/sloglint v0.3.0 // indirect
go.mondoo.com/mondoo-go v0.0.0-20231208095824-90b6fcd58afb // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.tmz.dev/musttag v0.7.2 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231127180814-3a041ad873d4 // indirect
Expand Down
Loading

0 comments on commit c3f93a2

Please sign in to comment.