From 44057f2cae7e6d85e152596583022e9c40a561aa Mon Sep 17 00:00:00 2001 From: Kyle Hammond Date: Wed, 7 Feb 2024 22:25:21 -0600 Subject: [PATCH 1/7] Auto-corrected most rubocop problems. Some manual corrections. Signed-off-by: Kyle Hammond --- Gemfile | 2 +- Rakefile | 4 +- bin/console | 6 +- cyclonedx-cocoapods.gemspec | 8 +- exe/cyclonedx-cocoapods | 6 +- lib/cyclonedx/cocoapods/component.rb | 17 +- lib/cyclonedx/cocoapods/license.rb | 10 +- lib/cyclonedx/cocoapods/pod.rb | 64 +++--- lib/cyclonedx/cocoapods/pod_attributes.rb | 19 +- lib/cyclonedx/cocoapods/podfile_analyzer.rb | 138 +++++++------ lib/cyclonedx/cocoapods/source.rb | 9 +- spec/cyclonedx/cocoapods/cocoapods_spec.rb | 2 +- spec/cyclonedx/cocoapods/component_spec.rb | 24 ++- spec/cyclonedx/cocoapods/license_spec.rb | 2 +- .../cocoapods/pod_attributes_spec.rb | 31 +-- spec/cyclonedx/cocoapods/pod_spec.rb | 195 +++++++++++------- .../cocoapods/podfile_analyzer_spec.rb | 40 ++-- spec/cyclonedx/cocoapods/source_spec.rb | 15 +- spec/spec_helper.rb | 4 +- 19 files changed, 361 insertions(+), 235 deletions(-) diff --git a/Gemfile b/Gemfile index bb94df8..5f10ba8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ # frozen_string_literal: true -source "https://rubygems.org" +source 'https://rubygems.org' gemspec diff --git a/Rakefile b/Rakefile index b6ae734..82bb534 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ # frozen_string_literal: true -require "bundler/gem_tasks" -require "rspec/core/rake_task" +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) diff --git a/bin/console b/bin/console index cde9df0..b21339b 100755 --- a/bin/console +++ b/bin/console @@ -1,8 +1,8 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require "bundler/setup" -require "cyclonedx/cocoapods" +require 'bundler/setup' +require 'cyclonedx/cocoapods' # 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. @@ -11,5 +11,5 @@ require "cyclonedx/cocoapods" # require "pry" # Pry.start -require "irb" +require 'irb' IRB.start(__FILE__) diff --git a/cyclonedx-cocoapods.gemspec b/cyclonedx-cocoapods.gemspec index cd14c52..864eee2 100644 --- a/cyclonedx-cocoapods.gemspec +++ b/cyclonedx-cocoapods.gemspec @@ -9,7 +9,9 @@ Gem::Specification.new do |spec| spec.email = ['jose.gonzalez@openinput.com', 'kyle.hammond@jamf.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 CocoaPods projects.' + 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 CocoaPods projects.' spec.homepage = 'https://github.com/CycloneDX/cyclonedx-cocoapods' spec.license = 'Apache-2.0' spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0') @@ -18,7 +20,7 @@ Gem::Specification.new do |spec| spec.metadata['source_code_uri'] = 'https://github.com/CycloneDX/cyclonedx-cocoapods.git' # Specify which files should be added to the gem when it is released. - spec.files = Dir['lib/**/*.{rb,json}'] + %w{ exe/cyclonedx-cocoapods README.md CHANGELOG.md LICENSE NOTICE } + spec.files = Dir['lib/**/*.{rb,json}'] + %w[exe/cyclonedx-cocoapods README.md CHANGELOG.md LICENSE NOTICE] spec.bindir = 'exe' spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } @@ -27,7 +29,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'cocoapods', ['>= 1.10.1', '< 2.0'] spec.add_runtime_dependency 'nokogiri', ['>= 1.11.2', '< 2.0'] + spec.add_development_dependency 'equivalent-xml', '~> 0.6.0' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rspec', '~> 3.0' - spec.add_development_dependency 'equivalent-xml', '~> 0.6.0' end diff --git a/exe/cyclonedx-cocoapods b/exe/cyclonedx-cocoapods index b401b97..fe3c405 100755 --- a/exe/cyclonedx-cocoapods +++ b/exe/cyclonedx-cocoapods @@ -1,4 +1,6 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + # # This file is part of CycloneDX CocoaPods # @@ -18,6 +20,6 @@ # Copyright (c) OWASP Foundation. All Rights Reserved. # -require "cyclonedx/cocoapods/cli_runner" +require 'cyclonedx/cocoapods/cli_runner' -CycloneDX::CocoaPods::CLIRunner.new().run +CycloneDX::CocoaPods::CLIRunner.new.run diff --git a/lib/cyclonedx/cocoapods/component.rb b/lib/cyclonedx/cocoapods/component.rb index 82526c7..9f4e447 100644 --- a/lib/cyclonedx/cocoapods/component.rb +++ b/lib/cyclonedx/cocoapods/component.rb @@ -26,13 +26,20 @@ class Component attr_reader :group, :name, :version, :type - def initialize(group: nil, name:, version:, type:) - raise ArgumentError, "Group, if specified, must be non empty" if !group.nil? && group.to_s.strip.empty? - raise ArgumentError, "Name must be non empty" if name.nil? || name.to_s.strip.empty? + def initialize(name:, version:, type:, group: nil) + raise ArgumentError, 'Group, if specified, must be non empty' if !group.nil? && group.to_s.strip.empty? + raise ArgumentError, 'Name must be non empty' if name.nil? || name.to_s.strip.empty? + Gem::Version.new(version) # To check that the version string is well formed - raise ArgumentError, "#{type} is not valid component type (#{VALID_COMPONENT_TYPES.join('|')})" unless VALID_COMPONENT_TYPES.include?(type) + unless VALID_COMPONENT_TYPES.include?(type) + raise ArgumentError, + "#{type} is not valid component type (#{VALID_COMPONENT_TYPES.join('|')})" + end - @group, @name, @version, @type = group, name, version, type + @group = group + @name = name + @version = version + @type = type end end end diff --git a/lib/cyclonedx/cocoapods/license.rb b/lib/cyclonedx/cocoapods/license.rb index 61522b4..737ed8a 100644 --- a/lib/cyclonedx/cocoapods/license.rb +++ b/lib/cyclonedx/cocoapods/license.rb @@ -26,15 +26,13 @@ module CocoaPods class Pod class License SPDX_LICENSES = JSON.parse(File.read("#{__dir__}/spdx-licenses.json")).freeze - IDENTIFIER_TYPES = [:id, :name].freeze + IDENTIFIER_TYPES = %i[id name].freeze - attr_reader :identifier - attr_reader :identifier_type - attr_accessor :text - attr_accessor :url + attr_reader :identifier, :identifier_type + attr_accessor :text, :url def initialize(identifier:) - raise ArgumentError, "License identifier must be non empty" if identifier.nil? || identifier.to_s.strip.empty? + raise ArgumentError, 'License identifier must be non empty' if identifier.nil? || identifier.to_s.strip.empty? @identifier = SPDX_LICENSES.find { |license_id| license_id.downcase == identifier.to_s.downcase } @identifier_type = @identifier.nil? ? :name : :id diff --git a/lib/cyclonedx/cocoapods/pod.rb b/lib/cyclonedx/cocoapods/pod.rb index 94f32c6..90081ac 100644 --- a/lib/cyclonedx/cocoapods/pod.rb +++ b/lib/cyclonedx/cocoapods/pod.rb @@ -25,26 +25,40 @@ module CycloneDX module CocoaPods class Pod - attr_reader :name # xs:normalizedString - attr_reader :version # xs:normalizedString - attr_reader :source # Anything responding to :source_qualifier - attr_reader :homepage # xs:anyURI - https://cyclonedx.org/docs/1.4/#type_externalReference - attr_reader :checksum # https://cyclonedx.org/docs/1.4/#type_hashValue (We only use SHA-1 hashes - length == 40) - attr_reader :author # xs:normalizedString - attr_reader :description # xs:normalizedString - attr_reader :license # https://cyclonedx.org/docs/1.4/#type_licenseType - # We don't currently support several licenses or license expressions https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/ + # xs:normalizedString + attr_reader :name + # xs:normalizedString + attr_reader :version + # Anything responding to :source_qualifier + attr_reader :source + # xs:anyURI - https://cyclonedx.org/docs/1.4/xml/#type_externalReference + attr_reader :homepage + # https://cyclonedx.org/docs/1.4/xml/#type_hashValue (We only use SHA-1 hashes - length == 40) + attr_reader :checksum + # xs:normalizedString + attr_reader :author + # xs:normalizedString + attr_reader :description + # https://cyclonedx.org/docs/1.4/xml/#type_licenseType + # We don't currently support several licenses or license expressions https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/ + attr_reader :license + def initialize(name:, version:, source: nil, checksum: nil) - raise ArgumentError, "Name must be non empty" if name.nil? || name.to_s.empty? + raise ArgumentError, 'Name must be non empty' if name.nil? || name.to_s.empty? raise ArgumentError, "Name shouldn't contain spaces" if name.to_s.include?(' ') raise ArgumentError, "Name shouldn't start with a dot" if name.to_s.start_with?('.') + # `pod create` also enforces no plus sign, but more than 500 public pods have a plus in the root name. # https://github.com/CocoaPods/CocoaPods/blob/9461b346aeb8cba6df71fd4e71661688138ec21b/lib/cocoapods/command/lib/create.rb#L35 Gem::Version.new(version) # To check that the version string is well formed - raise ArgumentError, "Invalid pod source" unless source.nil? || source.respond_to?(:source_qualifier) + raise ArgumentError, 'Invalid pod source' unless source.nil? || source.respond_to?(:source_qualifier) raise ArgumentError, "#{checksum} is not valid SHA-1 hash" unless checksum.nil? || checksum =~ /[a-fA-F0-9]{40}/ - @name, @version, @source, @checksum = name.to_s, version, source, checksum + + @name = name.to_s + @version = version + @source = source + @checksum = checksum end def root_name @@ -61,23 +75,21 @@ def populate(attributes) end def to_s - "Pod<#{name}, #{version.to_s}>" + "Pod<#{name}, #{version}>" end private def populate_author(attributes) authors = attributes[:author] || attributes[:authors] - case authors - when String - @author = authors - when Array - @author = authors.join(', ') - when Hash - @author = authors.map { |name, email| "#{name} <#{email}>" }.join(', ') - else - @author = nil - end + @author = case authors + when String + authors + when Array + authors.join(', ') + when Hash + authors.map { |name, email| "#{name} <#{email}>" }.join(', ') + end end def populate_description(attributes) @@ -91,11 +103,11 @@ def populate_license(attributes) when Hash attributes[:license].transform_keys!(&:to_sym) identifier = attributes[:license][:type] - unless identifier.nil? || identifier.to_s.strip.empty? + if identifier.nil? || identifier.to_s.strip.empty? + @license = nil + else @license = License.new(identifier: identifier) @license.text = attributes[:license][:text] - else - @license = nil end else @license = nil diff --git a/lib/cyclonedx/cocoapods/pod_attributes.rb b/lib/cyclonedx/cocoapods/pod_attributes.rb index ab6ebfd..c01e17f 100644 --- a/lib/cyclonedx/cocoapods/pod_attributes.rb +++ b/lib/cyclonedx/cocoapods/pod_attributes.rb @@ -30,16 +30,26 @@ class CocoaPodsRepository def self.searchable_source(url:, source_manager:) source = CocoaPodsRepository.new(url: url) source.source_manager = source_manager - return source + source end def attributes_for(pod:) specification_sets = @source_manager.search_by_name("^#{Regexp.escape(pod.root_name)}$") - raise SearchError, "No pod found named #{pod.name}; run 'pod repo update' and try again" if specification_sets.length == 0 - raise SearchError, "More than one pod found named #{pod.name}; a pod in a private spec repo should not have the same name as a public pod" if specification_sets.length > 1 + if specification_sets.empty? + raise SearchError, + "No pod found named #{pod.name}; run 'pod repo update' and try again" + end + if specification_sets.length > 1 + raise SearchError, + "More than one pod found named #{pod.name}; a pod in a private spec repo " \ + 'should not have the same name as a public pod' + end paths = specification_sets[0].specification_paths_for_version(pod.version) - raise SearchError, "Version #{pod.version} not found for pod #{pod.name}; run 'pod repo update' and try again" if paths.length == 0 + if paths.empty? + raise SearchError, + "Version #{pod.version} not found for pod #{pod.name}; run 'pod repo update' and try again" + end ::Pod::Specification.from_file(paths[0]).attributes_hash end @@ -64,7 +74,6 @@ def attributes_for(pod:) end end - class Pod def complete_information_from_source populate(source.attributes_for(pod: self)) diff --git a/lib/cyclonedx/cocoapods/podfile_analyzer.rb b/lib/cyclonedx/cocoapods/podfile_analyzer.rb index f6023b4..d03a39b 100644 --- a/lib/cyclonedx/cocoapods/podfile_analyzer.rb +++ b/lib/cyclonedx/cocoapods/podfile_analyzer.rb @@ -46,8 +46,8 @@ def load_plugins(podfile_path) @logger.debug("Loading plugin #{plugin_name}") begin plugin_spec = Gem::Specification.find_by_name(plugin_name) - plugin_spec.activate if plugin_spec - load(plugin_spec.gem_dir + '/lib/cocoapods_plugin.rb') if plugin_spec + plugin_spec&.activate + load("#{plugin_spec.gem_dir}/lib/cocoapods_plugin.rb") if plugin_spec rescue Gem::LoadError => e @logger.warn("Failed to load plugin #{plugin_name}. #{e.message}") end @@ -57,10 +57,19 @@ def load_plugins(podfile_path) def ensure_podfile_and_lock_are_present(options) project_dir = Pathname.new(options[:path] || Dir.pwd) raise PodfileParsingError, "#{options[:path]} is not a valid directory." unless File.directory?(project_dir) - options[:podfile_path] = project_dir + 'Podfile' - raise PodfileParsingError, "Missing Podfile in #{project_dir}. Please use the --path option if not running from the CocoaPods project directory." unless File.exist?(options[:podfile_path]) - options[:podfile_lock_path] = project_dir + 'Podfile.lock' - raise PodfileParsingError, "Missing Podfile.lock, please run 'pod install' before generating BOM" unless File.exist?(options[:podfile_lock_path]) + + options[:podfile_path] = "#{project_dir}Podfile" + unless File.exist?(options[:podfile_path]) + raise PodfileParsingError, + "Missing Podfile in #{project_dir}. Please use the --path option if " \ + 'not running from the CocoaPods project directory.' + end + + options[:podfile_lock_path] = "#{project_dir}Podfile.lock" + unless File.exist?(options[:podfile_lock_path]) + raise PodfileParsingError, + "Missing Podfile.lock, please run 'pod install' before generating BOM" + end initialize_cocoapods_config(project_dir) @@ -68,81 +77,88 @@ def ensure_podfile_and_lock_are_present(options) verify_synced_sandbox(lockfile) load_plugins(options[:podfile_path]) - return ::Pod::Podfile.from_file(options[:podfile_path]), lockfile + [::Pod::Podfile.from_file(options[:podfile_path]), lockfile] end - def parse_pods(podfile, lockfile) @logger.debug "Parsing pods from #{podfile.defined_in_file}" included_pods, dependencies = create_list_of_included_pods(podfile, lockfile) pods = lockfile.pod_names.select { |name| included_pods.include?(name) }.map do |name| - Pod.new(name: name, version: lockfile.version(name), source: source_for_pod(podfile, lockfile, name), checksum: lockfile.checksum(name)) + Pod.new(name: name, version: lockfile.version(name), source: source_for_pod(podfile, lockfile, name), + checksum: lockfile.checksum(name)) end - pod_dependencies = { } - dependencies.each {|key, value| - if lockfile.pod_names.include? key - pod = Pod.new(name: key, version: lockfile.version(key), source: source_for_pod(podfile, lockfile, key), checksum: lockfile.checksum(key)) + pod_dependencies = {} + dependencies.each do |key, value| + next unless lockfile.pod_names.include? key - pod_dependencies[pod.purl] = lockfile.pod_names.select { |name| value.include?(name) }.map do |name| - pod = Pod.new(name: name, version: lockfile.version(name), source: source_for_pod(podfile, lockfile, name), checksum: lockfile.checksum(name)) - pod.purl - end + pod = Pod.new(name: key, version: lockfile.version(key), source: source_for_pod(podfile, lockfile, key), + checksum: lockfile.checksum(key)) + + pod_dependencies[pod.purl] = lockfile.pod_names.select { |name| value.include?(name) }.map do |name| + pod = Pod.new(name: name, + version: lockfile.version(name), + source: source_for_pod(podfile, lockfile, name), + checksum: lockfile.checksum(name)) + pod.purl end - } + end - return pods, pod_dependencies + [pods, pod_dependencies] end - def populate_pods_with_additional_info(pods) pods.each do |pod| @logger.debug "Completing information for #{pod.name}" pod.complete_information_from_source end - return pods + pods end private - def initialize_cocoapods_config(project_dir) ::Pod::Config.instance.installation_root = project_dir end - def verify_synced_sandbox(lockfile) - manifestFile = ::Pod::Config.instance.sandbox.manifest - raise PodfileParsingError, "Missing Manifest.lock, please run 'pod install' before generating BOM" if manifestFile.nil? - raise PodfileParsingError, "The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." unless lockfile == manifestFile + manifest_file = ::Pod::Config.instance.sandbox.manifest + if manifest_file.nil? + raise PodfileParsingError, + "Missing Manifest.lock, please run 'pod install' before generating BOM" + end + return if lockfile == manifest_file + + raise PodfileParsingError, + "The sandbox is not in sync with the Podfile.lock. Run 'pod install' " \ + 'or update your CocoaPods installation.' end def simple_hash_of_lockfile_pods(lockfile) - pods_hash = { } + pods_hash = {} pods_used = lockfile.internal_data['PODS'] - pods_used&.each { |pod| + pods_used&.each do |pod| if pod.is_a?(String) # Pods stored as String have no dependencies pod_name = pod.split.first pods_hash[pod_name] = [] else # Pods stored as a hash have pod name and dependencies. - pod.each { |pod, dependencies| + pod.each do |pod, dependencies| pod_name = pod.split.first pods_hash[pod_name] = dependencies.map { |d| d.split.first } - } + end end - } + end pods_hash end - def append_all_pod_dependencies(pods_used, pods_cache) result = pods_used original_number = 0 - dependencies_hash = { } + dependencies_hash = {} # Loop adding pod dependencies until we are not adding any more dependencies to the result # This brings in all the transitive dependencies of every top level pod. @@ -152,76 +168,80 @@ def append_all_pod_dependencies(pods_used, pods_cache) while result.length != original_number original_number = result.length - pods_used.each { |pod_name| + pods_used.each do |pod_name| if pods_cache.key?(pod_name) result.push(*pods_cache[pod_name]) dependencies_hash[pod_name] = pods_cache[pod_name].empty? ? [] : pods_cache[pod_name] end - } + end result = result.uniq # maybe additional dependency processing needed here??? pods_used = result end - return result, dependencies_hash + [result, dependencies_hash] end def create_list_of_included_pods(podfile, lockfile) pods_cache = simple_hash_of_lockfile_pods(lockfile) - includedTargets = podfile.target_definition_list.select{ |target| include_target_named(target.label) } - includedTargetNames = includedTargets.map { |target| target.label } - @logger.debug "Including all pods for targets: #{includedTargetNames}" + included_targets = podfile.target_definition_list.select { |target| include_target_named(target.label) } + included_target_names = included_targets.map(&:label) + @logger.debug "Including all pods for targets: #{included_target_names}" - topLevelDeps = includedTargets.map(&:dependencies).flatten.uniq - pods_used = topLevelDeps.map(&:name).uniq + top_level_deps = included_targets.map(&:dependencies).flatten.uniq + pods_used = top_level_deps.map(&:name).uniq pods_used, dependencies = append_all_pod_dependencies(pods_used, pods_cache) - return pods_used.sort, dependencies + [pods_used.sort, dependencies] end - def include_target_named(targetname) - !@exclude_test_targets || !targetname.downcase.include?('test') + !@exclude_test_targets || !targetname.downcase.include?('test') end - def cocoapods_repository_source(podfile, lockfile, pod_name) @source_manager ||= create_source_manager(podfile) - return Source::CocoaPodsRepository.searchable_source(url: lockfile.spec_repo(pod_name), source_manager: @source_manager) + Source::CocoaPodsRepository.searchable_source(url: lockfile.spec_repo(pod_name), + source_manager: @source_manager) end - def git_source(lockfile, pod_name) checkout_options = lockfile.checkout_options_for_pod_named(pod_name) url = checkout_options[:git] - [:tag, :branch, :commit].each do |type| - return Source::GitRepository.new(url: url, type: type, label: checkout_options[type]) if checkout_options[type] + %i[tag branch commit].each do |type| + if checkout_options[type] + return Source::GitRepository.new(url: url, type: type, + label: checkout_options[type]) + end end - return Source::GitRepository.new(url: url) + Source::GitRepository.new(url: url) end - def source_for_pod(podfile, lockfile, pod_name) root_name = pod_name.split('/').first return cocoapods_repository_source(podfile, lockfile, root_name) unless lockfile.spec_repo(root_name).nil? return git_source(lockfile, root_name) unless lockfile.checkout_options_for_pod_named(root_name).nil? - return Source::LocalPod.new(path: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path]) if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path] - return Source::Podspec.new(url: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec]) if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec] - return nil - end + if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path] + return Source::LocalPod.new(path: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path]) + end + if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec] + return Source::Podspec.new(url: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec]) + end + nil + end def create_source_manager(podfile) - sourceManager = ::Pod::Source::Manager.new(::Pod::Config::instance.repos_dir) + source_manager = ::Pod::Source::Manager.new(::Pod::Config.instance.repos_dir) @logger.debug "Parsing sources from #{podfile.defined_in_file}" podfile.sources.each do |source| @logger.debug "Ensuring #{source} is available for searches" - sourceManager.find_or_create_source_with_url(source) + source_manager.find_or_create_source_with_url(source) end - @logger.debug "Source manager successfully created with all needed sources" - return sourceManager + @logger.debug 'Source manager successfully created with all needed sources' + source_manager end end end diff --git a/lib/cyclonedx/cocoapods/source.rb b/lib/cyclonedx/cocoapods/source.rb index 654de17..42a3a6f 100644 --- a/lib/cyclonedx/cocoapods/source.rb +++ b/lib/cyclonedx/cocoapods/source.rb @@ -31,13 +31,16 @@ def initialize(url:) end class GitRepository - VALID_TYPES = [:branch, :tag, :commit].freeze + VALID_TYPES = %i[branch tag commit].freeze attr_reader :url, :type, :label def initialize(url:, type: nil, label: nil) - raise ArgumentError, "Invalid checkout information" if !type.nil? && !VALID_TYPES.include?(type) - @url, @type, @label = url, type, label + raise ArgumentError, 'Invalid checkout information' if !type.nil? && !VALID_TYPES.include?(type) + + @url = url + @type = type + @label = label end end diff --git a/spec/cyclonedx/cocoapods/cocoapods_spec.rb b/spec/cyclonedx/cocoapods/cocoapods_spec.rb index f872a7c..6fb53cf 100644 --- a/spec/cyclonedx/cocoapods/cocoapods_spec.rb +++ b/spec/cyclonedx/cocoapods/cocoapods_spec.rb @@ -23,7 +23,7 @@ require 'cyclonedx/cocoapods/cli_runner' RSpec.describe CycloneDX::CocoaPods do - it "has a version number" do + it 'has a version number' do expect(CycloneDX::CocoaPods::VERSION).not_to be nil end end diff --git a/spec/cyclonedx/cocoapods/component_spec.rb b/spec/cyclonedx/cocoapods/component_spec.rb index 582df04..00bfb86 100644 --- a/spec/cyclonedx/cocoapods/component_spec.rb +++ b/spec/cyclonedx/cocoapods/component_spec.rb @@ -31,31 +31,45 @@ context 'with an empty group' do it 'should raise an error' do - expect { described_class.new(group: ' ', name: name, version: version, type: type) }.to raise_error(ArgumentError, "Group, if specified, must be non empty") + expect do + described_class.new(group: ' ', name: name, version: version, + type: type) + end.to raise_error(ArgumentError, 'Group, if specified, must be non empty') end end context 'with a nil name' do it 'should raise an error' do - expect { described_class.new(name: nil, version: version, type: type) }.to raise_error(ArgumentError, "Name must be non empty") + expect do + described_class.new(name: nil, version: version, type: type) + end.to raise_error(ArgumentError, 'Name must be non empty') end end context 'with an empty name' do it 'should raise an error' do - expect { described_class.new(name: ' ', version: version, type: type) }.to raise_error(ArgumentError, "Name must be non empty") + expect do + described_class.new(name: ' ', version: version, + type: type) + end.to raise_error(ArgumentError, 'Name must be non empty') end end context 'with an invalid version' do it 'should raise an error' do - expect { described_class.new(name: name, version: 'not-a-valid.version', type: type) }.to raise_error(ArgumentError, /Malformed version number/) + expect do + described_class.new(name: name, version: 'not-a-valid.version', + type: type) + end.to raise_error(ArgumentError, /Malformed version number/) end end context 'with an invalid type' do it 'should raise an error' do - expect { described_class.new(name: name, version: version, type: 'invalid-type') }.to raise_error(ArgumentError, /is not valid component type/) + expect do + described_class.new(name: name, version: version, + type: 'invalid-type') + end.to raise_error(ArgumentError, /is not valid component type/) end end diff --git a/spec/cyclonedx/cocoapods/license_spec.rb b/spec/cyclonedx/cocoapods/license_spec.rb index 19b965b..0b0fdfa 100644 --- a/spec/cyclonedx/cocoapods/license_spec.rb +++ b/spec/cyclonedx/cocoapods/license_spec.rb @@ -29,7 +29,7 @@ expect { described_class.new(identifier: ' ') }.to raise_error(ArgumentError) end end - + context 'with an identifier included in the SPDX license list (regardless of case)' do it 'should create a license of type id' do existing_license_id = described_class::SPDX_LICENSES.sample diff --git a/spec/cyclonedx/cocoapods/pod_attributes_spec.rb b/spec/cyclonedx/cocoapods/pod_attributes_spec.rb index 0361ffa..8302f00 100644 --- a/spec/cyclonedx/cocoapods/pod_attributes_spec.rb +++ b/spec/cyclonedx/cocoapods/pod_attributes_spec.rb @@ -32,13 +32,13 @@ let(:attributes) { { name: pod.name, version: pod.version } } before(:each) do - @source_manager = double() - @specification_set = double() - @specification = double() + @source_manager = double + @specification_set = double + @specification = double allow(@source_manager).to receive(:search_by_name).and_return([@specification_set]) allow(@specification_set).to receive(:specification_paths_for_version).and_return(paths) - allow(::Pod::Specification).to receive(:from_file).and_return(@specification) + allow(Pod::Specification).to receive(:from_file).and_return(@specification) allow(@specification).to receive(:attributes_hash).and_return(attributes) @source = described_class.searchable_source(url: described_class::CDN_REPOSITORY, source_manager: @source_manager) @@ -58,15 +58,16 @@ @source.attributes_for(pod: pod_with_special_name) end - context 'when the source manager doesn''t find any pod with the provided name' do + context 'when the source manager doesn\'t find any pod with the provided name' do before(:each) do allow(@source_manager).to receive(:search_by_name).and_return([]) end it 'should raise an error' do - expect { + expect do @source.attributes_for(pod: pod) - }.to raise_error(CycloneDX::CocoaPods::SearchError, "No pod found named #{pod.name}; run 'pod repo update' and try again") + end.to raise_error(CycloneDX::CocoaPods::SearchError, + "No pod found named #{pod.name}; run 'pod repo update' and try again") end end @@ -76,28 +77,32 @@ end it 'should raise an error' do - expect { + expect do @source.attributes_for(pod: pod) - }.to raise_error(CycloneDX::CocoaPods::SearchError, "More than one pod found named #{pod.name}; a pod in a private spec repo should not have the same name as a public pod") + end.to raise_error(CycloneDX::CocoaPods::SearchError, + "More than one pod found named #{pod.name}; a pod in a private spec repo " \ + 'should not have the same name as a public pod') end end context 'when the source manager finds exactly one pod with the provided name' do - context 'when the search manager doesn''t find an specification for the provided version' do + context 'when the search manager doesn\'t find an specification for the provided version' do before(:each) do allow(@specification_set).to receive(:specification_paths_for_version).and_return([]) end it 'should raise an error' do - expect { + expect do @source.attributes_for(pod: pod) - }.to raise_error(CycloneDX::CocoaPods::SearchError, "Version #{pod.version} not found for pod #{pod.name}; run 'pod repo update' and try again") + end.to raise_error(CycloneDX::CocoaPods::SearchError, + "Version #{pod.version} not found for pod #{pod.name}; " \ + "run 'pod repo update' and try again") end end context 'when the source manager finds at least a specification for the provided version' do it 'returns the attributes of the first specification' do - expect(::Pod::Specification).to receive(:from_file).with(paths[0]) + expect(Pod::Specification).to receive(:from_file).with(paths[0]) expect(@source.attributes_for(pod: pod)).to eq(attributes) end end diff --git a/spec/cyclonedx/cocoapods/pod_spec.rb b/spec/cyclonedx/cocoapods/pod_spec.rb index 4de3cb4..c35315f 100644 --- a/spec/cyclonedx/cocoapods/pod_spec.rb +++ b/spec/cyclonedx/cocoapods/pod_spec.rb @@ -38,21 +38,27 @@ end context 'with an invalid name' do - let(:invalid_pod_names) { ['NoSpaces AllowedInsideName', ' NoSpacesAllowedOutsideName ', '.Can''tStartWithDot'] } + let(:invalid_pod_names) { ['NoSpaces AllowedInsideName', ' NoSpacesAllowedOutsideName ', '.Can\'tStartWithDot'] } it 'should raise an error' do - invalid_pod_names.each { |pod_name| + invalid_pod_names.each do |pod_name| expect { described_class.new(name: pod_name, version: '1.3.5') }.to raise_error(ArgumentError) - } + end end end context 'with a valid name' do - let(:valid_pod_names) { %w[Alamofire FirebaseAnalytics R.swift Sentry Dèja%Vú Sentry/Core GoogleUtilities/NSData+zlib NSDate+TimeAgo] } - let(:valid_pod_root_names) { %w[Alamofire FirebaseAnalytics R.swift Sentry Dèja%Vú Sentry GoogleUtilities NSDate+TimeAgo] } + let(:valid_pod_names) do + %w[Alamofire FirebaseAnalytics R.swift Sentry Dèja%Vú Sentry/Core GoogleUtilities/NSData+zlib NSDate+TimeAgo] + end + let(:valid_pod_root_names) do + %w[Alamofire FirebaseAnalytics R.swift Sentry Dèja%Vú Sentry GoogleUtilities NSDate+TimeAgo] + end context 'and an invalid version' do it 'should raise an error' do - expect { described_class.new(name: valid_pod_names[0], version: 'this.is-not_A_version') }.to raise_error(ArgumentError) + expect do + described_class.new(name: valid_pod_names[0], version: 'this.is-not_A_version') + end.to raise_error(ArgumentError) end end @@ -60,18 +66,28 @@ let(:valid_versions) { %w[5.0 6.8.3 2.2.0-alpha.372] } let(:valid_pod_names_and_versions) { valid_pod_names.product(valid_versions) } let(:valid_pod_root_names_and_versions) { valid_pod_root_names.product(valid_versions) } - let(:base_purls) { %w[ - pkg:cocoapods/Alamofire@5.0 pkg:cocoapods/Alamofire@6.8.3 pkg:cocoapods/Alamofire@2.2.0-alpha.372 - pkg:cocoapods/FirebaseAnalytics@5.0 pkg:cocoapods/FirebaseAnalytics@6.8.3 pkg:cocoapods/FirebaseAnalytics@2.2.0-alpha.372 - pkg:cocoapods/R.swift@5.0 pkg:cocoapods/R.swift@6.8.3 pkg:cocoapods/R.swift@2.2.0-alpha.372 - pkg:cocoapods/Sentry@5.0 pkg:cocoapods/Sentry@6.8.3 pkg:cocoapods/Sentry@2.2.0-alpha.372 - pkg:cocoapods/D%C3%A8ja%25V%C3%BA@5.0 pkg:cocoapods/D%C3%A8ja%25V%C3%BA@6.8.3 pkg:cocoapods/D%C3%A8ja%25V%C3%BA@2.2.0-alpha.372 - pkg:cocoapods/Sentry@5.0#Core pkg:cocoapods/Sentry@6.8.3#Core pkg:cocoapods/Sentry@2.2.0-alpha.372#Core - pkg:cocoapods/GoogleUtilities@5.0#NSData%2Bzlib pkg:cocoapods/GoogleUtilities@6.8.3#NSData%2Bzlib pkg:cocoapods/GoogleUtilities@2.2.0-alpha.372#NSData%2Bzlib - pkg:cocoapods/NSDate%2BTimeAgo@5.0 pkg:cocoapods/NSDate%2BTimeAgo@6.8.3 pkg:cocoapods/NSDate%2BTimeAgo@2.2.0-alpha.372 - ] } - - shared_examples "valid_pod" do + let(:base_purls) do + %w[ + pkg:cocoapods/Alamofire@5.0 pkg:cocoapods/Alamofire@6.8.3 + pkg:cocoapods/Alamofire@2.2.0-alpha.372 + pkg:cocoapods/FirebaseAnalytics@5.0 pkg:cocoapods/FirebaseAnalytics@6.8.3 + pkg:cocoapods/FirebaseAnalytics@2.2.0-alpha.372 + pkg:cocoapods/R.swift@5.0 pkg:cocoapods/R.swift@6.8.3 + pkg:cocoapods/R.swift@2.2.0-alpha.372 + pkg:cocoapods/Sentry@5.0 pkg:cocoapods/Sentry@6.8.3 + pkg:cocoapods/Sentry@2.2.0-alpha.372 + pkg:cocoapods/D%C3%A8ja%25V%C3%BA@5.0 pkg:cocoapods/D%C3%A8ja%25V%C3%BA@6.8.3 + pkg:cocoapods/D%C3%A8ja%25V%C3%BA@2.2.0-alpha.372 + pkg:cocoapods/Sentry@5.0#Core pkg:cocoapods/Sentry@6.8.3#Core + pkg:cocoapods/Sentry@2.2.0-alpha.372#Core + pkg:cocoapods/GoogleUtilities@5.0#NSData%2Bzlib pkg:cocoapods/GoogleUtilities@6.8.3#NSData%2Bzlib + pkg:cocoapods/GoogleUtilities@2.2.0-alpha.372#NSData%2Bzlib + pkg:cocoapods/NSDate%2BTimeAgo@5.0 pkg:cocoapods/NSDate%2BTimeAgo@6.8.3 + pkg:cocoapods/NSDate%2BTimeAgo@2.2.0-alpha.372 + ] + end + + shared_examples 'valid_pod' do it 'should properly build the pod' do expect(valid_pods.map(&:name)).to eq(valid_pod_names_and_versions.map { |name, _| name }) expect(valid_pods.map(&:version)).to eq(valid_pod_names_and_versions.map { |_, version| version }) @@ -89,24 +105,33 @@ end end - shared_examples "pod_with_checksum" do + shared_examples 'pod_with_checksum' do context 'without checksum' do let(:checksum) { nil } - let(:valid_pods) { valid_pod_names_and_versions.map { |name, version| described_class.new(name: name, version: version, source: source) } } - it_behaves_like "valid_pod" + let(:valid_pods) do + valid_pod_names_and_versions.map do |name, version| + described_class.new(name: name, version: version, source: source) + end + end + it_behaves_like 'valid_pod' end context 'with a valid checksum' do let(:checksum) { '9a8ccc3a24b87624f4b40883adab3d98a9fdc00d' } - let(:valid_pods) { valid_pod_names_and_versions.map { |name, version| described_class.new(name: name, version: version, source: source, checksum: checksum) } } - it_behaves_like "valid_pod" + let(:valid_pods) do + valid_pod_names_and_versions.map do |name, version| + described_class.new(name: name, version: version, source: source, checksum: checksum) + end + end + it_behaves_like 'valid_pod' end context 'with an invalid checksum' do it 'should raise an error' do - expect { - described_class.new(name: valid_pod_names.sample, version: valid_versions.sample, checksum: 'not-a-valid-checksum') - }.to raise_error(ArgumentError) + expect do + described_class.new(name: valid_pod_names.sample, version: valid_versions.sample, + checksum: 'not-a-valid-checksum') + end.to raise_error(ArgumentError) end end end @@ -114,7 +139,7 @@ context 'without source' do let(:source) { nil } let(:expected_purls) { base_purls } - it_behaves_like "pod_with_checksum" + it_behaves_like 'pod_with_checksum' end context 'with source' do @@ -122,58 +147,86 @@ context 'from the legacy repository' do let(:source) { CycloneDX::CocoaPods::Source::CocoaPodsRepository.new(url: 'https://github.com/CocoaPods/Specs.git') } let(:expected_purls) { base_purls } - it_behaves_like "pod_with_checksum" + it_behaves_like 'pod_with_checksum' end context 'from the CDN repository' do let(:source) { CycloneDX::CocoaPods::Source::CocoaPodsRepository.new(url: 'trunk') } let(:expected_purls) { base_purls } - it_behaves_like "pod_with_checksum" + it_behaves_like 'pod_with_checksum' end context 'from an alternative repository' do let(:source) { CycloneDX::CocoaPods::Source::CocoaPodsRepository.new(url: 'https://dl.cloudsmith.io/public/owner/repository/cocoapods/index.git') } - let(:expected_purls) { base_purls.map { |purl| purl.insert(purl.index('#') || -1, "?repository_url=#{CGI.escape(source.url)}" ) } } - it_behaves_like "pod_with_checksum" + let(:expected_purls) do + base_purls.map do |purl| + purl.insert(purl.index('#') || -1, "?repository_url=#{CGI.escape(source.url)}") + end + end + it_behaves_like 'pod_with_checksum' end end context 'from a git repository' do context 'with just a URL' do let(:source) { CycloneDX::CocoaPods::Source::GitRepository.new(url: 'https://github.com/gowalla/AFNetworking.git') } - let(:expected_purls) { base_purls.map { |purl| purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}" ) } } - it_behaves_like "pod_with_checksum" + let(:expected_purls) do + base_purls.map do |purl| + purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}") + end + end + it_behaves_like 'pod_with_checksum' end context 'with a tag' do let(:source) { CycloneDX::CocoaPods::Source::GitRepository.new(url: 'https://github.com/gowalla/AFNetworking.git', type: :tag, label: '0.7.0') } - let(:expected_purls) { base_purls.map { |purl| purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}%40#{CGI.escape(source.label)}" ) } } - it_behaves_like "pod_with_checksum" + let(:expected_purls) do + base_purls.map do |purl| + purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}%40#{CGI.escape(source.label)}") + end + end + it_behaves_like 'pod_with_checksum' end context 'with a branch' do let(:source) { CycloneDX::CocoaPods::Source::GitRepository.new(url: 'https://github.com/gowalla/AFNetworking.git', type: :branch, label: 'dev') } - let(:expected_purls) { base_purls.map { |purl| purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}%40#{CGI.escape(source.label)}" ) } } - it_behaves_like "pod_with_checksum" + let(:expected_purls) do + base_purls.map do |purl| + purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}%40#{CGI.escape(source.label)}") + end + end + it_behaves_like 'pod_with_checksum' end context 'with a commit' do let(:source) { CycloneDX::CocoaPods::Source::GitRepository.new(url: 'https://github.com/gowalla/AFNetworking.git', type: :commit, label: '082f8319af') } - let(:expected_purls) { base_purls.map { |purl| purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}%40#{CGI.escape(source.label)}" ) } } - it_behaves_like "pod_with_checksum" + let(:expected_purls) do + base_purls.map do |purl| + purl.insert(purl.index('#') || -1, "?vcs_url=#{CGI.escape(source.url)}%40#{CGI.escape(source.label)}") + end + end + it_behaves_like 'pod_with_checksum' end end context 'from local disk' do let(:source) { CycloneDX::CocoaPods::Source::LocalPod.new(path: '~/Documents/AFNetworking') } - let(:expected_purls) { base_purls.map { |purl| purl.insert(purl.index('#') || -1, "?file_name=#{CGI.escape(source.path)}" ) } } - it_behaves_like "pod_with_checksum" + let(:expected_purls) do + base_purls.map do |purl| + purl.insert(purl.index('#') || -1, "?file_name=#{CGI.escape(source.path)}") + end + end + it_behaves_like 'pod_with_checksum' end context 'from other location' do let(:source) { CycloneDX::CocoaPods::Source::Podspec.new(url: 'https://example.com/JSONKit.podspec') } - let(:expected_purls) { base_purls.map { |purl| purl.insert(purl.index('#') || -1, "?download_url=#{CGI.escape(source.url)}" ) } } - it_behaves_like "pod_with_checksum" + let(:expected_purls) do + base_purls.map do |purl| + purl.insert(purl.index('#') || -1, "?download_url=#{CGI.escape(source.url)}") + end + end + it_behaves_like 'pod_with_checksum' end end end @@ -183,10 +236,12 @@ context 'when populating a pod with attributes' do let(:author) { 'Darth Vader' } let(:author_list) { ['Darth Vader', 'Wookiee'] } - let(:author_hash) { { - 'Darth Vader' => 'darthvader@darkside.com', - 'Wookiee' => 'wookiee@aggrrttaaggrrt.com' - } } + let(:author_hash) do + { + 'Darth Vader' => 'darthvader@darkside.com', + 'Wookiee' => 'wookiee@aggrrttaaggrrt.com' + } + end let(:summary) { 'Elegant HTTP Networking in Swift' } let(:description) { 'Alamofire provides an elegant and composable interface to HTTP network requests.' } @@ -233,46 +288,46 @@ context 'when the attributes hash contains an author' do context 'and a list of authors' do - it 'should populate the pod''s author with the author from the attributes' do + it 'should populate the pod\'s author with the author from the attributes' do pod.populate(author: author, authors: author_list) expect(pod.author).to eq(author) end end context 'and a hash of authors' do - it 'should populate the pod''s author with the author from the attributes' do + it 'should populate the pod\'s author with the author from the attributes' do pod.populate(author: author, authors: author_hash) expect(pod.author).to eq(author) end end end - context 'when the attributes hash doesn''t contain an author' do + context 'when the attributes hash doesn\'t contain an author' do context 'and contains a list of authors' do - it 'should populate the pod''s author with the author list from the attributes' do + it 'should populate the pod\'s author with the author list from the attributes' do pod.populate(authors: author_list) expect(pod.author).to eq(author_list.join(', ')) end end context 'and a hash of authors' do - it 'should populate the pod''s author with the author from the attributes' do + it 'should populate the pod\'s author with the author from the attributes' do pod.populate(authors: author_hash) - expect(pod.author).to eq(author_hash.map { |name, email| "#{name} <#{email}>"}.join(', ')) + expect(pod.author).to eq(author_hash.map { |name, email| "#{name} <#{email}>" }.join(', ')) end end end context 'when the attributes hash contains a summary' do context 'and a description' do - it 'should populate the pod''s description with the description from the attributes' do + it 'should populate the pod\'s description with the description from the attributes' do pod.populate(summary: summary, description: description) expect(pod.description).to eq(description) end end context 'and no description' do - it 'should populate the pod''s description with the summary from the attributes' do + it 'should populate the pod\'s description with the summary from the attributes' do pod.populate(summary: summary) expect(pod.description).to eq(summary) end @@ -281,7 +336,7 @@ context 'when the attributes hash contains a license' do context 'as hash without type' do - let(:license) { { :file => 'MIT-LICENSE.txt' } } + let(:license) { { file: 'MIT-LICENSE.txt' } } it 'should set the license to nil' do pod.populate(license: license) @@ -305,7 +360,7 @@ context 'as hash' do it 'should accept both symbols and strings as attribute names' do - pod.populate(license: { :type => 'MIT' }) + pod.populate(license: { type: 'MIT' }) expect(pod.license).not_to be_nil expect(pod.license.identifier).to eq('MIT') expect(pod.license.identifier_type).to eq(:id) @@ -317,7 +372,7 @@ end context 'with file' do - let(:license) { { :type => 'MIT', :file => 'MIT-LICENSE.txt' } } + let(:license) { { type: 'MIT', file: 'MIT-LICENSE.txt' } } it 'should set a license with id' do pod.populate(license: license) @@ -330,14 +385,14 @@ end context 'with text' do - let(:license) { - { :type => 'MIT', - :text => <<-LICENSE + let(:license) do + { type: 'MIT', + text: <<-LICENSE Copyright 2012 Permission is granted to... LICENSE } - } + end it 'should set a license with id' do pod.populate(license: license) @@ -351,7 +406,7 @@ end end - context 'which doesn''t exist' do + context 'which doesn\'t exist' do context 'as text' do let(:license) { 'Custom license' } @@ -367,7 +422,7 @@ context 'as hash' do it 'should accept both symbols and strings as attribute names' do - pod.populate(license: { :type => 'Custom license' }) + pod.populate(license: { type: 'Custom license' }) expect(pod.license).not_to be_nil expect(pod.license.identifier).to eq('Custom license') expect(pod.license.identifier_type).to eq(:name) @@ -379,7 +434,7 @@ end context 'with file' do - let(:license) { { :type => 'Custom license', :file => 'LICENSE.txt' } } + let(:license) { { type: 'Custom license', file: 'LICENSE.txt' } } it 'should set a license with name' do pod.populate(license: license) @@ -392,14 +447,14 @@ end context 'with text' do - let(:license) { - { :type => 'Custom license', - :text => <<-LICENSE + let(:license) do + { type: 'Custom license', + text: <<-LICENSE Copyright 2012 Permission is granted to... LICENSE } - } + end it 'should set a license with name' do pod.populate(license: license) @@ -415,7 +470,7 @@ end context 'when the attributes hash contains a homepage' do - it 'should populate the pod''s homepage with the homepage from the attributes' do + it 'should populate the pod\'s homepage with the homepage from the attributes' do pod.populate(homepage: homepage) expect(pod.homepage).to eq(homepage) end diff --git a/spec/cyclonedx/cocoapods/podfile_analyzer_spec.rb b/spec/cyclonedx/cocoapods/podfile_analyzer_spec.rb index ccc89a1..55a39e2 100644 --- a/spec/cyclonedx/cocoapods/podfile_analyzer_spec.rb +++ b/spec/cyclonedx/cocoapods/podfile_analyzer_spec.rb @@ -22,13 +22,13 @@ require 'cyclonedx/cocoapods/podfile_analyzer' require 'rspec' RSpec.describe CycloneDX::CocoaPods::PodfileAnalyzer do - let(:fixtures) { Pathname.new(File.expand_path('../../../fixtures/', __FILE__)) } + let(:fixtures) { Pathname.new(File.expand_path('../../fixtures', __dir__)) } let(:empty_podfile) { 'EmptyPodfile/Podfile' } let(:simple_pod) { 'SimplePod/Podfile' } let(:restricted_pod) { 'RestrictedPod/Podfile' } let(:tests_pod) { 'TestingPod/Podfile' } - ::Pod::Config.instance.installation_root = File.expand_path('../../../fixtures/', __FILE__) + '/' + Pod::Config.instance.installation_root = "#{File.expand_path('../../fixtures', __dir__)}/" before(:each) do @log = StringIO.new @@ -38,11 +38,11 @@ context 'parsing pods' do context 'when created with standard parameters' do it 'should handle no pods correctly' do - analyzer = ::CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) + analyzer = CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) - pod_file = ::Pod::Podfile.from_file(fixtures + empty_podfile) + pod_file = Pod::Podfile.from_file(fixtures + empty_podfile) expect(pod_file).not_to be_nil - lock_file = ::Pod::Lockfile.from_file(fixtures + (empty_podfile + '.lock')) + lock_file = Pod::Lockfile.from_file(fixtures + "#{empty_podfile}.lock") expect(lock_file).not_to be_nil included_pods, dependencies = analyzer.parse_pods(pod_file, lock_file) @@ -54,11 +54,11 @@ end it 'should find all simple pods' do - analyzer = ::CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) + analyzer = CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) - pod_file = ::Pod::Podfile.from_file(fixtures + simple_pod) + pod_file = Pod::Podfile.from_file(fixtures + simple_pod) expect(pod_file).not_to be_nil - lock_file = ::Pod::Lockfile.from_file(fixtures + (simple_pod + '.lock')) + lock_file = Pod::Lockfile.from_file(fixtures + "#{simple_pod}.lock") expect(lock_file).not_to be_nil included_pods, dependencies = analyzer.parse_pods(pod_file, lock_file) @@ -74,11 +74,11 @@ end it 'should find all pods actually used' do - analyzer = ::CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) + analyzer = CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) - pod_file = ::Pod::Podfile.from_file(fixtures + restricted_pod) + pod_file = Pod::Podfile.from_file(fixtures + restricted_pod) expect(pod_file).not_to be_nil - lock_file = ::Pod::Lockfile.from_file(fixtures + (restricted_pod + '.lock')) + lock_file = Pod::Lockfile.from_file(fixtures + "#{restricted_pod}.lock") expect(lock_file).not_to be_nil included_pods, dependencies = analyzer.parse_pods(pod_file, lock_file) @@ -90,11 +90,11 @@ end it 'should find all pods' do - analyzer = ::CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) + analyzer = CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger) - pod_file = ::Pod::Podfile.from_file(fixtures + tests_pod) + pod_file = Pod::Podfile.from_file(fixtures + tests_pod) expect(pod_file).not_to be_nil - lock_file = ::Pod::Lockfile.from_file(fixtures + (tests_pod + '.lock')) + lock_file = Pod::Lockfile.from_file(fixtures + "#{tests_pod}.lock") expect(lock_file).not_to be_nil included_pods, dependencies = analyzer.parse_pods(pod_file, lock_file) @@ -112,11 +112,11 @@ context 'when configured to exclude test pods' do it 'should find all simple pods' do - analyzer = ::CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger, exclude_test_targets: true) + analyzer = CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger, exclude_test_targets: true) - pod_file = ::Pod::Podfile.from_file(fixtures + simple_pod) + pod_file = Pod::Podfile.from_file(fixtures + simple_pod) expect(pod_file).not_to be_nil - lock_file = ::Pod::Lockfile.from_file(fixtures + (simple_pod + '.lock')) + lock_file = Pod::Lockfile.from_file(fixtures + "#{simple_pod}.lock") expect(lock_file).not_to be_nil included_pods, dependencies = analyzer.parse_pods(pod_file, lock_file) @@ -132,11 +132,11 @@ end it 'should not include testing pods' do - analyzer = ::CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger, exclude_test_targets: true) + analyzer = CycloneDX::CocoaPods::PodfileAnalyzer.new(logger: @logger, exclude_test_targets: true) - pod_file = ::Pod::Podfile.from_file(fixtures + tests_pod) + pod_file = Pod::Podfile.from_file(fixtures + tests_pod) expect(pod_file).not_to be_nil - lock_file = ::Pod::Lockfile.from_file(fixtures + (tests_pod + '.lock')) + lock_file = Pod::Lockfile.from_file(fixtures + "#{tests_pod}.lock") expect(lock_file).not_to be_nil included_pods, dependencies = analyzer.parse_pods(pod_file, lock_file) diff --git a/spec/cyclonedx/cocoapods/source_spec.rb b/spec/cyclonedx/cocoapods/source_spec.rb index a8cb675..c3db5e6 100644 --- a/spec/cyclonedx/cocoapods/source_spec.rb +++ b/spec/cyclonedx/cocoapods/source_spec.rb @@ -26,7 +26,7 @@ context 'when created with the legacy repository URL' do let(:url) { 'https://github.com/CocoaPods/Specs.git' } - it 'shouldn''t generate any source qualifier' do + it 'shouldn\'t generate any source qualifier' do expect(described_class.new(url: url).source_qualifier).to eq({}) end end @@ -34,7 +34,7 @@ context 'when created with the trunk value' do let(:url) { 'trunk' } - it 'shouldn''t generate any source qualifier' do + it 'shouldn\'t generate any source qualifier' do expect(described_class.new(url: url).source_qualifier).to eq({}) end end @@ -48,7 +48,6 @@ end end - RSpec.describe CycloneDX::CocoaPods::Source::GitRepository do let(:url) { 'https://github.com/gowalla/AFNetworking.git' } @@ -70,7 +69,8 @@ let(:branch) { 'dev' } it 'should generate a proper source qualifier' do - expect(described_class.new(url: url, label: branch, type: :branch).source_qualifier).to eq({ vcs_url: "#{url}@#{branch}" }) + expect(described_class.new(url: url, label: branch, + type: :branch).source_qualifier).to eq({ vcs_url: "#{url}@#{branch}" }) end end @@ -78,23 +78,22 @@ let(:commit) { '082f8319af' } it 'should generate a proper source qualifier' do - expect(described_class.new(url: url, label: commit, type: :commit).source_qualifier).to eq({ vcs_url: "#{url}@#{commit}" }) + expect(described_class.new(url: url, label: commit, + type: :commit).source_qualifier).to eq({ vcs_url: "#{url}@#{commit}" }) end end end - RSpec.describe CycloneDX::CocoaPods::Source::LocalPod do context 'when created with a local path' do let(:path) { '~/Documents/AFNetworking' } it 'should generate a proper source qualifier' do - expect(described_class.new(path: path).source_qualifier).to eq({ file_name: "#{path}"}) + expect(described_class.new(path: path).source_qualifier).to eq({ file_name: path.to_s }) end end end - RSpec.describe CycloneDX::CocoaPods::Source::Podspec do context 'when created with a public URL' do let(:url) { 'https://example.com/JSONKit.podspec' } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 124165b..7bfd574 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,11 +19,11 @@ # Copyright (c) OWASP Foundation. All Rights Reserved. # -require "cyclonedx/cocoapods/cli_runner" +require 'cyclonedx/cocoapods/cli_runner' 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! From d02898a22300a13ad21010089d15915d361e0db5 Mon Sep 17 00:00:00 2001 From: Kyle Hammond Date: Wed, 7 Feb 2024 22:46:16 -0600 Subject: [PATCH 2/7] Fix problem with string concatenation vs string interpolation with paths. Signed-off-by: Kyle Hammond --- lib/cyclonedx/cocoapods/podfile_analyzer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cyclonedx/cocoapods/podfile_analyzer.rb b/lib/cyclonedx/cocoapods/podfile_analyzer.rb index d03a39b..05f735d 100644 --- a/lib/cyclonedx/cocoapods/podfile_analyzer.rb +++ b/lib/cyclonedx/cocoapods/podfile_analyzer.rb @@ -58,14 +58,14 @@ def ensure_podfile_and_lock_are_present(options) project_dir = Pathname.new(options[:path] || Dir.pwd) raise PodfileParsingError, "#{options[:path]} is not a valid directory." unless File.directory?(project_dir) - options[:podfile_path] = "#{project_dir}Podfile" + options[:podfile_path] = project_dir + 'Podfile' unless File.exist?(options[:podfile_path]) raise PodfileParsingError, "Missing Podfile in #{project_dir}. Please use the --path option if " \ 'not running from the CocoaPods project directory.' end - options[:podfile_lock_path] = "#{project_dir}Podfile.lock" + options[:podfile_lock_path] = project_dir + 'Podfile.lock' unless File.exist?(options[:podfile_lock_path]) raise PodfileParsingError, "Missing Podfile.lock, please run 'pod install' before generating BOM" From 544f95c947a8ba2092c0701f5c8bbb0d122200cc Mon Sep 17 00:00:00 2001 From: Kyle Hammond Date: Wed, 7 Feb 2024 23:37:23 -0600 Subject: [PATCH 3/7] Breaking up long functions into shorter ones for easier readability. Signed-off-by: Kyle Hammond --- .rubocop.yml | 4 +- lib/cyclonedx/cocoapods/cli_runner.rb | 18 ++- lib/cyclonedx/cocoapods/pod.rb | 20 +-- lib/cyclonedx/cocoapods/pod_attributes.rb | 24 ++-- lib/cyclonedx/cocoapods/podfile_analyzer.rb | 133 +++++++++++--------- 5 files changed, 118 insertions(+), 81 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 067dccf..93ca7fa 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -35,6 +35,6 @@ Metrics/BlockLength: # Allow some long methods because breaking them up doesn't help anything. Metrics/MethodLength: - AllowedMethods: ['parse_options', 'add_to_bom'] + AllowedMethods: ['parse_options', 'add_to_bom', 'append_all_pod_dependencies'] Metrics/AbcSize: - AllowedMethods: ['parse_options', 'add_to_bom'] + AllowedMethods: ['parse_options', 'add_to_bom', 'source_for_pod'] diff --git a/lib/cyclonedx/cocoapods/cli_runner.rb b/lib/cyclonedx/cocoapods/cli_runner.rb index e11343f..869cadf 100644 --- a/lib/cyclonedx/cocoapods/cli_runner.rb +++ b/lib/cyclonedx/cocoapods/cli_runner.rb @@ -159,6 +159,17 @@ def setup_logger(verbose: true) end def write_bom_to_file(bom:, options:) + bom_file_path = prep_for_bom_write(options) + + begin + File.write(bom_file_path, bom) + @logger.info "BOM written to #{bom_file_path}" + rescue StandardError + raise BOMOutputError, "Unable to write the BOM to #{bom_file_path}" + end + end + + def prep_for_bom_write(options) bom_file_path = Pathname.new(options[:bom_file_path] || './bom.xml').expand_path bom_dir = bom_file_path.dirname @@ -168,12 +179,7 @@ def write_bom_to_file(bom:, options:) raise BOMOutputError, "Unable to create the BOM output directory at #{bom_dir}" end - begin - File.write(bom_file_path, bom) - @logger.info "BOM written to #{bom_file_path}" - rescue StandardError - raise BOMOutputError, "Unable to write the BOM to #{bom_file_path}" - end + bom_file_path end end end diff --git a/lib/cyclonedx/cocoapods/pod.rb b/lib/cyclonedx/cocoapods/pod.rb index 90081ac..77717ac 100644 --- a/lib/cyclonedx/cocoapods/pod.rb +++ b/lib/cyclonedx/cocoapods/pod.rb @@ -101,19 +101,23 @@ def populate_license(attributes) when String @license = License.new(identifier: attributes[:license]) when Hash - attributes[:license].transform_keys!(&:to_sym) - identifier = attributes[:license][:type] - if identifier.nil? || identifier.to_s.strip.empty? - @license = nil - else - @license = License.new(identifier: identifier) - @license.text = attributes[:license][:text] - end + populate_hashed_license(attributes) else @license = nil end end + def populate_hashed_license(attributes) + attributes[:license].transform_keys!(&:to_sym) + identifier = attributes[:license][:type] + if identifier.nil? || identifier.to_s.strip.empty? + @license = nil + else + @license = License.new(identifier: identifier) + @license.text = attributes[:license][:text] + end + end + def populate_homepage(attributes) @homepage = attributes[:homepage] end diff --git a/lib/cyclonedx/cocoapods/pod_attributes.rb b/lib/cyclonedx/cocoapods/pod_attributes.rb index c01e17f..ad21177 100644 --- a/lib/cyclonedx/cocoapods/pod_attributes.rb +++ b/lib/cyclonedx/cocoapods/pod_attributes.rb @@ -35,15 +35,7 @@ def self.searchable_source(url:, source_manager:) def attributes_for(pod:) specification_sets = @source_manager.search_by_name("^#{Regexp.escape(pod.root_name)}$") - if specification_sets.empty? - raise SearchError, - "No pod found named #{pod.name}; run 'pod repo update' and try again" - end - if specification_sets.length > 1 - raise SearchError, - "More than one pod found named #{pod.name}; a pod in a private spec repo " \ - 'should not have the same name as a public pod' - end + validate_spec_sets(specification_sets, pod) paths = specification_sets[0].specification_paths_for_version(pod.version) if paths.empty? @@ -53,6 +45,20 @@ def attributes_for(pod:) ::Pod::Specification.from_file(paths[0]).attributes_hash end + + private + + def validate_spec_sets(specification_sets, pod) + if specification_sets.empty? + raise SearchError, + "No pod found named #{pod.name}; run 'pod repo update' and try again" + end + return unless specification_sets.length > 1 + + raise SearchError, + "More than one pod found named #{pod.name}; a pod in a private spec repo " \ + 'should not have the same name as a public pod' + end end class GitRepository diff --git a/lib/cyclonedx/cocoapods/podfile_analyzer.rb b/lib/cyclonedx/cocoapods/podfile_analyzer.rb index 05f735d..c294826 100644 --- a/lib/cyclonedx/cocoapods/podfile_analyzer.rb +++ b/lib/cyclonedx/cocoapods/podfile_analyzer.rb @@ -31,45 +31,17 @@ module CycloneDX module CocoaPods class PodfileParsingError < StandardError; end + # Uses cocoapods to analyze the Podfile and Podfile.lock for component dependency information class PodfileAnalyzer def initialize(logger:, exclude_test_targets: false) @logger = logger @exclude_test_targets = exclude_test_targets end - def load_plugins(podfile_path) - podfile_contents = File.read(podfile_path) - plugin_syntax = /\s*plugin\s+['"]([^'"]+)['"]/ - plugin_names = podfile_contents.scan(plugin_syntax).flatten - - plugin_names.each do |plugin_name| - @logger.debug("Loading plugin #{plugin_name}") - begin - plugin_spec = Gem::Specification.find_by_name(plugin_name) - plugin_spec&.activate - load("#{plugin_spec.gem_dir}/lib/cocoapods_plugin.rb") if plugin_spec - rescue Gem::LoadError => e - @logger.warn("Failed to load plugin #{plugin_name}. #{e.message}") - end - end - end - def ensure_podfile_and_lock_are_present(options) project_dir = Pathname.new(options[:path] || Dir.pwd) - raise PodfileParsingError, "#{options[:path]} is not a valid directory." unless File.directory?(project_dir) - - options[:podfile_path] = project_dir + 'Podfile' - unless File.exist?(options[:podfile_path]) - raise PodfileParsingError, - "Missing Podfile in #{project_dir}. Please use the --path option if " \ - 'not running from the CocoaPods project directory.' - end - options[:podfile_lock_path] = project_dir + 'Podfile.lock' - unless File.exist?(options[:podfile_lock_path]) - raise PodfileParsingError, - "Missing Podfile.lock, please run 'pod install' before generating BOM" - end + validate_options(project_dir, options) initialize_cocoapods_config(project_dir) @@ -89,21 +61,7 @@ def parse_pods(podfile, lockfile) checksum: lockfile.checksum(name)) end - pod_dependencies = {} - dependencies.each do |key, value| - next unless lockfile.pod_names.include? key - - pod = Pod.new(name: key, version: lockfile.version(key), source: source_for_pod(podfile, lockfile, key), - checksum: lockfile.checksum(key)) - - pod_dependencies[pod.purl] = lockfile.pod_names.select { |name| value.include?(name) }.map do |name| - pod = Pod.new(name: name, - version: lockfile.version(name), - source: source_for_pod(podfile, lockfile, name), - checksum: lockfile.checksum(name)) - pod.purl - end - end + pod_dependencies = parse_dependencies(dependencies, podfile, lockfile) [pods, pod_dependencies] end @@ -118,6 +76,66 @@ def populate_pods_with_additional_info(pods) private + def load_plugins(podfile_path) + podfile_contents = File.read(podfile_path) + plugin_syntax = /\s*plugin\s+['"]([^'"]+)['"]/ + plugin_names = podfile_contents.scan(plugin_syntax).flatten + + plugin_names.each do |plugin_name| + load_one_plugin(plugin_name) + end + end + + def load_one_plugin(plugin_name) + @logger.debug("Loading plugin #{plugin_name}") + begin + plugin_spec = Gem::Specification.find_by_name(plugin_name) + plugin_spec&.activate + load("#{plugin_spec.gem_dir}/lib/cocoapods_plugin.rb") if plugin_spec + rescue Gem::LoadError => e + @logger.warn("Failed to load plugin #{plugin_name}. #{e.message}") + end + end + + def validate_options(project_dir, options) + raise PodfileParsingError, "#{options[:path]} is not a valid directory." unless File.directory?(project_dir) + + options[:podfile_path] = project_dir + 'Podfile' + unless File.exist?(options[:podfile_path]) + raise PodfileParsingError, "Missing Podfile in #{project_dir}. Please use the --path option if " \ + 'not running from the CocoaPods project directory.' + end + + options[:podfile_lock_path] = project_dir + 'Podfile.lock' + return if File.exist?(options[:podfile_lock_path]) + + raise PodfileParsingError, "Missing Podfile.lock, please run 'pod install' before generating BOM" + end + + def parse_dependencies(dependencies, podfile, lockfile) + pod_dependencies = {} + dependencies.each do |key, podname_array| + next unless lockfile.pod_names.include? key + + pod = Pod.new(name: key, version: lockfile.version(key), source: source_for_pod(podfile, lockfile, key), + checksum: lockfile.checksum(key)) + + pod_dependencies[pod.purl] = dependencies_for_pod(podname_array, podfile, lockfile) + end + + pod_dependencies + end + + def dependencies_for_pod(podname_array, podfile, lockfile) + lockfile.pod_names.select { |name| podname_array.include?(name) }.map do |name| + pod = Pod.new(name: name, + version: lockfile.version(name), + source: source_for_pod(podfile, lockfile, name), + checksum: lockfile.checksum(name)) + pod.purl + end + end + def initialize_cocoapods_config(project_dir) ::Pod::Config.instance.installation_root = project_dir end @@ -140,19 +158,23 @@ def simple_hash_of_lockfile_pods(lockfile) pods_used = lockfile.internal_data['PODS'] pods_used&.each do |pod| - if pod.is_a?(String) - # Pods stored as String have no dependencies + map_single_pod(pod, pods_hash) + end + pods_hash + end + + def map_single_pod(pod, pods_hash) + if pod.is_a?(String) + # Pods stored as String have no dependencies + pod_name = pod.split.first + pods_hash[pod_name] = [] + else + # Pods stored as a hash have pod name and dependencies. + pod.each do |pod, dependencies| pod_name = pod.split.first - pods_hash[pod_name] = [] - else - # Pods stored as a hash have pod name and dependencies. - pod.each do |pod, dependencies| - pod_name = pod.split.first - pods_hash[pod_name] = dependencies.map { |d| d.split.first } - end + pods_hash[pod_name] = dependencies.map { |d| d.split.first } end end - pods_hash end def append_all_pod_dependencies(pods_used, pods_cache) @@ -176,7 +198,6 @@ def append_all_pod_dependencies(pods_used, pods_cache) end result = result.uniq - # maybe additional dependency processing needed here??? pods_used = result end From fbbe11df450003c3aaf1a9b1fec2241f4e717b4f Mon Sep 17 00:00:00 2001 From: Kyle Hammond Date: Wed, 7 Feb 2024 23:54:15 -0600 Subject: [PATCH 4/7] Update license checker to v0.5 and let it know that the exe/cyclonedx-cocoapods file is ruby. Signed-off-by: Kyle Hammond --- .github/workflows/ruby.yml | 2 +- .licenserc.yml | 4 ++++ lib/cyclonedx/cocoapods/component.rb | 3 +-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index b6b3430..5c89dd4 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -36,7 +36,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Check license headers - uses: apache/skywalking-eyes@v0.4.0 + uses: apache/skywalking-eyes@v0.5.0 with: config: .licenserc.yml diff --git a/.licenserc.yml b/.licenserc.yml index 990cc3e..4aa8435 100644 --- a/.licenserc.yml +++ b/.licenserc.yml @@ -34,3 +34,7 @@ header: - "spec/fixtures/**" - "LICENSE" - "NOTICE" + language: + Ruby: + filenames: + - "cyclonedx-cocoapods" diff --git a/lib/cyclonedx/cocoapods/component.rb b/lib/cyclonedx/cocoapods/component.rb index 9f4e447..f5cc4ef 100644 --- a/lib/cyclonedx/cocoapods/component.rb +++ b/lib/cyclonedx/cocoapods/component.rb @@ -32,8 +32,7 @@ def initialize(name:, version:, type:, group: nil) Gem::Version.new(version) # To check that the version string is well formed unless VALID_COMPONENT_TYPES.include?(type) - raise ArgumentError, - "#{type} is not valid component type (#{VALID_COMPONENT_TYPES.join('|')})" + raise ArgumentError, "#{type} is not valid component type (#{VALID_COMPONENT_TYPES.join('|')})" end @group = group From a1039dddd13e2864ec9d0823a88b6eaddd2508a4 Mon Sep 17 00:00:00 2001 From: Kyle Hammond Date: Thu, 8 Feb 2024 00:05:07 -0600 Subject: [PATCH 5/7] Explicitly declare all file types for license checks. Signed-off-by: Kyle Hammond --- .licenserc.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.licenserc.yml b/.licenserc.yml index 4aa8435..97bf416 100644 --- a/.licenserc.yml +++ b/.licenserc.yml @@ -35,6 +35,17 @@ header: - "LICENSE" - "NOTICE" language: + Ignore List: + extensions: + - ".gitignore" + comment_style_id: Hashtag Ruby: + extensions: + - ".rb" filenames: - "cyclonedx-cocoapods" + comment_style_id: Hashtag + YAML: + extensions: + - ".yml" + comment_style_id: Hashtag From 1cc401600f670e2604bff4e055b6d8fcbd148b27 Mon Sep 17 00:00:00 2001 From: Kyle Hammond Date: Thu, 8 Feb 2024 00:08:09 -0600 Subject: [PATCH 6/7] Let automatic detection do it's thing for license checking. Signed-off-by: Kyle Hammond --- .licenserc.yml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.licenserc.yml b/.licenserc.yml index 97bf416..d6acafa 100644 --- a/.licenserc.yml +++ b/.licenserc.yml @@ -34,18 +34,4 @@ header: - "spec/fixtures/**" - "LICENSE" - "NOTICE" - language: - Ignore List: - extensions: - - ".gitignore" - comment_style_id: Hashtag - Ruby: - extensions: - - ".rb" - filenames: - - "cyclonedx-cocoapods" - comment_style_id: Hashtag - YAML: - extensions: - - ".yml" - comment_style_id: Hashtag + - "exe/cyclonedx-cocoapods" From d61d75c32d46d0c85f5f37c866e2668765cd36ea Mon Sep 17 00:00:00 2001 From: Kyle Hammond Date: Thu, 8 Feb 2024 00:10:18 -0600 Subject: [PATCH 7/7] Revert to v0.4.0 of license checker. Signed-off-by: Kyle Hammond --- .github/workflows/ruby.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 5c89dd4..b6b3430 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -36,7 +36,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Check license headers - uses: apache/skywalking-eyes@v0.5.0 + uses: apache/skywalking-eyes@v0.4.0 with: config: .licenserc.yml