parent
7cc83ccbc5
commit
921acb26fe
@ -0,0 +1,118 @@
|
|||||||
|
// 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/coreos/clair/ext/versionfmt"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
searchPotentialAffectingVulneraibilities = `
|
||||||
|
SELECT nf.id, v.id, vaf.affected_version, vaf.id
|
||||||
|
FROM vulnerability_affected_feature AS vaf, vulnerability AS v,
|
||||||
|
namespaced_feature AS nf, feature AS f
|
||||||
|
WHERE nf.id = ANY($1)
|
||||||
|
AND nf.feature_id = f.id
|
||||||
|
AND nf.namespace_id = v.namespace_id
|
||||||
|
AND vaf.feature_name = f.name
|
||||||
|
AND vaf.feature_type = f.type
|
||||||
|
AND vaf.vulnerability_id = v.id
|
||||||
|
AND v.deleted_at IS NULL`
|
||||||
|
insertVulnerabilityAffected = `
|
||||||
|
INSERT INTO vulnerability_affected_feature(vulnerability_id, feature_name, affected_version, feature_type, fixedin)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
|
RETURNING ID
|
||||||
|
`
|
||||||
|
searchVulnerabilityAffected = `
|
||||||
|
SELECT vulnerability_id, feature_name, affected_version, t.name, fixedin
|
||||||
|
FROM vulnerability_affected_feature AS vaf, feature_type AS t
|
||||||
|
WHERE t.id = vaf.feature_type AND vulnerability_id = ANY($1)
|
||||||
|
`
|
||||||
|
|
||||||
|
searchVulnerabilityPotentialAffected = `
|
||||||
|
WITH req AS (
|
||||||
|
SELECT vaf.id AS vaf_id, n.id AS n_id, vaf.feature_name AS name, vaf.feature_type AS type, v.id AS vulnerability_id
|
||||||
|
FROM vulnerability_affected_feature AS vaf,
|
||||||
|
vulnerability AS v,
|
||||||
|
namespace AS n
|
||||||
|
WHERE vaf.vulnerability_id = ANY($1)
|
||||||
|
AND v.id = vaf.vulnerability_id
|
||||||
|
AND n.id = v.namespace_id
|
||||||
|
)
|
||||||
|
SELECT req.vulnerability_id, nf.id, f.version, req.vaf_id AS added_by
|
||||||
|
FROM feature AS f, namespaced_feature AS nf, req
|
||||||
|
WHERE f.name = req.name
|
||||||
|
AND f.type = req.type
|
||||||
|
AND nf.namespace_id = req.n_id
|
||||||
|
AND nf.feature_id = f.id`
|
||||||
|
)
|
||||||
|
|
||||||
|
type vulnerabilityCache struct {
|
||||||
|
nsFeatureID int64
|
||||||
|
vulnID int64
|
||||||
|
vulnAffectingID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func SearchAffectingVulnerabilities(tx *sql.Tx, features []database.NamespacedFeature) ([]vulnerabilityCache, error) {
|
||||||
|
if len(features) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ids, err := feature.FindNamespacedFeatureIDs(tx, features)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fMap := map[int64]database.NamespacedFeature{}
|
||||||
|
for i, f := range features {
|
||||||
|
if !ids[i].Valid {
|
||||||
|
return nil, database.ErrMissingEntities
|
||||||
|
}
|
||||||
|
fMap[ids[i].Int64] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheTable := []vulnerabilityCache{}
|
||||||
|
rows, err := tx.Query(searchPotentialAffectingVulneraibilities, pq.Array(ids))
|
||||||
|
if err != nil {
|
||||||
|
return nil, util.HandleError("searchPotentialAffectingVulneraibilities", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var (
|
||||||
|
cache vulnerabilityCache
|
||||||
|
affected string
|
||||||
|
)
|
||||||
|
|
||||||
|
err := rows.Scan(&cache.nsFeatureID, &cache.vulnID, &affected, &cache.vulnAffectingID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, err := versionfmt.InRange(fMap[cache.nsFeatureID].VersionFormat, fMap[cache.nsFeatureID].Version, affected); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if ok {
|
||||||
|
cacheTable = append(cacheTable, cache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheTable, nil
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
// 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
|
||||||
|
}
|
Loading…
Reference in new issue