2424#
2525# frozen_string_literal: true
2626
27+ require 'securerandom'
28+
2729require_relative 'bom_component'
2830
2931module Cyclonedx
@@ -42,15 +44,29 @@ def random_urn_uuid
4244 "urn:uuid:#{ SecureRandom . uuid } "
4345 end
4446
45- def build_bom ( gems , format , spec_version )
47+ # Determine if the selected spec version supports metadata/tools (>= 1.2)
48+ def metadata_supported? ( spec_version )
49+ %w[ 1.2 1.3 1.4 1.5 1.6 1.7 ] . include? ( spec_version )
50+ end
51+
52+ # Identity of this producer tool
53+ def tool_identity
54+ {
55+ vendor : 'CycloneDX' ,
56+ name : 'cyclonedx-ruby' ,
57+ version : ::Cyclonedx ::Ruby ::Version ::VERSION
58+ }
59+ end
60+
61+ def build_bom ( gems , format , spec_version , include_metadata : false )
4662 if format == 'json'
47- build_json_bom ( gems , spec_version )
63+ build_json_bom ( gems , spec_version , include_metadata : include_metadata )
4864 else
49- build_bom_xml ( gems , spec_version )
65+ build_bom_xml ( gems , spec_version , include_metadata : include_metadata )
5066 end
5167 end
5268
53- def build_json_bom ( gems , spec_version )
69+ def build_json_bom ( gems , spec_version , include_metadata : false )
5470 bom_hash = {
5571 "bomFormat" : "CycloneDX" ,
5672 "specVersion" : spec_version ,
@@ -59,17 +75,39 @@ def build_json_bom(gems, spec_version)
5975 "components" : [ ]
6076 }
6177
78+ # Optionally include metadata.tools when supported by selected spec
79+ if include_metadata && metadata_supported? ( spec_version )
80+ ti = tool_identity
81+ ti = ti . compact # omit nil values like version
82+ bom_hash [ :metadata ] = {
83+ tools : [ ti ]
84+ }
85+ end
86+
6287 gems . each do |gem |
6388 bom_hash [ :components ] += BomComponent . new ( gem ) . hash_val ( )
6489 end
6590
6691 JSON . pretty_generate ( bom_hash )
6792 end
6893
69- def build_bom_xml ( gems , spec_version )
94+ def build_bom_xml ( gems , spec_version , include_metadata : false )
7095 builder = Nokogiri ::XML ::Builder . new ( encoding : 'UTF-8' ) do |xml |
7196 attributes = { 'xmlns' => cyclonedx_xml_namespace ( spec_version ) , 'version' => '1' , 'serialNumber' => random_urn_uuid }
7297 xml . bom ( attributes ) do
98+ # Optionally include metadata.tools when supported by selected spec
99+ if include_metadata && metadata_supported? ( spec_version )
100+ xml . metadata do
101+ xml . tools do
102+ xml . tool do
103+ xml . vendor tool_identity [ :vendor ]
104+ xml . name tool_identity [ :name ]
105+ xml . version tool_identity [ :version ] if tool_identity [ :version ]
106+ end
107+ end
108+ end
109+ end
110+
73111 xml . components do
74112 gems . each do |gem |
75113 xml . component ( 'type' => 'library' ) do
@@ -166,7 +204,7 @@ def get_gem(name, version)
166204 url = "https://rubygems.org/api/v1/versions/#{ name } .json"
167205 begin
168206 RestClient . proxy = ENV [ 'http_proxy' ]
169- response = RestClient . get ( url )
207+ response = RestClient :: Request . execute ( method : :get , url : url , read_timeout : 2 , open_timeout : 2 )
170208 body = JSON . parse ( response . body )
171209 body . select { |item | item [ 'number' ] == version . to_s } . first
172210 rescue StandardError
0 commit comments