Skip to content

Commit

Permalink
✨ policy exceptions v2 (#943)
Browse files Browse the repository at this point in the history
* policy exceptions v2

Signed-off-by: Ivan Milchev <[email protected]>

* add tests

Signed-off-by: Ivan Milchev <[email protected]>

---------

Signed-off-by: Ivan Milchev <[email protected]>
  • Loading branch information
imilchev authored Nov 17, 2023
1 parent fc44ad6 commit 9f4a0a7
Show file tree
Hide file tree
Showing 9 changed files with 1,497 additions and 1,133 deletions.
2,275 changes: 1,152 additions & 1,123 deletions policy/cnspec_policy.pb.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions policy/cnspec_policy.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ message PolicyGroup {
string title = 24;
PolicyGroupDocs docs = 25;
repeated cnquery.explorer.Author authors = 26;
repeated cnquery.explorer.Author reviewers = 27;
// only applies to GroupType IGNORED/DISABLED
// Only if status is REJECTED, we ignore the group
// Which means that unapproved groups get handled as if they were approved
ReviewStatus review_status = 28;

int64 created = 32;
int64 modified = 33;
Expand Down
15 changes: 7 additions & 8 deletions policy/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -725,13 +725,13 @@ func (s *LocalServices) policyToJobs(ctx context.Context, policyMrn string, owne
}
for i := range group.Checks {
check := group.Checks[i]
if check.Action == explorer.Action_DEACTIVATE {
if check.Action == explorer.Action_DEACTIVATE || group.Type == GroupType_DISABLE {
cache.removedQueries[check.Mrn] = struct{}{}
}
}
for i := range group.Queries {
query := group.Queries[i]
if query.Action == explorer.Action_DEACTIVATE {
if query.Action == explorer.Action_DEACTIVATE || group.Type == GroupType_DISABLE {
cache.removedQueries[query.Mrn] = struct{}{}
}
}
Expand Down Expand Up @@ -891,7 +891,7 @@ func (s *LocalServices) policyGroupToJobs(ctx context.Context, group *PolicyGrou
continue
}

if check.Action == explorer.Action_IGNORE {
if check.Action == explorer.Action_IGNORE || group.Type == GroupType_IGNORED {
stillValid := CheckValidUntil(group.EndDate, check.Mrn)
if !stillValid {
// the exception is no longer valid => score the check
Expand All @@ -904,20 +904,20 @@ func (s *LocalServices) policyGroupToJobs(ctx context.Context, group *PolicyGrou
if impact == nil {
impact = &explorer.Impact{}
}
if check.Action == explorer.Action_IGNORE {
if check.Action == explorer.Action_IGNORE || group.Type == GroupType_IGNORED {
impact.Scoring = explorer.ScoringSystem_IGNORE_SCORE
impact.Action = explorer.Action_IGNORE
}

cache.global.propsCache.Add(check.Props...)

if check.Action == explorer.Action_UNSPECIFIED || check.Action == explorer.Action_ACTIVATE {
if (check.Action == explorer.Action_UNSPECIFIED || check.Action == explorer.Action_ACTIVATE) && group.Type != GroupType_IGNORED {
cache.addCheckJob(ctx, check, impact, ownerJob)
continue
}

// TODO: can we simplify this to simply IGNORE?
if check.Action == explorer.Action_MODIFY || check.Action == explorer.Action_IGNORE {
if check.Action == explorer.Action_MODIFY || check.Action == explorer.Action_IGNORE || group.Type == GroupType_IGNORED {
cache.modifyCheckJob(check, impact)
}
}
Expand Down Expand Up @@ -977,13 +977,12 @@ func (cache *policyResolverCache) addCheckJob(ctx context.Context, check *explor
cache.global.reportingJobsByUUID[uuid] = queryJob
cache.global.reportingJobsByMsum[check.Checksum] = append(cache.global.reportingJobsByMsum[check.Checksum], queryJob)
cache.childJobsByMrn[check.Mrn] = append(cache.childJobsByMrn[check.Mrn], queryJob)
ownerJob.ChildJobs[queryJob.Uuid] = impact
}

// local aspects for the resolved policy
queryJob.Notify = append(queryJob.Notify, ownerJob.Uuid)

ownerJob.ChildJobs[queryJob.Uuid] = impact

if len(check.Variants) != 0 {
err := cache.addCheckJobVariants(ctx, check, queryJob)
if err != nil {
Expand Down
226 changes: 226 additions & 0 deletions policy/scan/local_scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"go.mondoo.com/cnquery/v9/explorer"
"go.mondoo.com/cnquery/v9/llx"
"go.mondoo.com/cnquery/v9/providers"
"go.mondoo.com/cnquery/v9/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v9/providers-sdk/v1/testutils"
"go.mondoo.com/cnquery/v9/providers-sdk/v1/upstream"
"go.mondoo.com/cnspec/v9/policy"
)

func TestFilterPreprocess(t *testing.T) {
Expand Down Expand Up @@ -109,3 +114,224 @@ func TestDefaultConfig(t *testing.T) {
require.Equal(t, providers.NullRecording{}, scanner.recording)
})
}

type LocalScannerSuite struct {
suite.Suite
ctx context.Context
schema llx.Schema
job *Job
}

func (s *LocalScannerSuite) SetupSuite() {
s.ctx = context.Background()
runtime := testutils.Local()
s.schema = runtime.Schema()
}

func (s *LocalScannerSuite) BeforeTest(suiteName, testName string) {
s.job = &Job{
Inventory: &inventory.Inventory{
Spec: &inventory.InventorySpec{
Assets: []*inventory.Asset{
{
Connections: []*inventory.Config{
{
Type: "k8s",
Options: map[string]string{
"path": "./testdata/2pods.yaml",
},
},
},
},
},
},
},
}
}

func (s *LocalScannerSuite) TestRunIncognito_ExceptionGroups() {
bundle, err := policy.BundleFromPaths("./testdata/exception-groups.mql.yaml")
s.Require().NoError(err)

_, err = bundle.CompileExt(context.Background(), policy.BundleCompileConf{
Schema: s.schema,
RemoveFailing: true,
})
s.Require().NoError(err)

s.job.Bundle = bundle
s.job.PolicyFilters = []string{"asset-policy"}
bundleMap := bundle.ToMap()

ctx := context.Background()
scanner := NewLocalScanner()
res, err := scanner.RunIncognito(ctx, s.job)
s.Require().NoError(err)
s.Require().NotNil(res)

full := res.GetFull()
s.Require().NotNil(full)

s.Equal(1, len(full.Reports))

for k, r := range full.Reports {
// Verify the score is 100
s.Equal(uint32(100), r.GetScore().Value)

p := full.ResolvedPolicies[k]

// Get the code id for all the executed queries
executedQueries := []string{}
for qCodeId := range p.ExecutionJob.Queries {
executedQueries = append(executedQueries, qCodeId)
}

expectedQueries := []string{
bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/ignored-query"].CodeId,
bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/sshd-score-01"].CodeId,
}
s.ElementsMatch(expectedQueries, executedQueries)

queryIdToReportingJob := map[string]*policy.ReportingJob{}
for _, rj := range p.CollectorJob.ReportingJobs {
_, ok := queryIdToReportingJob[rj.QrId]
s.Require().False(ok)
queryIdToReportingJob[rj.QrId] = rj
}

// Make sure the ignored query is ignored
queryRj := queryIdToReportingJob[bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/ignored-query"].CodeId]
s.Require().NotNil(queryRj)

parent := queryRj.Notify[0]
parentJob := p.CollectorJob.ReportingJobs[parent]
s.Require().NotNil(parentJob)
s.Equal(explorer.ScoringSystem_IGNORE_SCORE, parentJob.ChildJobs[queryRj.Uuid].Scoring)
}
}

func (s *LocalScannerSuite) TestRunIncognito_QueryExceptions() {
bundle, err := policy.BundleFromPaths("./testdata/exceptions.mql.yaml")
s.Require().NoError(err)

_, err = bundle.CompileExt(context.Background(), policy.BundleCompileConf{
Schema: s.schema,
RemoveFailing: true,
})
s.Require().NoError(err)

s.job.Bundle = bundle
s.job.PolicyFilters = []string{"asset-policy"}
bundleMap := bundle.ToMap()

ctx := context.Background()
scanner := NewLocalScanner()
res, err := scanner.RunIncognito(ctx, s.job)
s.Require().NoError(err)
s.Require().NotNil(res)

full := res.GetFull()
s.Require().NotNil(full)

s.Equal(1, len(full.Reports))

for k, r := range full.Reports {
// Verify the score is 100
s.Equal(uint32(100), r.GetScore().Value)

p := full.ResolvedPolicies[k]

// Get the code id for all the executed queries
executedQueries := []string{}
for qCodeId := range p.ExecutionJob.Queries {
executedQueries = append(executedQueries, qCodeId)
}

expectedQueries := []string{
bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/ignored-query"].CodeId,
bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/sshd-score-01"].CodeId,
}
s.ElementsMatch(expectedQueries, executedQueries)

queryIdToReportingJob := map[string]*policy.ReportingJob{}
for _, rj := range p.CollectorJob.ReportingJobs {
_, ok := queryIdToReportingJob[rj.QrId]
s.Require().False(ok)
queryIdToReportingJob[rj.QrId] = rj
}

// Make sure the ignored query is ignored
queryRj := queryIdToReportingJob[bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/ignored-query"].CodeId]
s.Require().NotNil(queryRj)

parent := queryRj.Notify[0]
parentJob := p.CollectorJob.ReportingJobs[parent]
s.Require().NotNil(parentJob)
s.Equal(explorer.ScoringSystem_IGNORE_SCORE, parentJob.ChildJobs[queryRj.Uuid].Scoring)
}
}

func (s *LocalScannerSuite) TestRunIncognito_QueryExceptions_MultipleGroups() {
bundle, err := policy.BundleFromPaths("./testdata/exceptions-multiple-groups.mql.yaml")
s.Require().NoError(err)

_, err = bundle.CompileExt(context.Background(), policy.BundleCompileConf{
Schema: s.schema,
RemoveFailing: true,
})
s.Require().NoError(err)

s.job.Bundle = bundle
s.job.PolicyFilters = []string{"asset-policy"}
bundleMap := bundle.ToMap()

ctx := context.Background()
scanner := NewLocalScanner()
res, err := scanner.RunIncognito(ctx, s.job)
s.Require().NoError(err)
s.Require().NotNil(res)

full := res.GetFull()
s.Require().NotNil(full)

s.Equal(1, len(full.Reports))

for k, r := range full.Reports {
// Verify the score is 100
s.Equal(uint32(100), r.GetScore().Value)

p := full.ResolvedPolicies[k]

// Get the code id for all the executed queries
executedQueries := []string{}
for qCodeId := range p.ExecutionJob.Queries {
executedQueries = append(executedQueries, qCodeId)
}

expectedQueries := []string{
bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/ignored-query"].CodeId,
bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/sshd-score-01"].CodeId,
}
s.ElementsMatch(expectedQueries, executedQueries)

queryIdToReportingJob := map[string]*policy.ReportingJob{}
for _, rj := range p.CollectorJob.ReportingJobs {
_, ok := queryIdToReportingJob[rj.QrId]
s.Require().False(ok)
queryIdToReportingJob[rj.QrId] = rj
}

// Make sure the ignored query is ignored
queryRj := queryIdToReportingJob[bundleMap.Queries["//local.cnspec.io/run/local-execution/queries/ignored-query"].CodeId]
s.Require().NotNil(queryRj)

parent := queryRj.Notify[0]
parentJob := p.CollectorJob.ReportingJobs[parent]
s.Require().NotNil(parentJob)
s.Equal(explorer.ScoringSystem_IGNORE_SCORE, parentJob.ChildJobs[queryRj.Uuid].Scoring)
}
}

func TestLocalScannerSuite(t *testing.T) {
suite.Run(t, new(LocalScannerSuite))
}
2 changes: 1 addition & 1 deletion policy/scan/scan.pb.go

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

36 changes: 36 additions & 0 deletions policy/scan/testdata/exception-groups.mql.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (c) Mondoo, Inc.
# SPDX-License-Identifier: BUSL-1.1

# Read more about the policy structure at https://mondoo.com/docs
policies:
- uid: sshd-server-policy
name: SSH Server Policy
version: 1.0.0
groups:
- filters: true == true
checks:
- uid: sshd-score-01
- uid: ignored-query
- uid: deactivate-query
- uid : asset-policy
groups:
- checks:
- uid: ignored-query
type: 4
policies:
- uid: sshd-server-policy
- checks:
- uid: deactivate-query
type: 5
policies:
- uid: sshd-server-policy
queries:
- uid: sshd-score-01
title: Ensure SSH MaxAuthTries is set to 4 or less
mql: true == true
- uid: ignored-query
title: Ignored query
mql: asset.name == "test"
- uid: deactivate-query
title: Deactivate query
mql: asset.name == "test2"
36 changes: 36 additions & 0 deletions policy/scan/testdata/exceptions-multiple-groups.mql.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (c) Mondoo, Inc.
# SPDX-License-Identifier: BUSL-1.1

# Read more about the policy structure at https://mondoo.com/docs
policies:
- uid: sshd-server-policy
name: SSH Server Policy
version: 1.0.0
groups:
- filters: true == true
checks:
- uid: sshd-score-01
- uid: ignored-query
- uid: deactivate-query
- uid : asset-policy
groups:
- checks:
- uid: ignored-query
action: 4
policies:
- uid: sshd-server-policy
- checks:
- uid: deactivate-query
action: 2
policies:
- uid: sshd-server-policy
queries:
- uid: sshd-score-01
title: Ensure SSH MaxAuthTries is set to 4 or less
mql: true == true
- uid: ignored-query
title: Ignored query
mql: asset.name == "test"
- uid: deactivate-query
title: Deactivate query
mql: asset.name == "test2"
Loading

0 comments on commit 9f4a0a7

Please sign in to comment.