Skip to content

Commit

Permalink
Add artifact digest to query condition (#19102)
Browse files Browse the repository at this point in the history
Fixes #19023

Signed-off-by: stonezdj <daojunz@vmware.com>
  • Loading branch information
stonezdj authored Aug 7, 2023
1 parent 3de778e commit c62ec7d
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 14 deletions.
3 changes: 2 additions & 1 deletion api/v2.0/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6099,7 +6099,8 @@ paths:
repository_name(exact match)
project_id(exact match)
package(exact match)
and tag(exact match)
tag(exact match)
digest(exact match)
tags:
- securityhub
operationId: ListVulnerabilities
Expand Down
27 changes: 20 additions & 7 deletions src/pkg/securityhub/dao/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,24 @@ where a.digest = s.digest
)

type filterMetaData struct {
DataType string
// DataType is the data type of the filter, it could be stringType, rangeType
DataType string
// ColumnName is the column name in the database, if it is empty, the key will be used as the column name
ColumnName string
// FilterFunc is the function to generate the filter sql, default is exactMatchFilter
FilterFunc func(ctx context.Context, key string, query *q.Query) (sqlStr string, params []interface{})
}

// filterMap define the query condition
var filterMap = map[string]*filterMetaData{
"cve_id": &filterMetaData{DataType: stringType, FilterFunc: exactMatchFilter},
"severity": &filterMetaData{DataType: stringType, FilterFunc: exactMatchFilter},
"cve_id": &filterMetaData{DataType: stringType},
"severity": &filterMetaData{DataType: stringType},
"cvss_score_v3": &filterMetaData{DataType: rangeType, FilterFunc: rangeFilter},
"project_id": &filterMetaData{DataType: stringType, FilterFunc: exactMatchFilter},
"repository_name": &filterMetaData{DataType: stringType, FilterFunc: exactMatchFilter},
"package": &filterMetaData{DataType: stringType, FilterFunc: exactMatchFilter},
"project_id": &filterMetaData{DataType: stringType},
"repository_name": &filterMetaData{DataType: stringType},
"package": &filterMetaData{DataType: stringType},
"tag": &filterMetaData{DataType: stringType, FilterFunc: tagFilter},
"digest": &filterMetaData{DataType: stringType, ColumnName: "a.digest"},
}

