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  }