@@ -2,6 +2,7 @@ package edit
2
2
3
3
import (
4
4
"fmt"
5
+ "strings"
5
6
6
7
cydx "github.com/CycloneDX/cyclonedx-go"
7
8
"github.com/interlynk-io/sbomasm/pkg/logger"
@@ -63,7 +64,6 @@ func (d *cdxEditDoc) update() {
63
64
}
64
65
}
65
66
}
66
-
67
67
}
68
68
69
69
func (d * cdxEditDoc ) timeStamp () error {
@@ -82,7 +82,6 @@ func (d *cdxEditDoc) timeStamp() error {
82
82
} else {
83
83
d .bom .Metadata .Timestamp = utcNowTime ()
84
84
}
85
-
86
85
return nil
87
86
}
88
87
@@ -269,31 +268,234 @@ func (d *cdxEditDoc) copyright() error {
269
268
}
270
269
271
270
func (d * cdxEditDoc ) tools () error {
272
- if ! d .c .shouldTools () {
273
- return errNoConfiguration
271
+ // default sbomasm tool for tools.tools
272
+ sbomasmTool := cydx.Tool {
273
+ Name : SBOMASM ,
274
+ Version : SBOMASM_VERSION ,
274
275
}
275
276
276
- if d .c .search .subject != "document" {
277
- return errNotSupported
277
+ // default sbomasm tool for tools.components
278
+ sbomasmComponent := cydx.Component {
279
+ Type : cydx .ComponentTypeApplication ,
280
+ Name : SBOMASM ,
281
+ Version : SBOMASM_VERSION ,
278
282
}
279
283
280
- choice := cdxConstructTools (d .bom , d .c )
284
+ // initialize the tool to cover case when tool section is not present
285
+ // in that we still need to add sbomasm as a tool
286
+ d .initializeMetadataTools ()
287
+
288
+ // get all tools explicity specified by the user via flag `--tool`
289
+ newTools := cdxConstructTools (d .bom , d .c )
290
+
291
+ // detect whether sbomasm is explicity specified by the user via flag `--tool` or not
292
+ // if present then replace default sbomasm tool by provided sbomasm tool with version
293
+ explicitSbomasm := d .detectExplicitTool (newTools .Tools , SBOMASM , & sbomasmTool )
294
+ explicitSbomasmComponent := d .detectExplicitComponent (newTools .Components , SBOMASM , & sbomasmComponent )
295
+
296
+ if explicitSbomasm {
297
+ d .bom .Metadata .Tools .Tools = removeTool (d .bom .Metadata .Tools .Tools , SBOMASM )
298
+ }
299
+ if explicitSbomasmComponent {
300
+ d .bom .Metadata .Tools .Components = removeComponent (d .bom .Metadata .Tools .Components , SBOMASM )
301
+ }
281
302
282
303
if d .c .onMissing () {
304
+ d .addMissingToolsOrComponents (newTools , sbomasmTool , sbomasmComponent )
305
+ return nil
306
+ }
307
+
308
+ if d .c .onAppend () {
309
+ d .appendToolsOrComponents (newTools , sbomasmTool , sbomasmComponent )
310
+ return nil
311
+ }
312
+
313
+ // neither missing nor append case
314
+ d .mergeToolsOrComponents (newTools , sbomasmTool , sbomasmComponent )
315
+
316
+ return nil
317
+ }
318
+
319
+ func (d * cdxEditDoc ) initializeMetadataTools () {
320
+ if d .bom .SpecVersion > cydx .SpecVersion1_4 {
283
321
if d .bom .Metadata .Tools == nil {
284
- d .bom .Metadata .Tools = choice
322
+ d .bom .Metadata .Tools = & cydx.ToolsChoice {
323
+ Components : new ([]cydx.Component ),
324
+ }
285
325
}
286
- } else if d .c .onAppend () {
287
- if d .bom .Metadata .Tools != nil {
288
- d .bom .Metadata .Tools = cdxUniqTools (d .bom .Metadata .Tools , choice )
289
- } else {
290
- d .bom .Metadata .Tools = choice
326
+ if d .bom .Metadata .Tools .Components == nil {
327
+ d .bom .Metadata .Tools .Components = new ([]cydx.Component )
291
328
}
292
329
} else {
293
- d .bom .Metadata .Tools = choice
330
+ if d .bom .Metadata .Tools == nil {
331
+ d .bom .Metadata .Tools = & cydx.ToolsChoice {
332
+ Tools : new ([]cydx.Tool ),
333
+ }
334
+ }
335
+ if d .bom .Metadata .Tools .Tools == nil {
336
+ d .bom .Metadata .Tools .Tools = new ([]cydx.Tool )
337
+ }
294
338
}
339
+ }
295
340
296
- return nil
341
+ func (d * cdxEditDoc ) detectExplicitTool (tools * []cydx.Tool , sbomasmName string , sbomasmTool * cydx.Tool ) bool {
342
+ if tools != nil {
343
+ for _ , tool := range * tools {
344
+ if tool .Name == sbomasmName {
345
+ * sbomasmTool = tool
346
+ return true
347
+ }
348
+ }
349
+ }
350
+ return false
351
+ }
352
+
353
+ func (d * cdxEditDoc ) detectExplicitComponent (components * []cydx.Component , sbomasmName string , sbomasmComponent * cydx.Component ) bool {
354
+ if components != nil {
355
+ for _ , component := range * components {
356
+ if component .Name == sbomasmName {
357
+ * sbomasmComponent = component
358
+ return true
359
+ }
360
+ }
361
+ }
362
+ return false
363
+ }
364
+
365
+ // handle missing case for tools.tools and tools.components case
366
+ func (d * cdxEditDoc ) addMissingToolsOrComponents (newTools * cydx.ToolsChoice , sbomasmTool cydx.Tool , sbomasmComponent cydx.Component ) {
367
+ if d .bom .SpecVersion > cydx .SpecVersion1_4 {
368
+ d .bom .Metadata .Tools .Components = cdxUniqueComponents (* d .bom .Metadata .Tools .Components , * newTools .Components )
369
+ if ! componentExists (d .bom .Metadata .Tools .Components , sbomasmComponent ) {
370
+ * d .bom .Metadata .Tools .Components = append (* d .bom .Metadata .Tools .Components , sbomasmComponent )
371
+ }
372
+ } else {
373
+ d .bom .Metadata .Tools .Tools = cdxUniqueTools (* d .bom .Metadata .Tools .Tools , * newTools .Tools )
374
+ if ! toolExists (d .bom .Metadata .Tools .Tools , sbomasmTool ) {
375
+ * d .bom .Metadata .Tools .Tools = append (* d .bom .Metadata .Tools .Tools , sbomasmTool )
376
+ }
377
+ }
378
+ }
379
+
380
+ // handle append case for tools.tools and tools.components case
381
+ func (d * cdxEditDoc ) appendToolsOrComponents (newTools * cydx.ToolsChoice , sbomasmTool cydx.Tool , sbomasmComponent cydx.Component ) {
382
+ if d .bom .SpecVersion > cydx .SpecVersion1_4 {
383
+ d .bom .Metadata .Tools .Components = cdxUniqueComponents (* d .bom .Metadata .Tools .Components , * newTools .Components )
384
+ if ! componentExists (d .bom .Metadata .Tools .Components , sbomasmComponent ) {
385
+ * d .bom .Metadata .Tools .Components = append (* d .bom .Metadata .Tools .Components , sbomasmComponent )
386
+ }
387
+ } else {
388
+ d .bom .Metadata .Tools .Tools = cdxUniqueTools (* d .bom .Metadata .Tools .Tools , * newTools .Tools )
389
+ if ! toolExists (d .bom .Metadata .Tools .Tools , sbomasmTool ) {
390
+ * d .bom .Metadata .Tools .Tools = append (* d .bom .Metadata .Tools .Tools , sbomasmTool )
391
+ }
392
+ }
393
+ }
394
+
395
+ // handle default case for tools.tools and tools.components case
396
+ func (d * cdxEditDoc ) mergeToolsOrComponents (newTools * cydx.ToolsChoice , sbomasmTool cydx.Tool , sbomasmComponent cydx.Component ) {
397
+ if d .bom .SpecVersion > cydx .SpecVersion1_4 {
398
+ d .bom .Metadata .Tools .Components = cdxUniqueComponents (* d .bom .Metadata .Tools .Components , * newTools .Components )
399
+ if ! componentExists (d .bom .Metadata .Tools .Components , sbomasmComponent ) {
400
+ * d .bom .Metadata .Tools .Components = append (* d .bom .Metadata .Tools .Components , sbomasmComponent )
401
+ }
402
+ } else {
403
+ d .bom .Metadata .Tools .Tools = cdxUniqueTools (* d .bom .Metadata .Tools .Tools , * newTools .Tools )
404
+ if ! toolExists (d .bom .Metadata .Tools .Tools , sbomasmTool ) {
405
+ * d .bom .Metadata .Tools .Tools = append (* d .bom .Metadata .Tools .Tools , sbomasmTool )
406
+ }
407
+ }
408
+ }
409
+
410
+ func toolExists (tools * []cydx.Tool , tool cydx.Tool ) bool {
411
+ if tools == nil {
412
+ return false
413
+ }
414
+ for _ , t := range * tools {
415
+ if t .Name == tool .Name && t .Version == tool .Version {
416
+ return true
417
+ }
418
+ }
419
+ return false
420
+ }
421
+
422
+ // Check if a component exists
423
+ func componentExists (components * []cydx.Component , component cydx.Component ) bool {
424
+ if components == nil {
425
+ return false
426
+ }
427
+ for _ , c := range * components {
428
+ if c .Name == component .Name && c .Version == component .Version {
429
+ return true
430
+ }
431
+ }
432
+ return false
433
+ }
434
+
435
+ func cdxUniqueTools (existing , newTools []cydx.Tool ) * []cydx.Tool {
436
+ toolSet := make (map [string ]struct {})
437
+ uniqueTools := []cydx.Tool {}
438
+
439
+ for _ , t := range existing {
440
+ key := fmt .Sprintf ("%s-%s" , strings .ToLower (t .Name ), strings .ToLower (t .Version ))
441
+ toolSet [key ] = struct {}{}
442
+ uniqueTools = append (uniqueTools , t )
443
+ }
444
+
445
+ for _ , t := range newTools {
446
+ key := fmt .Sprintf ("%s-%s" , strings .ToLower (t .Name ), strings .ToLower (t .Version ))
447
+ if _ , exists := toolSet [key ]; ! exists {
448
+ uniqueTools = append (uniqueTools , t )
449
+ }
450
+ }
451
+
452
+ return & uniqueTools
453
+ }
454
+
455
+ func cdxUniqueComponents (existing , newComponents []cydx.Component ) * []cydx.Component {
456
+ componentSet := make (map [string ]struct {})
457
+ uniqueComponents := []cydx.Component {}
458
+
459
+ for _ , c := range existing {
460
+ key := fmt .Sprintf ("%s-%s" , strings .ToLower (c .Name ), strings .ToLower (c .Version ))
461
+ componentSet [key ] = struct {}{}
462
+ uniqueComponents = append (uniqueComponents , c )
463
+ }
464
+
465
+ for _ , c := range newComponents {
466
+ key := fmt .Sprintf ("%s-%s" , strings .ToLower (c .Name ), strings .ToLower (c .Version ))
467
+ if _ , exists := componentSet [key ]; ! exists {
468
+ uniqueComponents = append (uniqueComponents , c )
469
+ }
470
+ }
471
+
472
+ return & uniqueComponents
473
+ }
474
+
475
+ func removeTool (tools * []cydx.Tool , name string ) * []cydx.Tool {
476
+ if tools == nil {
477
+ return nil
478
+ }
479
+ filtered := []cydx.Tool {}
480
+ for _ , t := range * tools {
481
+ if t .Name != name {
482
+ filtered = append (filtered , t )
483
+ }
484
+ }
485
+ return & filtered
486
+ }
487
+
488
+ func removeComponent (components * []cydx.Component , name string ) * []cydx.Component {
489
+ if components == nil {
490
+ return nil
491
+ }
492
+ filtered := []cydx.Component {}
493
+ for _ , c := range * components {
494
+ if c .Name != name {
495
+ filtered = append (filtered , c )
496
+ }
497
+ }
498
+ return & filtered
297
499
}
298
500
299
501
func (d * cdxEditDoc ) hashes () error {
0 commit comments