go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/explorer/querypack.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package explorer
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"sort"
    10  
    11  	"github.com/rs/zerolog/log"
    12  	"go.mondoo.com/cnquery/checksums"
    13  	"go.mondoo.com/cnquery/utils/multierr"
    14  	"go.mondoo.com/cnquery/utils/sortx"
    15  )
    16  
    17  func (p *QueryPack) InvalidateAllChecksums() {
    18  	p.LocalContentChecksum = ""
    19  	p.LocalExecutionChecksum = ""
    20  }
    21  
    22  // RefreshMRN computes a MRN from the UID or validates the existing MRN.
    23  // Both of these need to fit the ownerMRN. It also removes the UID.
    24  func (p *QueryPack) RefreshMRN(ownerMRN string) error {
    25  	nu, err := RefreshMRN(ownerMRN, p.Mrn, MRN_RESOURCE_QUERYPACK, p.Uid)
    26  	if err != nil {
    27  		log.Error().Err(err).Str("owner", ownerMRN).Str("uid", p.Uid).Msg("failed to refresh mrn")
    28  		return multierr.Wrap(err, "failed to refresh mrn for query "+p.Name)
    29  	}
    30  
    31  	p.Mrn = nu
    32  	p.Uid = ""
    33  	return nil
    34  }
    35  
    36  func (p *QueryPack) UpdateChecksums() error {
    37  	p.LocalContentChecksum = ""
    38  	p.LocalExecutionChecksum = ""
    39  
    40  	// Note: this relies on the fact that the bundle was compiled before
    41  
    42  	executionChecksum := checksums.New
    43  	contentChecksum := checksums.New
    44  
    45  	contentChecksum = contentChecksum.Add(p.Mrn).Add(p.Name).Add(p.Version).Add(p.OwnerMrn)
    46  	for i := range p.Authors {
    47  		author := p.Authors[i]
    48  		contentChecksum = contentChecksum.Add(author.Email).Add(author.Name)
    49  	}
    50  	contentChecksum = contentChecksum.AddUint(uint64(p.Created)).AddUint(uint64(p.Modified))
    51  
    52  	if p.Docs != nil {
    53  		contentChecksum = contentChecksum.Add(p.Docs.Desc)
    54  	}
    55  
    56  	executionChecksum = executionChecksum.Add(p.Mrn)
    57  
    58  	// tags
    59  	keys := sortx.Keys(p.Tags)
    60  	for _, k := range keys {
    61  		contentChecksum = contentChecksum.Add(k).Add(p.Tags[k])
    62  	}
    63  
    64  	c, e := p.Filters.Checksum()
    65  	contentChecksum = contentChecksum.AddUint(uint64(c))
    66  	executionChecksum = executionChecksum.AddUint(uint64(e))
    67  
    68  	c, e = ChecksumQueries(p.Queries)
    69  	contentChecksum = contentChecksum.AddUint(uint64(c))
    70  	executionChecksum = executionChecksum.AddUint(uint64(e))
    71  
    72  	// Groups
    73  	for i := range p.Groups {
    74  		group := p.Groups[i]
    75  
    76  		contentChecksum = contentChecksum.
    77  			Add(group.Title).
    78  			AddUint(uint64(group.Created)).
    79  			AddUint(uint64(group.Modified))
    80  
    81  		c, e := group.Filters.Checksum()
    82  		contentChecksum = contentChecksum.AddUint(uint64(c))
    83  		executionChecksum = executionChecksum.AddUint(uint64(e))
    84  
    85  		c, e = ChecksumQueries(p.Queries)
    86  		contentChecksum = contentChecksum.AddUint(uint64(c))
    87  		executionChecksum = executionChecksum.AddUint(uint64(e))
    88  	}
    89  
    90  	contentChecksum = contentChecksum.AddUint(uint64(executionChecksum))
    91  
    92  	p.LocalContentChecksum = contentChecksum.String()
    93  	p.LocalExecutionChecksum = executionChecksum.String()
    94  
    95  	return nil
    96  }
    97  
    98  // Computes the checksums for a list of queries, which is sorted and then
    99  // split into a content and execution checksum. These queries must have been
   100  // previously compiled and ready, otherwise the checksums cannot be computed.
   101  func ChecksumQueries(queries []*Mquery) (checksums.Fast, checksums.Fast) {
   102  	content := checksums.New
   103  	execution := checksums.New
   104  
   105  	if len(queries) == 0 {
   106  		return content, execution
   107  	}
   108  
   109  	queryIDs := make([]string, len(queries))
   110  	queryMap := make(map[string]*Mquery, len(queries))
   111  	for i := range queries {
   112  		query := queries[i]
   113  		queryIDs[i] = query.Mrn
   114  		queryMap[query.Mrn] = query
   115  	}
   116  	sort.Strings(queryIDs)
   117  
   118  	for _, queryID := range queryIDs {
   119  		query := queryMap[queryID]
   120  
   121  		// we add this sanity check since we expose the method, but can't ensure
   122  		// that users have compiled everything beforehand
   123  		if query.Checksum == "" || query.CodeId == "" {
   124  			panic("internal error processing filter checksums: query is compiled")
   125  		}
   126  
   127  		// we use the checksum for doc, tag and ref changes
   128  		content = content.Add(query.Checksum)
   129  		execution = execution.Add(query.CodeId)
   130  	}
   131  
   132  	content = content.AddUint(uint64(execution))
   133  
   134  	return content, execution
   135  }
   136  
   137  // ComputeFilters into mql
   138  func (p *QueryPack) ComputeFilters(ctx context.Context, ownerMRN string) ([]*Mquery, error) {
   139  	numFilters := 0
   140  	if p.Filters != nil {
   141  		numFilters += len(p.Filters.Items)
   142  	}
   143  	for i := range p.Groups {
   144  		if p.Groups[i].Filters == nil {
   145  			return nil, errors.New("cannot compute filters for a querypack, unless it was compiled first")
   146  		}
   147  		numFilters += len(p.Groups[i].Filters.Items)
   148  	}
   149  
   150  	res := make([]*Mquery, numFilters)
   151  	idx := 0
   152  	if p.Filters != nil {
   153  		for _, v := range p.Filters.Items {
   154  			res[idx] = v
   155  			idx++
   156  		}
   157  	}
   158  	for i := range p.Groups {
   159  		for _, v := range p.Groups[i].Filters.Items {
   160  			res[idx] = v
   161  			idx++
   162  		}
   163  	}
   164  
   165  	sort.Slice(res, func(i, j int) bool {
   166  		return res[i].CodeId < res[j].CodeId
   167  	})
   168  
   169  	return res, nil
   170  }