gem_guard 0.1.4 → 0.1.6

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: 633376d487060ef6045bec39ca986f4845c8eebf7d2bfeca1ae5b79b7848f5d8
4
- data.tar.gz: 20fddc00eb1840578c795ec810c2b25ab189940b2187ed88f3b387bf09e81b3d
3
+ metadata.gz: '073099f4fd844fee5ffd2b5466fbee6df36a8c5b9d640039efb0cbdb4db89b97'
4
+ data.tar.gz: 21ba615adb70721d4f9ede82e3a43361a92e72e683c53530504913250ccd7309
5
5
  SHA512:
6
- metadata.gz: 35354521a0b984df2370f5e9b2f81e9aa0e0d0888d060dd652089f15751536f35ff357b4f8b62affed894c4b8c6879f7c7107e1d08f8a5c077b864e8c0e5dd16
7
- data.tar.gz: 1ef4f61f5cdc1046dad193e61e6e6e54cc7bd3cf19fcb21b4130298b192012057c5362c0d7f70ada7391272c59dd0059a05e57c652dc21c4770942e0efb5d588
6
+ metadata.gz: d1bc314331a645c4b2d9791f41d852620fc7a977fc7eaf90ea927743f297057f4af93c40da2788422c8c468b11fbb1ad4d80d59f16cb4a0c9c063f37ebed809c
7
+ data.tar.gz: 1dc4f927542ae0983fb73a0d285c66f61c93960f1128bde659a313bbda24a7e6625d41acfdc48fcadeb8600f78bdb2f6de2b052e072a553aa78a937ad841b7dc
data/lib/gem_guard/cli.rb CHANGED
@@ -22,6 +22,42 @@ module GemGuard
22
22
  exit 1 if analysis.has_vulnerabilities?
23
23
  end
24
24
 
25
+ desc "sbom", "Generate Software Bill of Materials (SBOM)"
26
+ option :format, type: :string, default: "spdx", desc: "SBOM format (spdx, cyclone-dx)"
27
+ option :lockfile, type: :string, default: "Gemfile.lock", desc: "Path to Gemfile.lock"
28
+ option :output, type: :string, desc: "Output file path (default: stdout)"
29
+ option :project, type: :string, default: "ruby-project", desc: "Project name for SBOM"
30
+ def sbom
31
+ lockfile_path = options[:lockfile]
32
+
33
+ unless File.exist?(lockfile_path)
34
+ puts "Error: #{lockfile_path} not found"
35
+ exit 1
36
+ end
37
+
38
+ dependencies = Parser.new.parse(lockfile_path)
39
+ generator = SbomGenerator.new
40
+
41
+ sbom_data = case options[:format].downcase
42
+ when "spdx"
43
+ generator.generate_spdx(dependencies, options[:project])
44
+ when "cyclone-dx", "cyclonedx"
45
+ generator.generate_cyclone_dx(dependencies, options[:project])
46
+ else
47
+ puts "Error: Unsupported format '#{options[:format]}'. Use 'spdx' or 'cyclone-dx'"
48
+ exit 1
49
+ end
50
+
51
+ output_json = JSON.pretty_generate(sbom_data)
52
+
53
+ if options[:output]
54
+ File.write(options[:output], output_json)
55
+ puts "SBOM written to #{options[:output]}"
56
+ else
57
+ puts output_json
58
+ end
59
+ end
60
+
25
61
  desc "version", "Show gem_guard version"
26
62
  def version
27
63
  puts GemGuard::VERSION
