Class: Bundler::Sbom::Generator

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

Class Method Summary collapse

Class Method Details

.analyze_licenses(sbom) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/bundler/sbom.rb', line 77

def self.analyze_licenses(sbom)
  license_count = Hash.new(0)

  sbom["packages"].each do |package|
    license = package["licenseDeclared"]

    if license.include?(",")
      licenses = license.split(",").map(&:strip)
      licenses.each do |lic|
        license_count[lic] += 1
      end
    else
      license_count[license] += 1
    end
  end

  license_count
end

.display_license_report(sbom) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/bundler/sbom.rb', line 96

def self.display_license_report(sbom)
  license_count = analyze_licenses(sbom)
  sorted_licenses = license_count.sort_by { |_, count| -count }

  puts "=== License Usage in SBOM ==="
  puts "Total packages: #{sbom["packages"].size}"
  puts

  sorted_licenses.each do |license, count|
    puts "#{license}: #{count} package(s)"
  end

  puts "\n=== Packages by License ==="
  sorted_licenses.each do |license, _|
    packages = sbom["packages"].select do |package|
      if package["licenseDeclared"].include?(",")
        package["licenseDeclared"].split(",").map(&:strip).include?(license)
      else
        package["licenseDeclared"] == license
      end
    end

    puts "\n#{license} (#{packages.size} package(s)):"
    packages.each do |package|
      puts "  - #{package["name"]} (#{package["versionInfo"]})"
    end
  end
end

.generate_sbomObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/bundler/sbom.rb', line 9

def self.generate_sbom
  lockfile_path = Bundler.default_lockfile
  unless lockfile_path.exist?
    abort "No Gemfile.lock found. Run `bundle install` first."
  end

  lockfile = Bundler::LockfileParser.new(lockfile_path.read)
  document_name = File.basename(Dir.pwd)
  spdx_id = SecureRandom.uuid

  sbom = {
    "SPDXID" => "SPDXRef-DOCUMENT",
    "spdxVersion" => "SPDX-2.2",
    "creationInfo" => {
      "created" => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
      "creators" => ["Tool: bundle-sbom"],
      "licenseListVersion" => "3.17"
    },
    "name" => document_name,
    "dataLicense" => "CC0-1.0",
    "documentNamespace" => "https://spdx.org/spdxdocs/#{document_name}-#{spdx_id}",
    "packages" => []
  }

  lockfile.specs.each do |spec|
    begin
      gemspec = Gem::Specification.find_by_name(spec.name, spec.version)
      licenses = []
      if gemspec
        if gemspec.license && !gemspec.license.empty?
          licenses << gemspec.license
        end

        if gemspec.licenses && !gemspec.licenses.empty?
          licenses.concat(gemspec.licenses)
        end

        licenses.uniq!
      end

      license_string = licenses.empty? ? "NOASSERTION" : licenses.join(", ")
    rescue Gem::LoadError
      license_string = "NOASSERTION"
    end

    package = {
      "SPDXID" => "SPDXRef-Package-#{spec.name}",
      "name" => spec.name,
      "versionInfo" => spec.version.to_s,
      "downloadLocation" => "NOASSERTION",
      "filesAnalyzed" => false,
      "licenseConcluded" => license_string,
      "licenseDeclared" => license_string,
      "supplier" => "NOASSERTION",
      "externalRefs" => [
        {
          "referenceCategory" => "PACKAGE_MANAGER",
          "referenceType" => "purl",
          "referenceLocator" => "pkg:gem/#{spec.name}@#{spec.version}"
        }
      ]
    }
    sbom["packages"] << package
  end

  sbom
end