From 6a174b8a9248c6eb29d90bd2e5f100f7213f2cd5 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 20:36:51 -0600 Subject: [PATCH 01/22] =?UTF-8?q?=F0=9F=94=A8=20binstubs:=20console,=20set?= =?UTF-8?q?up,=20rake,=20rspec,=20cucumber?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- bin/console | 11 +++++++++++ bin/cucumber | 16 ++++++++++++++++ bin/rake | 16 ++++++++++++++++ bin/rspec | 16 ++++++++++++++++ bin/setup | 8 ++++++++ 5 files changed, 67 insertions(+) create mode 100755 bin/console create mode 100755 bin/cucumber create mode 100755 bin/rake create mode 100755 bin/rspec create mode 100755 bin/setup diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..61860f8 --- /dev/null +++ b/bin/console @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" +require "cyclonedx/ruby" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +require "irb" +IRB.start(__FILE__) diff --git a/bin/cucumber b/bin/cucumber new file mode 100755 index 0000000..16c9390 --- /dev/null +++ b/bin/cucumber @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'cucumber' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("cucumber", "cucumber") diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000..9efbee9 --- /dev/null +++ b/bin/rake @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rake' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rake", "rake") diff --git a/bin/rspec b/bin/rspec new file mode 100755 index 0000000..93e191c --- /dev/null +++ b/bin/rspec @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rspec' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rspec-core", "rspec") diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here From 76c388fea697b32d727f35cdf9c2291acb827278 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 20:37:22 -0600 Subject: [PATCH 02/22] =?UTF-8?q?=F0=9F=9A=9A=20rename=20LICENSE=20=3D>=20?= =?UTF-8?q?LICENSE.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- LICENSE => LICENSE.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename LICENSE => LICENSE.txt (100%) diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt From 6d29520dc88f5adc72b54a450028bdb26b41e621 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 20:43:34 -0600 Subject: [PATCH 03/22] =?UTF-8?q?=F0=9F=8E=A8=20modernize=20dotfiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .gitignore - .rspec Signed-off-by: Peter H. Boling --- .gitignore | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++---- .rspec | 8 ++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 .rspec diff --git a/.gitignore b/.gitignore index 29a7627..271eebd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,50 @@ -.idea -/Gemfile.lock -/tmp/aruba -/coverage \ No newline at end of file +# Build Artifacts +/pkg/ +/tmp/ +*.gem + +# Bundler +/vendor/bundle/ +/.bundle/ +/gemfiles/*.lock +/gemfiles/.bundle/ +/gemfiles/.bundle/config +/gemfiles/vendor/ +Appraisal.*.gemfile.lock + +# Specs +.rspec_status +/coverage/ +/spec/reports/ +/results/ +.output.txt + +# Documentation +/.yardoc/ +/_yardoc/ +/rdoc/ +/doc/ + +# Ruby Version Managers (RVM, rbenv, etc) +.rvmrc +.ruby-version +.ruby-gemset +.tool-versions + +# Benchmarking +/measurement/ + +# Debugger detritus +.byebug_history + +# direnv - brew install direnv +.env.local + +# OS Detritus +.DS_Store + +# Editors +*~ + +# vendor +/vendor/ diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..7d3a2e2 --- /dev/null +++ b/.rspec @@ -0,0 +1,8 @@ +--format documentation +--color +--require spec_helper +--color +--order random +--warnings +--format html +--out results/test_results.html From ea1339d7745645957c3d1f3069d4dda4627a3111 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 20:43:49 -0600 Subject: [PATCH 04/22] =?UTF-8?q?=F0=9F=8E=A8=20modernize=20Rakefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- Rakefile | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/Rakefile b/Rakefile index a18e7a9..a176eaf 100644 --- a/Rakefile +++ b/Rakefile @@ -1,42 +1,28 @@ -#!/usr/bin/env rake -$LOAD_PATH << File.expand_path(__dir__) - -require "aruba/platform" - -require "bundler" -Bundler.setup +# frozen_string_literal: true require 'bundler/gem_tasks' -require "cucumber/rake/task" -require "rspec/core/rake_task" +require 'aruba/platform' require 'rake/clean' -# Work around a bug in `rake/clean` from `rake` versions older than 13. It's -# failing when it calls `FileUtils::rm_r` because that method needs to receive -# the `opts` parameter as parameters instead of as a `Hash`. -module Rake - module Cleaner - module_function - - def cleanup(file_name, **opts) - begin - opts = { verbose: Rake.application.options.trace }.merge(opts) - rm_r file_name, **opts - rescue StandardError => ex - puts "Failed to remove #{file_name}: #{ex}" unless file_already_gone?(file_name) - end - end - end -end - # Remove the `coverage` directory when the `:clobber` task is run. CLOBBER.include('coverage') +# Cucumber +require 'cucumber/rake/task' + Cucumber::Rake::Task.new do |t| t.cucumber_opts = %w(--format progress) end -RSpec::Core::RakeTask.new('spec') +# RSpec +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) + +# RuboCop +require 'rubocop/rake_task' + +RuboCop::RakeTask.new # Run the `clobber` task when running the entire test suite, because the # coverage information reported by `simplecov` can be skewed when a `coverage` @@ -44,4 +30,5 @@ RSpec::Core::RakeTask.new('spec') desc "Run the whole test suite." task test: [:clobber, :spec, :cucumber] -task default: :test \ No newline at end of file +# Default Task +task default: :test From bd7f47f071c135d2fc4d8f549fdb19ed51a7392e Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 20:45:05 -0600 Subject: [PATCH 05/22] =?UTF-8?q?=F0=9F=8E=A8=20modernize=20for=202025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Added - `CHANGELOG.md` file to document notable changes in keep-a-changelog format - `Cyclonedx::BomHelpers` module to house helper methods, replacing global methods - `Cyclonedx::BomBuilder` class, replacing `Bombuilder` (note the capitalization change) - `Cyclonedx::BomComponent` class, replacing `BomComponent` - `Cyclonedx::Ruby::Version::VERSION` constant to hold the version number (also available as `Cyclonedx::VERSION`) - `Cyclonedx::Ruby::Deprecation` module to help manage deprecations ### Changed - Updated gemspec metadata for clarity and consistency - Modernized Rakefile, dotfiles, and test setup - `LICENSE` => `LICENSE.txt` to simplify parsing consistency on various platforms and tools - `cucumber` v8 => v10 - `aruba` v2.1 => v2.2 ### Deprecated - `BomComponent` => `Cyclonedx::BomComponent` - `Bombuilder` => `Cyclonedx::BomBuilder` (note the capitalization change) - `Object.purl` => `Cyclonedx::BomHelpers.purl` - `Object.random_urn_uuid` => `Cyclonedx::BomHelpers.random_urn_uuid` - `Object.build_bom` => `Cyclonedx::BomHelpers.build_bom` - `Object.build_json_bom` => `Cyclonedx::BomHelpers.build_json_bom` - `Object.build_bom_xml` => `Cyclonedx::BomHelpers.build_bom_xml` - `Object.get_gem` => `Cyclonedx::BomHelpers.get_gem` Signed-off-by: Peter H. Boling --- CHANGELOG.md | 64 +++++++ Gemfile | 4 +- Gemfile.lock | 197 +++++++++++++++++++++ bom.json | 108 +++++++++++ bom.xml | 75 ++++++++ cyclonedx-ruby.gemspec | 63 +++++-- features/fixtures/simple/Gemfile | 6 +- features/fixtures/simple/Gemfile.lock | 12 +- lib/bom_builder.rb | 161 +---------------- lib/bom_component.rb | 47 +---- lib/bom_helpers.rb | 84 +-------- lib/cyclonedx/bom_builder.rb | 151 ++++++++++++++++ lib/cyclonedx/bom_component.rb | 46 +++++ lib/cyclonedx/bom_helpers.rb | 112 ++++++++++++ lib/cyclonedx/ruby.rb | 26 +++ lib/cyclonedx/ruby/deprecation.rb | 55 ++++++ lib/cyclonedx/ruby/version.rb | 10 ++ lib/cyclonedx_deprecated.rb | 17 ++ sig/cyclonedx/ruby.rbs | 6 + spec/bom_helpers_spec.rb | 10 -- spec/{ => cyclonedx}/bom_component_spec.rb | 9 +- spec/cyclonedx/bom_helpers_spec.rb | 14 ++ spec/cyclonedx/ruby_spec.rb | 7 + spec/spec_helper.rb | 23 ++- 24 files changed, 978 insertions(+), 329 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 Gemfile.lock create mode 100644 bom.json create mode 100644 bom.xml create mode 100644 lib/cyclonedx/bom_builder.rb create mode 100644 lib/cyclonedx/bom_component.rb create mode 100644 lib/cyclonedx/bom_helpers.rb create mode 100644 lib/cyclonedx/ruby.rb create mode 100644 lib/cyclonedx/ruby/deprecation.rb create mode 100644 lib/cyclonedx/ruby/version.rb create mode 100644 lib/cyclonedx_deprecated.rb create mode 100644 sig/cyclonedx/ruby.rbs delete mode 100644 spec/bom_helpers_spec.rb rename spec/{ => cyclonedx}/bom_component_spec.rb (92%) create mode 100644 spec/cyclonedx/bom_helpers_spec.rb create mode 100644 spec/cyclonedx/ruby_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8969c08 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,64 @@ +# Changelog + +[![SemVer 2.0.0][📌semver-img]][📌semver] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog][📗keep-changelog], +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), +and [yes][📌major-versions-not-sacred], platform and engine support are part of the [public API][📌semver-breaking]. +Please file a bug if you notice a violation of semantic versioning. + +[📌semver]: https://semver.org/spec/v2.0.0.html +[📌semver-img]: https://img.shields.io/badge/semver-2.0.0-FFDD67.svg?style=flat +[📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139 +[📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html +[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/ +[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat + +## [Unreleased] + +### Added + +- `CHANGELOG.md` file to document notable changes in keep-a-changelog format +- `Cyclonedx::BomHelpers` module to house helper methods, replacing global methods +- `Cyclonedx::BomBuilder` class, replacing `Bombuilder` (note the capitalization change) +- `Cyclonedx::BomComponent` class, replacing `BomComponent` +- `Cyclonedx::Ruby::Version::VERSION` constant to hold the version number (also available as `Cyclonedx::VERSION`) +- `Cyclonedx::Ruby::Deprecation` module to help manage deprecations + +### Changed + +- Updated gemspec metadata for clarity and consistency +- Modernized Rakefile, dotfiles, and test setup +- `LICENSE` => `LICENSE.txt` to simplify parsing consistency on various platforms and tools +- `cucumber` v8 => v10 +- `aruba` v2.1 => v2.2 + +### Deprecated + +- `BomComponent` => `Cyclonedx::BomComponent` +- `Bombuilder` => `Cyclonedx::BomBuilder` (note the capitalization change) +- `Object.purl` => `Cyclonedx::BomHelpers.purl` +- `Object.random_urn_uuid` => `Cyclonedx::BomHelpers.random_urn_uuid` +- `Object.build_bom` => `Cyclonedx::BomHelpers.build_bom` +- `Object.build_json_bom` => `Cyclonedx::BomHelpers.build_json_bom` +- `Object.build_bom_xml` => `Cyclonedx::BomHelpers.build_bom_xml` +- `Object.get_gem` => `Cyclonedx::BomHelpers.get_gem` + +### Removed + +### Fixed + +### Security + +## [1.1.0] - 2019-07-13 + +- TAG: [v1.1.0][1.1.0t] + +### Added + +- Initial release + +[1.1.0]: https://github.com/CycloneDX/cyclonedx-ruby-gem/compare/eecfebe3cb0ce961fef8e424162ac94298f02a9f...v1.1.0 +[1.1.0t]: https://github.com/CycloneDX/cyclonedx-ruby-gem/releases/tag/v1.1.0 diff --git a/Gemfile b/Gemfile index 3195f8a..cb4f27b 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,6 @@ -source 'https://rubygems.org' +# frozen_string_literal: true + +source "https://rubygems.org" # Specify your gem's dependencies in cyclonedx-ruby.gemspec gemspec diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..3e79096 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,197 @@ +PATH + remote: . + specs: + cyclonedx-ruby (1.2.0) + activesupport (~> 7.0) + json (~> 2.6) + nokogiri (~> 1.15) + ostruct (~> 0.5.5) + rest-client (~> 2.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (7.2.2.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + aruba (2.3.2) + bundler (>= 1.17, < 3.0) + contracts (>= 0.16.0, < 0.18.0) + cucumber (>= 8.0, < 11.0) + rspec-expectations (>= 3.4, < 5.0) + thor (~> 1.0) + ast (2.4.3) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (3.3.1) + builder (3.3.0) + concurrent-ruby (1.3.5) + connection_pool (2.5.4) + contracts (0.17.2) + cucumber (10.1.1) + base64 (~> 0.2) + builder (~> 3.2) + cucumber-ci-environment (> 9, < 11) + cucumber-core (> 15, < 17) + cucumber-cucumber-expressions (> 17, < 19) + cucumber-html-formatter (> 20.3, < 22) + diff-lcs (~> 1.5) + logger (~> 1.6) + mini_mime (~> 1.1) + multi_test (~> 1.1) + sys-uname (~> 1.3) + cucumber-ci-environment (10.0.1) + cucumber-core (15.3.0) + cucumber-gherkin (> 27, < 35) + cucumber-messages (> 26, < 30) + cucumber-tag-expressions (> 5, < 9) + cucumber-cucumber-expressions (18.0.1) + bigdecimal + cucumber-gherkin (34.0.0) + cucumber-messages (> 25, < 29) + cucumber-html-formatter (21.15.1) + cucumber-messages (> 19, < 28) + cucumber-messages (27.2.0) + cucumber-tag-expressions (8.0.0) + diff-lcs (1.6.2) + docile (1.4.1) + domain_name (0.6.20240107) + drb (2.2.3) + ffi (1.17.2) + ffi (1.17.2-aarch64-linux-gnu) + ffi (1.17.2-aarch64-linux-musl) + ffi (1.17.2-arm-linux-gnu) + ffi (1.17.2-arm-linux-musl) + ffi (1.17.2-arm64-darwin) + ffi (1.17.2-x86_64-darwin) + ffi (1.17.2-x86_64-linux-gnu) + ffi (1.17.2-x86_64-linux-musl) + http-accept (1.7.0) + http-cookie (1.1.0) + domain_name (~> 0.5) + i18n (1.14.7) + concurrent-ruby (~> 1.0) + json (2.15.2) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + memoist3 (1.0.0) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2025.0924) + mini_mime (1.1.5) + mini_portile2 (2.8.9) + minitest (5.26.0) + multi_test (1.1.0) + netrc (0.11.0) + nokogiri (1.18.10) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.18.10-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.10-aarch64-linux-musl) + racc (~> 1.4) + nokogiri (1.18.10-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.10-arm-linux-musl) + racc (~> 1.4) + nokogiri (1.18.10-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.10-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.10-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.10-x86_64-linux-musl) + racc (~> 1.4) + ostruct (0.5.5) + parallel (1.27.0) + parser (3.3.9.0) + ast (~> 2.4.1) + racc + prism (1.6.0) + racc (1.8.1) + rainbow (3.1.1) + rake (13.3.0) + regexp_parser (2.11.3) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.6) + rubocop (1.81.6) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.47.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.47.1) + parser (>= 3.3.7.2) + prism (~> 1.4) + ruby-progressbar (1.13.0) + securerandom (0.4.1) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.2) + simplecov_json_formatter (0.1.4) + sys-uname (1.4.1) + ffi (~> 1.1) + memoist3 (~> 1.0.0) + thor (1.4.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.1.0) + +PLATFORMS + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + ruby + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + aruba (~> 2.2) + cucumber (~> 10.0) + cyclonedx-ruby! + rake (~> 13) + rspec (~> 3.12) + rubocop (~> 1.54) + simplecov (~> 0.22.0) + +BUNDLED WITH + 2.7.2 diff --git a/bom.json b/bom.json new file mode 100644 index 0000000..2cd09f7 --- /dev/null +++ b/bom.json @@ -0,0 +1,108 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:294dd750-1036-4fd5-b457-2fb8d54bb310", + "version": 1, + "components": [ + { + "type": "library", + "name": "activesupport", + "version": "7.0.4.3", + "description": "A toolkit of support libraries and Ruby core extensions extracted from the Rails framework.", + "purl": "pkg:gem/activesupport@7.0.4.3", + "hashes": [ + { + "alg": "SHA-256", + "content": "571ed0fac8510f1fc8a1d66aa070d07ea269913bf9ef50960a8044536358a096" + } + ], + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ] + }, + { + "type": "library", + "name": "concurrent-ruby", + "version": "1.2.2", + "description": "Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.", + "purl": "pkg:gem/concurrent-ruby@1.2.2", + "hashes": [ + { + "alg": "SHA-256", + "content": "3879119b8b75e3b62616acc256c64a134d0b0a7a9a3fcba5a233025bcde22c4f" + } + ], + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ] + }, + { + "type": "library", + "name": "i18n", + "version": "1.12.0", + "description": "New wave Internationalization support for Ruby", + "purl": "pkg:gem/i18n@1.12.0", + "hashes": [ + { + "alg": "SHA-256", + "content": "91e3cc1b97616d308707eedee413d82ee021d751c918661fb82152793e64aced" + } + ], + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ] + }, + { + "type": "library", + "name": "minitest", + "version": "5.18.0", + "description": "minitest provides a complete suite of testing facilities supporting TDD, BDD, mocking, and benchmarking", + "purl": "pkg:gem/minitest@5.18.0", + "hashes": [ + { + "alg": "SHA-256", + "content": "06f43aa0692ce3acf19cb5bc539ad2c6095ca3d2c7e5fbafc58a7d847e898745" + } + ], + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ] + }, + { + "type": "library", + "name": "tzinfo", + "version": "2.0.6", + "description": "Time Zone Library", + "purl": "pkg:gem/tzinfo@2.0.6", + "hashes": [ + { + "alg": "SHA-256", + "content": "8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b" + } + ], + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/bom.xml b/bom.xml new file mode 100644 index 0000000..1fa5899 --- /dev/null +++ b/bom.xml @@ -0,0 +1,75 @@ + + + + + activesupport + 7.0.4.3 + A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. + + 571ed0fac8510f1fc8a1d66aa070d07ea269913bf9ef50960a8044536358a096 + + + + MIT + + + pkg:gem/activesupport@7.0.4.3 + + + concurrent-ruby + 1.2.2 + Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns. + + 3879119b8b75e3b62616acc256c64a134d0b0a7a9a3fcba5a233025bcde22c4f + + + + MIT + + + pkg:gem/concurrent-ruby@1.2.2 + + + i18n + 1.12.0 + New wave Internationalization support for Ruby + + 91e3cc1b97616d308707eedee413d82ee021d751c918661fb82152793e64aced + + + + MIT + + + pkg:gem/i18n@1.12.0 + + + minitest + 5.18.0 + minitest provides a complete suite of testing facilities supporting TDD, BDD, mocking, and benchmarking + + 06f43aa0692ce3acf19cb5bc539ad2c6095ca3d2c7e5fbafc58a7d847e898745 + + + + MIT + + + pkg:gem/minitest@5.18.0 + + + tzinfo + 2.0.6 + Time Zone Library + + 8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b + + + + MIT + + + pkg:gem/tzinfo@2.0.6 + + + diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index 087712e..cc5016a 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -1,26 +1,59 @@ # frozen_string_literal: true +require_relative "lib/cyclonedx/ruby/version" + Gem::Specification.new do |spec| - spec.name = 'cyclonedx-ruby' - spec.version = '1.2.0' - spec.date = '2023-07-14' + spec.name = 'cyclonedx-ruby' + spec.version = Cyclonedx::Ruby::VERSION + spec.authors = ['Joseph Kobti', 'Steve Springett'] + spec.email = ['josephkobti@outlook.com'] + spec.summary = 'CycloneDX software bill-of-material (SBoM) generation utility' spec.description = 'CycloneDX is a lightweight software bill-of-material (SBOM) specification designed for use in application security contexts and supply chain component analysis. This Gem generates CycloneDX BOMs from Ruby projects.' - spec.authors = ['Joseph Kobti', 'Steve Springett'] - spec.email = 'josephkobti@outlook.com' spec.homepage = 'https://github.com/CycloneDX/cyclonedx-ruby-gem' spec.license = 'Apache-2.0' - spec.required_ruby_version = '>= 2.7.0' - spec.files = Dir.chdir(__dir__) do - `git ls-files -z`.split("\x0").reject do |f| - (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor]) - end - end - spec.bindir = 'exe' - spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.metadata["homepage_uri"] = "https://#{spec.name.tr("_", "-")}.galtzo.com/" + spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/v#{spec.version}" + spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md" + spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues" + spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}" + spec.metadata["funding_uri"] = "https://owasp.org/donate/?reponame=www-project-cyclonedx&title=OWASP+CycloneDX" + spec.metadata["wiki_uri"] = "#{spec.homepage}/wiki" + spec.metadata["rubygems_mfa_required"] = "true" + + # Specify which files are part of the released package. + spec.files = Dir[ + # Executables and tasks + "exe/*", + "lib/**/*.rb", + # Signatures + "sig/**/*.rbs", + ] + + # Automatically included with gem package, no need to list again in files. + spec.extra_rdoc_files = Dir[ + # Files (alphabetical) + "CHANGELOG.md", + "LICENSE.txt", + "README.md", + ] + spec.rdoc_options += [ + "--title", + "#{spec.name} - #{spec.summary}", + "--main", + "README.md", + "--exclude", + "^sig/", + "--line-numbers", + "--inline-source", + "--quiet", + ] spec.require_paths = ['lib'] + spec.bindir = "exe" + # Listed files are the relative paths from bindir above. + spec.executables = ['cyclonedx-ruby'] spec.add_dependency('json', '~> 2.6') spec.add_dependency('nokogiri', '~> 1.15') @@ -29,8 +62,8 @@ Gem::Specification.new do |spec| spec.add_dependency('activesupport', '~> 7.0') spec.add_development_dependency 'rake', '~> 13' spec.add_development_dependency 'rspec', '~> 3.12' - spec.add_development_dependency 'cucumber', '~> 8.0' - spec.add_development_dependency 'aruba', '~> 2.1' + spec.add_development_dependency 'cucumber', '~> 10.0' + spec.add_development_dependency 'aruba', '~> 2.2' spec.add_development_dependency 'simplecov', '~> 0.22.0' spec.add_development_dependency 'rubocop', '~> 1.54' end diff --git a/features/fixtures/simple/Gemfile b/features/fixtures/simple/Gemfile index 180e0f6..3458f68 100644 --- a/features/fixtures/simple/Gemfile +++ b/features/fixtures/simple/Gemfile @@ -1,3 +1,7 @@ source 'https://rubygems.org' -gem 'activesupport' +gem 'activesupport', '7.0.4.3' +gem 'concurrent-ruby', '1.2.2' +gem 'i18n', '1.12.0' +gem 'minitest', '5.18.0' +gem 'tzinfo', '2.0.6' diff --git a/features/fixtures/simple/Gemfile.lock b/features/fixtures/simple/Gemfile.lock index 66a0f90..cdb6220 100644 --- a/features/fixtures/simple/Gemfile.lock +++ b/features/fixtures/simple/Gemfile.lock @@ -1,15 +1,15 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.7.1) + activesupport (7.0.4.3) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) concurrent-ruby (1.2.2) - i18n (1.14.1) + i18n (1.12.0) concurrent-ruby (~> 1.0) - minitest (5.19.0) + minitest (5.18.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) @@ -18,7 +18,11 @@ PLATFORMS x86_64-linux DEPENDENCIES - activesupport + activesupport (= 7.0.4.3) + concurrent-ruby (= 1.2.2) + i18n (= 1.12.0) + minitest (= 5.18.0) + tzinfo (= 2.0.6) BUNDLED WITH 2.4.10 diff --git a/lib/bom_builder.rb b/lib/bom_builder.rb index 388614e..35997ed 100644 --- a/lib/bom_builder.rb +++ b/lib/bom_builder.rb @@ -21,163 +21,6 @@ # Copyright (c) OWASP Foundation. All Rights Reserved. # # frozen_string_literal: true -require 'bundler' -require 'fileutils' -require 'json' -require 'logger' -require 'nokogiri' -require 'optparse' -require 'ostruct' -require 'rest_client' -require 'securerandom' -require_relative 'bom_helpers' -require 'active_support/core_ext/hash' -class Bombuilder - SUPPORTED_BOM_FORMATS = %w[xml json] - - def self.build(path) - original_working_directory = Dir.pwd - setup(path) - specs_list - bom = build_bom(@gems, @bom_output_format) - - begin - @logger.info("Changing directory to the original working directory located at #{original_working_directory}") - Dir.chdir original_working_directory - rescue StandardError => e - @logger.error("Unable to change directory the original working directory located at #{original_working_directory}. #{e.message}: #{e.backtrace.join('\n')}") - abort - end - - bom_directory = File.dirname(@bom_file_path) - begin - FileUtils.mkdir_p(bom_directory) unless File.directory?(bom_directory) - rescue StandardError => e - @logger.error("Unable to create the directory to hold the BOM output at #{@bom_directory}. #{e.message}: #{e.backtrace.join('\n')}") - abort - end - - begin - @logger.info("Writing BOM to #{@bom_file_path}...") - File.open(@bom_file_path, 'w') { |file| file.write(bom) } - - if @options[:verbose] - @logger.info("#{@gems.size} gems were written to BOM located at #{@bom_file_path}") - else - puts "#{@gems.size} gems were written to BOM located at #{@bom_file_path}" - end - rescue StandardError => e - @logger.error("Unable to write BOM to #{@bom_file_path}. #{e.message}: #{e.backtrace.join('\n')}") - abort - end - end - private - def self.setup(path) - @options = {} - OptionParser.new do |opts| - opts.banner = 'Usage: cyclonedx-ruby [options]' - - opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v| - @options[:verbose] = v - end - opts.on('-p', '--path path', '(Required) Path to Ruby project directory') do |path| - @options[:path] = path - end - opts.on('-o', '--output bom_file_path', '(Optional) Path to output the bom.xml file to') do |bom_file_path| - @options[:bom_file_path] = bom_file_path - end - opts.on('-f', '--format bom_output_format', '(Optional) Output format for bom. Currently support xml (default) and json.') do |bom_output_format| - @options[:bom_output_format] = bom_output_format - end - opts.on_tail('-h', '--help', 'Show help message') do - puts opts - exit - end - end.parse! - - @logger = Logger.new($stdout) - @logger.level = if @options[:verbose] - Logger::INFO - else - Logger::ERROR - end - - @gems = [] - licenses_file = File.read "#{__dir__}/licenses.json" - @licenses_list = JSON.parse(licenses_file) - - if @options[:path].nil? - @logger.error('missing path to project directory') - abort - end - - unless File.directory?(@options[:path]) - @logger.error("path provided is not a valid directory. path provided was: #{@options[:path]}") - abort - end - - begin - @logger.info("Changing directory to Ruby project directory located at #{@options[:path]}") - Dir.chdir @options[:path] - rescue StandardError => e - @logger.error("Unable to change directory to Ruby project directory located at #{@options[:path]}. #{e.message}: #{e.backtrace.join('\n')}") - abort - end - - if @options[:bom_output_format].nil? - @bom_output_format = 'xml' - elsif SUPPORTED_BOM_FORMATS.include?(@options[:bom_output_format]) - @bom_output_format = @options[:bom_output_format] - else - @logger.error("Unrecognized cyclonedx bom output format provided. Please choose one of #{SUPPORTED_BOM_FORMATS}") - abort - end - - @bom_file_path = if @options[:bom_file_path].nil? - "./bom.#{@bom_output_format}" - else - @options[:bom_file_path] - end - - @logger.info("BOM will be written to #{@bom_file_path}") - - begin - gemfile_path = "#{@options[:path]}/Gemfile.lock" - @logger.info("Parsing specs from #{gemfile_path}...") - gemfile_contents = File.read(gemfile_path) - @specs = Bundler::LockfileParser.new(gemfile_contents).specs - @logger.info('Specs successfully parsed!') - rescue StandardError => e - @logger.error("Unable to parse specs from #{gemfile_path}. #{e.message}: #{e.backtrace.join('\n')}") - abort - end - end - - def self.specs_list - count = 0 - @specs.each do |dependency| - object = OpenStruct.new - object.name = dependency.name - object.version = dependency.version - object.purl = purl(object.name, object.version) - gem = get_gem(object.name, object.version) - next if gem.nil? - - if gem['licenses']&.length&.positive? - if @licenses_list.include? gem['licenses'].first - object.license_id = gem['licenses'].first - else - object.license_name = gem['licenses'].first - end - end - - object.author = gem['authors'] - object.description = gem['summary'] - object.hash = gem['sha'] - @gems.push(object) - count += 1 - @logger.info("#{object.name}:#{object.version} gem added") - end - end -end +require_relative "cyclonedx/ruby" +require_relative "cyclonedx_deprecated" diff --git a/lib/bom_component.rb b/lib/bom_component.rb index c2af8fe..598cc2c 100644 --- a/lib/bom_component.rb +++ b/lib/bom_component.rb @@ -1,45 +1,2 @@ - -class BomComponent - DEFAULT_TYPE = "library".freeze - HASH_ALG = 'SHA-256'.freeze - - def initialize(gem) - @name = gem['name'] - @version = gem['version'] - @description = gem['description'] - @hash = gem['hash'] - @purl = gem['purl'] - @gem = gem - end - - def hash_val - component_hash = { - "type": DEFAULT_TYPE, - "name": @name, - "version": @version, - "description": @description, - "purl": @purl, - "hashes": [ - "alg": HASH_ALG, - "content": @hash - ] - } - - if @gem['license_id'] - component_hash[:"licenses"] = [ - "license": { - "id": @gem['license_id'] - } - ] - elsif @gem['license_name'] - component_hash[:"licenses"] = [ - "license": { - "name": @gem['license_name'] - } - ] - end - - [component_hash] - - end -end \ No newline at end of file +require_relative "cyclonedx/ruby" +require_relative "cyclonedx_deprecated" diff --git a/lib/bom_helpers.rb b/lib/bom_helpers.rb index 01c0392..35997ed 100644 --- a/lib/bom_helpers.rb +++ b/lib/bom_helpers.rb @@ -22,85 +22,5 @@ # # frozen_string_literal: true -require_relative 'bom_component' - -def purl(name, version) - "pkg:gem/#{name}@#{version}" -end - -def random_urn_uuid - "urn:uuid:#{SecureRandom.uuid}" -end - -def build_bom(gems, format) - if format == 'json' - build_json_bom(gems) - else - build_bom_xml(gems) - end -end - -def build_json_bom(gems) - bom_hash = { - "bomFormat": "CycloneDX", - "specVersion": "1.1", - "serialNumber": random_urn_uuid, - "version": 1, - "components": [] - } - - gems.each do |gem| - bom_hash[:components] += BomComponent.new(gem).hash_val() - end - - JSON.pretty_generate(bom_hash) -end - -def build_bom_xml(gems) - builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml| - attributes = { 'xmlns' => 'http://cyclonedx.org/schema/bom/1.1', 'version' => '1', 'serialNumber' => random_urn_uuid } - xml.bom(attributes) do - xml.components do - gems.each do |gem| - xml.component('type' => 'library') do - xml.name gem['name'] - xml.version gem['version'] - xml.description gem['description'] - xml.hashes do - xml.hash_ gem['hash'], alg: 'SHA-256' - end - if gem['license_id'] - xml.licenses do - xml.license do - xml.id gem['license_id'] - end - end - elsif gem['license_name'] - xml.licenses do - xml.license do - xml.name gem['license_name'] - end - end - end - xml.purl gem['purl'] - end - end - end - end - end - - builder.to_xml -end - -def get_gem(name, version) - url = "https://rubygems.org/api/v1/versions/#{name}.json" - begin - RestClient.proxy = ENV['http_proxy'] - response = RestClient.get(url) - body = JSON.parse(response.body) - body.select { |item| item['number'] == version.to_s }.first - rescue StandardError - @logger.warn("#{name} couldn't be fetched") - nil - end -end +require_relative "cyclonedx/ruby" +require_relative "cyclonedx_deprecated" diff --git a/lib/cyclonedx/bom_builder.rb b/lib/cyclonedx/bom_builder.rb new file mode 100644 index 0000000..2aa0423 --- /dev/null +++ b/lib/cyclonedx/bom_builder.rb @@ -0,0 +1,151 @@ + +module Cyclonedx + class BomBuilder + SUPPORTED_BOM_FORMATS = %w[xml json] + + def self.build(path) + original_working_directory = Dir.pwd + setup(path) + specs_list + bom = build_bom(@gems, @bom_output_format) + + begin + @logger.info("Changing directory to the original working directory located at #{original_working_directory}") + Dir.chdir original_working_directory + rescue StandardError => e + @logger.error("Unable to change directory the original working directory located at #{original_working_directory}. #{e.message}: #{e.backtrace.join('\n')}") + abort + end + + bom_directory = File.dirname(@bom_file_path) + begin + FileUtils.mkdir_p(bom_directory) unless File.directory?(bom_directory) + rescue StandardError => e + @logger.error("Unable to create the directory to hold the BOM output at #{@bom_directory}. #{e.message}: #{e.backtrace.join('\n')}") + abort + end + + begin + @logger.info("Writing BOM to #{@bom_file_path}...") + File.open(@bom_file_path, 'w') { |file| file.write(bom) } + + if @options[:verbose] + @logger.info("#{@gems.size} gems were written to BOM located at #{@bom_file_path}") + else + puts "#{@gems.size} gems were written to BOM located at #{@bom_file_path}" + end + rescue StandardError => e + @logger.error("Unable to write BOM to #{@bom_file_path}. #{e.message}: #{e.backtrace.join('\n')}") + abort + end + end + private + def self.setup(path) + @options = {} + OptionParser.new do |opts| + opts.banner = 'Usage: cyclonedx-ruby [options]' + + opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v| + @options[:verbose] = v + end + opts.on('-p', '--path path', '(Required) Path to Ruby project directory') do |path| + @options[:path] = path + end + opts.on('-o', '--output bom_file_path', '(Optional) Path to output the bom.xml file to') do |bom_file_path| + @options[:bom_file_path] = bom_file_path + end + opts.on('-f', '--format bom_output_format', '(Optional) Output format for bom. Currently support xml (default) and json.') do |bom_output_format| + @options[:bom_output_format] = bom_output_format + end + opts.on_tail('-h', '--help', 'Show help message') do + puts opts + exit + end + end.parse! + + @logger = Logger.new($stdout) + @logger.level = if @options[:verbose] + Logger::INFO + else + Logger::ERROR + end + + @gems = [] + licenses_file = File.read "#{__dir__}/licenses.json" + @licenses_list = JSON.parse(licenses_file) + + if @options[:path].nil? + @logger.error('missing path to project directory') + abort + end + + unless File.directory?(@options[:path]) + @logger.error("path provided is not a valid directory. path provided was: #{@options[:path]}") + abort + end + + begin + @logger.info("Changing directory to Ruby project directory located at #{@options[:path]}") + Dir.chdir @options[:path] + rescue StandardError => e + @logger.error("Unable to change directory to Ruby project directory located at #{@options[:path]}. #{e.message}: #{e.backtrace.join('\n')}") + abort + end + + if @options[:bom_output_format].nil? + @bom_output_format = 'xml' + elsif SUPPORTED_BOM_FORMATS.include?(@options[:bom_output_format]) + @bom_output_format = @options[:bom_output_format] + else + @logger.error("Unrecognized cyclonedx bom output format provided. Please choose one of #{SUPPORTED_BOM_FORMATS}") + abort + end + + @bom_file_path = if @options[:bom_file_path].nil? + "./bom.#{@bom_output_format}" + else + @options[:bom_file_path] + end + + @logger.info("BOM will be written to #{@bom_file_path}") + + begin + gemfile_path = "#{@options[:path]}/Gemfile.lock" + @logger.info("Parsing specs from #{gemfile_path}...") + gemfile_contents = File.read(gemfile_path) + @specs = Bundler::LockfileParser.new(gemfile_contents).specs + @logger.info('Specs successfully parsed!') + rescue StandardError => e + @logger.error("Unable to parse specs from #{gemfile_path}. #{e.message}: #{e.backtrace.join('\n')}") + abort + end + end + + def self.specs_list + count = 0 + @specs.each do |dependency| + object = OpenStruct.new + object.name = dependency.name + object.version = dependency.version + object.purl = purl(object.name, object.version) + gem = get_gem(object.name, object.version) + next if gem.nil? + + if gem['licenses']&.length&.positive? + if @licenses_list.include? gem['licenses'].first + object.license_id = gem['licenses'].first + else + object.license_name = gem['licenses'].first + end + end + + object.author = gem['authors'] + object.description = gem['summary'] + object.hash = gem['sha'] + @gems.push(object) + count += 1 + @logger.info("#{object.name}:#{object.version} gem added") + end + end + end +end diff --git a/lib/cyclonedx/bom_component.rb b/lib/cyclonedx/bom_component.rb new file mode 100644 index 0000000..17cce6c --- /dev/null +++ b/lib/cyclonedx/bom_component.rb @@ -0,0 +1,46 @@ +module Cyclonedx + class BomComponent + DEFAULT_TYPE = "library".freeze + HASH_ALG = 'SHA-256'.freeze + + def initialize(gem) + @name = gem['name'] + @version = gem['version'] + @description = gem['description'] + @hash = gem['hash'] + @purl = gem['purl'] + @gem = gem + end + + def hash_val + component_hash = { + "type": DEFAULT_TYPE, + "name": @name, + "version": @version, + "description": @description, + "purl": @purl, + "hashes": [ + "alg": HASH_ALG, + "content": @hash + ] + } + + if @gem['license_id'] + component_hash[:"licenses"] = [ + "license": { + "id": @gem['license_id'] + } + ] + elsif @gem['license_name'] + component_hash[:"licenses"] = [ + "license": { + "name": @gem['license_name'] + } + ] + end + + [component_hash] + + end + end +end diff --git a/lib/cyclonedx/bom_helpers.rb b/lib/cyclonedx/bom_helpers.rb new file mode 100644 index 0000000..cd2dc18 --- /dev/null +++ b/lib/cyclonedx/bom_helpers.rb @@ -0,0 +1,112 @@ +# This file is part of CycloneDX Ruby Gem. +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) OWASP Foundation. All Rights Reserved. +# +# frozen_string_literal: true + +require_relative 'bom_component' + +module Cyclonedx + module BomHelpers + extend self + + def purl(name, version) + "pkg:gem/#{name}@#{version}" + end + + def random_urn_uuid + "urn:uuid:#{SecureRandom.uuid}" + end + + def build_bom(gems, format) + if format == 'json' + build_json_bom(gems) + else + build_bom_xml(gems) + end + end + + def build_json_bom(gems) + bom_hash = { + "bomFormat": "CycloneDX", + "specVersion": "1.1", + "serialNumber": random_urn_uuid, + "version": 1, + "components": [] + } + + gems.each do |gem| + bom_hash[:components] += BomComponent.new(gem).hash_val() + end + + JSON.pretty_generate(bom_hash) + end + + def build_bom_xml(gems) + builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml| + attributes = { 'xmlns' => 'http://cyclonedx.org/schema/bom/1.1', 'version' => '1', 'serialNumber' => random_urn_uuid } + xml.bom(attributes) do + xml.components do + gems.each do |gem| + xml.component('type' => 'library') do + xml.name gem['name'] + xml.version gem['version'] + xml.description gem['description'] + xml.hashes do + xml.hash_ gem['hash'], alg: 'SHA-256' + end + if gem['license_id'] + xml.licenses do + xml.license do + xml.id gem['license_id'] + end + end + elsif gem['license_name'] + xml.licenses do + xml.license do + xml.name gem['license_name'] + end + end + end + xml.purl gem['purl'] + end + end + end + end + end + + builder.to_xml + end + + def get_gem(name, version) + url = "https://rubygems.org/api/v1/versions/#{name}.json" + begin + RestClient.proxy = ENV['http_proxy'] + response = RestClient.get(url) + body = JSON.parse(response.body) + body.select { |item| item['number'] == version.to_s }.first + rescue StandardError + @logger.warn("#{name} couldn't be fetched") + nil + end + end + end +end diff --git a/lib/cyclonedx/ruby.rb b/lib/cyclonedx/ruby.rb new file mode 100644 index 0000000..4640609 --- /dev/null +++ b/lib/cyclonedx/ruby.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# External gems +require 'bundler' +require 'fileutils' +require 'json' +require 'logger' +require 'nokogiri' +require 'optparse' +require 'ostruct' +require 'rest_client' +require 'securerandom' +require 'active_support/core_ext/hash' + +# This gem +require_relative "ruby/version" +require_relative "bom_builder" +require_relative "bom_component" +require_relative "bom_helpers" + +module Cyclonedx + module Ruby + class Error < StandardError; end + # Your code goes here... + end +end diff --git a/lib/cyclonedx/ruby/deprecation.rb b/lib/cyclonedx/ruby/deprecation.rb new file mode 100644 index 0000000..5183d8d --- /dev/null +++ b/lib/cyclonedx/ruby/deprecation.rb @@ -0,0 +1,55 @@ +# Provides tools that allow methods to be deprecated with new releases of the gem. +# +# Usage: +# # class method deprecation example +# class MyClass +# extend Cyclonedx::Ruby::Deprecation +# +# deprecated_alias :old_method, :new_method, self +# +# def self.new_method +# # new implementation +# end +# end +# +# # instance method deprecation example +# class MyClass +# extend Cyclonedx::Ruby::Deprecation +# +# deprecated_alias :old_method, :new_method +# +# def new_method +# # new implementation +# end +# end + +module Cyclonedx + module Ruby + module Deprecation + class << self + attr_accessor :deprecate_in_silence + end + + @deprecate_in_silence = false + + # Define a deprecated alias for a method + # @param [Symbol] scope - :instance or :class (default :instance) + # @param [Symbol] name - name of method to define + # @param [Symbol] replacement - name of method (to alias) + # @param [Constant] receiver - Receiver of the replacement method, use nil for instance methods (default nil) + def deprecated_alias(scope, name, replacement, receiver = nil) + if scope == :class + define_singleton_method(name) do |*args, &block| + warn("Cyclonedx: #{self}.#{name} deprecated (please use ##{replacement})") unless Cyclonedx::Ruby::Deprecation.deprecate_in_silence + receiver ? receiver.send(replacement, *args, &block) : send(replacement, *args, &block) + end + else + define_method(name) do |*args, &block| + warn("Cyclonedx: #{self.class}##{name} deprecated (please use ##{receiver ? "#{receiver}.#{replacement}" : replacement})") unless Cyclonedx::Ruby::Deprecation.deprecate_in_silence + receiver ? receiver.send(replacement, *args, &block) : send(replacement, *args, &block) + end + end + end + end + end +end diff --git a/lib/cyclonedx/ruby/version.rb b/lib/cyclonedx/ruby/version.rb new file mode 100644 index 0000000..990fd9a --- /dev/null +++ b/lib/cyclonedx/ruby/version.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Cyclonedx + module Ruby + module Version + VERSION = "1.2.0" + end + VERSION = Version::VERSION # Make VERSION available in traditional way + end +end diff --git a/lib/cyclonedx_deprecated.rb b/lib/cyclonedx_deprecated.rb new file mode 100644 index 0000000..9f23d76 --- /dev/null +++ b/lib/cyclonedx_deprecated.rb @@ -0,0 +1,17 @@ +require_relative "cyclonedx/ruby/deprecation" + +# Legacy class name kept for compatibility until v2.0.0 +Bombuilder = Cyclonedx::BomBuilder + +# Legacy class name kept for compatibility until v2.0.0 +BomComponent = Cyclonedx::BomComponent + +# Legacy global methods included in Object (root namespace) kept for compatibility until v2.0.0 +extend Cyclonedx::Ruby::Deprecation + +deprecated_alias :instance, :purl, :purl, Cyclonedx::BomHelpers +deprecated_alias :instance, :random_urn_uuid, :random_urn_uuid, Cyclonedx::BomHelpers +deprecated_alias :instance, :build_bom, :build_bom, Cyclonedx::BomHelpers +deprecated_alias :instance, :build_json_bom, :build_json_bom, Cyclonedx::BomHelpers +deprecated_alias :instance, :build_bom_xml, :build_bom_xml, Cyclonedx::BomHelpers +deprecated_alias :instance, :get_gem, :get_gem, Cyclonedx::BomHelpers diff --git a/sig/cyclonedx/ruby.rbs b/sig/cyclonedx/ruby.rbs new file mode 100644 index 0000000..87df4e5 --- /dev/null +++ b/sig/cyclonedx/ruby.rbs @@ -0,0 +1,6 @@ +module Cyclonedx + module Ruby + VERSION: String + # See the writing guide of rbs: https://github.com/ruby/rbs#guides + end +end diff --git a/spec/bom_helpers_spec.rb b/spec/bom_helpers_spec.rb deleted file mode 100644 index 75adec2..0000000 --- a/spec/bom_helpers_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'bom_helpers' - -RSpec.describe 'helper methods' do - context '#purl' do - it 'builds a purl' do - expect(purl('activesupport', '7.0.1')).to eq("pkg:gem/activesupport@7.0.1") - end - end -end diff --git a/spec/bom_component_spec.rb b/spec/cyclonedx/bom_component_spec.rb similarity index 92% rename from spec/bom_component_spec.rb rename to spec/cyclonedx/bom_component_spec.rb index e02f4d5..f5ff562 100644 --- a/spec/bom_component_spec.rb +++ b/spec/cyclonedx/bom_component_spec.rb @@ -1,6 +1,3 @@ -require 'spec_helper' -require 'bom_component' - RSpec.shared_examples "a valid hash_val result for gem" do it { expect(result.count).to eq(1) } it { expect(result[0][:type]).to eq('library') } @@ -13,7 +10,7 @@ it { expect(result[0][:hashes][0][:content]).to eq(gem.hash) } end -RSpec.describe BomComponent do +RSpec.describe Cyclonedx::BomComponent do context '#hash_val' do let(:base_gem) do OpenStruct.new( @@ -25,11 +22,11 @@ ) end - let(:gem) do + let(:gem) do base_gem end - subject(:result) { BomComponent.new(gem).hash_val } + subject(:result) { Cyclonedx::BomComponent.new(gem).hash_val } context 'with a gem without a license' do include_examples 'a valid hash_val result for gem' diff --git a/spec/cyclonedx/bom_helpers_spec.rb b/spec/cyclonedx/bom_helpers_spec.rb new file mode 100644 index 0000000..ae6d7fb --- /dev/null +++ b/spec/cyclonedx/bom_helpers_spec.rb @@ -0,0 +1,14 @@ +RSpec.describe Cyclonedx::BomHelpers do + context '#purl' do + context "when legacy method is called" do + it 'builds a purl' do + skip("Deprecated in favor of Cyclonedx::BomHelpers.purl") unless defined?(purl) + expect(purl('activesupport', '7.0.1')).to eq("pkg:gem/activesupport@7.0.1") + end + end + + it 'builds a purl' do + expect(described_class.purl('activesupport', '7.0.1')).to eq("pkg:gem/activesupport@7.0.1") + end + end +end diff --git a/spec/cyclonedx/ruby_spec.rb b/spec/cyclonedx/ruby_spec.rb new file mode 100644 index 0000000..50e2cb4 --- /dev/null +++ b/spec/cyclonedx/ruby_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +RSpec.describe Cyclonedx::Ruby do + it "has a version number" do + expect(Cyclonedx::Ruby::VERSION).not_to be nil + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 24b4245..660e0a9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,16 @@ -# Copied from https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/spec/spec_helper.rb -# Licensed under MIT - https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/LICENSE +# frozen_string_literal: true -$LOAD_PATH << File.expand_path('../lib', __dir__) +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end unless RUBY_PLATFORM.include?('java') require 'simplecov' @@ -11,6 +20,8 @@ SimpleCov.start unless ENV.key? 'ARUBA_NO_COVERAGE' end -# Loading support files -Dir.glob(File.expand_path('support/*.rb', __dir__)).sort.each { |f| require_relative f } -Dir.glob(File.expand_path('support/**/*.rb', __dir__)).sort.each { |f| require_relative f } +mimic_next_major = ENV.fetch("MIMIC_NEXT_MAJOR_VERSION", "false") +# Require via legacy path until v2.0.0, and unless testing functionality in preparation for next major release +require 'bom_builder' if mimic_next_major.casecmp?("false") +# Modern path is already covered by the legacy path, but doesn't hurt to include it twice +require "cyclonedx/ruby" From 378f62ed84e79aa12b12580ab141cbf0510ca8bd Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 21:18:29 -0600 Subject: [PATCH 06/22] =?UTF-8?q?=F0=9F=8E=A8=20modernize=20for=202025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Added - `CHANGELOG.md` file to document notable changes in keep-a-changelog format - `Cyclonedx::BomHelpers` module to house helper methods, replacing global methods - `Cyclonedx::BomBuilder` class, replacing `Bombuilder` (note the capitalization change) - `Cyclonedx::BomComponent` class, replacing `BomComponent` - `Cyclonedx::Ruby::Version::VERSION` constant to hold the version number (also available as `Cyclonedx::VERSION`) - `Cyclonedx::Ruby::Deprecation` module to help manage deprecations ### Changed - Updated gemspec metadata for clarity and consistency - Modernized Rakefile, dotfiles, and test setup - `LICENSE` => `LICENSE.txt` to simplify parsing consistency on various platforms and tools - `cucumber` v8 => v10 - `aruba` v2.1 => v2.2 ### Deprecated - `BomComponent` => `Cyclonedx::BomComponent` - `Bombuilder` => `Cyclonedx::BomBuilder` (note the capitalization change) - `Object.purl` => `Cyclonedx::BomHelpers.purl` - `Object.random_urn_uuid` => `Cyclonedx::BomHelpers.random_urn_uuid` - `Object.build_bom` => `Cyclonedx::BomHelpers.build_bom` - `Object.build_json_bom` => `Cyclonedx::BomHelpers.build_json_bom` - `Object.build_bom_xml` => `Cyclonedx::BomHelpers.build_bom_xml` - `Object.get_gem` => `Cyclonedx::BomHelpers.get_gem` ### Fixed - `Nokogiri::XML::Builder` context relies on `method_missing` - Globally defined `Object#purl` conflicted with ``. - Moved to `Cyclonedx::BomHelpers.purl` to avoid conflict in v2.0.0 (along with all other global methods) - Fixed existing usage via the built-in Nokogiri workaround of adding an underscore `purl_` - The XML tag is unchanged as `` Signed-off-by: Peter H. Boling --- CHANGELOG.md | 6 ++++++ cyclonedx-ruby.gemspec | 2 ++ exe/cyclonedx-ruby | 9 +++++++-- lib/cyclonedx/bom_builder.rb | 7 +++++-- lib/cyclonedx/bom_helpers.rb | 8 ++++++-- lib/cyclonedx/ruby.rb | 4 ++-- lib/cyclonedx_deprecated.rb | 3 +++ 7 files changed, 31 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8969c08..a0a256e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,12 @@ Please file a bug if you notice a violation of semantic versioning. ### Fixed +- `Nokogiri::XML::Builder` context relies on `method_missing` + - Globally defined `Object#purl` conflicted with ``. + - Moved to `Cyclonedx::BomHelpers.purl` to avoid conflict in v2.0.0 (along with all other global methods) + - Fixed existing usage via the built-in Nokogiri workaround of adding an underscore `purl_` + - The XML tag is unchanged as `` + ### Security ## [1.1.0] - 2019-07-13 diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index cc5016a..781520c 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -25,9 +25,11 @@ Gem::Specification.new do |spec| # Specify which files are part of the released package. spec.files = Dir[ + # # Executables and tasks "exe/*", "lib/**/*.rb", + "lib/licenses.json", # Signatures "sig/**/*.rbs", ] diff --git a/exe/cyclonedx-ruby b/exe/cyclonedx-ruby index 10ff189..5316aa5 100755 --- a/exe/cyclonedx-ruby +++ b/exe/cyclonedx-ruby @@ -1,5 +1,10 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'bom_builder' -Bombuilder.build(ARGV[0]) +if ENV.fetch("MIMIC_NEXT_MAJOR_VERSION", "false").casecmp?("true") + require 'cyclonedx/ruby' + Cyclonedx::BomBuilder.build(ARGV[0]) +else + require 'bom_builder' + Bombuilder.build(ARGV[0]) +end diff --git a/lib/cyclonedx/bom_builder.rb b/lib/cyclonedx/bom_builder.rb index 2aa0423..6a96994 100644 --- a/lib/cyclonedx/bom_builder.rb +++ b/lib/cyclonedx/bom_builder.rb @@ -1,8 +1,9 @@ - module Cyclonedx class BomBuilder SUPPORTED_BOM_FORMATS = %w[xml json] + extend Cyclonedx::BomHelpers + def self.build(path) original_working_directory = Dir.pwd setup(path) @@ -71,7 +72,9 @@ def self.setup(path) end @gems = [] - licenses_file = File.read "#{__dir__}/licenses.json" + # Adjusted to point to lib/licenses.json relative to this file's directory (lib/cyclonedx) + licenses_path = File.expand_path('../licenses.json', __dir__) + licenses_file = File.read(licenses_path) @licenses_list = JSON.parse(licenses_file) if @options[:path].nil? diff --git a/lib/cyclonedx/bom_helpers.rb b/lib/cyclonedx/bom_helpers.rb index cd2dc18..349f261 100644 --- a/lib/cyclonedx/bom_helpers.rb +++ b/lib/cyclonedx/bom_helpers.rb @@ -26,7 +26,7 @@ module Cyclonedx module BomHelpers - extend self + module_function def purl(name, version) "pkg:gem/#{name}@#{version}" @@ -86,7 +86,11 @@ def build_bom_xml(gems) end end end - xml.purl gem['purl'] + # The globally scoped legacy `Object#purl` method breaks the Nokogiri builder context + # Fortunately Nokogiri has a built-in workaround, adding an underscore to the method name. + # The resulting XML tag is still ``. + # Globally scoped legacy `Object#purl` will be removed in v2.0.0, and this hack can be removed then. + xml.purl_ gem['purl'] end end end diff --git a/lib/cyclonedx/ruby.rb b/lib/cyclonedx/ruby.rb index 4640609..0060c3b 100644 --- a/lib/cyclonedx/ruby.rb +++ b/lib/cyclonedx/ruby.rb @@ -14,9 +14,9 @@ # This gem require_relative "ruby/version" -require_relative "bom_builder" -require_relative "bom_component" require_relative "bom_helpers" +require_relative "bom_builder" # depends on bom_helpers +require_relative "bom_component" module Cyclonedx module Ruby diff --git a/lib/cyclonedx_deprecated.rb b/lib/cyclonedx_deprecated.rb index 9f23d76..c67b360 100644 --- a/lib/cyclonedx_deprecated.rb +++ b/lib/cyclonedx_deprecated.rb @@ -15,3 +15,6 @@ deprecated_alias :instance, :build_json_bom, :build_json_bom, Cyclonedx::BomHelpers deprecated_alias :instance, :build_bom_xml, :build_bom_xml, Cyclonedx::BomHelpers deprecated_alias :instance, :get_gem, :get_gem, Cyclonedx::BomHelpers + +# Sanity +raise "Deprecated methods broken" unless purl('activesupport', '7.0.1') == "pkg:gem/activesupport@7.0.1" From c91de22b5db4b6bb1a6186311664aabb5ae2abe1 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 23:06:29 -0600 Subject: [PATCH 07/22] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Frozen=20Strings=20(?= =?UTF-8?q?performance=20benefit)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- .simplecov | 2 ++ features/fixtures/simple/Gemfile | 2 ++ features/step_definitions/json_bom_matching.rb | 2 ++ features/step_definitions/xml_bom_matching.rb | 2 ++ features/support/env.rb | 2 ++ features/support/simplecov_support.rb | 2 ++ lib/bom_builder.rb | 2 ++ lib/bom_component.rb | 2 ++ lib/bom_helpers.rb | 3 ++- lib/cyclonedx/bom_builder.rb | 2 ++ lib/cyclonedx/bom_component.rb | 2 ++ lib/cyclonedx/bom_helpers.rb | 2 ++ lib/cyclonedx/ruby/deprecation.rb | 2 ++ lib/cyclonedx_deprecated.rb | 2 ++ spec/cyclonedx/bom_component_spec.rb | 2 ++ spec/cyclonedx/bom_helpers_spec.rb | 2 ++ 16 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.simplecov b/.simplecov index f1709c5..dcd8afc 100644 --- a/.simplecov +++ b/.simplecov @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Copied from https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/.simplecov # Licensed under MIT - https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/LICENSE diff --git a/features/fixtures/simple/Gemfile b/features/fixtures/simple/Gemfile index 3458f68..32e772f 100644 --- a/features/fixtures/simple/Gemfile +++ b/features/fixtures/simple/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' gem 'activesupport', '7.0.4.3' diff --git a/features/step_definitions/json_bom_matching.rb b/features/step_definitions/json_bom_matching.rb index 0a81ea1..d012084 100644 --- a/features/step_definitions/json_bom_matching.rb +++ b/features/step_definitions/json_bom_matching.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Then('the generated Json BOM file {string} matches {string}') do |generated_file, expected_file| generated_file_contents = File.read(expand_path(generated_file)) expected_file_contents = File.read(expand_path(expected_file)) diff --git a/features/step_definitions/xml_bom_matching.rb b/features/step_definitions/xml_bom_matching.rb index 004c9b2..50c6e6f 100644 --- a/features/step_definitions/xml_bom_matching.rb +++ b/features/step_definitions/xml_bom_matching.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Then('the generated XML BOM file {string} matches {string}') do |generated_file, expected_file| generated_file_contents = File.read(expand_path(generated_file)) expected_file_contents = File.read(expand_path(expected_file)) diff --git a/features/support/env.rb b/features/support/env.rb index 331ff6a..96b3862 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Based on https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/features/support/env.rb # Licensed under MIT - https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/LICENSE diff --git a/features/support/simplecov_support.rb b/features/support/simplecov_support.rb index b3a91a3..3c5465f 100644 --- a/features/support/simplecov_support.rb +++ b/features/support/simplecov_support.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Copied from https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/features/support/simplecov_setup.rb # Licensed under MIT - https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/LICENSE diff --git a/lib/bom_builder.rb b/lib/bom_builder.rb index 35997ed..b333660 100644 --- a/lib/bom_builder.rb +++ b/lib/bom_builder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is part of CycloneDX Ruby Gem. # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/lib/bom_component.rb b/lib/bom_component.rb index 598cc2c..be8402f 100644 --- a/lib/bom_component.rb +++ b/lib/bom_component.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + require_relative "cyclonedx/ruby" require_relative "cyclonedx_deprecated" diff --git a/lib/bom_helpers.rb b/lib/bom_helpers.rb index 35997ed..b74ce59 100644 --- a/lib/bom_helpers.rb +++ b/lib/bom_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is part of CycloneDX Ruby Gem. # # Licensed to the Apache Software Foundation (ASF) under one @@ -20,7 +22,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. # -# frozen_string_literal: true require_relative "cyclonedx/ruby" require_relative "cyclonedx_deprecated" diff --git a/lib/cyclonedx/bom_builder.rb b/lib/cyclonedx/bom_builder.rb index 6a96994..cd7f85b 100644 --- a/lib/cyclonedx/bom_builder.rb +++ b/lib/cyclonedx/bom_builder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Cyclonedx class BomBuilder SUPPORTED_BOM_FORMATS = %w[xml json] diff --git a/lib/cyclonedx/bom_component.rb b/lib/cyclonedx/bom_component.rb index 17cce6c..376ccac 100644 --- a/lib/cyclonedx/bom_component.rb +++ b/lib/cyclonedx/bom_component.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Cyclonedx class BomComponent DEFAULT_TYPE = "library".freeze diff --git a/lib/cyclonedx/bom_helpers.rb b/lib/cyclonedx/bom_helpers.rb index 349f261..22f9783 100644 --- a/lib/cyclonedx/bom_helpers.rb +++ b/lib/cyclonedx/bom_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is part of CycloneDX Ruby Gem. # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/lib/cyclonedx/ruby/deprecation.rb b/lib/cyclonedx/ruby/deprecation.rb index 5183d8d..55f5e69 100644 --- a/lib/cyclonedx/ruby/deprecation.rb +++ b/lib/cyclonedx/ruby/deprecation.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Provides tools that allow methods to be deprecated with new releases of the gem. # # Usage: diff --git a/lib/cyclonedx_deprecated.rb b/lib/cyclonedx_deprecated.rb index c67b360..a814fa1 100644 --- a/lib/cyclonedx_deprecated.rb +++ b/lib/cyclonedx_deprecated.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "cyclonedx/ruby/deprecation" # Legacy class name kept for compatibility until v2.0.0 diff --git a/spec/cyclonedx/bom_component_spec.rb b/spec/cyclonedx/bom_component_spec.rb index f5ff562..0d1ea5f 100644 --- a/spec/cyclonedx/bom_component_spec.rb +++ b/spec/cyclonedx/bom_component_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples "a valid hash_val result for gem" do it { expect(result.count).to eq(1) } it { expect(result[0][:type]).to eq('library') } diff --git a/spec/cyclonedx/bom_helpers_spec.rb b/spec/cyclonedx/bom_helpers_spec.rb index ae6d7fb..c183669 100644 --- a/spec/cyclonedx/bom_helpers_spec.rb +++ b/spec/cyclonedx/bom_helpers_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.describe Cyclonedx::BomHelpers do context '#purl' do context "when legacy method is called" do From cbd8f711c94931160e809e17140e057089ad4295 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 25 Oct 2025 23:12:00 -0600 Subject: [PATCH 08/22] =?UTF-8?q?=E2=8F=AA=EF=B8=8F=20Revert=20accidental?= =?UTF-8?q?=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- .rspec | 1 - bom.json | 6 +++--- bom.xml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.rspec b/.rspec index 7d3a2e2..9007e08 100644 --- a/.rspec +++ b/.rspec @@ -1,5 +1,4 @@ --format documentation ---color --require spec_helper --color --order random diff --git a/bom.json b/bom.json index 2cd09f7..50b7098 100644 --- a/bom.json +++ b/bom.json @@ -1,7 +1,7 @@ { "bomFormat": "CycloneDX", - "specVersion": "1.7", - "serialNumber": "urn:uuid:294dd750-1036-4fd5-b457-2fb8d54bb310", + "specVersion": "1.1", + "serialNumber": "urn:uuid:d498cdc2-5494-4031-b37d-ff3d10d336bf", "version": 1, "components": [ { @@ -105,4 +105,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/bom.xml b/bom.xml index 1fa5899..b379102 100644 --- a/bom.xml +++ b/bom.xml @@ -1,5 +1,5 @@ - + activesupport From dded808c635d2da543100b9897af67e7cf329eb4 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Thu, 30 Oct 2025 00:47:25 -0600 Subject: [PATCH 09/22] =?UTF-8?q?=F0=9F=9A=A8=20Linting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- .rubocop.yml | 3 + .rubocop_todo.yml | 208 +++------------------------ Gemfile | 2 +- bin/console | 6 +- bin/cucumber | 8 +- bin/rake | 8 +- bin/rspec | 8 +- cyclonedx-ruby.gemspec | 8 +- exe/cyclonedx-ruby | 2 +- features/fixtures/simple/Gemfile | 2 +- lib/bom_builder.rb | 5 +- lib/cyclonedx/bom_builder.rb | 6 +- lib/cyclonedx/bom_component.rb | 33 +++-- lib/cyclonedx/bom_helpers.rb | 15 +- lib/cyclonedx/ruby.rb | 8 +- lib/cyclonedx/ruby/version.rb | 2 +- lib/cyclonedx_deprecated.rb | 4 +- spec/cyclonedx/bom_component_spec.rb | 2 +- spec/cyclonedx/bom_helpers_spec.rb | 8 +- spec/cyclonedx/ruby_spec.rb | 2 +- spec/spec_helper.rb | 8 +- 21 files changed, 90 insertions(+), 258 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 0dbd7ac..8d139a1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,6 +4,9 @@ AllCops: inherit_from: .rubocop_todo.yml +Gemspec/DevelopmentDependencies: + EnforcedStyle: gemspec + # The behavior of RuboCop can be controlled via the .rubocop.yml # configuration file. It makes it possible to enable/disable # certain cops (checks) and to alter their behavior if they accept diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index accbce0..5f5b288 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,136 +1,29 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2023-04-05 19:57:43 UTC using RuboCop version 1.49.0. +# on 2025-10-30 06:45:50 UTC using RuboCop version 1.81.6. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Severity, Include. -# Include: **/*.gemspec -Gemspec/DeprecatedAttributeAssignment: - Exclude: - - 'cyclonedx-ruby.gemspec' - # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. -# Include: **/*.gemspec +# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation. Gemspec/OrderedDependencies: Exclude: - 'cyclonedx-ruby.gemspec' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Severity, Include. -# Include: **/*.gemspec -Gemspec/RequireMFA: - Exclude: - - 'cyclonedx-ruby.gemspec' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Layout/EmptyLineAfterMagicComment: - Exclude: - - 'lib/bom_builder.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: around, only_before -Layout/EmptyLinesAroundAccessModifier: - Exclude: - - 'lib/bom_builder.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Layout/EmptyLinesAroundMethodBody: - Exclude: - - 'lib/bom_component.rb' - -# Offense count: 1 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Exclude: - 'cyclonedx-ruby.gemspec' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_brackets -Layout/FirstArrayElementIndentation: - Exclude: - - 'lib/bom_component.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Layout/LeadingEmptyLines: - Exclude: - - 'lib/bom_component.rb' - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: final_newline, final_blank_line -Layout/TrailingEmptyLines: - Exclude: - - 'Rakefile' - - 'lib/bom_component.rb' - -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowInHeredoc. -Layout/TrailingWhitespace: - Exclude: - - 'Rakefile' - - 'spec/bom_component_spec.rb' - -# Offense count: 2 -Lint/IneffectiveAccessModifier: - Exclude: - - 'lib/bom_builder.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Lint/ScriptPermission: - Exclude: - - 'Rakefile' - -# Offense count: 1 -Lint/ShadowingOuterLocalVariable: - Exclude: - - 'lib/bom_builder.rb' - -# Offense count: 19 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: strict, consistent -Lint/SymbolConversion: - Exclude: - - 'lib/bom_component.rb' - - 'lib/bom_helpers.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. -Lint/UnusedMethodArgument: - Exclude: - - 'lib/bom_builder.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. -Lint/UselessAccessModifier: - Exclude: - - 'lib/bom_builder.rb' - # Offense count: 4 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: - Max: 67 + Max: 68 # Offense count: 4 # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. @@ -141,78 +34,38 @@ Metrics/BlockLength: # Offense count: 1 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 128 + Max: 129 # Offense count: 1 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: Max: 9 -# Offense count: 6 +# Offense count: 7 # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: - Max: 68 + Max: 69 # Offense count: 1 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: Max: 12 -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: PreferredName. -Naming/RescuedExceptionsVariableName: - Exclude: - - 'Rakefile' - -# Offense count: 3 +# Offense count: 4 # Configuration parameters: AllowedConstants. Style/Documentation: Exclude: - 'spec/**/*' - 'test/**/*' - - 'Rakefile' - - 'lib/bom_builder.rb' - - 'lib/bom_component.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedVars. -Style/FetchEnvVar: - Exclude: - - 'lib/bom_helpers.rb' + - 'lib/cyclonedx/bom_builder.rb' + - 'lib/cyclonedx/bom_component.rb' + - 'lib/cyclonedx/bom_helpers.rb' + - 'lib/cyclonedx/ruby/deprecation.rb' # Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/FileWrite: +Style/MixinUsage: Exclude: - - 'lib/bom_builder.rb' - -# Offense count: 12 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: always, always_true, never -Style/FrozenStringLiteralComment: - Exclude: - - '.simplecov' - - 'Gemfile' - - 'Rakefile' - - 'features/fixtures/simple/Gemfile' - - 'features/step_definitions/json_bom_matching.rb' - - 'features/step_definitions/xml_bom_matching.rb' - - 'features/support/env.rb' - - 'features/support/simplecov_support.rb' - - 'lib/bom_component.rb' - - 'spec/bom_component_spec.rb' - - 'spec/bom_helpers_spec.rb' - - 'spec/spec_helper.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods, AllowedPatterns. -Style/MethodCallWithoutArgsParentheses: - Exclude: - - 'lib/bom_helpers.rb' + - 'lib/cyclonedx_deprecated.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -220,13 +73,13 @@ Style/MethodCallWithoutArgsParentheses: # SupportedStyles: literals, strict Style/MutableConstant: Exclude: - - 'lib/bom_builder.rb' + - 'lib/cyclonedx/bom_builder.rb' # Offense count: 2 Style/OpenStructUse: Exclude: - - 'lib/bom_builder.rb' - - 'spec/bom_component_spec.rb' + - 'lib/cyclonedx/bom_builder.rb' + - 'spec/cyclonedx/bom_component_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -235,19 +88,6 @@ Style/PercentLiteralDelimiters: Exclude: - 'Rakefile' -# Offense count: 19 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: . -# SupportedStyles: same_as_string_literals, single_quotes, double_quotes -Style/QuotedSymbols: - EnforcedStyle: double_quotes - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantBegin: - Exclude: - - 'Rakefile' - # Offense count: 6 # This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpEscape: @@ -255,7 +95,7 @@ Style/RedundantRegexpEscape: - 'features/step_definitions/json_bom_matching.rb' - 'features/step_definitions/xml_bom_matching.rb' -# Offense count: 20 +# Offense count: 41 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes @@ -266,8 +106,6 @@ Style/StringLiterals: - 'cyclonedx-ruby.gemspec' - 'lib/bom_component.rb' - 'lib/bom_helpers.rb' - - 'spec/bom_component_spec.rb' - - 'spec/bom_helpers_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -276,15 +114,9 @@ Style/StringLiterals: Style/SymbolArray: EnforcedStyle: brackets -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -Style/SymbolLiteral: - Exclude: - - 'lib/bom_component.rb' - -# Offense count: 5 +# Offense count: 7 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. +# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: Max: 237 diff --git a/Gemfile b/Gemfile index cb4f27b..ce4a7ff 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ # frozen_string_literal: true -source "https://rubygems.org" +source 'https://rubygems.org' # Specify your gem's dependencies in cyclonedx-ruby.gemspec gemspec diff --git a/bin/console b/bin/console index 61860f8..12ac7a7 100755 --- a/bin/console +++ b/bin/console @@ -1,11 +1,11 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require "bundler/setup" -require "cyclonedx/ruby" +require 'bundler/setup' +require 'cyclonedx/ruby' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. -require "irb" +require 'irb' IRB.start(__FILE__) diff --git a/bin/cucumber b/bin/cucumber index 16c9390..7c35145 100755 --- a/bin/cucumber +++ b/bin/cucumber @@ -8,9 +8,9 @@ # this file is here to facilitate running it. # -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -require "rubygems" -require "bundler/setup" +require 'rubygems' +require 'bundler/setup' -load Gem.bin_path("cucumber", "cucumber") +load Gem.bin_path('cucumber', 'cucumber') diff --git a/bin/rake b/bin/rake index 9efbee9..9510b16 100755 --- a/bin/rake +++ b/bin/rake @@ -8,9 +8,9 @@ # this file is here to facilitate running it. # -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -require "rubygems" -require "bundler/setup" +require 'rubygems' +require 'bundler/setup' -load Gem.bin_path("rake", "rake") +load Gem.bin_path('rake', 'rake') diff --git a/bin/rspec b/bin/rspec index 93e191c..1b35905 100755 --- a/bin/rspec +++ b/bin/rspec @@ -8,9 +8,9 @@ # this file is here to facilitate running it. # -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -require "rubygems" -require "bundler/setup" +require 'rubygems' +require 'bundler/setup' -load Gem.bin_path("rspec-core", "rspec") +load Gem.bin_path('rspec-core', 'rspec') diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index 781520c..fc005ab 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |spec| spec.license = 'Apache-2.0' spec.required_ruby_version = '>= 2.7.0' - spec.metadata["homepage_uri"] = "https://#{spec.name.tr("_", "-")}.galtzo.com/" + spec.metadata["homepage_uri"] = "https://#{spec.name.tr('_', '-')}.galtzo.com/" spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/v#{spec.version}" spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md" spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues" @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| "lib/**/*.rb", "lib/licenses.json", # Signatures - "sig/**/*.rbs", + "sig/**/*.rbs" ] # Automatically included with gem package, no need to list again in files. @@ -39,7 +39,7 @@ Gem::Specification.new do |spec| # Files (alphabetical) "CHANGELOG.md", "LICENSE.txt", - "README.md", + "README.md" ] spec.rdoc_options += [ "--title", @@ -50,7 +50,7 @@ Gem::Specification.new do |spec| "^sig/", "--line-numbers", "--inline-source", - "--quiet", + "--quiet" ] spec.require_paths = ['lib'] spec.bindir = "exe" diff --git a/exe/cyclonedx-ruby b/exe/cyclonedx-ruby index 5316aa5..5ecf025 100755 --- a/exe/cyclonedx-ruby +++ b/exe/cyclonedx-ruby @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -if ENV.fetch("MIMIC_NEXT_MAJOR_VERSION", "false").casecmp?("true") +if ENV.fetch('MIMIC_NEXT_MAJOR_VERSION', 'false').casecmp?('true') require 'cyclonedx/ruby' Cyclonedx::BomBuilder.build(ARGV[0]) else diff --git a/features/fixtures/simple/Gemfile b/features/fixtures/simple/Gemfile index 32e772f..0f7d066 100644 --- a/features/fixtures/simple/Gemfile +++ b/features/fixtures/simple/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' gem 'activesupport', '7.0.4.3' -gem 'concurrent-ruby', '1.2.2' +gem 'concurrent-ruby', '1.2.2' gem 'i18n', '1.12.0' gem 'minitest', '5.18.0' gem 'tzinfo', '2.0.6' diff --git a/lib/bom_builder.rb b/lib/bom_builder.rb index b333660..dae336e 100644 --- a/lib/bom_builder.rb +++ b/lib/bom_builder.rb @@ -22,7 +22,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. # -# frozen_string_literal: true -require_relative "cyclonedx/ruby" -require_relative "cyclonedx_deprecated" +require_relative 'cyclonedx/ruby' +require_relative 'cyclonedx_deprecated' diff --git a/lib/cyclonedx/bom_builder.rb b/lib/cyclonedx/bom_builder.rb index cd7f85b..83d5a89 100644 --- a/lib/cyclonedx/bom_builder.rb +++ b/lib/cyclonedx/bom_builder.rb @@ -30,7 +30,7 @@ def self.build(path) begin @logger.info("Writing BOM to #{@bom_file_path}...") - File.open(@bom_file_path, 'w') { |file| file.write(bom) } + File.write(@bom_file_path, bom) if @options[:verbose] @logger.info("#{@gems.size} gems were written to BOM located at #{@bom_file_path}") @@ -42,8 +42,8 @@ def self.build(path) abort end end - private - def self.setup(path) + + def self.setup(_path) @options = {} OptionParser.new do |opts| opts.banner = 'Usage: cyclonedx-ruby [options]' diff --git a/lib/cyclonedx/bom_component.rb b/lib/cyclonedx/bom_component.rb index 376ccac..672df9c 100644 --- a/lib/cyclonedx/bom_component.rb +++ b/lib/cyclonedx/bom_component.rb @@ -2,8 +2,8 @@ module Cyclonedx class BomComponent - DEFAULT_TYPE = "library".freeze - HASH_ALG = 'SHA-256'.freeze + DEFAULT_TYPE = 'library' + HASH_ALG = 'SHA-256' def initialize(gem) @name = gem['name'] @@ -16,33 +16,32 @@ def initialize(gem) def hash_val component_hash = { - "type": DEFAULT_TYPE, - "name": @name, - "version": @version, - "description": @description, - "purl": @purl, - "hashes": [ - "alg": HASH_ALG, - "content": @hash + type: DEFAULT_TYPE, + name: @name, + version: @version, + description: @description, + purl: @purl, + hashes: [ + alg: HASH_ALG, + content: @hash ] } if @gem['license_id'] - component_hash[:"licenses"] = [ - "license": { - "id": @gem['license_id'] + component_hash[:licenses] = [ + license: { + id: @gem['license_id'] } ] elsif @gem['license_name'] - component_hash[:"licenses"] = [ - "license": { - "name": @gem['license_name'] + component_hash[:licenses] = [ + license: { + name: @gem['license_name'] } ] end [component_hash] - end end end diff --git a/lib/cyclonedx/bom_helpers.rb b/lib/cyclonedx/bom_helpers.rb index 22f9783..2100321 100644 --- a/lib/cyclonedx/bom_helpers.rb +++ b/lib/cyclonedx/bom_helpers.rb @@ -22,7 +22,6 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. # -# frozen_string_literal: true require_relative 'bom_component' @@ -48,15 +47,15 @@ def build_bom(gems, format) def build_json_bom(gems) bom_hash = { - "bomFormat": "CycloneDX", - "specVersion": "1.1", - "serialNumber": random_urn_uuid, - "version": 1, - "components": [] + bomFormat: 'CycloneDX', + specVersion: '1.1', + serialNumber: random_urn_uuid, + version: 1, + components: [] } gems.each do |gem| - bom_hash[:components] += BomComponent.new(gem).hash_val() + bom_hash[:components] += BomComponent.new(gem).hash_val end JSON.pretty_generate(bom_hash) @@ -105,7 +104,7 @@ def build_bom_xml(gems) def get_gem(name, version) url = "https://rubygems.org/api/v1/versions/#{name}.json" begin - RestClient.proxy = ENV['http_proxy'] + RestClient.proxy = ENV.fetch('http_proxy', nil) response = RestClient.get(url) body = JSON.parse(response.body) body.select { |item| item['number'] == version.to_s }.first diff --git a/lib/cyclonedx/ruby.rb b/lib/cyclonedx/ruby.rb index 0060c3b..58bacdd 100644 --- a/lib/cyclonedx/ruby.rb +++ b/lib/cyclonedx/ruby.rb @@ -13,10 +13,10 @@ require 'active_support/core_ext/hash' # This gem -require_relative "ruby/version" -require_relative "bom_helpers" -require_relative "bom_builder" # depends on bom_helpers -require_relative "bom_component" +require_relative 'ruby/version' +require_relative 'bom_helpers' +require_relative 'bom_builder' # depends on bom_helpers +require_relative 'bom_component' module Cyclonedx module Ruby diff --git a/lib/cyclonedx/ruby/version.rb b/lib/cyclonedx/ruby/version.rb index 990fd9a..82d4071 100644 --- a/lib/cyclonedx/ruby/version.rb +++ b/lib/cyclonedx/ruby/version.rb @@ -3,7 +3,7 @@ module Cyclonedx module Ruby module Version - VERSION = "1.2.0" + VERSION = '1.2.0' end VERSION = Version::VERSION # Make VERSION available in traditional way end diff --git a/lib/cyclonedx_deprecated.rb b/lib/cyclonedx_deprecated.rb index a814fa1..0bd0c8b 100644 --- a/lib/cyclonedx_deprecated.rb +++ b/lib/cyclonedx_deprecated.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "cyclonedx/ruby/deprecation" +require_relative 'cyclonedx/ruby/deprecation' # Legacy class name kept for compatibility until v2.0.0 Bombuilder = Cyclonedx::BomBuilder @@ -19,4 +19,4 @@ deprecated_alias :instance, :get_gem, :get_gem, Cyclonedx::BomHelpers # Sanity -raise "Deprecated methods broken" unless purl('activesupport', '7.0.1') == "pkg:gem/activesupport@7.0.1" +raise 'Deprecated methods broken' unless purl('activesupport', '7.0.1') == 'pkg:gem/activesupport@7.0.1' diff --git a/spec/cyclonedx/bom_component_spec.rb b/spec/cyclonedx/bom_component_spec.rb index 0d1ea5f..127572d 100644 --- a/spec/cyclonedx/bom_component_spec.rb +++ b/spec/cyclonedx/bom_component_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples "a valid hash_val result for gem" do +RSpec.shared_examples 'a valid hash_val result for gem' do it { expect(result.count).to eq(1) } it { expect(result[0][:type]).to eq('library') } it { expect(result[0][:name]).to eq(gem.name) } diff --git a/spec/cyclonedx/bom_helpers_spec.rb b/spec/cyclonedx/bom_helpers_spec.rb index c183669..6a58128 100644 --- a/spec/cyclonedx/bom_helpers_spec.rb +++ b/spec/cyclonedx/bom_helpers_spec.rb @@ -2,15 +2,15 @@ RSpec.describe Cyclonedx::BomHelpers do context '#purl' do - context "when legacy method is called" do + context 'when legacy method is called' do it 'builds a purl' do - skip("Deprecated in favor of Cyclonedx::BomHelpers.purl") unless defined?(purl) - expect(purl('activesupport', '7.0.1')).to eq("pkg:gem/activesupport@7.0.1") + skip('Deprecated in favor of Cyclonedx::BomHelpers.purl') unless defined?(purl) + expect(purl('activesupport', '7.0.1')).to eq('pkg:gem/activesupport@7.0.1') end end it 'builds a purl' do - expect(described_class.purl('activesupport', '7.0.1')).to eq("pkg:gem/activesupport@7.0.1") + expect(described_class.purl('activesupport', '7.0.1')).to eq('pkg:gem/activesupport@7.0.1') end end end diff --git a/spec/cyclonedx/ruby_spec.rb b/spec/cyclonedx/ruby_spec.rb index 50e2cb4..b945b9e 100644 --- a/spec/cyclonedx/ruby_spec.rb +++ b/spec/cyclonedx/ruby_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Cyclonedx::Ruby do - it "has a version number" do + it 'has a version number' do expect(Cyclonedx::Ruby::VERSION).not_to be nil end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 660e0a9..5165239 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ RSpec.configure do |config| # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" + config.example_status_persistence_file_path = '.rspec_status' # Disable RSpec exposing methods globally on `Module` and `main` config.disable_monkey_patching! @@ -20,8 +20,8 @@ SimpleCov.start unless ENV.key? 'ARUBA_NO_COVERAGE' end -mimic_next_major = ENV.fetch("MIMIC_NEXT_MAJOR_VERSION", "false") +mimic_next_major = ENV.fetch('MIMIC_NEXT_MAJOR_VERSION', 'false') # Require via legacy path until v2.0.0, and unless testing functionality in preparation for next major release -require 'bom_builder' if mimic_next_major.casecmp?("false") +require 'bom_builder' if mimic_next_major.casecmp?('false') # Modern path is already covered by the legacy path, but doesn't hurt to include it twice -require "cyclonedx/ruby" +require 'cyclonedx/ruby' From 14413f080f692788e8df2f5a0de9dc9159290447 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Thu, 30 Oct 2025 00:57:11 -0600 Subject: [PATCH 10/22] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F=20Signed=20releases?= =?UTF-8?q?,=20checksums=20&=20CONTRIBUTING.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- CHANGELOG.md | 6 +- CONTRIBUTING.md | 151 +++++++++++++++++++++++++++++++++++++++++ Gemfile.lock | 15 ++-- certs/pboling.pem | 27 ++++++++ cyclonedx-ruby.gemspec | 19 ++++++ 5 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 certs/pboling.pem diff --git a/CHANGELOG.md b/CHANGELOG.md index a0a256e..a98dbdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ [![SemVer 2.0.0][📌semver-img]][📌semver] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] -All notable changes to this project will be documented in this file. +All notable changes to this project after v1.1.0 will be documented in this file. The format is based on [Keep a Changelog][📗keep-changelog], and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), @@ -20,12 +20,15 @@ Please file a bug if you notice a violation of semantic versioning. ### Added +- `CONTRIBUTING.md` file to help people find their way to contributing - `CHANGELOG.md` file to document notable changes in keep-a-changelog format - `Cyclonedx::BomHelpers` module to house helper methods, replacing global methods - `Cyclonedx::BomBuilder` class, replacing `Bombuilder` (note the capitalization change) - `Cyclonedx::BomComponent` class, replacing `BomComponent` - `Cyclonedx::Ruby::Version::VERSION` constant to hold the version number (also available as `Cyclonedx::VERSION`) - `Cyclonedx::Ruby::Deprecation` module to help manage deprecations +- dev dependency: `stone_checksums` + - For SHA-256 and SHA-512 checksum generation for each release. ### Changed @@ -66,5 +69,6 @@ Please file a bug if you notice a violation of semantic versioning. - Initial release +[Unreleased]: https://gitlab.com/CycloneDX/cyclonedx-ruby-gem/-/compare/v1.1.0...HEAD [1.1.0]: https://github.com/CycloneDX/cyclonedx-ruby-gem/compare/eecfebe3cb0ce961fef8e424162ac94298f02a9f...v1.1.0 [1.1.0t]: https://github.com/CycloneDX/cyclonedx-ruby-gem/releases/tag/v1.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b4502e5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,151 @@ +# Contributing + +Any contribution is welcome. +Please read the [CycloneDX contributing guidelines](https://github.com/CycloneDX/.github/blob/master/CONTRIBUTING.md) first. + +Pull-requests from forks are welcome. +We love to see your purposed changes, but we also like to discuss things first. Please open a [ticket][📜src-gh] and explain your intended changes to the community. And don't forget to mention that discussion in your pull-request later. +Find the needed basics here: +* [how to fork a repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) +* [how create a pull request from a fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) +* Remember to [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] if you make changes. + +## Setup + +This project uses ruby. Have a recent version installed and setup first. + +To install dev-dependencies and tools: + +```shell +bin/setup +``` + +## Environment Variables for Local Development + +Below are the primary environment variables recognized by stone_checksums (and its integrated tools). Unless otherwise noted, set boolean values to the string "true" to enable. + +General/runtime +- MIMIC_NEXT_MAJOR_VERSION: When set to true, simulates the next major version for testing breaking changes [📌semver-breaking] [📌major-versions-not-sacred] (default: false) +- ARUBA_NO_COVERAGE: Disable SimpleCov coverage in Aruba tests (default: false) + +Releasing and signing +- SKIP_GEM_SIGNING: If set, skip gem signing during build/release +- GEM_CERT_USER: Username for selecting your public cert in `certs/.pem` (defaults to $USER) + +For a quick starting point, this repository’s `.envrc` shows sane defaults, and `.env.local` can override them locally. + +## Testing + +To run all tests + +```console +bundle exec rake test +``` + +Or use the default task, which does the same + +```console +bundle exec rake +``` + +### Spec organization (required) + +- One spec file per class/module. For each class or module under `lib/`, keep all of its unit tests in a single spec file under `spec/` that mirrors the path and file name exactly: `lib/cyclonedx/my_class.rb` -> `spec/cyclonedx/my_class_spec.rb`. +- Exception: Integration specs that intentionally span multiple classes. Place these under `spec/integration/` (or a clearly named integration folder), and do not directly mirror a single class. Name them after the scenario, not a class. + +## Lint It + +Run the linter. + +```console +bundle exec rake rubocop +``` + +### Important: Do not add inline RuboCop disables + +Try not to add `# rubocop:disable ...` / `# rubocop:enable ...` comments to code or specs (except when following the few existing `rubocop:disable` patterns for a rule already being disabled elsewhere in the code). Instead: + +- Prefer configuration-based exclusions when a rule should not apply to certain paths or files (e.g., via `.rubocop.yml`). + - `bundle exec rubocop -a` (preferred) + - `bundle exec rubocop --regenerate-todo` (only when you cannot fix the violations immediately) + +As a general rule, fix style issues rather than ignoring them. For example, our specs should follow RSpec conventions like using `described_class` for the class under test. + +## Sign off your commits + +Please sign off your commits, to show that you agree to publish your changes under the current terms and licenses of the project +, and to indicate agreement with [Developer Certificate of Origin (DCO)](https://developercertificate.org/). + +```shell +git commit --signed-off ... +``` + +## Contributors + +Your picture could be here! + +[![Contributors][🖐contributors-img]][🖐contributors] + +Made with [contributors-img][🖐contrib-rocks]. + +## For Maintainers + +### One-time, Per-maintainer, Setup + +**IMPORTANT**: To sign a build, +a public key for signing gems will need to be picked up by the line in the +`gemspec` defining the `spec.cert_chain` (check the relevant ENV variables there). +All releases after v1.1.0 are signed releases. +See: [RubyGems Security Guide][🔒️rubygems-security-guide] + +NOTE: To build without signing the gem set `SKIP_GEM_SIGNING` to any value in the environment. Only do this for testing. + +### To release a new version: + +#### Automated process + +Coming Soon! + +#### Manual process + +1. Run `bin/setup && bin/rake` as a "test, coverage, & linting" sanity check +2. Update the version number in `version.rb`, and ensure `CHANGELOG.md` reflects changes +3. Run `bin/setup && bin/rake` again as a secondary check, and to update `Gemfile.lock` +4. Run `git commit -am "🔖 Prepare release v"` to commit the changes +5. Run `git push` to trigger the final CI pipeline before release, and merge PRs + - NOTE: Remember to [check the build][🧪build]. +6. Run `export GIT_TRUNK_BRANCH_NAME="$(git remote show origin | grep 'HEAD branch' | cut -d ' ' -f5)" && echo $GIT_TRUNK_BRANCH_NAME` +7. Run `git checkout $GIT_TRUNK_BRANCH_NAME` +8. Run `git pull origin $GIT_TRUNK_BRANCH_NAME` to ensure latest trunk code +9. Optional for older Bundler (< 2.7.0): Set `SOURCE_DATE_EPOCH` so `rake build` and `rake release` use the same timestamp and generate the same checksums + - If your Bundler is >= 2.7.0, you can skip this; builds are reproducible by default. + - Run `export SOURCE_DATE_EPOCH=$EPOCHSECONDS && echo $SOURCE_DATE_EPOCH` + - If the echo above has no output, then it didn't work. + - Note: `zsh/datetime` module is needed, if running `zsh`. + - In older versions of `bash` you can use `date +%s` instead, i.e. `export SOURCE_DATE_EPOCH=$(date +%s) && echo $SOURCE_DATE_EPOCH` +10. Run `bundle exec rake build` +11. Run `bundle exec rake release` which will create a git tag for the version, + push git commits and tags, and push the `.gem` file to the gem host configured in the gemspec. +12. Run `bin/gem_checksums` (more context [1][🔒️rubygems-checksums-pr], [2][🔒️rubygems-guides-pr]) + to create SHA-256 and SHA-512 checksums. This functionality is provided by the `stone_checksums` + [gem][💎stone_checksums]. + - The script automatically commits but does not push the checksums +13. Sanity check the SHA256, comparing with the output from the `bin/gem_checksums` command: + - `sha256sum pkg/-.gem` + +[📜src-gh]: https://github.com/CycloneDX/cyclonedx-ruby-gem +[🧪build]: https://github.com/CycloneDX/cyclonedx-ruby-gem/actions +[🤝conduct]: https://gitlab.com/CycloneDX/cyclonedx-ruby-gem/-/blob/main/CODE_OF_CONDUCT.md +[🖐contrib-rocks]: https://contrib.rocks +[🖐contributors]: https://github.com/CycloneDX/cyclonedx-ruby-gem/graphs/contributors +[🖐contributors-img]: https://contrib.rocks/image?repo=CycloneDX/cyclonedx-ruby-gem +[💎gem-coop]: https://gem.coop +[🔒️rubygems-security-guide]: https://guides.rubygems.org/security/#building-gems +[🔒️rubygems-checksums-pr]: https://github.com/rubygems/rubygems/pull/6022 +[🔒️rubygems-guides-pr]: https://github.com/rubygems/guides/pull/325 +[💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums +[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/ +[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat +[📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139 +[📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html +[🏃‍♂️runner-tool-cache]: https://github.com/ruby/ruby-builder/releases/tag/toolcache diff --git a/Gemfile.lock b/Gemfile.lock index 3e79096..1fe1cca 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,7 +11,7 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (7.2.2.2) + activesupport (7.2.3) base64 benchmark (>= 0.3) bigdecimal @@ -90,13 +90,9 @@ GEM mime-types-data (~> 3.2025, >= 3.2025.0507) mime-types-data (3.2025.0924) mini_mime (1.1.5) - mini_portile2 (2.8.9) minitest (5.26.0) multi_test (1.1.0) netrc (0.11.0) - nokogiri (1.18.10) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) nokogiri (1.18.10-aarch64-linux-gnu) racc (~> 1.4) nokogiri (1.18.10-aarch64-linux-musl) @@ -115,13 +111,13 @@ GEM racc (~> 1.4) ostruct (0.5.5) parallel (1.27.0) - parser (3.3.9.0) + parser (3.3.10.0) ast (~> 2.4.1) racc prism (1.6.0) racc (1.8.1) rainbow (3.1.1) - rake (13.3.0) + rake (13.3.1) regexp_parser (2.11.3) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) @@ -163,6 +159,8 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) + stone_checksums (1.0.3) + version_gem (~> 1.1, >= 1.1.9) sys-uname (1.4.1) ffi (~> 1.1) memoist3 (~> 1.0.0) @@ -172,6 +170,7 @@ GEM unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) + version_gem (1.1.9) PLATFORMS aarch64-linux-gnu @@ -179,7 +178,6 @@ PLATFORMS arm-linux-gnu arm-linux-musl arm64-darwin - ruby x86_64-darwin x86_64-linux-gnu x86_64-linux-musl @@ -192,6 +190,7 @@ DEPENDENCIES rspec (~> 3.12) rubocop (~> 1.54) simplecov (~> 0.22.0) + stone_checksums (~> 1.0, >= 1.0.3) BUNDLED WITH 2.7.2 diff --git a/certs/pboling.pem b/certs/pboling.pem new file mode 100644 index 0000000..d5c7e8b --- /dev/null +++ b/certs/pboling.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl +ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW +A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM +DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy +LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA +uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61 +LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5 +mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN +coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV +FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj +yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1 +to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD +qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj +fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ +HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG +A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD +ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9 +wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR +L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm +GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k +kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq +QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA +0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p +DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt +L9nRqA== +-----END CERTIFICATE----- diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index fc005ab..24fd520 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -14,6 +14,24 @@ Gem::Specification.new do |spec| spec.license = 'Apache-2.0' spec.required_ruby_version = '>= 2.7.0' + # Linux distros often package gems and securely certify them independent + # of the official RubyGem certification process. Allowed via ENV["SKIP_GEM_SIGNING"] + # Ref: https://gitlab.com/ruby-oauth/version_gem/-/issues/3 + # Hence, only enable signing if `SKIP_GEM_SIGNING` is not set in ENV. + # See CONTRIBUTING.md + unless ENV.include?("SKIP_GEM_SIGNING") + user_cert = "certs/#{ENV.fetch("GEM_CERT_USER", ENV["USER"])}.pem" + cert_file_path = File.join(__dir__, user_cert) + cert_chain = cert_file_path.split(",") + cert_chain.select! { |fp| File.exist?(fp) } + if cert_file_path && cert_chain.any? + spec.cert_chain = cert_chain + if $PROGRAM_NAME.end_with?("gem") && ARGV[0] == "build" + spec.signing_key = File.join(Gem.user_home, ".ssh", "gem-private_key.pem") + end + end + end + spec.metadata["homepage_uri"] = "https://#{spec.name.tr('_', '-')}.galtzo.com/" spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/v#{spec.version}" spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md" @@ -68,4 +86,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'aruba', '~> 2.2' spec.add_development_dependency 'simplecov', '~> 0.22.0' spec.add_development_dependency 'rubocop', '~> 1.54' + spec.add_development_dependency 'stone_checksums', '~> 1.0', '>= 1.0.3' end From 9980c192008c044c399a64e8196319989399cbd8 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Thu, 30 Oct 2025 01:22:55 -0600 Subject: [PATCH 11/22] =?UTF-8?q?=F0=9F=91=B7=20Update=20build=20matrix=20?= =?UTF-8?q?(light)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bigger build matrix changes are needed to continue running against older bunlders. - will introduce the appraisal pattern later Signed-off-by: Peter H. Boling --- .github/workflows/ruby.yml | 12 ++++++++---- CHANGELOG.md | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index a1df89e..a575090 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -18,10 +18,14 @@ jobs: fail-fast: false matrix: ruby: - - '3.2.2' - - '3.1.4' - - '3.0.6' - - '2.7.7' + - '3.4.7' + - '3.3.9' + - '3.2.9' +# Not supported by latest bundler. These will be added back to the build matrix later. +# They are not being dropped from support, just from CI testing for now. +# - '3.1.7' +# - '3.0.7' +# - '2.7.8' steps: - uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index a98dbdd..e50ee65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,11 @@ Please file a bug if you notice a violation of semantic versioning. - `Cyclonedx::Ruby::Deprecation` module to help manage deprecations - dev dependency: `stone_checksums` - For SHA-256 and SHA-512 checksum generation for each release. +- signed gem releases + - See: [RubyGems Security Guide][🔒️rubygems-security-guide] +- CI matrix testing on Ruby 3.3, 3.4 + +[🔒️rubygems-security-guide]: https://guides.rubygems.org/security/#building-gems ### Changed From e0de4319fc198e1b6ff43a741a306dae1a30a949 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Thu, 30 Oct 2025 01:25:51 -0600 Subject: [PATCH 12/22] =?UTF-8?q?=F0=9F=94=A8=20binstub:=20gem=5Fchecksums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- bin/gem_checksums | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 bin/gem_checksums diff --git a/bin/gem_checksums b/bin/gem_checksums new file mode 100755 index 0000000..dfefdd3 --- /dev/null +++ b/bin/gem_checksums @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'gem_checksums' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("stone_checksums", "gem_checksums") From 94222963a72b4efa719468b36f2b8876adb4fff9 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 30 Oct 2025 20:57:29 +0100 Subject: [PATCH 13/22] add `NOTICE` to `extra_rdoc_files` Signed-off-by: Jan Kowalleck --- cyclonedx-ruby.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index 24fd520..9076cde 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -57,6 +57,7 @@ Gem::Specification.new do |spec| # Files (alphabetical) "CHANGELOG.md", "LICENSE.txt", + "NOTICE", "README.md" ] spec.rdoc_options += [ From a492361c41a85fa33294c3d1c8af2876ded21e02 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Thu, 30 Oct 2025 14:39:15 -0600 Subject: [PATCH 14/22] =?UTF-8?q?=F0=9F=93=9D=20Use=20hardcoded=20homepage?= =?UTF-8?q?=20roots?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cleanup spec.files Signed-off-by: Peter H. Boling --- cyclonedx-ruby.gemspec | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index 9076cde..e8bfbab 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -32,20 +32,17 @@ Gem::Specification.new do |spec| end end - spec.metadata["homepage_uri"] = "https://#{spec.name.tr('_', '-')}.galtzo.com/" - spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/v#{spec.version}" - spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md" - spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues" + spec.metadata["homepage_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem?tab=readme-ov-file#readme" + spec.metadata["source_code_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/tree/v#{spec.version}" + spec.metadata["changelog_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/blob/v#{spec.version}/CHANGELOG.md" + spec.metadata["bug_tracker_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/issues" spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}" spec.metadata["funding_uri"] = "https://owasp.org/donate/?reponame=www-project-cyclonedx&title=OWASP+CycloneDX" - spec.metadata["wiki_uri"] = "#{spec.homepage}/wiki" spec.metadata["rubygems_mfa_required"] = "true" # Specify which files are part of the released package. spec.files = Dir[ - # - # Executables and tasks - "exe/*", + # Code / tasks / data (NOTE: exe/ is specified via spec.bindir and spec.executables below) "lib/**/*.rb", "lib/licenses.json", # Signatures From d243fe2154eb55573587e46ee1b7c1d5326124a5 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Thu, 30 Oct 2025 15:23:40 -0600 Subject: [PATCH 15/22] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unnecessary=20tes?= =?UTF-8?q?t=20artifacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- .gitignore | 4 ++ bom.json | 108 ----------------------------------------------------- bom.xml | 75 ------------------------------------- 3 files changed, 4 insertions(+), 183 deletions(-) delete mode 100644 bom.json delete mode 100644 bom.xml diff --git a/.gitignore b/.gitignore index 271eebd..9aec75a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,7 @@ Appraisal.*.gemfile.lock # vendor /vendor/ + +# text artifacts +bom.xml +bom.json diff --git a/bom.json b/bom.json deleted file mode 100644 index 50b7098..0000000 --- a/bom.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "bomFormat": "CycloneDX", - "specVersion": "1.1", - "serialNumber": "urn:uuid:d498cdc2-5494-4031-b37d-ff3d10d336bf", - "version": 1, - "components": [ - { - "type": "library", - "name": "activesupport", - "version": "7.0.4.3", - "description": "A toolkit of support libraries and Ruby core extensions extracted from the Rails framework.", - "purl": "pkg:gem/activesupport@7.0.4.3", - "hashes": [ - { - "alg": "SHA-256", - "content": "571ed0fac8510f1fc8a1d66aa070d07ea269913bf9ef50960a8044536358a096" - } - ], - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ] - }, - { - "type": "library", - "name": "concurrent-ruby", - "version": "1.2.2", - "description": "Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.", - "purl": "pkg:gem/concurrent-ruby@1.2.2", - "hashes": [ - { - "alg": "SHA-256", - "content": "3879119b8b75e3b62616acc256c64a134d0b0a7a9a3fcba5a233025bcde22c4f" - } - ], - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ] - }, - { - "type": "library", - "name": "i18n", - "version": "1.12.0", - "description": "New wave Internationalization support for Ruby", - "purl": "pkg:gem/i18n@1.12.0", - "hashes": [ - { - "alg": "SHA-256", - "content": "91e3cc1b97616d308707eedee413d82ee021d751c918661fb82152793e64aced" - } - ], - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ] - }, - { - "type": "library", - "name": "minitest", - "version": "5.18.0", - "description": "minitest provides a complete suite of testing facilities supporting TDD, BDD, mocking, and benchmarking", - "purl": "pkg:gem/minitest@5.18.0", - "hashes": [ - { - "alg": "SHA-256", - "content": "06f43aa0692ce3acf19cb5bc539ad2c6095ca3d2c7e5fbafc58a7d847e898745" - } - ], - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ] - }, - { - "type": "library", - "name": "tzinfo", - "version": "2.0.6", - "description": "Time Zone Library", - "purl": "pkg:gem/tzinfo@2.0.6", - "hashes": [ - { - "alg": "SHA-256", - "content": "8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b" - } - ], - "licenses": [ - { - "license": { - "id": "MIT" - } - } - ] - } - ] -} diff --git a/bom.xml b/bom.xml deleted file mode 100644 index b379102..0000000 --- a/bom.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - activesupport - 7.0.4.3 - A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. - - 571ed0fac8510f1fc8a1d66aa070d07ea269913bf9ef50960a8044536358a096 - - - - MIT - - - pkg:gem/activesupport@7.0.4.3 - - - concurrent-ruby - 1.2.2 - Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns. - - 3879119b8b75e3b62616acc256c64a134d0b0a7a9a3fcba5a233025bcde22c4f - - - - MIT - - - pkg:gem/concurrent-ruby@1.2.2 - - - i18n - 1.12.0 - New wave Internationalization support for Ruby - - 91e3cc1b97616d308707eedee413d82ee021d751c918661fb82152793e64aced - - - - MIT - - - pkg:gem/i18n@1.12.0 - - - minitest - 5.18.0 - minitest provides a complete suite of testing facilities supporting TDD, BDD, mocking, and benchmarking - - 06f43aa0692ce3acf19cb5bc539ad2c6095ca3d2c7e5fbafc58a7d847e898745 - - - - MIT - - - pkg:gem/minitest@5.18.0 - - - tzinfo - 2.0.6 - Time Zone Library - - 8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b - - - - MIT - - - pkg:gem/tzinfo@2.0.6 - - - From a5df2d4cd98410b0bbdf9dc09c5fcf6c8e19895b Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 31 Oct 2025 10:35:05 -0600 Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=94=A5=20Remove=20gem=20release=20s?= =?UTF-8?q?igning=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- .envrc | 30 ++++++++++++++++++++++++++++++ CONTRIBUTING.md | 14 -------------- README.md | 2 +- bin/gem_checksums | 8 ++++---- certs/pboling.pem | 27 --------------------------- cyclonedx-ruby.gemspec | 18 ------------------ 6 files changed, 35 insertions(+), 64 deletions(-) create mode 100755 .envrc delete mode 100644 certs/pboling.pem diff --git a/.envrc b/.envrc new file mode 100755 index 0000000..2ca4328 --- /dev/null +++ b/.envrc @@ -0,0 +1,30 @@ +# Run any command in this library's bin/ without the bin/ prefix! +# Prefer exe version over binstub +PATH_add exe +PATH_add bin + +# Only add things to this file that should be shared with the team. + +# **dotenv** (See end of file for .env.local integration) +# .env would override anything in this file, if enabled. +# .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments. +# Override and customize anything below in your own .env.local +# If you are using dotenv and not direnv, +# copy the following `export` statements to your own .env file. +export MIMIC_NEXT_MAJOR_VERSION=false +export ARUBA_NO_COVERAGE=false + +### General Ruby ### +# Turn off Ruby Warnings about deprecated code +# export RUBYOPT="-W0" + +# Internal Debugging Controls +export DEBUG=false # do not allow byebug statements (override in .env.local) + +# .env would override anything in this file, if `dotenv` is uncommented below. +# .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments, +# and that is why we generally want to leave it commented out. +# dotenv + +# .env.local will override anything in this file. +dotenv_if_exists .env.local diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4502e5..7157a53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,10 +28,6 @@ General/runtime - MIMIC_NEXT_MAJOR_VERSION: When set to true, simulates the next major version for testing breaking changes [📌semver-breaking] [📌major-versions-not-sacred] (default: false) - ARUBA_NO_COVERAGE: Disable SimpleCov coverage in Aruba tests (default: false) -Releasing and signing -- SKIP_GEM_SIGNING: If set, skip gem signing during build/release -- GEM_CERT_USER: Username for selecting your public cert in `certs/.pem` (defaults to $USER) - For a quick starting point, this repository’s `.envrc` shows sane defaults, and `.env.local` can override them locally. ## Testing @@ -90,16 +86,6 @@ Made with [contributors-img][🖐contrib-rocks]. ## For Maintainers -### One-time, Per-maintainer, Setup - -**IMPORTANT**: To sign a build, -a public key for signing gems will need to be picked up by the line in the -`gemspec` defining the `spec.cert_chain` (check the relevant ENV variables there). -All releases after v1.1.0 are signed releases. -See: [RubyGems Security Guide][🔒️rubygems-security-guide] - -NOTE: To build without signing the gem set `SKIP_GEM_SIGNING` to any value in the environment. Only do this for testing. - ### To release a new version: #### Automated process diff --git a/README.md b/README.md index d2f94ec..7f06f1c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ # CycloneDX Ruby Gem -The CycloneDX Ruby Gem creates a valid CycloneDX Software Bill of Materials (SBOM) from all project dependencies. CycloneDX is a lightweight SBOM specification that is easily created, human readable, and simple to parse. +The CycloneDX Ruby Gem creates a valid CycloneDX Software Bill of Materials (SBOM) from all project dependencies. CycloneDX is a lightweight SBOM specification that is easily created, human-readable, and simple to parse. #### Installing from RubyGems diff --git a/bin/gem_checksums b/bin/gem_checksums index dfefdd3..1d625b5 100755 --- a/bin/gem_checksums +++ b/bin/gem_checksums @@ -8,9 +8,9 @@ # this file is here to facilitate running it. # -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -require "rubygems" -require "bundler/setup" +require 'rubygems' +require 'bundler/setup' -load Gem.bin_path("stone_checksums", "gem_checksums") +load Gem.bin_path('stone_checksums', 'gem_checksums') diff --git a/certs/pboling.pem b/certs/pboling.pem deleted file mode 100644 index d5c7e8b..0000000 --- a/certs/pboling.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl -ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW -A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM -DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy -LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA -uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61 -LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5 -mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN -coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV -FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj -yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1 -to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD -qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj -fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ -HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG -A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD -ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9 -wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR -L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm -GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k -kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq -QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA -0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p -DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt -L9nRqA== ------END CERTIFICATE----- diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index e8bfbab..38af530 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -14,24 +14,6 @@ Gem::Specification.new do |spec| spec.license = 'Apache-2.0' spec.required_ruby_version = '>= 2.7.0' - # Linux distros often package gems and securely certify them independent - # of the official RubyGem certification process. Allowed via ENV["SKIP_GEM_SIGNING"] - # Ref: https://gitlab.com/ruby-oauth/version_gem/-/issues/3 - # Hence, only enable signing if `SKIP_GEM_SIGNING` is not set in ENV. - # See CONTRIBUTING.md - unless ENV.include?("SKIP_GEM_SIGNING") - user_cert = "certs/#{ENV.fetch("GEM_CERT_USER", ENV["USER"])}.pem" - cert_file_path = File.join(__dir__, user_cert) - cert_chain = cert_file_path.split(",") - cert_chain.select! { |fp| File.exist?(fp) } - if cert_file_path && cert_chain.any? - spec.cert_chain = cert_chain - if $PROGRAM_NAME.end_with?("gem") && ARGV[0] == "build" - spec.signing_key = File.join(Gem.user_home, ".ssh", "gem-private_key.pem") - end - end - end - spec.metadata["homepage_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem?tab=readme-ov-file#readme" spec.metadata["source_code_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/tree/v#{spec.version}" spec.metadata["changelog_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/blob/v#{spec.version}/CHANGELOG.md" From b2182d3260874d3fbf3604213a87d44d7c333402 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 31 Oct 2025 10:38:29 -0600 Subject: [PATCH 17/22] =?UTF-8?q?=F0=9F=94=A5=20Remove=20documentation=20u?= =?UTF-8?q?ri?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- cyclonedx-ruby.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/cyclonedx-ruby.gemspec b/cyclonedx-ruby.gemspec index 38af530..1395cf4 100644 --- a/cyclonedx-ruby.gemspec +++ b/cyclonedx-ruby.gemspec @@ -18,7 +18,6 @@ Gem::Specification.new do |spec| spec.metadata["source_code_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/tree/v#{spec.version}" spec.metadata["changelog_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/blob/v#{spec.version}/CHANGELOG.md" spec.metadata["bug_tracker_uri"] = "https://github.com/CycloneDX/cyclonedx-ruby-gem/issues" - spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}" spec.metadata["funding_uri"] = "https://owasp.org/donate/?reponame=www-project-cyclonedx&title=OWASP+CycloneDX" spec.metadata["rubygems_mfa_required"] = "true" From a36b45af284f110394a0b8b1b74547017ae1d900 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Mon, 3 Nov 2025 12:25:09 -0700 Subject: [PATCH 18/22] =?UTF-8?q?=F0=9F=90=9B=20Fix=20latent=20reference?= =?UTF-8?q?=20to=20BomComponent=20without=20namespace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- lib/cyclonedx/bom_helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cyclonedx/bom_helpers.rb b/lib/cyclonedx/bom_helpers.rb index 2100321..5f8597d 100644 --- a/lib/cyclonedx/bom_helpers.rb +++ b/lib/cyclonedx/bom_helpers.rb @@ -55,7 +55,7 @@ def build_json_bom(gems) } gems.each do |gem| - bom_hash[:components] += BomComponent.new(gem).hash_val + bom_hash[:components] += Cyclonedx::BomComponent.new(gem).hash_val end JSON.pretty_generate(bom_hash) From 9834ae32edc3b26a1c0491045ad709b1e2791dc8 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Mon, 3 Nov 2025 12:27:49 -0700 Subject: [PATCH 19/22] =?UTF-8?q?=F0=9F=90=9B=20Fix=20latent=20reference?= =?UTF-8?q?=20to=20@logger=20out=20of=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- lib/cyclonedx/bom_builder.rb | 2 +- lib/cyclonedx/bom_helpers.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/cyclonedx/bom_builder.rb b/lib/cyclonedx/bom_builder.rb index 83d5a89..05f1637 100644 --- a/lib/cyclonedx/bom_builder.rb +++ b/lib/cyclonedx/bom_builder.rb @@ -133,7 +133,7 @@ def self.specs_list object.name = dependency.name object.version = dependency.version object.purl = purl(object.name, object.version) - gem = get_gem(object.name, object.version) + gem = get_gem(object.name, object.version, @logger) next if gem.nil? if gem['licenses']&.length&.positive? diff --git a/lib/cyclonedx/bom_helpers.rb b/lib/cyclonedx/bom_helpers.rb index 5f8597d..3985f51 100644 --- a/lib/cyclonedx/bom_helpers.rb +++ b/lib/cyclonedx/bom_helpers.rb @@ -101,7 +101,7 @@ def build_bom_xml(gems) builder.to_xml end - def get_gem(name, version) + def get_gem(name, version, logger) url = "https://rubygems.org/api/v1/versions/#{name}.json" begin RestClient.proxy = ENV.fetch('http_proxy', nil) @@ -109,7 +109,7 @@ def get_gem(name, version) body = JSON.parse(response.body) body.select { |item| item['number'] == version.to_s }.first rescue StandardError - @logger.warn("#{name} couldn't be fetched") + logger.warn("#{name} couldn't be fetched") nil end end From a17b6a3165abbdbdf2ea345a600f7acca4fbf98f Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Mon, 3 Nov 2025 12:34:37 -0700 Subject: [PATCH 20/22] =?UTF-8?q?=F0=9F=93=84=20Add=20back=20license,=20sc?= =?UTF-8?q?oped=20to=20relevant=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- spec/spec_helper.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5165239..a41b751 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,6 +12,8 @@ end end +# Copied from https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/spec/spec_helper.rb +# Licensed under MIT - https://github.com/cucumber/aruba/blob/3b1a6cea6e3ba55370c3396eef0a955aeb40f287/LICENSE unless RUBY_PLATFORM.include?('java') require 'simplecov' SimpleCov.command_name 'RSpec' @@ -19,6 +21,7 @@ # Run simplecov by default SimpleCov.start unless ENV.key? 'ARUBA_NO_COVERAGE' end +# End copied from Aruba mimic_next_major = ENV.fetch('MIMIC_NEXT_MAJOR_VERSION', 'false') # Require via legacy path until v2.0.0, and unless testing functionality in preparation for next major release From ff973618265add96b1d13e196ddfe384af14bcfd Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Mon, 3 Nov 2025 14:43:51 -0700 Subject: [PATCH 21/22] =?UTF-8?q?=F0=9F=90=9B=20fix=20reference=20to=20var?= =?UTF-8?q?iable=20bom=5Fdirectory=20within=20rescue=20block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- lib/cyclonedx/bom_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cyclonedx/bom_builder.rb b/lib/cyclonedx/bom_builder.rb index 05f1637..17c2a25 100644 --- a/lib/cyclonedx/bom_builder.rb +++ b/lib/cyclonedx/bom_builder.rb @@ -24,7 +24,7 @@ def self.build(path) begin FileUtils.mkdir_p(bom_directory) unless File.directory?(bom_directory) rescue StandardError => e - @logger.error("Unable to create the directory to hold the BOM output at #{@bom_directory}. #{e.message}: #{e.backtrace.join('\n')}") + @logger.error("Unable to create the directory to hold the BOM output at #{bom_directory}. #{e.message}: #{e.backtrace.join('\n')}") abort end From 7a55d340b540f27ff09abd6a7d28bc82fcc27ed4 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Mon, 3 Nov 2025 14:51:15 -0700 Subject: [PATCH 22/22] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20fix=20method=20indic?= =?UTF-8?q?ator=20in=20deprecation=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter H. Boling --- lib/cyclonedx/ruby/deprecation.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cyclonedx/ruby/deprecation.rb b/lib/cyclonedx/ruby/deprecation.rb index 55f5e69..e2a9469 100644 --- a/lib/cyclonedx/ruby/deprecation.rb +++ b/lib/cyclonedx/ruby/deprecation.rb @@ -42,12 +42,12 @@ class << self def deprecated_alias(scope, name, replacement, receiver = nil) if scope == :class define_singleton_method(name) do |*args, &block| - warn("Cyclonedx: #{self}.#{name} deprecated (please use ##{replacement})") unless Cyclonedx::Ruby::Deprecation.deprecate_in_silence + warn("Cyclonedx: #{self}.#{name} deprecated (please use .#{replacement})") unless Cyclonedx::Ruby::Deprecation.deprecate_in_silence receiver ? receiver.send(replacement, *args, &block) : send(replacement, *args, &block) end else define_method(name) do |*args, &block| - warn("Cyclonedx: #{self.class}##{name} deprecated (please use ##{receiver ? "#{receiver}.#{replacement}" : replacement})") unless Cyclonedx::Ruby::Deprecation.deprecate_in_silence + warn("Cyclonedx: #{self.class}##{name} deprecated (please use #{receiver ? "#{receiver}.#{replacement}" : replacement})") unless Cyclonedx::Ruby::Deprecation.deprecate_in_silence receiver ? receiver.send(replacement, *args, &block) : send(replacement, *args, &block) end end