diff --git a/policy/policy.go b/policy/policy.go index 6d12b236..4472fb90 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -17,6 +17,7 @@ import ( "go.mondoo.com/cnquery/v10/mqlc" "go.mondoo.com/cnquery/v10/mrn" "go.mondoo.com/cnquery/v10/types" + "go.mondoo.com/cnquery/v10/utils/sortx" "google.golang.org/protobuf/proto" ) @@ -300,11 +301,8 @@ func (p *Policy) UpdateChecksums(ctx context.Context, // POLICIES (must be sorted) policyMRNs := make([]string, len(group.Policies)) - i = 0 for i := range group.Policies { - policy := group.Policies[i] - policyMRNs[i] = policy.Mrn - i++ + policyMRNs[i] = group.Policies[i].Mrn } sort.Strings(policyMRNs) for _, policyMRN := range policyMRNs { @@ -570,6 +568,23 @@ func (p *Policy) updateAllChecksums(ctx context.Context, } } + // RISKS + riskIdx := make(map[string]*RiskFactor, len(p.RiskFactors)) + for i := range p.RiskFactors { + cur := p.RiskFactors[i] + riskIdx[cur.Mrn] = cur + } + + sortedRiskMRNs := sortx.Keys(riskIdx) + for _, riskMRN := range sortedRiskMRNs { + esum, csum, err := riskIdx[riskMRN].RefreshChecksum(ctx, conf) + if err != nil { + return err + } + executionChecksum = executionChecksum.AddUint(uint64(esum)) + contentChecksum = contentChecksum.AddUint(uint64(csum)) + } + p.LocalExecutionChecksum = executionChecksum.String() p.LocalContentChecksum = executionChecksum.AddUint(uint64(contentChecksum)).String() diff --git a/policy/risk_factor.go b/policy/risk_factor.go index 7efe6cc6..2e1b8246 100644 --- a/policy/risk_factor.go +++ b/policy/risk_factor.go @@ -6,6 +6,7 @@ package policy import ( "context" + "github.com/pkg/errors" "github.com/rs/zerolog/log" "go.mondoo.com/cnquery/v10/checksums" "go.mondoo.com/cnquery/v10/mqlc" @@ -53,49 +54,69 @@ func (r *RiskFactor) RefreshMRN(ownerMRN string) error { return nil } -func (r *RiskFactor) RefreshChecksum(conf mqlc.CompilerConfig) error { +func (r *RiskFactor) ExecutionChecksum(ctx context.Context, conf mqlc.CompilerConfig) (checksums.Fast, error) { c := checksums.New. - Add(r.Mrn). - Add(r.Title). - Add(r.Docs.Active). - Add(r.Docs.Inactive). AddUint(uint64(r.Scope)). AddUint(uint64(r.Magnitude)) + if r.IsAbsolute { + c = c.AddUint(1) + } else { + c = c.AddUint(0) + } + var err error c, err = r.Filters.ComputeChecksum(c, r.Mrn, conf) if err != nil { - return err + return c, err } for i := range r.Checks { check := r.Checks[i] - if err := check.RefreshChecksum(context.Background(), conf, nil); err != nil { - return err + if err := check.RefreshChecksum(ctx, conf, nil); err != nil { + return c, err } - } - if r.IsAbsolute { - c = c.Add("1") - } else { - c = c.Add("0") + if check.Checksum == "" { + return c, errors.New("failed to get checksum for risk query " + check.Mrn) + } + + c = c.Add(check.Checksum) } for i := range r.Software { - cur := r.Software[i] - c = c.Add(cur.Type). - Add(cur.Name). - Add(cur.Namespace). - Add(cur.Version). - Add(cur.MqlMrn) + sw := r.Software[i] + c = c.Add(sw.MqlMrn).Add(sw.Name).Add(sw.Namespace).Add(sw.Type).Add(sw.Version) } + for i := range r.Resources { - cur := r.Resources[i] - c = c.Add(cur.Selector) + rc := r.Resources[i] + c = c.Add(rc.Selector) } - r.Checksum = c.String() - return nil + return c, nil +} + +// RefreshChecksum updates the Checksum field of this RiskFactor and returns +// both the ExecutionChecksum and the ContentChecksum. +func (r *RiskFactor) RefreshChecksum(ctx context.Context, conf mqlc.CompilerConfig) (checksums.Fast, checksums.Fast, error) { + csum := checksums.New + + esum, err := r.ExecutionChecksum(ctx, conf) + if err != nil { + return esum, csum, err + } + + csum = csum.AddUint(uint64(esum)). + Add(r.Mrn). + Add(r.Title) + + if r.Docs != nil { + csum = csum.Add(r.Docs.Active).Add(r.Docs.Inactive) + } + + r.Checksum = csum.String() + return esum, csum, nil } func (r *RiskFactor) AdjustRiskScore(score *Score, isDetected bool) { diff --git a/policy/risk_factor_test.go b/policy/risk_factor_test.go index 554cb15d..8a277039 100644 --- a/policy/risk_factor_test.go +++ b/policy/risk_factor_test.go @@ -4,16 +4,137 @@ package policy import ( + "context" "strconv" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.mondoo.com/cnquery/v10" + "go.mondoo.com/cnquery/v10/explorer" + "go.mondoo.com/cnquery/v10/mqlc" + "go.mondoo.com/cnquery/v10/providers-sdk/v1/testutils" ) func risks(risks ...*ScoredRiskFactor) *ScoredRiskFactors { return &ScoredRiskFactors{Items: risks} } +func TestRiskFactor_Checksums(t *testing.T) { + base := RiskFactor{ + Mrn: "//long/mrn", + Title: "Some title", + Docs: &RiskFactorDocs{ + Active: "Does exist", + Inactive: "Does not exist", + }, + Filters: &explorer.Filters{ + Items: map[string]*explorer.Mquery{ + "//filters/mrn": { + Mrn: "//filters/mrn", + Mql: "true", + }, + }, + }, + Checks: []*explorer.Mquery{ + { + Mrn: "//some/check/1", + Mql: "1 == 1", + }, + { + Mrn: "//some/check/2", + Mql: "2 == 2", + }, + }, + Scope: ScopeType_SOFTWARE_AND_RESOURCE, + Magnitude: 0.5, + IsAbsolute: true, + Software: []*SoftwareSelector{{ + Name: "mypackage", + Version: "1.2.3", + }}, + Resources: []*ResourceSelector{{ + Selector: "mondoo", + }}, + } + + coreSchema := testutils.MustLoadSchema(testutils.SchemaProvider{Provider: "core"}) + conf := mqlc.NewConfig(coreSchema, cnquery.DefaultFeatures) + + ctx := context.Background() + baseEsum, baseCsum, err := base.RefreshChecksum(ctx, conf) + require.NoError(t, err) + require.NotEqual(t, baseEsum, baseCsum) + + contentChanges := []func(RiskFactor) RiskFactor{ + // 0 + func(rf RiskFactor) RiskFactor { + rf.Title = "" + return rf + }, + // 1 + func(rf RiskFactor) RiskFactor { + rf.Docs = nil + return rf + }, + } + + for i := range contentChanges { + test := contentChanges[i] + t.Run("contentChange/"+strconv.Itoa(i), func(t *testing.T) { + mod := test(base) + esum, csum, err := mod.RefreshChecksum(ctx, conf) + assert.NoError(t, err) + assert.Equal(t, baseEsum, esum) + assert.NotEqual(t, baseCsum, csum) + }) + } + + executionChanges := []func(RiskFactor) RiskFactor{ + // 0 + func(rf RiskFactor) RiskFactor { + rf.Checks = rf.Checks[0:1] + return rf + }, + // 1 + func(rf RiskFactor) RiskFactor { + rf.Checks[0].Mql = "0 != 1" + return rf + }, + // 2 + func(rf RiskFactor) RiskFactor { + rf.Resources[0].Selector = "asset" + return rf + }, + // 3 + func(rf RiskFactor) RiskFactor { + rf.Software[0].Name = "mondoo" + return rf + }, + // 4 + func(rf RiskFactor) RiskFactor { + rf.IsAbsolute = false + return rf + }, + // 5 + func(rf RiskFactor) RiskFactor { + rf.Magnitude = 0.79 + return rf + }, + } + + for i := range executionChanges { + test := executionChanges[i] + t.Run("executionChanges/"+strconv.Itoa(i), func(t *testing.T) { + mod := test(base) + esum, csum, err := mod.RefreshChecksum(ctx, conf) + assert.NoError(t, err) + assert.NotEqual(t, baseEsum, esum) + assert.NotEqual(t, baseCsum, csum) + }) + } +} + func TestRiskFactor_AdjustRiskScore(t *testing.T) { tests := []struct { risk RiskFactor