bundler-sbom 0.1.2 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d6fb14eceffbbdca88089399601ca1cd1b5e6840c0bbc59afc22e7cc24478ff
4
- data.tar.gz: 597c5b04752e93fb03f6eb9c6d4b319aaefb7ae57602d30c4855aec07c9d011d
3
+ metadata.gz: 0b738f4193f079f261d93aeb53ac8d3e467cd509b14ccf8a8370296c6c029673
4
+ data.tar.gz: fd6f0c0014fb5cdf6ecc1821ece9a7c1d678b9796830d8ebae0c87c81b7fe8fa
5
5
  SHA512:
6
- metadata.gz: 3c358312f6a01b9b3700965a2cda957d2b3875742787daf1fae6a3e42cbed130f7cf1967e32895a5b012623a595eac3771a53eb6ad3dd133c6bc51138e3667fe
7
- data.tar.gz: b4614d89f1c4188a3e2d5e7f0a7cd620627567bf653a4abc4af816b4cc32421b1f9f18396d2140ae7873d646110ae0d7982f1e57e7c7e997b8c0134b24e61fb5
6
+ metadata.gz: 89545f261837196ef20c7582f61d10e474a232f22bc87927120f639e22a1d8398e74936a406378b8024f47b05172209ab0fd15195e755477bad16bdc647e9ed4
7
+ data.tar.gz: f7a48d2f7768ee3f594f248b24a7b3e0ec62aa803c23ed17f8fa17bd4559770b4ae75b2b3d4bda1184797cf22c1cff99fa56705fbc4f359547b1bcbea74f5423
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "rake"
7
+ gem "rspec"
8
+ gem "simplecov", require: false
9
+ gem "rspec-its"
10
+ gem "rspec-mocks"
11
+ end
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,32 @@
1
+ require "thor"
2
+ require "json"
3
+ require "bundler/sbom/generator"
4
+
5
+ module Bundler
6
+ module Sbom
7
+ class CLI < Thor
8
+ desc "dump", "Generate SBOM and save to bom.json"
9
+ def dump
10
+ sbom = Bundler::Sbom::Generator.generate_sbom
11
+ File.write("bom.json", JSON.pretty_generate(sbom))
12
+ Bundler.ui.info("Generated SBOM at bom.json")
13
+ end
14
+
15
+ desc "license", "Display license report from existing bom.json"
16
+ def license
17
+ unless File.exist?("bom.json")
18
+ Bundler.ui.error("Error: bom.json not found. Run 'bundle sbom dump' first.")
19
+ exit 1
20
+ end
21
+
22
+ begin
23
+ sbom = JSON.parse(File.read("bom.json"))
24
+ Bundler::Sbom::Generator.display_license_report(sbom)
25
+ rescue JSON::ParserError
26
+ Bundler.ui.error("Error: bom.json is not a valid JSON file")
27
+ exit 1
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,118 @@
1
+ require "bundler"
2
+ require "securerandom"
3
+
4
+ module Bundler
5
+ module Sbom
6
+ class Generator
7
+ def self.generate_sbom
8
+ lockfile_path = Bundler.default_lockfile
9
+ unless lockfile_path.exist?
10
+ abort "No Gemfile.lock found. Run `bundle install` first."
11
+ end
12
+
13
+ lockfile = Bundler::LockfileParser.new(lockfile_path.read)
14
+ document_name = File.basename(Dir.pwd)
15
+ spdx_id = SecureRandom.uuid
16
+
17
+ sbom = {
18
+ "SPDXID" => "SPDXRef-DOCUMENT",
19
+ "spdxVersion" => "SPDX-2.2",
20
+ "creationInfo" => {
21
+ "created" => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
22
+ "creators" => ["Tool: bundle-sbom"],
23
+ "licenseListVersion" => "3.17"
24
+ },
25
+ "name" => document_name,
26
+ "dataLicense" => "CC0-1.0",
27
+ "documentNamespace" => "https://spdx.org/spdxdocs/#{document_name}-#{spdx_id}",
28
+ "packages" => []
29
+ }
30
+
31
+ lockfile.specs.each do |spec|
32
+ begin
33
+ gemspec = Gem::Specification.find_by_name(spec.name, spec.version)
34
+ licenses = []
35
+ if gemspec
36
+ if gemspec.license && !gemspec.license.empty?
37
+ licenses << gemspec.license
38
+ end
39
+
40
+ if gemspec.licenses && !gemspec.licenses.empty?
41
+ licenses.concat(gemspec.licenses)
42
+ end
43
+
44
+ licenses.uniq!
45
+ end
46
+
47
+ license_string = licenses.empty? ? "NOASSERTION" : licenses.join(", ")
48
+ rescue Gem::LoadError
49
+ license_string = "NOASSERTION"
50
+ end
51
+
52
+ package = {
53
+ "SPDXID" => "SPDXRef-Package-#{spec.name}",
54
+ "name" => spec.name,
55
+ "versionInfo" => spec.version.to_s,
56
+ "downloadLocation" => "NOASSERTION",
57
+ "filesAnalyzed" => false,
58
+ "licenseConcluded" => license_string,
59
+ "licenseDeclared" => license_string,
60
+ "supplier" => "NOASSERTION",
61
+ "externalRefs" => [
62
+ {
63
+ "referenceCategory" => "PACKAGE_MANAGER",
64
+ "referenceType" => "purl",
65
+ "referenceLocator" => "pkg:gem/#{spec.name}@#{spec.version}"
66
+ }
67
+ ]
68
+ }
69
+ sbom["packages"] << package
70
+ end
71
+
72
+ sbom
73
+ end
74
+
75
+ def self.display_license_report(sbom)
76
+ license_count = analyze_licenses(sbom)
77
+ sorted_licenses = license_count.sort_by { |_, count| -count }
78
+
79
+ puts "=== License Usage in SBOM ==="
80
+ puts "Total packages: #{sbom["packages"].size}"
81
+ puts
82
+
83
+ sorted_licenses.each do |license, count|
84
+ puts "#{license}: #{count} package(s)"
85
+ end
86
+
87
+ puts "\n=== Packages by License ==="
88
+ sorted_licenses.each do |license, _|
89
+ packages = sbom["packages"].select do |package|
90
+ if package["licenseDeclared"].include?(",")
91
+ package["licenseDeclared"].split(",").map(&:strip).include?(license)
92
+ else
93
+ package["licenseDeclared"] == license
94
+ end
95
+ end
96
+
97
+ puts "\n#{license} (#{packages.size} package(s)):"
98
+ packages.each do |package|
99
+ puts " - #{package["name"]} (#{package["versionInfo"]})"
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def self.analyze_licenses(sbom)
107
+ license_count = Hash.new(0)
108
+ sbom["packages"].each do |package|
109
+ licenses = package["licenseDeclared"].split(",").map(&:strip)
110
+ licenses.each do |license|
111
+ license_count[license] += 1
112
+ end
113
+ end
114
+ license_count
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,5 @@
1
+ module Bundler
2
+ module Sbom
3
+ VERSION = "0.1.4"
4
+ end
5
+ end
data/lib/bundler/sbom.rb CHANGED
@@ -1,126 +1,3 @@
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
1
+ require "bundler/sbom/version"
2
+ require "bundler/sbom/generator"
3
+ require "bundler/sbom/cli"
data/plugins.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler"
2
+ require "bundler/sbom"
3
+
4
+ module Bundler
5
+ module Sbom
6
+ class Plugin < ::Bundler::Plugin::API
7
+ command "sbom"
8
+
9
+ def exec(command_name, args)
10
+ ::Bundler::Sbom::CLI.start(args)
11
+ end
12
+ end
13
+ end
14
+ end
metadata CHANGED
@@ -1,28 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler-sbom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - SHIBATA Hiroshi
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 2025-03-05 00:00:00.000000000 Z
11
- dependencies: []
12
- description: Generate SPDX format SBOM from Gemfile.lock and analyze license information
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: :runtime
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: thor
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: Generate CycloneDX SBOM(Software Bill of Materials) files with Bundler
13
41
  email:
