// 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 }