|
6 | 6 | "log/slog" |
7 | 7 | "strconv" |
8 | 8 | "time" |
| 9 | + "unique" |
9 | 10 |
|
10 | 11 | "github.com/jackc/pgx/v5" |
11 | 12 | "github.com/prometheus/client_golang/prometheus" |
@@ -172,8 +173,101 @@ func (s *MatcherStore) Get(ctx context.Context, records []*claircore.IndexRecord |
172 | 173 | getVulnerabilitiesCounter.WithLabelValues("query_batch").Add(1) |
173 | 174 | getVulnerabilitiesDuration.WithLabelValues("query_batch").Observe(time.Since(start).Seconds()) |
174 | 175 |
|
| 176 | + if err := populateAliases(ctx, tx, results); err != nil { |
| 177 | + return nil, fmt.Errorf("populating aliases: %w", err) |
| 178 | + } |
| 179 | + |
175 | 180 | if err := tx.Commit(ctx); err != nil { |
176 | 181 | return nil, fmt.Errorf("failed to commit tx: %v", err) |
177 | 182 | } |
178 | 183 | return results, nil |
179 | 184 | } |
| 185 | + |
| 186 | +// populateAliases fetches aliases and self references for all vulnerabilities |
| 187 | +// in the results map and populates the Aliases and Self fields. |
| 188 | +func populateAliases(ctx context.Context, tx pgx.Tx, results map[string][]*claircore.Vulnerability) error { |
| 189 | + vulnByID := make(map[string]*claircore.Vulnerability) |
| 190 | + for _, vulns := range results { |
| 191 | + for _, v := range vulns { |
| 192 | + vulnByID[v.ID] = v |
| 193 | + } |
| 194 | + } |
| 195 | + if len(vulnByID) == 0 { |
| 196 | + return nil |
| 197 | + } |
| 198 | + |
| 199 | + ids := make([]int64, 0, len(vulnByID)) |
| 200 | + for id := range vulnByID { |
| 201 | + n, err := strconv.ParseInt(id, 10, 64) |
| 202 | + if err != nil { |
| 203 | + continue |
| 204 | + } |
| 205 | + ids = append(ids, n) |
| 206 | + } |
| 207 | + |
| 208 | + const aliasQuery = ` |
| 209 | + SELECT va.vulnerability, ns.namespace, a.name |
| 210 | + FROM vulnerability_alias va |
| 211 | + JOIN alias a ON va.alias = a.id |
| 212 | + JOIN alias_namespace ns ON a.namespace = ns.id |
| 213 | + WHERE va.vulnerability = ANY($1) |
| 214 | + ` |
| 215 | + aliasRows, err := tx.Query(ctx, aliasQuery, ids) |
| 216 | + if err != nil { |
| 217 | + return fmt.Errorf("querying aliases: %w", err) |
| 218 | + } |
| 219 | + defer aliasRows.Close() |
| 220 | + |
| 221 | + for aliasRows.Next() { |
| 222 | + var vulnID int64 |
| 223 | + var namespace, name string |
| 224 | + if err := aliasRows.Scan(&vulnID, &namespace, &name); err != nil { |
| 225 | + return fmt.Errorf("scanning alias row: %w", err) |
| 226 | + } |
| 227 | + v := vulnByID[strconv.FormatInt(vulnID, 10)] |
| 228 | + if v == nil { |
| 229 | + continue |
| 230 | + } |
| 231 | + v.Aliases = append(v.Aliases, claircore.Alias{ |
| 232 | + Space: unique.Make(namespace), |
| 233 | + Name: name, |
| 234 | + }) |
| 235 | + } |
| 236 | + if err := aliasRows.Err(); err != nil { |
| 237 | + return fmt.Errorf("iterating alias rows: %w", err) |
| 238 | + } |
| 239 | + |
| 240 | + const selfQuery = ` |
| 241 | + SELECT vs.vulnerability, ns.namespace, a.name |
| 242 | + FROM vulnerability_self vs |
| 243 | + JOIN alias a ON vs.self = a.id |
| 244 | + JOIN alias_namespace ns ON a.namespace = ns.id |
| 245 | + WHERE vs.vulnerability = ANY($1) |
| 246 | + ` |
| 247 | + selfRows, err := tx.Query(ctx, selfQuery, ids) |
| 248 | + if err != nil { |
| 249 | + return fmt.Errorf("querying self aliases: %w", err) |
| 250 | + } |
| 251 | + defer selfRows.Close() |
| 252 | + |
| 253 | + for selfRows.Next() { |
| 254 | + var vulnID int64 |
| 255 | + var namespace, name string |
| 256 | + if err := selfRows.Scan(&vulnID, &namespace, &name); err != nil { |
| 257 | + return fmt.Errorf("scanning self row: %w", err) |
| 258 | + } |
| 259 | + v := vulnByID[strconv.FormatInt(vulnID, 10)] |
| 260 | + if v == nil { |
| 261 | + continue |
| 262 | + } |
| 263 | + v.Self = claircore.Alias{ |
| 264 | + Space: unique.Make(namespace), |
| 265 | + Name: name, |
| 266 | + } |
| 267 | + } |
| 268 | + if err := selfRows.Err(); err != nil { |
| 269 | + return fmt.Errorf("iterating self rows: %w", err) |
| 270 | + } |
| 271 | + |
| 272 | + return nil |
| 273 | +} |
0 commit comments