go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/explorer/filters.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package explorer 5 6 import ( 7 "context" 8 "encoding/json" 9 "errors" 10 "sort" 11 "strconv" 12 "strings" 13 14 "go.mondoo.com/cnquery/checksums" 15 llx "go.mondoo.com/cnquery/llx" 16 "go.mondoo.com/cnquery/utils/multierr" 17 ) 18 19 // NewFilters creates a Filters object from a simple list of MQL snippets 20 func NewFilters(queries ...string) *Filters { 21 res := &Filters{ 22 Items: map[string]*Mquery{}, 23 } 24 25 for i := range queries { 26 res.Items[strconv.Itoa(i)] = &Mquery{Mql: queries[i]} 27 } 28 29 return res 30 } 31 32 // Computes the checksum for the filters and adds it to the aggregate 33 // execution and content checksums. Filters must have been previously compiled! 34 // We need it to be ready for checksums and don't want to do the compile 35 // step here because it's not the primary function. 36 func (filters *Filters) Checksum() (checksums.Fast, checksums.Fast) { 37 content := checksums.New 38 execution := checksums.New 39 40 if filters == nil { 41 return content, execution 42 } 43 44 keys := make([]string, len(filters.Items)) 45 i := 0 46 for k := range filters.Items { 47 // we add this sanity check since we expose the method, but can't ensure 48 // that users have compiled everything beforehand 49 if len(k) < 2 { 50 panic("internal error processing filter checksums: queries are not compiled") 51 } 52 53 keys[i] = k 54 i++ 55 } 56 sort.Strings(keys) 57 58 for i := range keys { 59 filter := filters.Items[keys[i]] 60 content = content.Add(filter.Title).Add(filter.Desc) 61 62 // we add this sanity check since we expose the method, but can't ensure 63 // that users have compiled everything beforehand 64 if filter.Checksum == "" || filter.CodeId == "" { 65 panic("internal error processing filter checksums: query is compiled") 66 } 67 68 content = content.Add(filter.Checksum) 69 execution = execution.Add(filter.CodeId) 70 } 71 72 content = content.AddUint(uint64(execution)) 73 74 return content, execution 75 } 76 77 func (s *Filters) UnmarshalJSON(data []byte) error { 78 var str string 79 err := json.Unmarshal(data, &str) 80 if err == nil { 81 s.Items = map[string]*Mquery{} 82 s.Items[""] = &Mquery{ 83 Mql: str, 84 } 85 return nil 86 } 87 88 // FIXME: DEPRECATED, remove in v9.0 vv 89 // This old style of specifying filters is going to be removed, we 90 // have an alternative with list and keys 91 var arr []string 92 err = json.Unmarshal(data, &arr) 93 if err == nil { 94 s.Items = map[string]*Mquery{} 95 for i := range arr { 96 s.Items[strconv.Itoa(i)] = &Mquery{Mql: arr[i]} 97 } 98 return nil 99 } 100 // ^^ 101 102 var list []*Mquery 103 err = json.Unmarshal(data, &list) 104 if err == nil { 105 s.Items = map[string]*Mquery{} 106 for i := range list { 107 s.Items[strconv.Itoa(i)] = list[i] 108 } 109 return nil 110 } 111 112 // prevent recursive calls into UnmarshalJSON with a placeholder type 113 type tmp Filters 114 return json.Unmarshal(data, (*tmp)(s)) 115 } 116 117 func (s *Filters) Compile(ownerMRN string, schema llx.Schema) error { 118 if s == nil || len(s.Items) == 0 { 119 return nil 120 } 121 122 res := make(map[string]*Mquery, len(s.Items)) 123 for _, query := range s.Items { 124 query.RefreshAsFilter(ownerMRN, schema) 125 126 if _, ok := res[query.CodeId]; ok { 127 continue 128 } 129 130 res[query.CodeId] = query 131 } 132 133 s.Items = res 134 return nil 135 } 136 137 // AddFilters takes all given filters (or nil) and adds them to the parent. 138 // Note: The parent must be non-empty and non-nil, or this method will panic. 139 func (s *Filters) AddFilters(child *Filters) { 140 if child == nil { 141 return 142 } 143 144 for k, v := range child.Items { 145 s.Items[k] = v 146 } 147 } 148 149 var ErrQueryNotFound = errors.New("query not found") 150 151 // AddQueryFilters attempt to take a query (or nil) and register all its filters. 152 // This includes any variants that the query might have as well. 153 func (s *Filters) AddQueryFilters(query *Mquery, lookupQueries map[string]*Mquery) error { 154 if query == nil { 155 return nil 156 } 157 158 return s.AddQueryFiltersFn(context.Background(), query, func(_ context.Context, mrn string) (*Mquery, error) { 159 q, ok := lookupQueries[mrn] 160 if !ok { 161 return nil, ErrQueryNotFound 162 } 163 return q, nil 164 }) 165 } 166 167 // AddQueryFiltersFn attempt to take a query (or nil) and register all its filters. 168 // This includes any variants that the query might have as well. 169 func (s *Filters) AddQueryFiltersFn(ctx context.Context, query *Mquery, lookupQuery func(ctx context.Context, mrn string) (*Mquery, error)) error { 170 if query == nil { 171 return nil 172 } 173 174 s.AddFilters(query.Filters) 175 176 for i := range query.Variants { 177 mrn := query.Variants[i].Mrn 178 variant, err := lookupQuery(ctx, mrn) 179 if err != nil { 180 return multierr.Wrap(err, "cannot find query variant "+mrn) 181 } 182 s.AddQueryFiltersFn(ctx, variant, lookupQuery) 183 } 184 return nil 185 } 186 187 // Checks if the given queries (via CodeIDs) are supported by this set of 188 // asset filters. Asset filters that are not defined return true. 189 // If any of the filters is supported, the set returns true. 190 func (s *Filters) Supports(supported map[string]struct{}) bool { 191 if s == nil || len(s.Items) == 0 { 192 return true 193 } 194 195 for k := range s.Items { 196 if _, ok := supported[k]; ok { 197 return true 198 } 199 } 200 201 return false 202 } 203 204 func (s *Filters) Summarize() string { 205 if s == nil || len(s.Items) == 0 { 206 return "" 207 } 208 209 filters := make([]string, len(s.Items)) 210 i := 0 211 for _, filter := range s.Items { 212 if filter.Title != "" { 213 filters[i] = filter.Title 214 } else { 215 filters[i] = filter.Mql 216 } 217 i++ 218 } 219 220 sort.Strings(filters) 221 return strings.Join(filters, ", ") 222 }