github.com/quay/claircore@v1.5.28/datastore/postgres/querybuilder.go (about) 1 package postgres 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/doug-martin/goqu/v8" 9 _ "github.com/doug-martin/goqu/v8/dialect/postgres" 10 11 "github.com/quay/claircore" 12 "github.com/quay/claircore/datastore" 13 "github.com/quay/claircore/libvuln/driver" 14 ) 15 16 // getQueryBuilder validates a IndexRecord and creates a query string for vulnerability matching 17 func buildGetQuery(record *claircore.IndexRecord, opts *datastore.GetOpts) (string, error) { 18 matchers := opts.Matchers 19 psql := goqu.Dialect("postgres") 20 exps := []goqu.Expression{} 21 22 // Add package name as first condition in query. 23 if record.Package.Name == "" { 24 return "", fmt.Errorf("IndexRecord must provide a Package.Name") 25 } 26 packageQuery := goqu.And( 27 goqu.Ex{"package_name": record.Package.Name}, 28 goqu.Ex{"package_kind": record.Package.Kind}, 29 ) 30 exps = append(exps, packageQuery) 31 32 // If the package has a source, convert the first expression to an OR. 33 if record.Package.Source.Name != "" { 34 sourcePackageQuery := goqu.And( 35 goqu.Ex{"package_name": record.Package.Source.Name}, 36 goqu.Ex{"package_kind": record.Package.Source.Kind}, 37 ) 38 or := goqu.Or( 39 packageQuery, 40 sourcePackageQuery, 41 ) 42 exps[0] = or 43 } 44 45 // add matchers 46 seen := make(map[driver.MatchConstraint]struct{}) 47 for _, m := range matchers { 48 if _, ok := seen[m]; ok { 49 continue 50 } 51 var ex goqu.Ex 52 switch m { 53 case driver.PackageModule: 54 ex = goqu.Ex{"package_module": record.Package.Module} 55 case driver.DistributionDID: 56 ex = goqu.Ex{"dist_id": record.Distribution.DID} 57 case driver.DistributionName: 58 ex = goqu.Ex{"dist_name": record.Distribution.Name} 59 case driver.DistributionVersionID: 60 ex = goqu.Ex{"dist_version_id": record.Distribution.VersionID} 61 case driver.DistributionVersion: 62 ex = goqu.Ex{"dist_version": record.Distribution.Version} 63 case driver.DistributionVersionCodeName: 64 ex = goqu.Ex{"dist_version_code_name": record.Distribution.VersionCodeName} 65 case driver.DistributionPrettyName: 66 ex = goqu.Ex{"dist_pretty_name": record.Distribution.PrettyName} 67 case driver.DistributionCPE: 68 ex = goqu.Ex{"dist_cpe": record.Distribution.CPE} 69 case driver.DistributionArch: 70 ex = goqu.Ex{"dist_arch": record.Distribution.Arch} 71 case driver.RepositoryName: 72 ex = goqu.Ex{"repo_name": record.Repository.Name} 73 default: 74 return "", fmt.Errorf("was provided unknown matcher: %v", m) 75 } 76 exps = append(exps, ex) 77 seen[m] = struct{}{} 78 } 79 if opts.VersionFiltering { 80 v := &record.Package.NormalizedVersion 81 var lit strings.Builder 82 b := make([]byte, 0, 16) 83 lit.WriteString("'{") 84 for i := 0; i < 10; i++ { 85 if i != 0 { 86 lit.WriteByte(',') 87 } 88 lit.Write(strconv.AppendInt(b, int64(v.V[i]), 10)) 89 } 90 lit.WriteString("}'::int[]") 91 exps = append(exps, goqu.And( 92 goqu.C("version_kind").Eq(v.Kind), 93 goqu.L("vulnerable_range @> "+lit.String()), 94 )) 95 } 96 exps = append(exps, goqu.I("latest_update_operations.kind").Eq("vulnerability")) 97 98 query := psql.Select( 99 "vuln.id", 100 "name", 101 "description", 102 "issued", 103 "links", 104 "severity", 105 "normalized_severity", 106 "package_name", 107 "package_version", 108 "package_module", 109 "package_arch", 110 "package_kind", 111 "dist_id", 112 "dist_name", 113 "dist_version", 114 "dist_version_code_name", 115 "dist_version_id", 116 "dist_arch", 117 "dist_cpe", 118 "dist_pretty_name", 119 "arch_operation", 120 "repo_name", 121 "repo_key", 122 "repo_uri", 123 "fixed_in_version", 124 "vuln.updater", 125 ).From("vuln"). 126 Join(goqu.I("uo_vuln"), goqu.On(goqu.Ex{"vuln.id": goqu.I("uo_vuln.vuln")})). 127 Join(goqu.I("latest_update_operations"), goqu.On(goqu.Ex{"latest_update_operations.id": goqu.I("uo_vuln.uo")})). 128 Where(exps...) 129 130 sql, _, err := query.ToSQL() 131 if err != nil { 132 return "", err 133 } 134 return sql, nil 135 }