143 lines
4.2 KiB
Go
143 lines
4.2 KiB
Go
// Copyright 2019 clair authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package vulnerability
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
"github.com/coreos/clair/database"
|
|
"github.com/coreos/clair/database/pgsql/feature"
|
|
"github.com/coreos/clair/database/pgsql/util"
|
|
"github.com/lib/pq"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
searchNamespacedFeaturesVulnerabilities = `
|
|
SELECT vanf.namespaced_feature_id, v.name, v.description, v.link,
|
|
v.severity, v.metadata, vaf.fixedin, n.name, n.version_format
|
|
FROM vulnerability_affected_namespaced_feature AS vanf,
|
|
Vulnerability AS v,
|
|
vulnerability_affected_feature AS vaf,
|
|
namespace AS n
|
|
WHERE vanf.namespaced_feature_id = ANY($1)
|
|
AND vaf.id = vanf.added_by
|
|
AND v.id = vanf.vulnerability_id
|
|
AND n.id = v.namespace_id
|
|
AND v.deleted_at IS NULL`
|
|
|
|
lockVulnerabilityAffects = `LOCK vulnerability_affected_namespaced_feature IN SHARE ROW EXCLUSIVE MODE`
|
|
|
|
insertVulnerabilityAffectedNamespacedFeature = `
|
|
INSERT INTO vulnerability_affected_namespaced_feature(vulnerability_id, namespaced_feature_id, added_by)
|
|
VALUES ($1, $2, $3)`
|
|
)
|
|
|
|
func queryPersistVulnerabilityAffectedNamespacedFeature(count int) string {
|
|
return util.QueryPersist(count, "vulnerability_affected_namespaced_feature",
|
|
"vulnerability_affected_namesp_vulnerability_id_namespaced_f_key",
|
|
"vulnerability_id",
|
|
"namespaced_feature_id",
|
|
"added_by")
|
|
}
|
|
|
|
// FindAffectedNamespacedFeatures retrieves vulnerabilities associated with the
|
|
// feature.
|
|
func FindAffectedNamespacedFeatures(tx *sql.Tx, features []database.NamespacedFeature) ([]database.NullableAffectedNamespacedFeature, error) {
|
|
if len(features) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
vulnerableFeatures := make([]database.NullableAffectedNamespacedFeature, len(features))
|
|
featureIDs, err := feature.FindNamespacedFeatureIDs(tx, features)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i, id := range featureIDs {
|
|
if id.Valid {
|
|
vulnerableFeatures[i].Valid = true
|
|
vulnerableFeatures[i].NamespacedFeature = features[i]
|
|
}
|
|
}
|
|
|
|
rows, err := tx.Query(searchNamespacedFeaturesVulnerabilities, pq.Array(featureIDs))
|
|
if err != nil {
|
|
return nil, util.HandleError("searchNamespacedFeaturesVulnerabilities", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var (
|
|
featureID int64
|
|
vuln database.VulnerabilityWithFixedIn
|
|
)
|
|
|
|
err := rows.Scan(&featureID,
|
|
&vuln.Name,
|
|
&vuln.Description,
|
|
&vuln.Link,
|
|
&vuln.Severity,
|
|
&vuln.Metadata,
|
|
&vuln.FixedInVersion,
|
|
&vuln.Namespace.Name,
|
|
&vuln.Namespace.VersionFormat,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, util.HandleError("searchNamespacedFeaturesVulnerabilities", err)
|
|
}
|
|
|
|
for i, id := range featureIDs {
|
|
if id.Valid && id.Int64 == featureID {
|
|
vulnerableFeatures[i].AffectedNamespacedFeature.AffectedBy = append(vulnerableFeatures[i].AffectedNamespacedFeature.AffectedBy, vuln)
|
|
}
|
|
}
|
|
}
|
|
|
|
return vulnerableFeatures, nil
|
|
}
|
|
|
|
func CacheAffectedNamespacedFeatures(tx *sql.Tx, features []database.NamespacedFeature) error {
|
|
if len(features) == 0 {
|
|
return nil
|
|
}
|
|
|
|
_, err := tx.Exec(lockVulnerabilityAffects)
|
|
if err != nil {
|
|
return util.HandleError("lockVulnerabilityAffects", err)
|
|
}
|
|
|
|
cache, err := SearchAffectingVulnerabilities(tx, features)
|
|
|
|
keys := make([]interface{}, 0, len(cache)*3)
|
|
for _, c := range cache {
|
|
keys = append(keys, c.vulnID, c.nsFeatureID, c.vulnAffectingID)
|
|
}
|
|
|
|
if len(cache) == 0 {
|
|
return nil
|
|
}
|
|
|
|
affected, err := tx.Exec(queryPersistVulnerabilityAffectedNamespacedFeature(len(cache)), keys...)
|
|
if err != nil {
|
|
return util.HandleError("persistVulnerabilityAffectedNamespacedFeature", err)
|
|
}
|
|
if count, err := affected.RowsAffected(); err != nil {
|
|
log.Debugf("Cached %d features in vulnerability_affected_namespaced_feature", count)
|
|
}
|
|
return nil
|
|
}
|