cyclonedx-cocoapods 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e656666b5b0297c98ddebe571a31e68ada3ff7fa9a048fc10f4f824fee1982c8
4
- data.tar.gz: d398292e90265bf460874a2db759fb20a7d596067c4aa35afdb07d942aead192
3
+ metadata.gz: a3c7ebcac0eb8ea04cf5b1aed4da3bdbe1cadc2acb33e1bc4ebec9f52ac9bad9
4
+ data.tar.gz: bdc83e96c9b4750bf2135a85b30d384ae2096e4c0591d3db2f65434506f4c220
5
5
  SHA512:
6
- metadata.gz: 1d7e28f6e783cc643d6563671412747c57ed8f26c49fdb34df1bc708662e6369027444f3ef2a4b0be84e1429538d12f80017a7ea61be0b5612210ae272ee00fa
7
- data.tar.gz: 2e99db6f7a0f502190ace2511e954b6c43439eb21b5b13509165590ea3075b30ea8ee9fdb7f88a5986ae767c487abb941d6d5548e91448edc11784a377dec25b
6
+ metadata.gz: 94cdbbcb32c6d83dad78c383536dc484f08a040f5104318a3c2355b070fb915474e25027188bcaa8dfffc4222772525e4e051dfc8a1d673a08a31cd51f4d3cc2
7
+ data.tar.gz: 1c0da4d2d356eb06207f889605e3c65ea0594821b7de130ebe5ce3d182917f87415fdc615d727f54f540b5a3c9c2b58d915117627b7b71c7a115045e799bc0c4
data/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.1.0]
8
+
9
+ - Can now eliminate Podfile targets that include "test" in their name ([Issue #43](https://github.com/CycloneDX/cyclonedx-cocoapods/issues/43)) [@macblazer](https://github.com/macblazer).
10
+
7
11
  ## [1.0.0]
8
12
 
9
13
  ### Added
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  # CycloneDX CocoaPods (Objective-C/Swift)
11
11
 
12
- The CycloneDX CocoaPods Gem creates a valid CycloneDX software bill-of-material document from all project dependencies. CycloneDX is a lightweight BoM specification that is easily created, human readable, and simple to parse.
12
+ The CycloneDX CocoaPods Gem creates a valid CycloneDX software bill-of-material document from all [CocoaPods](https://cocoapods.org/) project dependencies. CycloneDX is a lightweight BoM specification that is easily created, human readable, and simple to parse.
13
13
 
14
14
  ## Installation
15
15
 
@@ -36,18 +36,30 @@ Building from source requires Ruby 2.4.0 or newer.
36
36
  You can use the [CycloneDX CLI](https://github.com/CycloneDX/cyclonedx-cli#convert-command) to convert between multiple BOM formats or specification versions.
37
37
 
38
38
  ## Usage
39
- Usage: `cyclonedx-cocoapods` [options]
40
-
41
- --[no-]verbose Run verbosely
42
- -p, --path path (Optional) Path to CocoaPods project directory, current directory if missing
43
- -o, --output bom_file_path (Optional) Path to output the bom.xml file to
44
- -b, --bom-version bom_version (Optional) Version of the generated BOM, 1 if not provided
45
- -g, --group group (Optional) Group of the component for which the BOM is generated
46
- -n, --name name (Optional, if specified version and type are also required) Name of the component for which the BOM is generated
47
- -v, --version version (Optional) Version of the component for which the BOM is generated
48
- -t, --type type (Optional) Type of the component for which the BOM is generated (one of application|framework|library|container|operating-system|device|firmware|file)
39
+ ```
40
+ Generates a BOM with the given parameters. BOM component metadata is only generated if the component's name, version, and type are provided using the --name, --version, and --type parameters.
41
+ [version <version_number>]
42
+
43
+ USAGE
44
+ cyclonedx-cocoapods [options]
45
+
46
+ OPTIONS
47
+ --[no-]verbose Show verbose debugging output
49
48
  -h, --help Show help message
50
49
 
50
+ BOM Generation
51
+ -p, --path path Path to CocoaPods project directory (default: current directory)
52
+ -o, --output bom_file_path Path to output the bom.xml file to (default: "bom.xml")
53
+ -b, --bom-version bom_version Version of the generated BOM (default: "1")
54
+ -x, --exclude-test-targets Eliminate Podfile targets whose name contains the word "test"
55
+
56
+ Component Metadata
57
+ -n, --name name (If specified version and type are also required) Name of the component for which the BOM is generated
58
+ -v, --version version Version of the component for which the BOM is generated
59
+ -t, --type type Type of the component for which the BOM is generated (one of application|framework|library|container|operating-system|device|firmware|file)
60
+ -g, --group group Group of the component for which the BOM is generated
61
+ ```
62
+
51
63
  **Output:** BoM file at specified location, `./bom.xml` if not specified
52
64
 
53
65
  ### Example
@@ -18,19 +18,15 @@
18
18
  # Copyright (c) OWASP Foundation. All Rights Reserved.
19
19
  #
20
20
 
21
- require 'optparse'
22
21
  require 'logger'
23
- require 'cocoapods'
22
+ require 'optparse'
24
23
 
25
- require_relative 'component'
26
- require_relative 'pod'
27
- require_relative 'pod_attributes'
28
- require_relative 'source'
29
24
  require_relative 'bom_builder'
25
+ require_relative 'component'
26
+ require_relative 'podfile_analyzer'
30
27
 
31
28
  module CycloneDX
32
29
  module CocoaPods
33
- class PodfileParsingError < StandardError; end
34
30
  class BOMOutputError < StandardError; end
35
31
 
36
32
  class CLIRunner
@@ -41,10 +37,10 @@ module CycloneDX
41
37
  setup_logger(verbose: options[:verbose])
42
38
  @logger.debug "Running cyclonedx-cocoapods with options: #{options}"
43
39
 
44
- podfile, lockfile = ensure_podfile_and_lock_are_present(options)
45
- pods = parse_pods(podfile, lockfile)
46
-
47
- populate_pods_with_additional_info(pods)
40
+ analyzer = PodfileAnalyzer.new(logger: @logger, exclude_test_targets: options[:exclude_test_targets])
41
+ podfile, lockfile = analyzer.ensure_podfile_and_lock_are_present(options)
42
+ pods = analyzer.parse_pods(podfile, lockfile)
43
+ analyzer.populate_pods_with_additional_info(pods)
48
44
 
49
45
  bom = BOMBuilder.new(component: component_from_options(options), pods: pods).bom(version: options[:bom_version] || 1)
50
46
  write_bom_to_file(bom: bom, options: options)
@@ -57,34 +53,48 @@ module CycloneDX
57
53
 
58
54
  private
59
55
 
56
+
60
57
  def parseOptions
61
58
  parsedOptions = {}
62
59
  component_types = Component::VALID_COMPONENT_TYPES
63
60
  OptionParser.new do |options|
64
61
  options.banner = <<~BANNER
65
- Usage: cyclonedx-cocoapods [options]
66
- Generates a BOM with the given parameters. BOM component metadata is only generated if the component's name and version are provided using the --name and --version parameters.
62
+ Generates a BOM with the given parameters. BOM component metadata is only generated if the component's name, version, and type are provided using the --name, --version, and --type parameters.
63
+ [version #{CycloneDX::CocoaPods::VERSION}]
64
+
65
+ USAGE
66
+ cyclonedx-cocoapods [options]
67
+
68
+ OPTIONS
67
69
  BANNER
68
70
 
69
- options.on('--[no-]verbose', 'Run verbosely') do |v|
71
+ options.on('--[no-]verbose', 'Show verbose debugging output') do |v|
70
72
  parsedOptions[:verbose] = v
71
73
  end
72
- options.on('-p', '--path path', '(Optional) Path to CocoaPods project directory, current directory if missing') do |path|
74
+ options.on('-h', '--help', 'Show help message') do
75
+ puts options
76
+ exit
77
+ end
78
+
79
+ options.separator("\n BOM Generation")
80
+ options.on('-p', '--path path', 'Path to CocoaPods project directory (default: current directory)') do |path|
73
81
  parsedOptions[:path] = path
74
82
  end
75
- options.on('-o', '--output bom_file_path', '(Optional) Path to output the bom.xml file to') do |bom_file_path|
83
+ options.on('-o', '--output bom_file_path', 'Path to output the bom.xml file to (default: "bom.xml")') do |bom_file_path|
76
84
  parsedOptions[:bom_file_path] = bom_file_path
77
85
  end
78
- options.on('-b', '--bom-version bom_version', Integer, '(Optional) Version of the generated BOM, 1 if not provided') do |version|
86
+ options.on('-b', '--bom-version bom_version', Integer, 'Version of the generated BOM (default: "1")') do |version|
79
87
  parsedOptions[:bom_version] = version
80
88
  end
81
- options.on('-g', '--group group', '(Optional) Group of the component for which the BOM is generated') do |group|
82
- parsedOptions[:group] = group
89
+ options.on('-x', '--exclude-test-targets', 'Eliminate Podfile targets whose name contains the word "test"') do |exclude|
90
+ parsedOptions[:exclude_test_targets] = exclude
83
91
  end
84
- options.on('-n', '--name name', '(Optional, if specified version and type are also required) Name of the component for which the BOM is generated') do |name|
92
+
93
+ options.separator("\n Component Metadata\n")
94
+ options.on('-n', '--name name', '(If specified version and type are also required) Name of the component for which the BOM is generated') do |name|
85
95
  parsedOptions[:name] = name
86
96
  end
87
- options.on('-v', '--version version', '(Optional) Version of the component for which the BOM is generated') do |version|
97
+ options.on('-v', '--version version', 'Version of the component for which the BOM is generated') do |version|
88
98
  begin
89
99
  Gem::Version.new(version)
90
100
  parsedOptions[:version] = version
@@ -92,13 +102,12 @@ module CycloneDX
92
102
  raise OptionParser::InvalidArgument, e.message
93
103
  end
94
104
  end
95
- options.on('-t', '--type type', "(Optional) Type of the component for which the BOM is generated (one of #{component_types.join('|')})") do |type|
105
+ options.on('-t', '--type type', "Type of the component for which the BOM is generated (one of #{component_types.join('|')})") do |type|
96
106
  raise OptionParser::InvalidArgument, "Invalid value for component's type (#{type}). It must be one of #{component_types.join('|')}" unless component_types.include?(type)
97
107
  parsedOptions[:type] = type
98
108
  end
99
- options.on_tail('-h', '--help', 'Show help message') do
100
- puts options
101
- exit
109
+ options.on('-g', '--group group', 'Group of the component for which the BOM is generated') do |group|
110
+ parsedOptions[:group] = group
102
111
  end
103
112
  end.parse!
104
113
 
@@ -118,90 +127,6 @@ module CycloneDX
118
127
  end
119
128
 
120
129
 
121
- def ensure_podfile_and_lock_are_present(options)
122
- project_dir = Pathname.new(options[:path] || Dir.pwd)
123
- raise PodfileParsingError, "#{options[:path]} is not a valid directory." unless File.directory?(project_dir)
124
- options[:podfile_path] = project_dir + 'Podfile'
125
- 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])
126
- options[:podfile_lock_path] = project_dir + 'Podfile.lock'
127
- raise PodfileParsingError, "Missing Podfile.lock, please run 'pod install' before generating BOM" unless File.exist?(options[:podfile_lock_path])
128
-
129
- initialize_cocoapods_config(project_dir)
130
-
131
- lockfile = ::Pod::Lockfile.from_file(options[:podfile_lock_path])
132
- verify_synced_sandbox(lockfile)
133
-
134
- return ::Pod::Podfile.from_file(options[:podfile_path]), lockfile
135
- end
136
-
137
-
138
- def initialize_cocoapods_config(project_dir)
139
- ::Pod::Config.instance.installation_root = project_dir
140
- end
141
-
142
-
143
- def verify_synced_sandbox(lockfile)
144
- manifestFile = ::Pod::Config.instance.sandbox.manifest
145
- raise PodfileParsingError, "Missing Manifest.lock, please run 'pod install' before generating BOM" if manifestFile.nil?
146
- raise PodfileParsingError, "The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." unless lockfile == manifestFile
147
- end
148
-
149
-
150
- def cocoapods_repository_source(podfile, lockfile, pod_name)
151
- @source_manager ||= create_source_manager(podfile)
152
- return Source::CocoaPodsRepository.searchable_source(url: lockfile.spec_repo(pod_name), source_manager: @source_manager)
153
- end
154
-
155
-
156
- def git_source(lockfile, pod_name)
157
- checkout_options = lockfile.checkout_options_for_pod_named(pod_name)
158
- url = checkout_options[:git]
159
- [:tag, :branch, :commit].each do |type|
160
- return Source::GitRepository.new(url: url, type: type, label: checkout_options[type]) if checkout_options[type]
161
- end
162
- return Source::GitRepository.new(url: url)
163
- end
164
-
165
-
166
- def source_for_pod(podfile, lockfile, pod_name)
167
- root_name = pod_name.split('/').first
168
- return cocoapods_repository_source(podfile, lockfile, root_name) unless lockfile.spec_repo(root_name).nil?
169
- return git_source(lockfile, root_name) unless lockfile.checkout_options_for_pod_named(root_name).nil?
170
- return Source::LocalPod.new(path: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path]) if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path]
171
- return Source::Podspec.new(url: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec]) if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec]
172
- return nil
173
- end
174
-
175
-
176
- def parse_pods(podfile, lockfile)
177
- @logger.debug "Parsing pods from #{podfile.defined_in_file}"
178
- return lockfile.pod_names.map do |name|
179
- Pod.new(name: name, version: lockfile.version(name), source: source_for_pod(podfile, lockfile, name), checksum: lockfile.checksum(name))
180
- end
181
- end
182
-
183
-
184
- def create_source_manager(podfile)
185
- sourceManager = ::Pod::Source::Manager.new(::Pod::Config::instance.repos_dir)
186
- @logger.debug "Parsing sources from #{podfile.defined_in_file}"
187
- podfile.sources.each do |source|
188
- @logger.debug "Ensuring #{source} is available for searches"
189
- sourceManager.find_or_create_source_with_url(source)
190
- end
191
- @logger.debug "Source manager successfully created with all needed sources"
192
- return sourceManager
193
- end
194
-
195
-
196
- def populate_pods_with_additional_info(pods)
197
- pods.each do |pod|
198
- @logger.debug "Completing information for #{pod.name}"
199
- pod.complete_information_from_source
200
- end
201
- return pods
202
- end
203
-
204
-
205
130
  def write_bom_to_file(bom:, options:)
