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 }