Module: StillActive::CyclonedxHelper

Extended by:
CyclonedxHelper
Included in:
CyclonedxHelper
Defined in:
lib/still_active/helpers/cyclonedx_helper.rb

Overview

Renders a still_active workflow result as a CycloneDX SBOM. Emits 1.6 by default (the version mainstream consumers — Dependency-Track via cyclonedx-core-java, Trivy/Syft via cyclonedx-go — actually ingest as of 2026); 1.7 is opt-in. Our emitted subset is identical across both versions, so only the specVersion string changes.

Maintenance signals that have no native CycloneDX field (scorecard, libyear, archived, last commit) are emitted as still_active:-namespaced component properties — lossy by spec design, ignorable by consumers that don't care.

Constant Summary collapse

SUPPORTED_SPEC_VERSIONS =
["1.6", "1.7"].freeze

Instance Method Summary collapse

Instance Method Details

#render(result:, ruby_info:, tool_version:, spec_version: "1.6", now: Time.now.utc) ⇒ Object

result: gem_name => gem_data (as StillActive::Workflow.call returns) ruby_info: Ruby freshness hash or nil now: injectable clock so output is deterministic in tests



28
29
30
31
32
33
34
35
# File 'lib/still_active/helpers/cyclonedx_helper.rb', line 28

def render(result:, ruby_info:, tool_version:, spec_version: "1.6", now: Time.now.utc)
  components = build_components(result, ruby_info)
  vulnerabilities = build_vulnerabilities(result)

  document = envelope(components, spec_version, tool_version, now)
  document["vulnerabilities"] = vulnerabilities unless vulnerabilities.empty?
  JSON.pretty_generate(document)
end

#render_sbom(result:, tool_version:, spec_version: "1.6", now: Time.now.utc) ⇒ Object

The cross-ecosystem sibling of render, for a --sbom audit. The one thing it must not do is rebuild PURLs: npm scopes, maven group:artifact and Go module paths are all easy to reconstruct subtly wrong, and the input SBOM already carries the authoritative one. So each component re-emits the PURL it arrived with, which means a consumer (Dependency-Track being the point of this) matches our output exactly as it matched the input, now annotated with the maintenance signals it had no way to compute.



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/still_active/helpers/cyclonedx_helper.rb', line 44

def render_sbom(result:, tool_version:, spec_version: "1.6", now: Time.now.utc)
  components = result.sort_by { |key, _| key.to_s }.map { |key, data| sbom_component(key.to_s, data) }
  vulnerabilities = result.sort_by { |key, _| key.to_s }.flat_map do |key, data|
    ref = sbom_ref(key.to_s, data)
    (data[:vulnerabilities] || []).map { |advisory| vulnerability(advisory, ref) }
  end

  document = envelope(components, spec_version, tool_version, now)
  document["vulnerabilities"] = vulnerabilities unless vulnerabilities.empty?
  JSON.pretty_generate(document)
end