Skip to content

Commit

Permalink
🐛 Don't exit on single asset policy mismatch (#63)
Browse files Browse the repository at this point in the history
Report on assets where we have something to report and skip the others.

Fixes #48

Signed-off-by: Christian Zunker <[email protected]>

Signed-off-by: Christian Zunker <[email protected]>
Signed-off-by: Christoph Hartmann <[email protected]>
Co-authored-by: Christoph Hartmann <[email protected]>
  • Loading branch information
czunker and chris-rock authored Oct 11, 2022
1 parent f0e3497 commit 3b586ce
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 28 deletions.
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@
"policy/examples/example.yaml"
]
},
{
"name": "Scan k8s bundle (incognito)",
"type": "go",
"request": "launch",
"program": "${workspaceRoot}/apps/cnspec/cnspec.go",
"cwd": "${workspaceRoot}/",
"args": [
"scan",
"k8s",
"--discover",
"all",
"--all-namespaces",
"-f",
"../cnspec-policies/core/mondoo-kubernetes-security.mql.yaml"
]
},
{
"name": "Scan local (incognito)",
"type": "go",
Expand Down
14 changes: 6 additions & 8 deletions apps/cnspec/cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,10 @@ This example connects to Microsoft 365 using the PKCS #12 formatted certificate:
log.Fatal().Err(err).Msg("failed to resolve policies")
}

report := RunScan(conf)
report, err := RunScan(conf)
if err != nil {
log.Fatal().Err(err).Msg("failed to run scan")
}
printReports(report, conf, cmd)
},
})
Expand Down Expand Up @@ -493,23 +496,18 @@ func (c *scanConfig) loadPolicies() error {
return errors.New("Cannot yet resolve policies other than incognito")
}

