harbor/src/pkg/scan/export/manager.go

156 lines
4.0 KiB
Go

package export
import (
"context"
"encoding/json"
"errors"
"fmt"
beego_orm "github.com/beego/beego/v2/client/orm"
"github.com/goharbor/harbor/src/lib/orm"
q2 "github.com/goharbor/harbor/src/lib/q"
)
const (
// This sql template aims to select vuln data from database,
// which receive one parameter:
// 1. artifacts id sets
// consider for performance, the caller will slice the artifact ids to multi
// groups if it's length over limit, so rowNum offset is designed to ensure the
// final row id is sequence in the final output csv file.
VulnScanReportQueryTemplate = `
select
artifact.digest as artifact_digest,
artifact.repository_id,
artifact.repository_name,
vulnerability_record.cve_id,
vulnerability_record.package,
vulnerability_record.severity,
vulnerability_record.cwe_ids,
vulnerability_record.package_version,
vulnerability_record.fixed_version,
to_jsonb(vulnerability_record.vendor_attributes) as vendor_attributes,
scanner_registration."name" as scanner_name
from
report_vulnerability_record
inner join scan_report on report_vulnerability_record.report_uuid = scan_report.uuid
inner join artifact on scan_report.digest = artifact.digest
left outer join artifact_reference on artifact.id = artifact_reference.child_id
inner join vulnerability_record on report_vulnerability_record.vuln_record_id = vulnerability_record.id
inner join scanner_registration on scan_report.registration_uuid = scanner_registration.uuid
and artifact.id in (%s)
group by
package,
vulnerability_record.severity,
vulnerability_record.cve_id,
artifact.digest,
artifact.repository_id,
artifact.repository_name,
vulnerability_record.cwe_ids,
vulnerability_record.package_version,
vulnerability_record.fixed_version,
to_jsonb(vulnerability_record.vendor_attributes),
scanner_registration.id
`
JobModeExport = "export"
JobModeKey = "mode"
)
var (
Mgr = NewManager()
)
// Params specifies the filters for controlling the scan data export process
type Params struct {
// cve ids
CVEIds string
// artifact ids
ArtifactIDs []int64
// PageNumber
PageNumber int64
// PageSize
PageSize int64
}
// FromJSON parses robot from json data
func (p *Params) FromJSON(jsonData string) error {
if len(jsonData) == 0 {
return errors.New("empty json data to parse")
}
return json.Unmarshal([]byte(jsonData), p)
}
// ToJSON marshals Robot to JSON data
func (p *Params) ToJSON() (string, error) {
data, err := json.Marshal(p)
if err != nil {
return "", err
}
return string(data), nil
}
type Manager interface {
Fetch(ctx context.Context, params Params) ([]Data, error)
}
type exportManager struct {
exportDataFilter VulnerabilityDataSelector
}
func NewManager() Manager {
return &exportManager{exportDataFilter: NewVulnerabilityDataSelector()}
}
func (em *exportManager) Fetch(ctx context.Context, params Params) ([]Data, error) {
exportData := make([]Data, 0)
rawSeter, _ := em.buildQuery(ctx, params)
_, err := rawSeter.QueryRows(&exportData)
if err != nil {
return nil, err
}
exportData, err = em.exportDataFilter.Select(exportData, CVEIDMatches, params.CVEIds)
if err != nil {
return nil, err
}
return exportData, nil
}
func (em *exportManager) buildQuery(ctx context.Context, params Params) (beego_orm.RawSeter, error) {
artIDs := ""
for _, artID := range params.ArtifactIDs {
if len(artIDs) == 0 {
artIDs += fmt.Sprintf("%d", artID)
} else {
artIDs += fmt.Sprintf(",%d", artID)
}
}
sql := fmt.Sprintf(VulnScanReportQueryTemplate, artIDs)
ormer, err := orm.FromContext(ctx)
if err != nil {
return nil, err
}
pageSize := params.PageSize
q := &q2.Query{
Keywords: nil,
Sorts: nil,
PageNumber: params.PageNumber,
PageSize: pageSize,
Sorting: "",
}
paginationParams := make([]interface{}, 0)
query, pageLimits := orm.PaginationOnRawSQL(q, sql, paginationParams)
// user can open ORM_DEBUG for log the sql
return ormer.Raw(query, pageLimits), nil
}