bundler-sbom 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 675a0be818dcf56528e18dcbb7a40bc77a88d31f2f168f5b9c27eb0c21bcdaf6
4
+ data.tar.gz: c0073141244450742d51354d94ad91359d531ebe66cd7c653c2c5ed47667d5a8
5
+ SHA512:
6
+ metadata.gz: 4d018b46d78d11b87723578ba1a489ad4ff940ef3296bc6f65706b5906269ebb785b64152699b408eb183705625ed7d95d251adfa340cfe82d78727b3f0ad106
7
+ data.tar.gz: 6a51a7389dd1d22607a6175c8046a3a375a1f7d12f484312f1ce807f63daa497173b897a4bbcf5fd4eb01aa7031798feddc83e9beeb49436dc090b6d9625870d
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Bundler SBOM Plugin
2
+
3
+ Generate and analyze Software Bill of Materials (SBOM) for your Ruby projects using Bundler.
4
+
5
+ ## Installation
6
+
7
+ Install this plugin by running:
8
+
9
+ ```
10
+ $ bundler plugin install bundler-sbom
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Generate SBOM
16
+
17
+ To generate an SBOM file in SPDX format from your project's Gemfile.lock:
18
+
19
+ ```
20
+ $ bundle sbom dump
21
+ ```
22
+
23
+ This will create a `bom.json` file in your project directory.
24
+
25
+ ### Analyze License Information
26
+
27
+ To view a summary of licenses used in your project's dependencies:
28
+
29
+ ```
30
+ $ bundle sbom license
31
+ ```
32
+
33
+ This command will show:
34
+ - A count of packages using each license
35
+ - A detailed list of packages grouped by license
36
+
37
+ Note: The `license` command requires that you've already generated the SBOM using `bundle sbom dump`.
@@ -0,0 +1,29 @@
1
+ require "bundler/cli"
2
+ require "bundler/sbom"
3
+
4
+ module Bundler
5
+ class CLI::Sbom
6
+ def initialize(options = {})
7
+ @options = options
8
+ end
9
+
10
+ def dump
11
+ sbom = Bundler::Sbom::Generator.generate_sbom
12
+ File.write("bom.json", JSON.pretty_generate(sbom))
13
+ Bundler.ui.info "Generated SBOM at bom.json"
14
+ end
15
+
16
+ def license
17
+ begin
18
+ sbom = JSON.parse(File.read("bom.json"))
19
+ Bundler::Sbom::Generator.display_license_report(sbom)
20
+ rescue Errno::ENOENT
21
+ Bundler.ui.error "Error: bom.json not found. Run 'bundle sbom dump' first."
22
+ exit 1
23
+ rescue JSON::ParserError
24
+ Bundler.ui.error "Error: bom.json is not a valid JSON file"
25
+ exit 1
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,126 @@
1
+ require "bundler"
2
+ require "json"
3
+ require "securerandom"
4
+ require "rubygems"
5
+
6
+ module Bundler
7
+ module Sbom
8
+ class Generator
9
+ def self.generate_sbom
10
+ lockfile_path = Bundler.default_lockfile
11
+ unless lockfile_path.exist?
12
+ abort "No Gemfile.lock found. Run `bundle install` first."
13
+ end
14
+
15
+ lockfile = Bundler::LockfileParser.new(lockfile_path.read)
16
+ document_name = File.basename(Dir.pwd)
17
+ spdx_id = SecureRandom.uuid
18
+
19
+ sbom = {
20
+ "SPDXID" => "SPDXRef-DOCUMENT",
21
+ "spdxVersion" => "SPDX-2.2",
22
+ "creationInfo" => {
23
+ "created" => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
24
+ "creators" => ["Tool: bundle-sbom"],
25
+ "licenseListVersion" => "3.17"
26
+ },
27
+ "name" => document_name,
28
+ "dataLicense" => "CC0-1.0",
29
+ "documentNamespace" => "https://spdx.org/spdxdocs/#{document_name}-#{spdx_id}",
30
+ "packages" => []
31
+ }
32
+
33
+ lockfile.specs.each do |spec|
34
+ begin
35
+ gemspec = Gem::Specification.find_by_name(spec.name, spec.version)
36
+ licenses = []
37
+ if gemspec
38
+ if gemspec.license && !gemspec.license.empty?
39
+ licenses << gemspec.license
40
+ end
41
+
42
+ if gemspec.licenses && !gemspec.licenses.empty?
43
+ licenses.concat(gemspec.licenses)
44
+ end
45
+
46
+ licenses.uniq!
47
+ end
48
+
49
+ license_string = licenses.empty? ? "NOASSERTION" : licenses.join(", ")
50
+ rescue Gem::LoadError
51
+ license_string = "NOASSERTION"
52
+ end
53
+
54
+ package = {
55
+ "SPDXID" => "SPDXRef-Package-#{spec.name}",
56
+ "name" => spec.name,
57
+ "versionInfo" => spec.version.to_s,
58
+ "downloadLocation" => "NOASSERTION",
59
+ "filesAnalyzed" => false,
60
+ "licenseConcluded" => license_string,
61
+ "licenseDeclared" => license_string,
62
+ "supplier" => "NOASSERTION",
63
+ "externalRefs" => [
64
+ {
65
+ "referenceCategory" => "PACKAGE_MANAGER",
66
+ "referenceType" => "purl",
67
+ "referenceLocator" => "pkg:gem/#{spec.name}@#{spec.version}"
68
+ }
69
+ ]
70
+ }
71
+ sbom["packages"] << package
72
+ end
73
+
74
+ sbom
75
+ end
76
+
77
+ def self.analyze_licenses(sbom)
78
+ license_count = Hash.new(0)
79
+
80
+ sbom["packages"].each do |package|
81
+ license = package["licenseDeclared"]
82
+
83
+ if license.include?(",")
84
+ licenses = license.split(",").map(&:strip)
85
+ licenses.each do |lic|
86
+ license_count[lic] += 1
87
+ end
88
+ else
89
+ license_count[license] += 1
90
+ end
91
+ end
92
+
93
+ license_count
94
+ end
95
+
96
+ def self.display_license_report(sbom)
97
+ license_count = analyze_licenses(sbom)
98
+ sorted_licenses = license_count.sort_by { |_, count| -count }
99
+
100
+ puts "=== License Usage in SBOM ==="
101
+ puts "Total packages: #{sbom["packages"].size}"
102
+ puts
103
+
104
+ sorted_licenses.each do |license, count|
105
+ puts "#{license}: #{count} package(s)"
106
+ end
107
+
108
+ puts "\n=== Packages by License ==="
109
+ sorted_licenses.each do |license, _|
110
+ packages = sbom["packages"].select do |package|
111
+ if package["licenseDeclared"].include?(",")
112
+ package["licenseDeclared"].split(",").map(&:strip).include?(license)
113
+ else
114
+ package["licenseDeclared"] == license
115
+ end
116
+ end
117
+
118
+ puts "\n#{license} (#{packages.size} package(s)):"
119
+ packages.each do |package|
120
+ puts " - #{package["name"]} (#{package["versionInfo"]})"
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,3 @@
1
+ require "bundler/cli/sbom"
2
+
3
+ Bundler::Plugin.add_command("sbom", Bundler::CLI::Sbom)
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bundler-sbom
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - SHIBATA Hiroshi
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-03-05 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: bundler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: Generate SPDX format SBOM from Gemfile.lock and analyze license information
41
+ email:
42
+ - hsbt@ruby-lang.org
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - README.md
48
+ - lib/bundler-sbom.rb
49
+ - lib/bundler/cli/sbom.rb
50
+ - lib/bundler/sbom.rb
51
+ homepage: https://github.com/hsbt/hsbt
52
+ licenses:
53
+ - MIT
54
+ metadata: {}
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubygems_version: 3.7.0.dev
70
+ specification_version: 4
71
+ summary: Bundler plugin to generate and analyze SBOM
72
+ test_files: []