sbom 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b296a99b1a2ba980aedaa542532fd88bdea816209af3a2be1999a6e4305d0167
4
- data.tar.gz: 1e9a7c1e385a4ec311baac0ea055453631d559e76ab4bab67e34cfcf3166f536
3
+ metadata.gz: 91a2d7468e5e306cb02358a3d4c55d5169cb54e1b022d9ef1cd99f9795713b27
4
+ data.tar.gz: 6bc9e80848847c7f9cd535acc4b2747f03e6e944982da35c1b5bcfcad9a55c99
5
5
  SHA512:
6
- metadata.gz: 60e7f56853ad7c7874b3d1b073c06ccbf6a455985ce86b10fba011273d0491a798a6067e866f3d399a2317150128aa035b05dbcae9b53979652fb56e3f924f73
7
- data.tar.gz: 4f35f84db7c6d1dfe219e73cc7ecdefd14f701ce5fd091c504318ba7bad4c036b18dc04a17a375ecf6b0ee396a5bbf590c30d4f4d71b96812a375da54d3a84f8
6
+ metadata.gz: 30b94e777404ed7bf30f3a4f4911079e391a1c51a046175a31a1333b4635a7e4aa16d52d85ff9ccbbc3ecbee29326b89cbd84e78eabb2a4b38a38e42731cddec
7
+ data.tar.gz: f6ee5ca68429f3bce881b7213e92fab8e030182ecf0cbdffa8ee94799d32cf6ef87c91b9bff589ed99e0792313c826eb8db24355ec7b55637335fc85ea1a2492
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 4.0.0
1
+ 4.0.5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2026-06-26
4
+
5
+ - CycloneDX generator: support `analysis` on vulnerabilities (`state`, `justification`, `response`, `detail`, `firstIssued`, `lastUpdated`)
6
+ - CycloneDX generator: support `pedigree` on components (`patches` with `type`/`diff`/`resolves`, and `notes`)
7
+
8
+ ## [0.4.1] - 2026-01-14
9
+
10
+ - Include CycloneDX and SPDX schema files in published gem
11
+
3
12
  ## [0.4.0] - 2026-01-08
4
13
 
5
14
  - Add CycloneDX vulnerabilities array support to generator
data/README.md CHANGED
@@ -63,6 +63,7 @@ data = {
63
63
  ratings: [{ severity: "high", score: 8.1, method: "CVSSv31" }],
64
64
  description: "A critical vulnerability",
65
65
  affects: [{ ref: "pkg:npm/lodash@4.17.20" }],
66
+ analysis: { state: "resolved", detail: "Patched by distro" },
66
67
  published: "2024-01-15T00:00:00Z",
67
68
  updated: "2024-01-20T12:00:00Z"
68
69
  }
@@ -131,6 +132,12 @@ package.version = "7.0.0"
131
132
  package.license_concluded = "MIT"
132
133
  package.add_checksum("SHA256", "abc123...")
133
134
 
135
+ # Go modules use base64-encoded hashes in go.sum - convert to hex first:
136
+ require "base64"
137
+ go_hash = "h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4="
138
+ hex = Base64.decode64(go_hash.sub(/^h1:/, "")).unpack1("H*")
139
+ package.add_checksum("SHA256", hex)
140
+
134
141
  # Generate a PURL
135
142
  package.generate_purl(type: "gem")
136
143
  # => "pkg:gem/rails@7.0.0"
@@ -207,9 +207,68 @@ module Sbom
207
207
  end
208
208
  end
209
209
 
210
+ pedigree = generate_pedigree(pkg[:pedigree])
211
+ component["pedigree"] = pedigree if pedigree
212
+
210
213
  @components << component
211
214
  end
212
215
 
216
+ def generate_pedigree(pedigree)
217
+ return nil unless pedigree
218
+
219
+ result = {}
220
+
221
+ if pedigree[:patches]&.any?
222
+ patches = pedigree[:patches].map { |p| generate_pedigree_patch(p) }.compact
223
+ result["patches"] = patches if patches.any?
224
+ end
225
+ result["notes"] = pedigree[:notes] if pedigree[:notes]
226
+
227
+ result.empty? ? nil : result
228
+ end
229
+
230
+ def generate_pedigree_patch(patch)
231
+ # CycloneDX requires `type` on patch objects; drop entries without one
232
+ # rather than emit `{}` or an empty-string type that fails validation.
233
+ return nil unless patch && patch[:type]
234
+
235
+ result = { "type" => patch[:type].to_s }
236
+
237
+ if patch[:diff]
238
+ diff = {}
239
+ diff["text"] = patch[:diff][:text] if patch[:diff][:text]
240
+ diff["url"] = patch[:diff][:url] if patch[:diff][:url]
241
+ result["diff"] = diff if diff.any?
242
+ end
243
+
244
+ if patch[:resolves]&.any?
245
+ resolves = patch[:resolves].map { |i| generate_issue(i) }.compact
246
+ result["resolves"] = resolves if resolves.any?
247
+ end
248
+
249
+ result
250
+ end
251
+
252
+ def generate_issue(issue)
253
+ # `type` is required on CycloneDX issue objects.
254
+ return nil unless issue && issue[:type]
255
+
256
+ result = { "type" => issue[:type].to_s }
257
+ result["id"] = issue[:id] if issue[:id]
258
+ result["name"] = issue[:name] if issue[:name]
259
+ result["description"] = issue[:description] if issue[:description]
260
+
261
+ if issue[:source]
262
+ source = {}
263
+ source["name"] = issue[:source][:name] if issue[:source][:name]
264
+ source["url"] = issue[:source][:url] if issue[:source][:url]
265
+ result["source"] = source if source.any?
266
+ end
267
+
268
+ result["references"] = Array(issue[:references]) if issue[:references]
269
+ result
270
+ end
271
+
213
272
  def generate_dependencies(relationships_data)
214
273
  return unless relationships_data&.any?
215
274
 
@@ -274,12 +333,29 @@ module Sbom
274
333
  end
275
334
  end
276
335
 
336
+ analysis = generate_analysis(vuln[:analysis])
337
+ vulnerability["analysis"] = analysis if analysis
338
+
277
339
  vulnerability["published"] = vuln[:published] if vuln[:published]
278
340
  vulnerability["updated"] = vuln[:updated] if vuln[:updated]
279
341
 
280
342
  @vulnerabilities << vulnerability
281
343
  end
282
344
 
345
+ def generate_analysis(analysis)
346
+ return nil unless analysis
347
+
348
+ result = {}
349
+ result["state"] = analysis[:state] if analysis[:state]
350
+ result["justification"] = analysis[:justification] if analysis[:justification]
351
+ result["response"] = Array(analysis[:response]) if analysis[:response]
352
+ result["detail"] = analysis[:detail] if analysis[:detail]
353
+ result["firstIssued"] = analysis[:first_issued] if analysis[:first_issued]
354
+ result["lastUpdated"] = analysis[:last_updated] if analysis[:last_updated]
355
+
356
+ result.empty? ? nil : result
357
+ end
358
+
283
359
  def finalize_output
284
360
  @output["components"] = @components if @components.any?
285
361
  @output["dependencies"] = @dependencies if @dependencies.any?
data/lib/sbom/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sbom
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end