2323# Copyright (c) OWASP Foundation. All Rights Reserved.
2424#
2525
26+ require 'securerandom'
27+
2628require_relative 'bom_component'
2729
2830module Cyclonedx
@@ -41,15 +43,29 @@ def random_urn_uuid
4143 "urn:uuid:#{ SecureRandom . uuid } "
4244 end
4345
44- def build_bom ( gems , format , spec_version )
46+ # Determine if the selected spec version supports metadata/tools (>= 1.2)
47+ def metadata_supported? ( spec_version )
48+ %w[ 1.2 1.3 1.4 1.5 1.6 1.7 ] . include? ( spec_version )
49+ end
50+
51+ # Identity of this producer tool
52+ def tool_identity
53+ {
54+ vendor : 'CycloneDX' ,
55+ name : 'cyclonedx-ruby' ,
56+ version : ::Cyclonedx ::Ruby ::Version ::VERSION
57+ }
58+ end
59+
60+ def build_bom ( gems , format , spec_version , include_metadata : false )
4561 if format == 'json'
46- build_json_bom ( gems , spec_version )
62+ build_json_bom ( gems , spec_version , include_metadata : include_metadata )
4763 else
48- build_bom_xml ( gems , spec_version )
64+ build_bom_xml ( gems , spec_version , include_metadata : include_metadata )
4965 end
5066 end
5167
52- def build_json_bom ( gems , spec_version )
68+ def build_json_bom ( gems , spec_version , include_metadata : false )
5369 bom_hash = {
5470 bomFormat : 'CycloneDX' ,
5571 specVersion : spec_version ,
@@ -58,17 +74,39 @@ def build_json_bom(gems, spec_version)
5874 components : [ ]
5975 }
6076
77+ # Optionally include metadata.tools when supported by selected spec
78+ if include_metadata && metadata_supported? ( spec_version )
79+ ti = tool_identity
80+ ti = ti . compact # omit nil values like version
81+ bom_hash [ :metadata ] = {
82+ tools : [ ti ]
83+ }
84+ end
85+
6186 gems . each do |gem |
6287 bom_hash [ :components ] += Cyclonedx ::BomComponent . new ( gem ) . hash_val
6388 end
6489
6590 JSON . pretty_generate ( bom_hash )
6691 end
6792
68- def build_bom_xml ( gems , spec_version )
93+ def build_bom_xml ( gems , spec_version , include_metadata : false )
6994 builder = Nokogiri ::XML ::Builder . new ( encoding : 'UTF-8' ) do |xml |
7095 attributes = { 'xmlns' => cyclonedx_xml_namespace ( spec_version ) , 'version' => '1' , 'serialNumber' => random_urn_uuid }
7196 xml . bom ( attributes ) do
97+ # Optionally include metadata.tools when supported by selected spec
98+ if include_metadata && metadata_supported? ( spec_version )
99+ xml . metadata do
100+ xml . tools do
101+ xml . tool do
102+ xml . vendor tool_identity [ :vendor ]
103+ xml . name tool_identity [ :name ]
104+ xml . version tool_identity [ :version ] if tool_identity [ :version ]
105+ end
106+ end
107+ end
108+ end
109+
72110 xml . components do
73111 gems . each do |gem |
74112 xml . component ( 'type' => 'library' ) do
@@ -165,7 +203,7 @@ def get_gem(name, version, logger)
165203 url = "https://rubygems.org/api/v1/versions/#{ name } .json"
166204 begin
167205 RestClient . proxy = ENV . fetch ( 'http_proxy' , nil )
168- response = RestClient . get ( url )
206+ response = RestClient :: Request . execute ( method : :get , url : url , read_timeout : 2 , open_timeout : 2 )
169207 body = JSON . parse ( response . body )
170208 body . select { |item | item [ 'number' ] == version . to_s } . first
171209 rescue StandardError
0 commit comments