206
131
  bom_file_path = Pathname.new(options[:bom_file_path] || './bom.xml').expand_path
207
132
  bom_dir = bom_file_path.dirname
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # This file is part of CycloneDX CocoaPods
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the “License”);
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an “AS IS” BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # SPDX-License-Identifier: Apache-2.0
18
+ # Copyright (c) OWASP Foundation. All Rights Reserved.
19
+ #
20
+
21
+ require 'cocoapods'
22
+ require 'logger'
23
+
24
+ require_relative 'pod'
25
+ require_relative 'pod_attributes'
26
+ require_relative 'source'
27
+
28
+ module CycloneDX
29
+ module CocoaPods
30
+ class PodfileParsingError < StandardError; end
31
+
32
+ class PodfileAnalyzer
33
+ def initialize(logger:, exclude_test_targets: false)
34
+ @logger = logger
35
+ @exclude_test_targets = exclude_test_targets
36
+ end
37
+
38
+ def ensure_podfile_and_lock_are_present(options)
39
+ project_dir = Pathname.new(options[:path] || Dir.pwd)
40
+ raise PodfileParsingError, "#{options[:path]} is not a valid directory." unless File.directory?(project_dir)
41
+ options[:podfile_path] = project_dir + 'Podfile'
42
+ 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])
43
+ options[:podfile_lock_path] = project_dir + 'Podfile.lock'
44
+ raise PodfileParsingError, "Missing Podfile.lock, please run 'pod install' before generating BOM" unless File.exist?(options[:podfile_lock_path])
45
+
46
+ initialize_cocoapods_config(project_dir)
47
+
48
+ lockfile = ::Pod::Lockfile.from_file(options[:podfile_lock_path])
49
+ verify_synced_sandbox(lockfile)
50
+
51
+ return ::Pod::Podfile.from_file(options[:podfile_path]), lockfile
52
+ end
53
+
54
+
55
+ def parse_pods(podfile, lockfile)
56
+ @logger.debug "Parsing pods from #{podfile.defined_in_file}"
57
+ included_pods = create_list_of_included_pods(podfile, lockfile)
58
+ return lockfile.pod_names.select { |name| included_pods.include?(name) }.map do |name|
59
+ Pod.new(name: name, version: lockfile.version(name), source: source_for_pod(podfile, lockfile, name), checksum: lockfile.checksum(name))
60
+ end
61
+ end
62
+
63
+
64
+ def populate_pods_with_additional_info(pods)
65
+ pods.each do |pod|
66
+ @logger.debug "Completing information for #{pod.name}"
67
+ pod.complete_information_from_source
68
+ end
69
+ return pods
70
+ end
71
+
72
+
73
+ private
74
+
75
+
76
+ def initialize_cocoapods_config(project_dir)
77
+ ::Pod::Config.instance.installation_root = project_dir
78
+ end
79
+
80
+
81
+ def verify_synced_sandbox(lockfile)
82
+ manifestFile = ::Pod::Config.instance.sandbox.manifest
83
+ raise PodfileParsingError, "Missing Manifest.lock, please run 'pod install' before generating BOM" if manifestFile.nil?
84
+ raise PodfileParsingError, "The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." unless lockfile == manifestFile
85
+ end
86
+
87
+ def simple_hash_of_lockfile_pods(lockfile)
88
+ pods_hash = { }
89
+
90
+ pods_used = lockfile.internal_data['PODS']
91
+ pods_used.each { |pod|
92
+ if pod.is_a?(String)
93
+ # Pods stored as String have no dependencies
94
+ pod_name = pod.split.first
95
+ pods_hash[pod_name] = []
96
+ else
97
+ # Pods stored as a hash have pod name and dependencies.
98
+ pod.each { |pod, dependencies|
99
+ pod_name = pod.split.first
100
+ pods_hash[pod_name] = dependencies.map { |d| d.split.first }
101
+ }
102
+ end
103
+ }
104
+ pods_hash
105
+ end
106
+
107
+ def append_all_pod_dependencies(pods_used, pods_cache)
108
+ result = pods_used
109
+ original_number = 0
110
+ # Loop adding pod dependencies until we are not adding any more dependencies to the result
111
+ # This brings in all the transitive dependencies of every top level pod.
112
+ # Note this also handles the edge case of having a Podfile with no pods used.
113
+ while result.length != original_number
114
+ original_number = result.length
115
+ pods_used.each { |pod_name|
116
+ result.push(*pods_cache[pod_name]) unless pods_cache[pod_name].empty?
117
+ }
118
+ result = result.uniq
119
+ pods_used = result
120
+ end
121
+ result
122
+ end
123
+
124
+ def create_list_of_included_pods(podfile, lockfile)
125
+ pods_cache = simple_hash_of_lockfile_pods(lockfile)
126
+
127
+ includedTargets = podfile.target_definition_list.select{ |target| include_target_named(target.label) }
128
+ includedTargetNames = includedTargets.map { |target| target.label }
129
+ @logger.debug "Including all pods for targets: #{includedTargetNames}"
130
+
131
+ topLevelDeps = includedTargets.map(&:dependencies).flatten.uniq
132
+ pods_used = topLevelDeps.map(&:name).uniq
133
+ pods_used = append_all_pod_dependencies(pods_used, pods_cache)
134
+
135
+ return pods_used.sort
136
+ end
137
+
138
+
139
+ def include_target_named(targetname)
140
+ !@exclude_test_targets || !targetname.downcase.include?('test')
141
+ end
142
+
143
+
144
+ def cocoapods_repository_source(podfile, lockfile, pod_name)
145
+ @source_manager ||= create_source_manager(podfile)
146
+ return Source::CocoaPodsRepository.searchable_source(url: lockfile.spec_repo(pod_name), source_manager: @source_manager)
147
+ end
148
+
149
+
150
+ def git_source(lockfile, pod_name)
151
+ checkout_options = lockfile.checkout_options_for_pod_named(pod_name)
152
+ url = checkout_options[:git]
153
+ [:tag, :branch, :commit].each do |type|
154
+ return Source::GitRepository.new(url: url, type: type, label: checkout_options[type]) if checkout_options[type]
155
+ end
156
+ return Source::GitRepository.new(url: url)
157
+ end
158
+
159
+
160
+ def source_for_pod(podfile, lockfile, pod_name)
161
+ root_name = pod_name.split('/').first
162
+ return cocoapods_repository_source(podfile, lockfile, root_name) unless lockfile.spec_repo(root_name).nil?
163
+ return git_source(lockfile, root_name) unless lockfile.checkout_options_for_pod_named(root_name).nil?
164
+ return Source::LocalPod.new(path: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path]) if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:path]
165
+ return Source::Podspec.new(url: lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec]) if lockfile.to_hash['EXTERNAL SOURCES'][root_name][:podspec]
166
+ return nil
167
+ end
168
+
169
+
170
+ def create_source_manager(podfile)
171
+ sourceManager = ::Pod::Source::Manager.new(::Pod::Config::instance.repos_dir)
172
+ @logger.debug "Parsing sources from #{podfile.defined_in_file}"
173
+ podfile.sources.each do |source|
174
+ @logger.debug "Ensuring #{source} is available for searches"
175
+ sourceManager.find_or_create_source_with_url(source)
176
+ end
177
+ @logger.debug "Source manager successfully created with all needed sources"
178
+ return sourceManager
179
+ end
180
+ end
181
+ end
182
+ end
@@ -20,7 +20,7 @@
20
20
 
