@@ -27,7 +27,7 @@ use uuid::Uuid;
27
27
struct PurlStatus {
28
28
cpe : Option < Cpe > ,
29
29
purl : Purl ,
30
- status : & ' static str ,
30
+ status : String ,
31
31
info : VersionInfo ,
32
32
}
33
33
@@ -105,16 +105,18 @@ impl<'a> StatusCreator<'a> {
105
105
connection : & C ,
106
106
) -> Result < ( ) , Error > {
107
107
let mut checked = HashMap :: new ( ) ;
108
- let mut product_statuses = Vec :: new ( ) ;
108
+ let mut product_status_models = Vec :: new ( ) ;
109
109
let mut purls = PurlCreator :: new ( ) ;
110
110
let mut cpes = CpeCreator :: new ( ) ;
111
111
112
- let mut org_cache: HashMap < & String , organization:: Model > = HashMap :: new ( ) ;
113
- let mut products = Vec :: new ( ) ;
112
+ let mut org_cache: HashMap < String , organization:: Model > = HashMap :: new ( ) ;
113
+ let mut product_models = Vec :: new ( ) ;
114
114
let mut version_ranges = Vec :: new ( ) ;
115
115
let mut product_version_ranges = Vec :: new ( ) ;
116
116
117
- for product in & self . products {
117
+ let product_statuses = self . products . clone ( ) ;
118
+
119
+ for product in product_statuses {
118
120
// ensure a correct status, and get id
119
121
if let Entry :: Vacant ( entry) = checked. entry ( product. status ) {
120
122
entry. insert ( Self :: check_status ( product. status , connection) . await ?) ;
@@ -130,21 +132,26 @@ impl<'a> StatusCreator<'a> {
130
132
// so simple caching should work here.
131
133
// If we find examples where this is not a case, we can switch to
132
134
// batch ingesting of organizations as well.
133
- let org_id = match & product. vendor {
134
- Some ( vendor) => match org_cache. get ( vendor) {
135
+ let org_id = match product. vendor . clone ( ) {
136
+ Some ( vendor) => match org_cache. get ( & vendor) {
135
137
Some ( entry) => Some ( entry. id ) ,
136
138
None => {
137
139
let organization_cpe_key = product
138
140
. cpe
139
141
. clone ( )
140
142
. map ( |cpe| cpe. vendor ( ) . as_ref ( ) . to_string ( ) ) ;
143
+
141
144
let org = OrganizationInformation {
142
145
cpe_key : organization_cpe_key,
143
146
website : None ,
144
147
} ;
148
+
145
149
let org: OrganizationContext < ' _ > =
146
- graph. ingest_organization ( vendor, org, connection) . await ?;
147
- org_cache. entry ( vendor) . or_insert ( org. organization . clone ( ) ) ;
150
+ graph. ingest_organization ( & vendor, org, connection) . await ?;
151
+ org_cache
152
+ . entry ( vendor. clone ( ) )
153
+ . or_insert ( org. organization . clone ( ) ) ;
154
+
148
155
Some ( org. organization . id )
149
156
}
150
157
} ,
@@ -165,7 +172,7 @@ impl<'a> StatusCreator<'a> {
165
172
vendor_id : Set ( org_id) ,
166
173
cpe_key : Set ( product_cpe_key) ,
167
174
} ;
168
- products . push ( product_entity. clone ( ) ) ;
175
+ product_models . push ( product_entity. clone ( ) ) ;
169
176
170
177
// Create all product ranges for batch ingesting
171
178
let product_version_range = match product. version {
@@ -220,32 +227,49 @@ impl<'a> StatusCreator<'a> {
220
227
cpes. add ( cpe. clone ( ) ) ;
221
228
}
222
229
223
- product_statuses . push ( base_product) ;
230
+ product_status_models . push ( base_product) ;
224
231
}
225
232
}
226
233
227
234
for purl in & product. purls {
228
- let purl = purl. clone ( ) ;
229
- // Ingest purl status
230
- let info = match purl. version . clone ( ) {
231
- Some ( version) => VersionInfo {
232
- scheme : VersionScheme :: Generic ,
233
- spec : VersionSpec :: Exact ( version) ,
234
- } ,
235
- None => VersionInfo {
236
- spec : VersionSpec :: Range ( Version :: Unbounded , Version :: Unbounded ) ,
237
- scheme : VersionScheme :: Semver ,
238
- } ,
239
- } ;
235
+ let scheme = Self :: from_purl_version_scheme ( purl) ;
240
236
241
- let purl_status = PurlStatus {
242
- cpe : product. cpe . clone ( ) ,
243
- purl : purl. clone ( ) ,
244
- status : product. status ,
245
- info,
237
+ // Default case: Exact version or unbounded range
238
+ let spec = match & purl. version {
239
+ Some ( version) => VersionSpec :: Exact ( version. clone ( ) ) ,
240
+ None => VersionSpec :: Range ( Version :: Unbounded , Version :: Unbounded ) ,
246
241
} ;
247
242
248
- self . entries . insert ( purl_status) ;
243
+ self . create_purl_status ( & product, purl, scheme, spec, product. status . to_string ( ) ) ;
244
+
245
+ // Special case for "fixed" status and RedHat CPE
246
+ if product. status == "fixed" {
247
+ if let Some ( cpe_vendor) = product
248
+ . cpe
249
+ . as_ref ( )
250
+ . map ( |cpe| cpe. vendor ( ) . as_ref ( ) . to_string ( ) )
251
+ {
252
+ if cpe_vendor == "redhat" {
253
+ if let Some ( version) = & purl. version {
254
+ //TODO improve status checking as we call db way too often for this simple task
255
+ if let Entry :: Vacant ( entry) = checked. entry ( "affected" ) {
256
+ entry. insert ( Self :: check_status ( "affected" , connection) . await ?) ;
257
+ } ;
258
+ let spec = VersionSpec :: Range (
259
+ Version :: Unbounded ,
260
+ Version :: Exclusive ( version. clone ( ) ) ,
261
+ ) ;
262
+ self . create_purl_status (
263
+ & product,
264
+ purl,
265
+ scheme,
266
+ spec,
267
+ "affected" . to_string ( ) ,
268
+ ) ;
269
+ }
270
+ }
271
+ }
272
+ }
249
273
}
250
274
}
251
275
@@ -265,7 +289,7 @@ impl<'a> StatusCreator<'a> {
265
289
266
290
self . create_status ( connection, checked) . await ?;
267
291
268
- for batch in & products . chunked ( ) {
292
+ for batch in & product_models . chunked ( ) {
269
293
product:: Entity :: insert_many ( batch)
270
294
. on_conflict_do_nothing ( )
271
295
. exec ( connection)
@@ -286,7 +310,7 @@ impl<'a> StatusCreator<'a> {
286
310
. await ?;
287
311
}
288
312
289
- for batch in & product_statuses . chunked ( ) {
313
+ for batch in & product_status_models . chunked ( ) {
290
314
product_status:: Entity :: insert_many ( batch)
291
315
. exec ( connection)
292
316
. await ?;
@@ -297,6 +321,34 @@ impl<'a> StatusCreator<'a> {
297
321
Ok ( ( ) )
298
322
}
299
323
324
+ fn from_purl_version_scheme ( purl : & Purl ) -> VersionScheme {
325
+ match purl. ty . as_str ( ) {
326
+ "maven" => VersionScheme :: Maven ,
327
+ "npm" => VersionScheme :: Semver ,
328
+ "python" => VersionScheme :: Python ,
329
+ "rpm" => VersionScheme :: Rpm ,
330
+ "semver" => VersionScheme :: Semver ,
331
+ _ => VersionScheme :: Generic ,
332
+ }
333
+ }
334
+
335
+ fn create_purl_status (
336
+ & mut self ,
337
+ product : & ProductStatus ,
338
+ purl : & Purl ,
339
+ scheme : VersionScheme ,
340
+ spec : VersionSpec ,
341
+ status : String ,
342
+ ) {
343
+ let purl_status = PurlStatus {
344
+ cpe : product. cpe . clone ( ) ,
345
+ purl : purl. clone ( ) ,
346
+ status,
347
+ info : VersionInfo { scheme, spec } ,
348
+ } ;
349
+ self . entries . insert ( purl_status) ;
350
+ }
351
+
300
352
#[ instrument( skip( self , connection) , ret) ]
301
353
async fn create_status (
302
354
& self ,
@@ -307,7 +359,7 @@ impl<'a> StatusCreator<'a> {
307
359
let mut package_statuses = Vec :: new ( ) ;
308
360
309
361
for ps in & self . entries {
310
- let status = checked. get ( & ps. status ) . ok_or_else ( || {
362
+ let status = checked. get ( & ps. status . as_str ( ) ) . ok_or_else ( || {
311
363
Error :: Graph ( crate :: graph:: error:: Error :: InvalidStatus (
312
364
ps. status . to_string ( ) ,
313
365
) )
0 commit comments