Try goqu as sql builder on listSoftwareDB (#2773)
* Try goqu as sql builder on listSoftwareDB * Fix case insensitive search and add test
This commit is contained in:
@@ -19,6 +19,7 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2
|
||||
github.com/dnaeon/go-vcr/v2 v2.0.1
|
||||
github.com/doug-martin/goqu/v9 v9.18.0 // indirect
|
||||
github.com/e-dard/netbug v0.0.0-20151029172837-e64d308a0b20
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0
|
||||
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c // indirect
|
||||
@@ -28,7 +29,7 @@ require (
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-kit/kit v0.9.0
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0
|
||||
github.com/gomodule/redigo v1.8.5
|
||||
github.com/google/go-cmp v0.5.6
|
||||
|
||||
@@ -212,6 +212,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218=
|
||||
github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA=
|
||||
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA=
|
||||
@@ -225,6 +226,8 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuW
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dnaeon/go-vcr/v2 v2.0.1 h1:KQnAAR6r4GbcJ71KfVxM7qhX85oVj3A0zWbuoxpbYcA=
|
||||
github.com/dnaeon/go-vcr/v2 v2.0.1/go.mod h1:bklL092gNVdADdsX/u2vDs4wGZ52NSgh7YNcZRSiArs=
|
||||
github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY=
|
||||
github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
@@ -299,6 +302,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.7.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@@ -340,6 +345,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -640,6 +646,7 @@ github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+s
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
@@ -672,6 +679,7 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
|
||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI=
|
||||
@@ -1046,6 +1054,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/VividCortex/mysqlerr"
|
||||
"github.com/WatchBeam/clock"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
"github.com/doug-martin/goqu/v9/exp"
|
||||
"github.com/fleetdm/fleet/v4/server/config"
|
||||
"github.com/fleetdm/fleet/v4/server/datastore/mysql/migrations/data"
|
||||
"github.com/fleetdm/fleet/v4/server/datastore/mysql/migrations/tables"
|
||||
@@ -31,10 +33,8 @@ const (
|
||||
mySQLTimestampFormat = "2006-01-02 15:04:05" // %Y/%m/%d %H:%M:%S
|
||||
)
|
||||
|
||||
var (
|
||||
// Matches all non-word and '-' characters for replacement
|
||||
columnCharsRegexp = regexp.MustCompile(`[^\w-]`)
|
||||
)
|
||||
// Matches all non-word and '-' characters for replacement
|
||||
var columnCharsRegexp = regexp.MustCompile(`[^\w-]`)
|
||||
|
||||
// dbReader is an interface that defines the methods required for reads.
|
||||
type dbReader interface {
|
||||
@@ -354,6 +354,38 @@ func sanitizeColumn(col string) string {
|
||||
return columnCharsRegexp.ReplaceAllString(col, "")
|
||||
}
|
||||
|
||||
// appendListOptionsToSelect will apply the given list options to ds and
|
||||
// return the new select dataset.
|
||||
//
|
||||
// NOTE: This is a copy of appendListOptionsToSQL that uses the goqu package.
|
||||
func appendListOptionsToSelect(ds *goqu.SelectDataset, opts fleet.ListOptions) *goqu.SelectDataset {
|
||||
if opts.OrderKey != "" {
|
||||
var orderedExp exp.OrderedExpression
|
||||
ident := goqu.I(sanitizeColumn(opts.OrderKey))
|
||||
if opts.OrderDirection == fleet.OrderDescending {
|
||||
orderedExp = ident.Desc()
|
||||
} else {
|
||||
orderedExp = ident.Asc()
|
||||
}
|
||||
ds = ds.Order(orderedExp)
|
||||
}
|
||||
|
||||
perPage := opts.PerPage
|
||||
// If caller doesn't supply a limit apply a default limit of 1000
|
||||
// to insure that an unbounded query with many results doesn't consume too
|
||||
// much memory or hang
|
||||
if perPage == 0 {
|
||||
perPage = defaultSelectLimit
|
||||
}
|
||||
ds = ds.Limit(perPage)
|
||||
|
||||
offset := perPage * opts.Page
|
||||
if offset > 0 {
|
||||
ds = ds.Offset(offset)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
func appendListOptionsToSQL(sql string, opts fleet.ListOptions) string {
|
||||
if opts.OrderKey != "" {
|
||||
direction := "ASC"
|
||||
@@ -545,6 +577,13 @@ func isChildForeignKeyError(err error) bool {
|
||||
return mysqlErr.Number == ER_NO_REFERENCED_ROW_2
|
||||
}
|
||||
|
||||
// likePattern returns a pattern to match m with LIKE.
|
||||
func likePattern(m string) string {
|
||||
m = strings.Replace(m, "_", "\\_", -1)
|
||||
m = strings.Replace(m, "%", "\\%", -1)
|
||||
return "%" + m + "%"
|
||||
}
|
||||
|
||||
// searchLike adds SQL and parameters for a "search" using LIKE syntax.
|
||||
//
|
||||
// The input columns must be sanitized if they are provided by the user.
|
||||
@@ -553,9 +592,7 @@ func searchLike(sql string, params []interface{}, match string, columns ...strin
|
||||
return sql, params
|
||||
}
|
||||
|
||||
match = strings.Replace(match, "_", "\\_", -1)
|
||||
match = strings.Replace(match, "%", "\\%", -1)
|
||||
pattern := "%" + match + "%"
|
||||
pattern := likePattern(match)
|
||||
ors := make([]string, 0, len(columns))
|
||||
for _, column := range columns {
|
||||
ors = append(ors, column+" LIKE ?")
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
_ "github.com/doug-martin/goqu/v9/dialect/mysql"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
@@ -200,58 +202,136 @@ func insertNewInstalledHostSoftwareDB(
|
||||
return nil
|
||||
}
|
||||
|
||||
func listSoftwareDB(ctx context.Context, q sqlx.QueryerContext, hostID *uint, opt fleet.SoftwareListOptions) ([]fleet.Software, error) {
|
||||
hostWhere := `hs.host_id=?`
|
||||
if hostID == nil {
|
||||
hostWhere = "TRUE"
|
||||
var dialect = goqu.Dialect("mysql")
|
||||
|
||||
// listSoftwareDB returns all the software installed in the given hostID and list options.
|
||||
// If hostID is nil, then the method will look into the installed software of all hosts.
|
||||
func listSoftwareDB(
|
||||
ctx context.Context, q sqlx.QueryerContext, hostID *uint, opts fleet.SoftwareListOptions,
|
||||
) ([]fleet.Software, error) {
|
||||
ds := dialect.From(goqu.I("host_software").As("hs")).SelectDistinct(
|
||||
"s.*",
|
||||
goqu.COALESCE(goqu.I("scp.cpe"), "").As("generated_cpe"),
|
||||
).Join(
|
||||
goqu.I("hosts").As("h"),
|
||||
goqu.On(
|
||||
goqu.I("hs.host_id").Eq(goqu.I("h.id")),
|
||||
),
|
||||
).Join(
|
||||
goqu.I("software").As("s"),
|
||||
goqu.On(
|
||||
goqu.I("hs.software_id").Eq(goqu.I("s.id")),
|
||||
),
|
||||
)
|
||||
|
||||
if hostID != nil {
|
||||
ds = ds.Where(goqu.I("hs.host_id").Eq(hostID))
|
||||
}
|
||||
teamWhere := `h.team_id=?`
|
||||
if opt.TeamID == nil {
|
||||
teamWhere = "TRUE"
|
||||
if opts.TeamID != nil {
|
||||
ds = ds.Where(goqu.I("h.team_id").Eq(opts.TeamID))
|
||||
}
|
||||
vulnerableJoin := "LEFT JOIN software_cpe scp ON (s.id=scp.software_id)"
|
||||
if opt.VulnerableOnly {
|
||||
vulnerableJoin = `JOIN software_cpe scp ON (s.id=scp.software_id)
|
||||
JOIN software_cve scv ON (scp.id=scv.cpe_id)`
|
||||
|
||||
if match := opts.MatchQuery; match != "" {
|
||||
match = likePattern(match)
|
||||
ds = ds.Where(
|
||||
goqu.Or(
|
||||
goqu.I("s.name").ILike(match),
|
||||
goqu.I("s.version").ILike(match),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
ds = ds.GroupBy(
|
||||
goqu.I("s.id"),
|
||||
goqu.I("s.name"),
|
||||
goqu.I("s.version"),
|
||||
goqu.I("s.source"),
|
||||
goqu.I("generated_cpe"),
|
||||
)
|
||||
|
||||
ds = appendListOptionsToSelect(ds, opts.ListOptions)
|
||||
|
||||
if opts.VulnerableOnly {
|
||||
ds = ds.Join(
|
||||
goqu.I("software_cpe").As("scp"),
|
||||
goqu.On(
|
||||
goqu.I("s.id").Eq(goqu.I("scp.software_id")),
|
||||
),
|
||||
).Join(
|
||||
goqu.I("software_cve").As("scv"),
|
||||
goqu.On(goqu.I("scp.id").Eq(goqu.I("scv.cpe_id"))),
|
||||
)
|
||||
} else {
|
||||
ds = ds.LeftJoin(
|
||||
goqu.I("software_cpe").As("scp"),
|
||||
goqu.On(
|
||||
goqu.I("s.id").Eq(goqu.I("scp.software_id")),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
sql, args, err := ds.ToSQL()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "sql build")
|
||||
}
|
||||
sql := fmt.Sprintf(`
|
||||
SELECT DISTINCT s.*, coalesce(scp.cpe, "") as generated_cpe
|
||||
FROM host_software hs
|
||||
JOIN hosts h ON (hs.host_id=h.id)
|
||||
JOIN software s ON (hs.software_id=s.id)
|
||||
%s
|
||||
WHERE %s AND %s
|
||||
`, vulnerableJoin, hostWhere, teamWhere)
|
||||
|
||||
var result []fleet.Software
|
||||
vars := []interface{}{}
|
||||
if hostID != nil {
|
||||
vars = append(vars, hostID)
|
||||
}
|
||||
if opt.TeamID != nil {
|
||||
vars = append(vars, opt.TeamID)
|
||||
}
|
||||
sql, listVars := searchLike(sql, vars, opt.MatchQuery, "s.name", "s.version")
|
||||
sql += ` GROUP BY s.id, s.name, s.version, s.source, generated_cpe `
|
||||
sql = appendListOptionsToSQL(sql, opt.ListOptions)
|
||||
if err := sqlx.SelectContext(ctx, q, &result, sql, listVars...); err != nil {
|
||||
if err := sqlx.SelectContext(ctx, q, &result, sql, args...); err != nil {
|
||||
return nil, errors.Wrap(err, "load host software")
|
||||
}
|
||||
|
||||
if opt.SkipLoadingCVEs {
|
||||
if opts.SkipLoadingCVEs {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
sql = fmt.Sprintf(`
|
||||
SELECT DISTINCT hs.software_id, scv.cve
|
||||
FROM host_software hs
|
||||
JOIN hosts h ON (hs.host_id=h.id)
|
||||
JOIN software_cpe scp ON (hs.software_id=scp.software_id)
|
||||
JOIN software_cve scv ON (scp.id=scv.cpe_id)
|
||||
WHERE %s AND %s
|
||||
`, hostWhere, teamWhere)
|
||||
cvesBySoftware, err := loadCVEsBySoftware(ctx, q, hostID, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load CVEs by software")
|
||||
}
|
||||
for i := range result {
|
||||
result[i].Vulnerabilities = cvesBySoftware[result[i].ID]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
rows, err := q.QueryxContext(ctx, sql, vars...)
|
||||
// loadCVEsbySoftware loads all the CVEs on software installed on the given hostID and list options.
|
||||
// If hostID is nil, then the method will look into the installed software of all hosts.
|
||||
func loadCVEsBySoftware(
|
||||
ctx context.Context, q sqlx.QueryerContext, hostID *uint, opt fleet.SoftwareListOptions,
|
||||
) (map[uint]fleet.VulnerabilitiesSlice, error) {
|
||||
ds := dialect.From(goqu.I("host_software").As("hs")).SelectDistinct(
|
||||
goqu.I("hs.software_id"),
|
||||
goqu.I("scv.cve"),
|
||||
).Join(
|
||||
goqu.I("hosts").As("h"),
|
||||
goqu.On(
|
||||
goqu.I("hs.host_id").Eq(goqu.I("h.id")),
|
||||
),
|
||||
).Join(
|
||||
goqu.I("software_cpe").As("scp"),
|
||||
goqu.On(
|
||||
goqu.I("hs.software_id").Eq(goqu.I("scp.software_id")),
|
||||
),
|
||||
).Join(
|
||||
goqu.I("software_cve").As("scv"),
|
||||
goqu.On(
|
||||
goqu.I("scp.id").Eq(goqu.I("scv.cpe_id")),
|
||||
),
|
||||
)
|
||||
|
||||
if hostID != nil {
|
||||
ds = ds.Where(goqu.I("hs.host_id").Eq(hostID))
|
||||
}
|
||||
if opt.TeamID != nil {
|
||||
ds = ds.Where(goqu.I("h.team_id").Eq(opt.TeamID))
|
||||
}
|
||||
|
||||
sql, args, err := ds.ToSQL()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "sql2 build")
|
||||
}
|
||||
|
||||
rows, err := q.QueryxContext(ctx, sql, args...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load host software")
|
||||
}
|
||||
@@ -272,14 +352,7 @@ func listSoftwareDB(ctx context.Context, q sqlx.QueryerContext, hostID *uint, op
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, errors.Wrap(err, "error iterating through cve rows")
|
||||
}
|
||||
|
||||
var resultWithCVEs []fleet.Software
|
||||
for _, software := range result {
|
||||
software.Vulnerabilities = cvesBySoftware[software.ID]
|
||||
resultWithCVEs = append(resultWithCVEs, software)
|
||||
}
|
||||
|
||||
return resultWithCVEs, nil
|
||||
return cvesBySoftware, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) LoadHostSoftware(ctx context.Context, host *fleet.Host) error {
|
||||
|
||||
@@ -425,7 +425,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
soft2 := fleet.HostSoftware{
|
||||
Modified: true,
|
||||
Software: []fleet.Software{
|
||||
{Name: "foo", Version: "0.0.2", Source: "chrome_extensions"},
|
||||
{Name: "foo", Version: "v0.0.2", Source: "chrome_extensions"},
|
||||
{Name: "foo", Version: "0.0.3", Source: "chrome_extensions"},
|
||||
{Name: "bar", Version: "0.0.3", Source: "deb_packages"},
|
||||
},
|
||||
@@ -452,7 +452,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
{CVE: "cve-333-444-555", DetailsLink: "https://nvd.nist.gov/vuln/detail/cve-333-444-555"},
|
||||
},
|
||||
}
|
||||
foo002 := fleet.Software{Name: "foo", Version: "0.0.2", Source: "chrome_extensions"}
|
||||
foo002 := fleet.Software{Name: "foo", Version: "v0.0.2", Source: "chrome_extensions"}
|
||||
foo003 := fleet.Software{Name: "foo", Version: "0.0.3", Source: "chrome_extensions", GenerateCPE: "someothercpewithoutvulns"}
|
||||
bar003 := fleet.Software{Name: "bar", Version: "0.0.3", Source: "deb_packages"}
|
||||
|
||||
@@ -479,7 +479,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, software, 1)
|
||||
expected := []fleet.Software{foo002}
|
||||
expected := []fleet.Software{foo003}
|
||||
test.ElementsMatchSkipID(t, software, expected)
|
||||
})
|
||||
|
||||
@@ -519,11 +519,23 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
})
|
||||
|
||||
t.Run("filters by query", func(t *testing.T) {
|
||||
software, err := ds.ListSoftware(context.Background(), fleet.SoftwareListOptions{ListOptions: fleet.ListOptions{MatchQuery: "bar"}})
|
||||
// query by name (case insensitive)
|
||||
software, err := ds.ListSoftware(context.Background(), fleet.SoftwareListOptions{ListOptions: fleet.ListOptions{MatchQuery: "baR"}})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, software, 1)
|
||||
expected := []fleet.Software{bar003}
|
||||
test.ElementsMatchSkipID(t, software, expected)
|
||||
// query by version
|
||||
software, err = ds.ListSoftware(context.Background(), fleet.SoftwareListOptions{ListOptions: fleet.ListOptions{MatchQuery: "0.0.3"}})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, software, 2)
|
||||
expected = []fleet.Software{foo003, bar003}
|
||||
test.ElementsMatchSkipID(t, software, expected)
|
||||
// query by version (case insensitive)
|
||||
software, err = ds.ListSoftware(context.Background(), fleet.SoftwareListOptions{ListOptions: fleet.ListOptions{MatchQuery: "V0.0.2"}})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, software, 1)
|
||||
expected = []fleet.Software{foo002}
|
||||
test.ElementsMatchSkipID(t, software, expected)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user