21
21
  module CycloneDX
22
22
  module CocoaPods
23
- VERSION = '1.0.0'
23
+ VERSION = '1.1.0'
24
24
  DEPENDENCIES = {
25
25
  cocoapods: '~> 1.10.1',
26
26
  nokogiri: '~> 1.11.2'
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cyclonedx-cocoapods
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - José González
8
+ - Kyle Hammond
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2022-08-12 00:00:00.000000000 Z
12
+ date: 2022-09-13 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: cocoapods
@@ -85,6 +86,7 @@ description: CycloneDX is a lightweight software bill-of-material (SBOM) specifi
85
86
  This Gem generates CycloneDX BOMs from CocoaPods projects.
86
87
  email:
87
88
  - jose.gonzalez@openinput.com
89
+ - kyle.hammond@jamf.com
88
90
  executables:
89
91
  - cyclonedx-cocoapods
90
92
  extensions: []
@@ -101,6 +103,7 @@ files:
101
103
  - lib/cyclonedx/cocoapods/license.rb
102
104
  - lib/cyclonedx/cocoapods/pod.rb
103
105
  - lib/cyclonedx/cocoapods/pod_attributes.rb
106
+ - lib/cyclonedx/cocoapods/podfile_analyzer.rb
104
107
  - lib/cyclonedx/cocoapods/source.rb
105
108
  - lib/cyclonedx/cocoapods/spdx-licenses.json
106
109
  - lib/cyclonedx/cocoapods/version.rb