14
42
  - hsbt@ruby-lang.org
15
43
  executables: []
16
44
  extensions: []
17
45
  extra_rdoc_files: []
18
46
  files:
47
+ - Gemfile
19
48
  - README.md
20
- - lib/bundler/cli/sbom.rb
21
- - lib/bundler/plugins.rb
49
+ - Rakefile
22
50
  - lib/bundler/sbom.rb
51
+ - lib/bundler/sbom/cli.rb
52
+ - lib/bundler/sbom/generator.rb
53
+ - lib/bundler/sbom/version.rb
54
+ - plugins.rb
23
55
  homepage: https://github.com/hsbt/bundler-sbom
24
- licenses:
25
- - MIT
56
+ licenses: []
26
57
  metadata:
27
58
  homepage_uri: https://github.com/hsbt/bundler-sbom
28
59
  source_code_uri: https://github.com/hsbt/bundler-sbom
@@ -35,7 +66,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
35
66
  requirements:
36
67
  - - ">="
37
68
  - !ruby/object:Gem::Version
38
- version: '0'
69
+ version: 2.6.0
39
70
  required_rubygems_version: !ruby/object:Gem::Requirement
40
71
  requirements:
41
72
  - - ">="
@@ -44,5 +75,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
75
  requirements: []
45
76
  rubygems_version: 3.6.2
46
77
  specification_version: 4
47
- summary: Bundler plugin to generate and analyze SBOM
78
+ summary: Generate CycloneDX SBOM(Software Bill of Materials) files with Bundler
48
79
  test_files: []
@@ -1,29 +0,0 @@
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
@@ -1,3 +0,0 @@
1
- require "bundler/cli/sbom"
2
-
3
- Bundler::Plugin::API.command("sbom", Bundler::CLI::Sbom)