var applyFilterFunc func(ctx context.Context, key string, query *q.Query) (sqlStr string, params []interface{})
Expand All @@ -125,7 +131,11 @@ func exactMatchFilter(ctx context.Context, key string, query *q.Query) (sqlStr s
return
}
if val, ok := query.Keywords[key]; ok {
sqlStr = fmt.Sprintf(" and %v = ?", key)
col := key
if len(filterMap[key].ColumnName) > 0 {
col = filterMap[key].ColumnName
}
sqlStr = fmt.Sprintf(" and %v = ?", col)
params = append(params, val)
return
}
Expand Down Expand Up @@ -325,6 +335,9 @@ func applyVulFilter(ctx context.Context, sqlStr string, query *q.Query, params [
queryStr = sqlStr
newParam = params
for k, m := range filterMap {
if m.FilterFunc == nil {
m.FilterFunc = exactMatchFilter // default filter function is exactMatchFilter
}
s, p := m.FilterFunc(ctx, k, query)
queryStr = queryStr + s
newParam = append(newParam, p...)
Expand Down
63 changes: 57 additions & 6 deletions src/pkg/securityhub/dao/security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package dao

import (
"context"
"strings"
"testing"

"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -60,6 +61,7 @@ values (1003, 1, 'library/hello-world', 'digest1003', 'IMAGE', '2023-06-02 09:16
`insert into scanner_registration (name, url, uuid, auth) values('trivy', 'https://www.vmware.com', 'ruuid', 'empty')`,
`insert into vulnerability_record (id, cve_id, registration_uuid, cvss_score_v3) values (1, '2023-4567-12345', 'ruuid', 9.8)`,
`insert into report_vulnerability_record (report_uuid, vuln_record_id) VALUES ('uuid', 1)`,
`INSERT INTO tag (repository_id, artifact_id, name) VALUES (1, (select id from artifact where repository_name = 'library/hello-world' limit 1), 'tag_test')`,
})

testDao.ExecuteBatchSQL([]string{
Expand All @@ -85,6 +87,7 @@ func (suite *SecurityDaoTestSuite) TearDownTest() {
`delete from vulnerability_record where cve_id='2023-4567-12345'`,
`delete from report_vulnerability_record where report_uuid='ruuid'`,
`delete from vulnerability_record where registration_uuid ='uuid2'`,
`delete from tag where name='tag_test'`,
})
}

Expand Down Expand Up @@ -128,13 +131,13 @@ func Test_checkQFilter(t *testing.T) {
args args
wantErr bool
}{
{"happy_path", args{q.New(q.KeyWords{"sample": 1}), map[string]*filterMetaData{"sample": &filterMetaData{intType, exactMatchFilter}}}, false},
{"happy_path_cve_id", args{q.New(q.KeyWords{"cve_id": "CVE-2023-2345"}), map[string]*filterMetaData{"cve_id": &filterMetaData{stringType, exactMatchFilter}}}, false},
{"happy_path_severity", args{q.New(q.KeyWords{"severity": "Critical"}), map[string]*filterMetaData{"severity": &filterMetaData{stringType, exactMatchFilter}}}, false},
{"happy_path_cvss_score_v3", args{q.New(q.KeyWords{"cvss_score_v3": &q.Range{Min: 2.0, Max: 3.0}}), map[string]*filterMetaData{"cvss_score_v3": &filterMetaData{rangeType, rangeFilter}}}, false},
{"happy_path", args{q.New(q.KeyWords{"sample": 1}), map[string]*filterMetaData{"sample": &filterMetaData{DataType: intType}}}, false},
{"happy_path_cve_id", args{q.New(q.KeyWords{"cve_id": "CVE-2023-2345"}), map[string]*filterMetaData{"cve_id": &filterMetaData{DataType: stringType}}}, false},
{"happy_path_severity", args{q.New(q.KeyWords{"severity": "Critical"}), map[string]*filterMetaData{"severity": &filterMetaData{DataType: stringType}}}, false},
{"happy_path_cvss_score_v3", args{q.New(q.KeyWords{"cvss_score_v3": &q.Range{Min: 2.0, Max: 3.0}}), map[string]*filterMetaData{"cvss_score_v3": &filterMetaData{DataType: rangeType, FilterFunc: rangeFilter}}}, false},
{"unhappy_path", args{q.New(q.KeyWords{"sample": 1}), map[string]*filterMetaData{"a": &filterMetaData{DataType: intType}}}, true},
{"unhappy_path2", args{q.New(q.KeyWords{"cve_id": 1}), map[string]*filterMetaData{"cve_id": &filterMetaData{stringType, exactMatchFilter}}}, true},
{"unhappy_path3", args{q.New(q.KeyWords{"severity": &q.Range{Min: 2.0, Max: 10.0}}), map[string]*filterMetaData{"severity": &filterMetaData{stringType, exactMatchFilter}}}, true},
{"unhappy_path2", args{q.New(q.KeyWords{"cve_id": 1}), map[string]*filterMetaData{"cve_id": &filterMetaData{DataType: stringType}}}, true},
{"unhappy_path3", args{q.New(q.KeyWords{"severity": &q.Range{Min: 2.0, Max: 10.0}}), map[string]*filterMetaData{"severity": &filterMetaData{DataType: stringType}}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -158,6 +161,7 @@ func (suite *SecurityDaoTestSuite) TestExacthMatchFilter() {
wantParams []interface{}
}{
{"normal", args{suite.Context(), "cve_id", q.New(q.KeyWords{"cve_id": "CVE-2023-2345"})}, " and cve_id = ?", []interface{}{"CVE-2023-2345"}},
{"digest", args{suite.Context(), "digest", q.New(q.KeyWords{"digest": "digest123"})}, " and a.digest = ?", []interface{}{"digest123"}},
}
for _, tt := range tests {
suite.Run(tt.name, func() {
Expand Down Expand Up @@ -207,3 +211,50 @@ func (suite *SecurityDaoTestSuite) TestListVul() {
suite.NoError(err)
suite.Equal(1, len(vuls))
}

func (suite *SecurityDaoTestSuite) TestTagFilter() {
type args struct {
ctx context.Context
key string
query *q.Query
}
tests := []struct {
name string
args args
wantSqlStr string
wantParams []interface{}
}{
{"normal", args{suite.Context(), "tag", q.New(q.KeyWords{"tag": "tag_test"})}, " and a.id IN", nil},
}
for _, tt := range tests {
suite.Run(tt.name, func() {
gotSqlStr, gotParams := tagFilter(tt.args.ctx, tt.args.key, tt.args.query)
suite.True(strings.Contains(gotSqlStr, tt.wantSqlStr), "tagFilter() gotSqlStr = %v, want %v", gotSqlStr, tt.wantSqlStr)
suite.Equal(gotParams, tt.wantParams, "tagFilter() gotParams = %v, want %v", gotParams, tt.wantParams)
})
}
}

func (suite *SecurityDaoTestSuite) TestApplyVulFilter() {
type args struct {
ctx context.Context
sqlStr string
query *q.Query
params []interface{}
}
tests := []struct {
name string
args args
wantSqlStr string
wantParams []interface{}
}{
{"normal", args{suite.Context(), "select * from vulnerability_record", q.New(q.KeyWords{"tag": "tag_test"}), nil}, " and a.id IN", nil},
}
for _, tt := range tests {
suite.Run(tt.name, func() {
gotSqlStr, gotParams := applyVulFilter(tt.args.ctx, tt.args.sqlStr, tt.args.query, tt.args.params)
suite.True(strings.Contains(gotSqlStr, tt.wantSqlStr), "applyVulFilter() gotSqlStr = %v, want %v", gotSqlStr, tt.wantSqlStr)
suite.Equal(gotParams, tt.wantParams, "applyVulFilter() gotParams = %v, want %v", gotParams, tt.wantParams)
})
}
}

0 comments on commit c62ec7d

Please sign in to comment.