func RunScan(config *scanConfig) *policy.ReportCollection {
func RunScan(config *scanConfig) (*policy.ReportCollection, error) {
scanner := scan.NewLocalScanner()
ctx := cnquery.SetFeatures(context.Background(), config.Features)

reports, err := scanner.RunIncognito(
return scanner.RunIncognito(
ctx,
&scan.Job{
DoRecord: config.DoRecord,
Inventory: config.Inventory,
Bundle: config.Bundle,
PolicyFilters: config.PolicyNames,
})
if err != nil {
log.Fatal().Err(err).Msg("failed to run scan")
}

return reports
}

func printReports(report *policy.ReportCollection, conf *scanConfig, cmd *cobra.Command) {
Expand Down
28 changes: 18 additions & 10 deletions cli/reporter/print_compact.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,26 @@ func (r *defaultReporter) printAssetSummary(assetMrn string, asset *policy.Asset
target = assetMrn
}

r.out.Write([]byte(termenv.String(fmt.Sprintf("\nTarget: %s\n", target)).Foreground(r.Colors.Primary).String()))

report, ok := r.data.Reports[assetMrn]
if !ok {
// If scanning the asset has failed, there will be no report, we should first look if there's an error for that target.
if err, ok := r.data.Errors[assetMrn]; ok {
r.out.Write([]byte(termenv.String(fmt.Sprintf(
`✕ Error for asset %s: %s`,
target, err,
`✕ Errors: %s`, err,
)).Foreground(r.Colors.Error).String()))
} else {
r.out.Write([]byte(fmt.Sprintf(
`✕ Could not find asset %s`,
target,
)))
}
r.out.Write([]byte{'\n'})
return
}
if report == nil {
// the asset didn't match any policy, so no report was generated
return
}

Expand All @@ -114,8 +120,6 @@ func (r *defaultReporter) printAssetSummary(assetMrn string, asset *policy.Asset
score := printCompactScoreSummary(report.Score)
report.ComputeStats(resolved)

r.out.Write([]byte(termenv.String(fmt.Sprintf("\nTarget: %s\n", target)).Foreground(r.Colors.Primary).String()))

if report.Stats == nil || report.Stats.Total == 0 {
r.out.Write([]byte(fmt.Sprintf("Datapoints: %d\n", len(report.Data))))
} else {
Expand Down Expand Up @@ -184,22 +188,26 @@ func (r *defaultReporter) printAssetSections(orderedAssets []assetMrnName) {
target = assetMrn
}

report, ok := r.data.Reports[assetMrn]
if !ok {
// nothing to do, we get an error message in the summary code
continue
}
if report == nil {
// the asset didn't match any policy, so no report was generated
continue
}
assetString := fmt.Sprintf("Asset: %s", target)
assetDivider := strings.Repeat("=", utf8.RuneCountInString(assetString))
r.out.Write([]byte(termenv.String("Asset: ").Foreground(r.Colors.Secondary).String()))
r.out.Write([]byte(termenv.String(fmt.Sprintf("%s\n", target)).Foreground(r.Colors.Primary).String()))
r.out.Write([]byte(termenv.String(assetDivider).Foreground(r.Colors.Secondary).String()))
r.out.Write([]byte{'\n'})
report, ok := r.data.Reports[assetMrn]
if !ok {
// nothing to do, we get an error message in the summary code
break
}

resolved, ok := r.data.ResolvedPolicies[assetMrn]
if !ok {
// nothing to do, we get an additional error message in the summary code
break
continue
}

r.printAssetQueries(resolved, report, queries, assetMrn, asset)
Expand Down
26 changes: 16 additions & 10 deletions policy/scan/local_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (s *LocalScanner) distributeJob(job *Job, ctx context.Context) (*policy.Rep
select {
case <-ctx.Done():
log.Warn().Msg("request context has been canceled")
return reporter.Reports(), false, reporter.Error()
return reporter.Reports(), false, nil
default:
}

Expand All @@ -140,7 +140,7 @@ func (s *LocalScanner) distributeJob(job *Job, ctx context.Context) (*policy.Rep
})
}

return reporter.Reports(), true, reporter.Error()
return reporter.Reports(), true, nil
}

func (s *LocalScanner) RunAssetJob(job *AssetJob) {
Expand Down Expand Up @@ -183,6 +183,7 @@ func (s *LocalScanner) RunAssetJob(job *AssetJob) {
job.connection = m
results, err := s.runMotorizedAsset(job)
if err != nil {
log.Warn().Err(err).Str("asset", job.Asset.Name).Msg("could not scan asset")
job.Reporter.AddScanError(job.Asset, err)
return
}
Expand Down Expand Up @@ -237,6 +238,9 @@ type localAssetScanner struct {
Progress progress.Progress
}

// run() runs a bundle on a single asset. It returns the results of the scan and an error if the scan failed. Even in
// case of an error, the results may contain partial results. The error is only returned if the scan failed to run not
// when individual policies failed.
func (s *localAssetScanner) run() (*AssetReport, error) {
s.Progress.Open()

Expand All @@ -252,18 +256,20 @@ func (s *localAssetScanner) run() (*AssetReport, error) {
return nil, err
}

ar := &AssetReport{
Mrn: s.job.Asset.Mrn,
ResolvedPolicy: resolvedPolicy,
Bundle: bundle,
}

report, err := s.getReport()
if err != nil {
return nil, err
return ar, err
}

log.Debug().Str("asset", s.job.Asset.Mrn).Msg("scan complete")
return &AssetReport{
Mrn: s.job.Asset.Mrn,
ResolvedPolicy: resolvedPolicy,
Bundle: bundle,
Report: report,
}, nil
ar.Report = report
return ar, nil
}

func (s *localAssetScanner) prepareAsset() error {
Expand Down Expand Up @@ -396,7 +402,7 @@ func (s *localAssetScanner) getReport() (*policy.Report, error) {
var resolver policy.PolicyResolver = s.services

// TODO: we do not needs this anymore since we recieve updates already
log.Info().Str("asset", s.job.Asset.Mrn).Msg("client> send all results")
log.Debug().Str("asset", s.job.Asset.Mrn).Msg("client> send all results")
_, err := policy.WaitUntilDone(resolver, s.job.Asset.Mrn, s.job.Asset.Mrn, 1*time.Second)
s.Progress.Close()
// handle error
Expand Down

0 comments on commit 3b586ce

Please sign in to comment.