Class: Sbom::Merger

Inherits:
Object
  • Object
show all
Defined in:
lib/sbom/merger.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sboms, dedupe: :purl) ⇒ Merger

Returns a new instance of Merger.



7
8
9
10
11
# File 'lib/sbom/merger.rb', line 7

def initialize(sboms, dedupe: :purl)
  @sboms = sboms
  @options = { dedupe: dedupe }
  @result = nil
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



5
6
7
# File 'lib/sbom/merger.rb', line 5

def options
  @options
end

#resultObject (readonly)

Returns the value of attribute result.



5
6
7
# File 'lib/sbom/merger.rb', line 5

def result
  @result
end

#sbomsObject (readonly)

Returns the value of attribute sboms.



5
6
7
# File 'lib/sbom/merger.rb', line 5

def sboms
  @sboms
end

Class Method Details

.merge(sboms, dedupe: :purl) ⇒ Object



110
111
112
# File 'lib/sbom/merger.rb', line 110

def merge(sboms, dedupe: :purl)
  new(sboms, dedupe: dedupe).merge
end

.merge_files(filenames, dedupe: :purl) ⇒ Object



114
115
116
117
# File 'lib/sbom/merger.rb', line 114

def merge_files(filenames, dedupe: :purl)
  sboms = filenames.map { |f| Parser.parse_file(f) }
  merge(sboms, dedupe: dedupe)
end

Instance Method Details

#build_documentObject



39
40
41
42
43
44
45
46
47
48
# File 'lib/sbom/merger.rb', line 39

def build_document
  names = @sboms.map { |s| s.document&.dig(:name) }.compact
  merged_name = names.any? ? "Merged: #{names.join(', ')}" : "Merged SBOM"

  @result.document = {
    name: merged_name,
    id: "SPDXRef-DOCUMENT",
    created: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
  }
end

#determine_sbom_typeObject



27
28
29
30
31
32
# File 'lib/sbom/merger.rb', line 27

def determine_sbom_type
  types = @sboms.map(&:sbom_type).uniq
  return types.first if types.size == 1

  :spdx
end

#determine_versionObject



34
35
36
37
# File 'lib/sbom/merger.rb', line 34

def determine_version
  versions = @sboms.map(&:version).compact.uniq
  versions.first
end

#mergeObject



13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/sbom/merger.rb', line 13

def merge
  @result = Data::Sbom.new(sbom_type: determine_sbom_type)
  @result.version = determine_version

  build_document
  merge_packages
  merge_files
  merge_relationships
  merge_licenses
  merge_annotations

  @result
end

#merge_annotationsObject



101
102
103
104
105
106
107
# File 'lib/sbom/merger.rb', line 101

def merge_annotations
  @sboms.each do |sbom|
    sbom.annotations.each do |ann|
      @result.add_annotation(ann)
    end
  end
end

#merge_filesObject



66
67
68
69
70
71
72
# File 'lib/sbom/merger.rb', line 66

def merge_files
  @sboms.each do |sbom|
    sbom.files.each do |file|
      @result.add_file(file)
    end
  end
end

#merge_licensesObject



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/sbom/merger.rb', line 88

def merge_licenses
  seen = Set.new

  @sboms.each do |sbom|
    sbom.licenses.each do |lic|
      next if seen.include?(lic)

      seen.add(lic)
      @result.add_license(lic)
    end
  end
end

#merge_packagesObject



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/sbom/merger.rb', line 50

def merge_packages
  seen_purls = {}

  @sboms.each do |sbom|
    sbom.packages.each do |pkg|
      if @options[:dedupe] == :purl && pkg[:purl]
        next if seen_purls[pkg[:purl]]

        seen_purls[pkg[:purl]] = true
      end

      @result.add_package(pkg)
    end
  end
end

#merge_relationshipsObject



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/sbom/merger.rb', line 74

def merge_relationships
  seen = Set.new

  @sboms.each do |sbom|
    sbom.relationships.each do |rel|
      key = [rel[:source], rel[:type], rel[:target]]
      next if seen.include?(key)

      seen.add(key)
      @result.add_relationship(rel)
    end
  end
end