@@ -0,0 +1,152 @@
1
+ require "json"
2
+ require "digest"
3
+ require "time"
4
+
5
+ module GemGuard
6
+ class SbomGenerator
7
+ SPDX_VERSION = "SPDX-2.3"
8
+ CYCLONE_DX_VERSION = "1.5"
9
+
10
+ def initialize
11
+ @document_id = "SPDXRef-DOCUMENT"
12
+ @creation_time = Time.now.utc.iso8601
13
+ end
14
+
15
+ def generate_spdx(dependencies, project_name = "ruby-project")
16
+ {
17
+ "spdxVersion" => SPDX_VERSION,
18
+ "dataLicense" => "CC0-1.0",
19
+ "SPDXID" => @document_id,
20
+ "name" => "#{project_name}-sbom",
21
+ "documentNamespace" => "https://gem-guard.dev/#{project_name}/#{@creation_time}",
22
+ "creationInfo" => {
23
+ "created" => @creation_time,
24
+ "creators" => ["Tool: gem_guard-#{GemGuard::VERSION}"],
25
+ "licenseListVersion" => "3.21"
26
+ },
27
+ "packages" => build_spdx_packages(dependencies, project_name),
28
+ "relationships" => build_spdx_relationships(dependencies)
29
+ }
30
+ end
31
+
32
+ def generate_cyclone_dx(dependencies, project_name = "ruby-project")
33
+ {
34
+ "bomFormat" => "CycloneDX",
35
+ "specVersion" => CYCLONE_DX_VERSION,
36
+ "serialNumber" => "urn:uuid:#{generate_uuid}",
37
+ "version" => 1,
38
+ "metadata" => {
39
+ "timestamp" => @creation_time,
40
+ "tools" => [
41
+ {
42
+ "vendor" => "GemGuard",
43
+ "name" => "gem_guard",
44
+ "version" => GemGuard::VERSION
45
+ }
46
+ ],
47
+ "component" => {
48
+ "type" => "application",
49
+ "name" => project_name,
50
+ "version" => "1.0.0"
51
+ }
52
+ },
53
+ "components" => build_cyclone_dx_components(dependencies)
54
+ }
55
+ end
56
+
57
+ private
58
+
59
+ def build_spdx_packages(dependencies, project_name)
60
+ packages = []
61
+
62
+ # Add root package
63
+ packages << {
64
+ "SPDXID" => "SPDXRef-Package-#{sanitize_name(project_name)}",
65
+ "name" => project_name,
66
+ "downloadLocation" => "NOASSERTION",
67
+ "filesAnalyzed" => false,
68
+ "copyrightText" => "NOASSERTION"
69
+ }
70
+
71
+ # Add dependency packages
72
+ dependencies.each_with_index do |dep, index|
73
+ packages << {
74
+ "SPDXID" => "SPDXRef-Package-#{sanitize_name(dep.name)}",
75
+ "name" => dep.name,
76
+ "versionInfo" => dep.version,
77
+ "downloadLocation" => gem_download_url(dep.name, dep.version),
78
+ "filesAnalyzed" => false,
79
+ "homepage" => gem_homepage_url(dep.name),
80
+ "copyrightText" => "NOASSERTION",
81
+ "externalRefs" => [
82
+ {
83
+ "referenceCategory" => "PACKAGE-MANAGER",
84
+ "referenceType" => "purl",
85
+ "referenceLocator" => "pkg:gem/#{dep.name}@#{dep.version}"
86
+ }
87
+ ]
88
+ }
89
+ end
90
+
91
+ packages
92
+ end
93
+
94
+ def build_spdx_relationships(dependencies)
95
+ relationships = []
96
+
97
+ dependencies.each do |dep|
98
+ relationships << {
99
+ "spdxElementId" => @document_id,
100
+ "relationshipType" => "DESCRIBES",
101
+ "relatedSpdxElement" => "SPDXRef-Package-#{sanitize_name(dep.name)}"
102
+ }
103
+ end
104
+
105
+ relationships
106
+ end
107
+
108
+ def build_cyclone_dx_components(dependencies)
109
+ dependencies.map do |dep|
110
+ {
111
+ "type" => "library",
112
+ "bom-ref" => "pkg:gem/#{dep.name}@#{dep.version}",
113
+ "name" => dep.name,
114
+ "version" => dep.version,
115
+ "purl" => "pkg:gem/#{dep.name}@#{dep.version}",
116
+ "externalReferences" => [
117
+ {
118
+ "type" => "distribution",
119
+ "url" => gem_download_url(dep.name, dep.version)
120
+ },
121
+ {
122
+ "type" => "website",
123
+ "url" => gem_homepage_url(dep.name)
124
+ }
125
+ ]
126
+ }
127
+ end
128
+ end
129
+
130
+ def sanitize_name(name)
131
+ name.gsub(/[^a-zA-Z0-9\-_]/, "-")
132
+ end
133
+
134
+ def gem_download_url(name, version)
135
+ "https://rubygems.org/downloads/#{name}-#{version}.gem"
136
+ end
137
+
138
+ def gem_homepage_url(name)
139
+ "https://rubygems.org/gems/#{name}"
140
+ end
141
+
142
+ def generate_uuid
143
+ # Simple UUID v4 generation
144
+ bytes = Array.new(16) { rand(256) }
145
+ bytes[6] = (bytes[6] & 0x0f) | 0x40 # Version 4
146
+ bytes[8] = (bytes[8] & 0x3f) | 0x80 # Variant bits
147
+
148
+ format = "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
149
+ format % bytes
150
+ end
151
+ end
152
+ end
@@ -1,3 +1,3 @@
1
1
  module GemGuard
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.6"
3
3
  end
data/lib/gem_guard.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  require_relative "gem_guard/version"
2
- require_relative "gem_guard/cli"
3
2
  require_relative "gem_guard/parser"
4
3
  require_relative "gem_guard/vulnerability_fetcher"
5
4
  require_relative "gem_guard/analyzer"
6
5
  require_relative "gem_guard/reporter"
6
+ require_relative "gem_guard/sbom_generator"
7
+ require_relative "gem_guard/cli"
7
8
 
8
9
  module GemGuard
9
10
  class Error < StandardError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_guard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wilbur Suero
@@ -103,6 +103,7 @@ files:
103
103
  - lib/gem_guard/cli.rb
104
104
  - lib/gem_guard/parser.rb
105
105
  - lib/gem_guard/reporter.rb
106
+ - lib/gem_guard/sbom_generator.rb
106
107
  - lib/gem_guard/version.rb
107
108
  - lib/gem_guard/vulnerability_fetcher.rb
108
109
  - plan.md