cyclonedx-cocoapods 1.3.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b02ca712eff5c74b3c7a4e3c59ab7a402a87cf2f3053be388f64a702ca0d3a1
4
- data.tar.gz: ca6e3d81e4b255dfcd43add1017e1b5dc939274544b6bade803da0246c544ebc
3
+ metadata.gz: 84ed77501efec7ca77fce507dd1dbc4a29ffb4b8cf45fc6b942eafe3901af95a
4
+ data.tar.gz: 85204bb25786de11c3dc7ec302d016431ebecd7078149081a6b294ba65f756aa
5
5
  SHA512:
6
- metadata.gz: 74af3ed1ceded419670e4e4cc2b69a955730f963ef92510159505809a02681ae39347c91a24ac99d988f142f987465998fdd4bf4629e799d0dc33e01f96fbb9a
7
- data.tar.gz: b1ce5ceae85c7b3ff993109203a53f9f7fa61397f8d58f6b18220ee2ed5e866ab1bc210b6ac48b375de3ec49e6928abe2245413fac56b437403317fb1c2ba783
6
+ metadata.gz: 7f4b84eb0a11f7f6488fe9fccef7806e786db41ea647806046b729b39952172175df7b8884b17c60e5cac0b246a9bfc6e56d8e53f69b3ad9521c9cde0f19726b
7
+ data.tar.gz: f719564347931af554dbc2705022a548405bbd95320db5c094f5616166a46ecd830d8fddedcaebd9e24fd76ff6e8be2e1917d3a3be8bc6ede3f21e77e763af77
data/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ 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.4.1]
8
+
9
+ ### Changed
10
+ - Minimum Ruby version is now v2.6.3 so the [Array.union](https://apidock.com/ruby/v2_6_3/Array/union) function can be used.
11
+
12
+ ### Fixed
13
+ - Improved performance when analyzing a Podfile with many pods. ([Issue #78](https://github.com/CycloneDX/cyclonedx-cocoapods/issues/78)) [@macblazer](https://github.com/macblazer).
14
+
15
+ ## [1.4.0]
16
+
17
+ ### Added
18
+ - Added `evidence` element to the component output to indicate that we are doing manifest analysis to generate the bom. ([Issue #69](https://github.com/CycloneDX/cyclonedx-cocoapods/issues/69)) [@macblazer](https://github.com/macblazer).
19
+
20
+ ### Fixed
21
+ - Added top level dependencies when the metadata/component is specified (by using the `--name`, `--version`, and `--type` parameters). ([PR #70](https://github.com/CycloneDX/cyclonedx-cocoapods/pull/70)) [@fnxpt](https://github.com/fnxpt)
22
+ - Properly concatenate paths to Podfile and Podfile.lock (with unit tests!). ([Issue #71](https://github.com/CycloneDX/cyclonedx-cocoapods/issues/71)) [@macblazer](https://github.com/macblazer).
23
+
7
24
  ## [1.3.0]
8
25
 
9
26
  ### Added
@@ -104,10 +104,28 @@ module CycloneDX
104
104
  end
105
105
  end
106
106
 
107
- def add_to_bom(xml, trim_strings_length = 0)
107
+ # Add evidence of the purl identity.
108
+ # See https://github.com/CycloneDX/guides/blob/main/SBOM/en/0x60-Evidence.md for more info
109
+ def xml_add_evidence(xml, manifest_path)
110
+ xml.evidence do
111
+ xml.identity do
112
+ xml.field 'purl'
113
+ xml.confidence '0.6'
114
+ xml.methods_ do
115
+ xml.method_ do
116
+ xml.technique 'manifest-analysis'
117
+ xml.confidence '0.6'
118
+ xml.value manifest_path
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def add_to_bom(xml, manifest_path, trim_strings_length = 0)
108
126
  xml.component(type: 'library', 'bom-ref': purl) do
109
127
  xml_add_author(xml, trim_strings_length)
110
- xml.name name
128
+ xml.name_ name
111
129
  xml.version version.to_s
112
130
  xml.description { xml.cdata description } unless description.nil?
113
131
  unless checksum.nil?
@@ -126,6 +144,8 @@ module CycloneDX
126
144
  xml.purl purl.slice(0, trim_strings_length)
127
145
  end
128
146
  xml_add_homepage(xml)
147
+
148
+ xml_add_evidence(xml, manifest_path)
129
149
  end
130
150
  end
131
151
 
@@ -133,7 +153,7 @@ module CycloneDX
133
153
  def add_to_bom(xml)
134
154
  xml.license do
135
155
  xml.id identifier if identifier_type == :id
136
- xml.name identifier if identifier_type == :name
156
+ xml.name_ identifier if identifier_type == :name
137
157
  xml.text_ text unless text.nil?
138
158
  xml.url url unless url.nil?
139
159
  end
@@ -143,21 +163,23 @@ module CycloneDX
143
163
 
144
164
  class Component
145
165
  def add_to_bom(xml)
146
- xml.component(type: type) do
166
+ xml.component(type: type, 'bom-ref': bomref) do
147
167
  xml.group group unless group.nil?
148
- xml.name name
168
+ xml.name_ name
149
169
  xml.version version
150
170
  end
151
171
  end
152
172
  end
153
173
 
174
+ # Turns the internal model data into an XML bom.
154
175
  class BOMBuilder
155
176
  NAMESPACE = 'http://cyclonedx.org/schema/bom/1.5'
156
177
 
157
- attr_reader :component, :pods, :dependencies
178
+ attr_reader :component, :pods, :manifest_path, :dependencies
158
179
 
159
- def initialize(pods:, component: nil, dependencies: nil)
180
+ def initialize(pods:, manifest_path:, component: nil, dependencies: nil)
160
181
  @pods = pods.sort_by(&:purl)
182
+ @manifest_path = manifest_path
161
183
  @component = component
162
184
  @dependencies = dependencies&.sort
163
185
  end
@@ -184,17 +206,17 @@ module CycloneDX
184
206
  xml.bom(xmlns: NAMESPACE, version: version.to_i.to_s, serialNumber: "urn:uuid:#{SecureRandom.uuid}") do
185
207
  bom_metadata(xml)
186
208
 
187
- bom_components(xml, pods, trim_strings_length)
209
+ bom_components(xml, pods, manifest_path, trim_strings_length)
188
210
 
189
211
  bom_dependencies(xml, dependencies)
190
212
  end
191
213
  end.to_xml
192
214
  end
193
215
 
194
- def bom_components(xml, pods, trim_strings_length)
216
+ def bom_components(xml, pods, manifest_path, trim_strings_length)
195
217
  xml.components do
196
218
  pods.each do |pod|
197
- pod.add_to_bom(xml, trim_strings_length)
219
+ pod.add_to_bom(xml, manifest_path, trim_strings_length)
198
220
  end
199
221
  end
200
222
  end
@@ -223,7 +245,7 @@ module CycloneDX
223
245
  xml.tools do
224
246
  xml.tool do
225
247
  xml.vendor 'CycloneDX'
226
- xml.name 'cyclonedx-cocoapods'
248
+ xml.name_ 'cyclonedx-cocoapods'
227
249
  xml.version VERSION
228
250
  end
229
251
  end
@@ -39,9 +39,9 @@ module CycloneDX
39
39
  setup_logger(verbose: options[:verbose])
40
40
  @logger.debug "Running cyclonedx-cocoapods with options: #{options}"
41
41
 
42
- pods, dependencies = analyze(options)
42
+ component, pods, manifest_path, dependencies = analyze(options)
43
43
 
44
- build_and_write_bom(options, pods, dependencies)
44
+ build_and_write_bom(options, component, pods, manifest_path, dependencies)
45
45
  rescue StandardError => e
46
46
  @logger.error ([e.message] + e.backtrace).join($INPUT_RECORD_SEPARATOR)
47
47
  exit 1
@@ -100,12 +100,10 @@ module CycloneDX
100
100
  parsed_options[:name] = name
101
101
  end
102
102
  options.on('-v', '--version version', 'Version of the component for which the BOM is generated') do |version|
103
- begin
104
- Gem::Version.new(version)
105
- parsed_options[:version] = version
106
- rescue StandardError => e
107
- raise OptionParser::InvalidArgument, e.message
108
- end
103
+ Gem::Version.new(version)
104
+ parsed_options[:version] = version
105
+ rescue StandardError => e
106
+ raise OptionParser::InvalidArgument, e.message
109
107
  end
110
108
  options.on('-t', '--type type',
111
109
  'Type of the component for which the BOM is generated ' \
@@ -136,11 +134,26 @@ module CycloneDX
136
134
  pods, dependencies = analyzer.parse_pods(podfile, lockfile)
137
135
  analyzer.populate_pods_with_additional_info(pods)
138
136
 
139
- [pods, dependencies]
137
+ component = component_from_options(options)
138
+
139
+ unless component.nil?
140
+ # add top level pods to main component
141
+ top_deps = analyzer.top_level_deps(podfile, lockfile)
142
+ dependencies[component.bomref] = top_deps
143
+ end
144
+
145
+ manifest_path = lockfile.defined_in_file
146
+ if manifest_path.absolute?
147
+ # Use the folder that we are building in, then the path to the manifest file
148
+ manifest_path = Pathname.pwd.basename + manifest_path.relative_path_from(Pathname.pwd)
149
+ end
150
+
151
+ [component, pods, manifest_path, dependencies]
140
152
  end
141
153
 
142
- def build_and_write_bom(options, pods, dependencies)
143
- builder = BOMBuilder.new(pods: pods, component: component_from_options(options), dependencies: dependencies)
154
+ def build_and_write_bom(options, component, pods, manifest_path, dependencies)
155
+ builder = BOMBuilder.new(pods: pods, manifest_path: manifest_path,
156
+ component: component, dependencies: dependencies)
144
157
  bom = builder.bom(version: options[:bom_version] || 1,
145
158
  trim_strings_length: options[:trim_strings_length] || 0)
146
159
  write_bom_to_file(bom: bom, options: options)
@@ -24,7 +24,7 @@ module CycloneDX
24
24
  class Component
25
25
  VALID_COMPONENT_TYPES = %w[application framework library container operating-system device firmware file].freeze
26
26
 
27
- attr_reader :group, :name, :version, :type
27
+ attr_reader :group, :name, :version, :type, :bomref
28
28
 
29
29
  def initialize(name:, version:, type:, group: nil)
30
30
  raise ArgumentError, 'Group, if specified, must be non empty' if !group.nil? && group.to_s.strip.empty?
@@ -39,6 +39,11 @@ module CycloneDX
39
39
  @name = name
40
40
  @version = version
41
41
  @type = type
42
+ @bomref = "#{name}@#{version}"
43
+
44
+ return if group.nil?
45
+
46
+ @bomref = "#{group}/#{@bomref}"
42
47
  end
43
48
  end
44
49
  end
@@ -74,6 +74,11 @@ module CycloneDX
74
74
  pods
75
75
  end
76
76
 
77
+ def top_level_deps(podfile, lockfile)
78
+ pods_used = top_level_pods(podfile)
79
+ dependencies_for_pod(pods_used, podfile, lockfile)
80
+ end
81
+
77
82
  private
78
83
 
79
84
  def load_plugins(podfile_path)
@@ -137,6 +142,8 @@ module CycloneDX
137
142
  end
138
143
 
139
144
  def initialize_cocoapods_config(project_dir)
145
+ # First, reset the ::Pod::Config instance in case we need to use this analyzer on multiple pods
146
+ ::Pod::Config.instance = nil
140
147
  ::Pod::Config.instance.installation_root = project_dir
141
148
  end
142
149
 
@@ -177,12 +184,18 @@ module CycloneDX
177
184
  end
178
185
  end
179
186
 
180
- def append_all_pod_dependencies(pods_used, pods_cache)
181
- result = pods_used
187
+ # Calculate simple array of all used pods plus their direct dependencies
188
+ #
189
+ # @param [Array<String>] top_level_pods List of pod names that are directly imported by the Podfile
190
+ # @param [Hash<String,Array<String>>] pods_cache Dependency information directly from the Podfile.lock;
191
+ # keys are string pod names, values are list of direct dependencies of the given pod.
192
+ # @return [Array<String>, Hash<String,Array<String>>] First element is list of all used pod names.
193
+ # Second element is a hash: keys are string pod names, values are the direct dependencies of that pod.
194
+ def append_all_pod_dependencies(top_level_pods, pods_cache)
195
+ result = top_level_pods
182
196
  original_number = 0
183
- dependencies_hash = {}
184
197
 
185
- # Loop adding pod dependencies until we are not adding any more dependencies to the result
198
+ # Loop adding pod dependencies until we are not adding any more dependencies to the result.
186
199
  # This brings in all the transitive dependencies of every top level pod.
187
200
  # Note this also handles two edge cases:
188
201
  # 1. Having a Podfile with no pods used.
@@ -190,29 +203,38 @@ module CycloneDX
190
203
  while result.length != original_number
191
204
  original_number = result.length
192
205
 
193
- pods_used.each do |pod_name|
206
+ top_level_pods.each do |pod_name|
194
207
  if pods_cache.key?(pod_name)
195
- result.push(*pods_cache[pod_name])
196
- dependencies_hash[pod_name] = pods_cache[pod_name].empty? ? [] : pods_cache[pod_name]
208
+ # Append all of the dependencies of this pod into the main list, if they aren't already in the list
209
+ result = result.union(pods_cache[pod_name])
197
210
  end
198
211
  end
199
212
 
200
- result = result.uniq
201
- pods_used = result
213
+ top_level_pods = result
214
+ end
215
+
216
+ # Now that we have the simple list of all unique pods, grab their direct dependencies
217
+ dependencies_hash = {}
218
+ result.each do |pod_name|
219
+ dependencies_hash[pod_name] = pods_cache.key?(pod_name) ? pods_cache[pod_name] : []
202
220
  end
203
221
 
204
222
  [result, dependencies_hash]
205
223
  end
206
224
 
207
- def create_list_of_included_pods(podfile, lockfile)
208
- pods_cache = simple_hash_of_lockfile_pods(lockfile)
209
-
225
+ def top_level_pods(podfile)
210
226
  included_targets = podfile.target_definition_list.select { |target| include_target_named(target.label) }
211
227
  included_target_names = included_targets.map(&:label)
212
228
  @logger.debug "Including all pods for targets: #{included_target_names}"
213
229
 
214
230
  top_level_deps = included_targets.map(&:dependencies).flatten.uniq
215
- pods_used = top_level_deps.map(&:name).uniq
231
+ top_level_deps.map(&:name).uniq
232
+ end
233
+
234
+ def create_list_of_included_pods(podfile, lockfile)
235
+ pods_cache = simple_hash_of_lockfile_pods(lockfile)
236
+
237
+ pods_used = top_level_pods(podfile)
216
238
  pods_used, dependencies = append_all_pod_dependencies(pods_used, pods_cache)
217
239
 
218
240
  [pods_used.sort, dependencies]
@@ -21,6 +21,6 @@
21
21
 
22
22
  module CycloneDX
23
23
  module CocoaPods
24
- VERSION = '1.3.0'
24
+ VERSION = '1.4.1'
25
25
  end
26
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cyclonedx-cocoapods
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - José González
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-02-08 00:00:00.000000000 Z
12
+ date: 2024-11-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: cocoapods
@@ -51,48 +51,6 @@ dependencies:
51
51
  - - "<"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '2.0'
54
- - !ruby/object:Gem::Dependency
55
- name: equivalent-xml
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: 0.6.0
61
- type: :development
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: 0.6.0
68
- - !ruby/object:Gem::Dependency
69
- name: rake
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '13.0'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '13.0'
82
- - !ruby/object:Gem::Dependency
83
- name: rspec
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '3.0'
89
- type: :development
90
- prerelease: false
91
- version_requirements: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '3.0'
96
54
  description: CycloneDX is a lightweight software bill-of-material (SBOM) specification
97
55
  designed for use in application security contexts and supply chain component analysis.
98
56
  This Gem generates CycloneDX BOMs from CocoaPods projects.
@@ -133,14 +91,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
91
  requirements:
134
92
  - - ">="
135
93
  - !ruby/object:Gem::Version
136
- version: 2.4.0
94
+ version: 2.6.3
137
95
  required_rubygems_version: !ruby/object:Gem::Requirement
138
96
  requirements:
139
97
  - - ">="
140
98
  - !ruby/object:Gem::Version
141
99
  version: '0'
142
100
  requirements: []
143
- rubygems_version: 3.5.4
101
+ rubygems_version: 3.5.23
144
102
  signing_key:
145
103
  specification_version: 4
146
104
  summary: CycloneDX software bill-of-material (SBOM) generation utility