cyclonedx-cocoapods 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +21 -0
- data/LICENSE +201 -0
- data/NOTICE +5 -0
- data/README.md +92 -0
- data/exe/cyclonedx-cocoapods +23 -0
- data/lib/cyclonedx/cocoapods/bom_builder.rb +158 -0
- data/lib/cyclonedx/cocoapods/cli_runner.rb +224 -0
- data/lib/cyclonedx/cocoapods/component.rb +37 -0
- data/lib/cyclonedx/cocoapods/license.rb +44 -0
- data/lib/cyclonedx/cocoapods/pod.rb +108 -0
- data/lib/cyclonedx/cocoapods/pod_attributes.rb +72 -0
- data/lib/cyclonedx/cocoapods/source.rb +59 -0
- data/lib/cyclonedx/cocoapods/spdx-licenses.json +485 -0
- data/lib/cyclonedx/cocoapods/version.rb +29 -0
- metadata +132 -0
@@ -0,0 +1,224 @@
|
|
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 'optparse'
|
22
|
+
require 'logger'
|
23
|
+
require 'cocoapods'
|
24
|
+
|
25
|
+
require_relative 'component'
|
26
|
+
require_relative 'pod'
|
27
|
+
require_relative 'pod_attributes'
|
28
|
+
require_relative 'source'
|
29
|
+
require_relative 'bom_builder'
|
30
|
+
|
31
|
+
module CycloneDX
|
32
|
+
module CocoaPods
|
33
|
+
class PodfileParsingError < StandardError; end
|
34
|
+
class BOMOutputError < StandardError; end
|
35
|
+
|
36
|
+
class CLIRunner
|
37
|
+
def run
|
38
|
+
begin
|
39
|
+
setup_logger # Needed in case we have errors while processing CLI parameters
|
40
|
+
options = parseOptions
|
41
|
+
setup_logger(verbose: options[:verbose])
|
42
|
+
@logger.debug "Running cyclonedx-cocoapods with options: #{options}"
|
43
|
+
|
44
|
+
podfile, lockfile = ensure_podfile_and_lock_are_present(options)
|
45
|
+
pods = parse_pods(podfile, lockfile)
|
46
|
+
|
47
|
+
populate_pods_with_additional_info(pods)
|
48
|
+
|
49
|
+
bom = BOMBuilder.new(component: component_from_options(options), pods: pods).bom(version: options[:bom_version] || 1)
|
50
|
+
write_bom_to_file(bom: bom, options: options)
|
51
|
+
rescue StandardError => e
|
52
|
+
@logger.error ([e.message] + e.backtrace).join($/)
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def parseOptions
|
61
|
+
parsedOptions = {}
|
62
|
+
component_types = Component::VALID_COMPONENT_TYPES
|
63
|
+
OptionParser.new do |options|
|
64
|
+
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.
|
67
|
+
BANNER
|
68
|
+
|
69
|
+
options.on('--[no-]verbose', 'Run verbosely') do |v|
|
70
|
+
parsedOptions[:verbose] = v
|
71
|
+
end
|
72
|
+
options.on('-p', '--path path', '(Optional) Path to CocoaPods project directory, current directory if missing') do |path|
|
73
|
+
parsedOptions[:path] = path
|
74
|
+
end
|
75
|
+
options.on('-o', '--output bom_file_path', '(Optional) Path to output the bom.xml file to') do |bom_file_path|
|
76
|
+
parsedOptions[:bom_file_path] = bom_file_path
|
77
|
+
end
|
78
|
+
options.on('-b', '--bom-version bom_version', Integer, '(Optional) Version of the generated BOM, 1 if not provided') do |version|
|
79
|
+
parsedOptions[:bom_version] = version
|
80
|
+
end
|
81
|
+
options.on('-g', '--group group', '(Optional) Group of the component for which the BOM is generated') do |group|
|
82
|
+
parsedOptions[:group] = group
|
83
|
+
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|
|
85
|
+
parsedOptions[:name] = name
|
86
|
+
end
|
87
|
+
options.on('-v', '--version version', '(Optional) Version of the component for which the BOM is generated') do |version|
|
88
|
+
begin
|
89
|
+
Gem::Version.new(version)
|
90
|
+
parsedOptions[:version] = version
|
91
|
+
rescue StandardError => e
|
92
|
+
raise OptionParser::InvalidArgument, e.message
|
93
|
+
end
|
94
|
+
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|
|
96
|
+
raise OptionParser::InvalidArgument, "Invalid value for component's type (#{type}). It must be one of #{component_types.join('|')}" unless component_types.include?(type)
|
97
|
+
parsedOptions[:type] = type
|
98
|
+
end
|
99
|
+
options.on_tail('-h', '--help', 'Show help message') do
|
100
|
+
puts options
|
101
|
+
exit
|
102
|
+
end
|
103
|
+
end.parse!
|
104
|
+
|
105
|
+
raise OptionParser::InvalidArgument, 'You must also specify --version and --type if --name is provided' if !parsedOptions[:name].nil? && (parsedOptions[:version].nil? || parsedOptions[:type].nil?)
|
106
|
+
return parsedOptions
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def component_from_options(options)
|
111
|
+
Component.new(group: options[:group], name: options[:name], version: options[:version], type: options[:type]) if options[:name]
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def setup_logger(verbose: true)
|
116
|
+
@logger ||= Logger.new($stdout)
|
117
|
+
@logger.level = verbose ? Logger::DEBUG : Logger::INFO
|
118
|
+
end
|
119
|
+
|
120
|
+
|
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
|
+
def write_bom_to_file(bom:, options:)
|
206
|
+
bom_file_path = Pathname.new(options[:bom_file_path] || './bom.xml').expand_path
|
207
|
+
bom_dir = bom_file_path.dirname
|
208
|
+
|
209
|
+
begin
|
210
|
+
FileUtils.mkdir_p(bom_dir) unless bom_dir.directory?
|
211
|
+
rescue
|
212
|
+
raise BOMOutputError, "Unable to create the BOM output directory at #{bom_dir}"
|
213
|
+
end
|
214
|
+
|
215
|
+
begin
|
216
|
+
File.open(bom_file_path, 'w') { |file| file.write(bom) }
|
217
|
+
@logger.info "BOM written to #{bom_file_path}"
|
218
|
+
rescue
|
219
|
+
raise BOMOutputError, "Unable to write the BOM to #{bom_file_path}"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# This file is part of CycloneDX CocoaPods
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# SPDX-License-Identifier: Apache-2.0
|
17
|
+
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
18
|
+
#
|
19
|
+
|
20
|
+
module CycloneDX
|
21
|
+
module CocoaPods
|
22
|
+
class Component
|
23
|
+
VALID_COMPONENT_TYPES = %w[application framework library container operating-system device firmware file].freeze
|
24
|
+
|
25
|
+
attr_reader :group, :name, :version, :type
|
26
|
+
|
27
|
+
def initialize(group: nil, name:, version:, type:)
|
28
|
+
raise ArgumentError, "Group, if specified, must be non empty" if !group.nil? && group.to_s.strip.empty?
|
29
|
+
raise ArgumentError, "Name must be non empty" if name.nil? || name.to_s.strip.empty?
|
30
|
+
Gem::Version.new(version) # To check that the version string is well formed
|
31
|
+
raise ArgumentError, "#{type} is not valid component type (#{VALID_COMPONENT_TYPES.join('|')})" unless VALID_COMPONENT_TYPES.include?(type)
|
32
|
+
|
33
|
+
@group, @name, @version, @type = group, name, version, type
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#
|
2
|
+
# This file is part of CycloneDX CocoaPods
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# SPDX-License-Identifier: Apache-2.0
|
17
|
+
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'json'
|
21
|
+
|
22
|
+
module CycloneDX
|
23
|
+
module CocoaPods
|
24
|
+
class Pod
|
25
|
+
class License
|
26
|
+
SPDX_LICENSES = JSON.parse(File.read("#{__dir__}/spdx-licenses.json")).freeze
|
27
|
+
IDENTIFIER_TYPES = [:id, :name].freeze
|
28
|
+
|
29
|
+
attr_reader :identifier
|
30
|
+
attr_reader :identifier_type
|
31
|
+
attr_accessor :text
|
32
|
+
attr_accessor :url
|
33
|
+
|
34
|
+
def initialize(identifier:)
|
35
|
+
raise ArgumentError, "License identifier must be non empty" if identifier.nil? || identifier.to_s.strip.empty?
|
36
|
+
|
37
|
+
@identifier = SPDX_LICENSES.find { |license_id| license_id.downcase == identifier.to_s.downcase }
|
38
|
+
@identifier_type = @identifier.nil? ? :name : :id
|
39
|
+
@identifier ||= identifier
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
#
|
2
|
+
# This file is part of CycloneDX CocoaPods
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# SPDX-License-Identifier: Apache-2.0
|
17
|
+
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'rubygems/version'
|
21
|
+
require_relative 'license'
|
22
|
+
|
23
|
+
module CycloneDX
|
24
|
+
module CocoaPods
|
25
|
+
class Pod
|
26
|
+
attr_reader :name # xs:normalizedString
|
27
|
+
attr_reader :version # xs:normalizedString
|
28
|
+
attr_reader :source # Anything responding to :source_qualifier
|
29
|
+
attr_reader :homepage # xs:anyURI - https://cyclonedx.org/docs/1.4/#type_externalReference
|
30
|
+
attr_reader :checksum # https://cyclonedx.org/docs/1.4/#type_hashValue (We only use SHA-1 hashes - length == 40)
|
31
|
+
attr_reader :author # xs:normalizedString
|
32
|
+
attr_reader :description # xs:normalizedString
|
33
|
+
attr_reader :license # https://cyclonedx.org/docs/1.4/#type_licenseType
|
34
|
+
# We don't currently support several licenses or license expressions https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/
|
35
|
+
def initialize(name:, version:, source: nil, checksum: nil)
|
36
|
+
raise ArgumentError, "Name must be non empty" if name.nil? || name.to_s.empty?
|
37
|
+
raise ArgumentError, "Name shouldn't contain spaces" if name.to_s.include?(' ')
|
38
|
+
raise ArgumentError, "Name shouldn't start with a dot" if name.to_s.start_with?('.')
|
39
|
+
# `pod create` also enforces no plus sign, but more than 500 public pods have a plus in the root name.
|
40
|
+
# https://github.com/CocoaPods/CocoaPods/blob/9461b346aeb8cba6df71fd4e71661688138ec21b/lib/cocoapods/command/lib/create.rb#L35
|
41
|
+
|
42
|
+
Gem::Version.new(version) # To check that the version string is well formed
|
43
|
+
raise ArgumentError, "Invalid pod source" unless source.nil? || source.respond_to?(:source_qualifier)
|
44
|
+
raise ArgumentError, "#{checksum} is not valid SHA-1 hash" unless checksum.nil? || checksum =~ /[a-fA-F0-9]{40}/
|
45
|
+
@name, @version, @source, @checksum = name.to_s, version, source, checksum
|
46
|
+
end
|
47
|
+
|
48
|
+
def root_name
|
49
|
+
@name.split('/').first
|
50
|
+
end
|
51
|
+
|
52
|
+
def populate(attributes)
|
53
|
+
attributes.transform_keys!(&:to_sym)
|
54
|
+
populate_author(attributes)
|
55
|
+
populate_description(attributes)
|
56
|
+
populate_license(attributes)
|
57
|
+
populate_homepage(attributes)
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
"Pod<#{name}, #{version.to_s}>"
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def populate_author(attributes)
|
68
|
+
authors = attributes[:author] || attributes[:authors]
|
69
|
+
case authors
|
70
|
+
when String
|
71
|
+
@author = authors
|
72
|
+
when Array
|
73
|
+
@author = authors.join(', ')
|
74
|
+
when Hash
|
75
|
+
@author = authors.map { |name, email| "#{name} <#{email}>" }.join(', ')
|
76
|
+
else
|
77
|
+
@author = nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def populate_description(attributes)
|
82
|
+
@description = attributes[:description] || attributes[:summary]
|
83
|
+
end
|
84
|
+
|
85
|
+
def populate_license(attributes)
|
86
|
+
case attributes[:license]
|
87
|
+
when String
|
88
|
+
@license = License.new(identifier: attributes[:license])
|
89
|
+
when Hash
|
90
|
+
attributes[:license].transform_keys!(&:to_sym)
|
91
|
+
identifier = attributes[:license][:type]
|
92
|
+
unless identifier.nil? || identifier.to_s.strip.empty?
|
93
|
+
@license = License.new(identifier: identifier)
|
94
|
+
@license.text = attributes[:license][:text]
|
95
|
+
else
|
96
|
+
@license = nil
|
97
|
+
end
|
98
|
+
else
|
99
|
+
@license = nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def populate_homepage(attributes)
|
104
|
+
@homepage = attributes[:homepage]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#
|
2
|
+
# This file is part of CycloneDX CocoaPods
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# SPDX-License-Identifier: Apache-2.0
|
17
|
+
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
18
|
+
#
|
19
|
+
|
20
|
+
module CycloneDX
|
21
|
+
module CocoaPods
|
22
|
+
class SearchError < StandardError; end
|
23
|
+
|
24
|
+
module Source
|
25
|
+
class CocoaPodsRepository
|
26
|
+
attr_accessor :source_manager
|
27
|
+
|
28
|
+
def self.searchable_source(url:, source_manager:)
|
29
|
+
source = CocoaPodsRepository.new(url: url)
|
30
|
+
source.source_manager = source_manager
|
31
|
+
return source
|
32
|
+
end
|
33
|
+
|
34
|
+
def attributes_for(pod:)
|
35
|
+
specification_sets = @source_manager.search_by_name("^#{Regexp.escape(pod.root_name)}$")
|
36
|
+
raise SearchError, "No pod found named #{pod.name}" if specification_sets.length == 0
|
37
|
+
raise SearchError, "More than one pod found named #{pod.name}" if specification_sets.length > 1
|
38
|
+
|
39
|
+
paths = specification_sets[0].specification_paths_for_version(pod.version)
|
40
|
+
raise SearchError, "Version #{pod.version} not found for pod #{pod.name}" if paths.length == 0
|
41
|
+
|
42
|
+
::Pod::Specification.from_file(paths[0]).attributes_hash
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class GitRepository
|
47
|
+
def attributes_for(pod:)
|
48
|
+
::Pod::Config.instance.sandbox.specification(pod.name).attributes_hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class LocalPod
|
53
|
+
def attributes_for(pod:)
|
54
|
+
::Pod::Config.instance.sandbox.specification(pod.name).attributes_hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Podspec
|
59
|
+
def attributes_for(pod:)
|
60
|
+
::Pod::Config.instance.sandbox.specification(pod.name).attributes_hash
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class Pod
|
67
|
+
def complete_information_from_source
|
68
|
+
populate(source.attributes_for(pod: self))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# This file is part of CycloneDX CocoaPods
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# SPDX-License-Identifier: Apache-2.0
|
17
|
+
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
18
|
+
#
|
19
|
+
|
20
|
+
module CycloneDX
|
21
|
+
module CocoaPods
|
22
|
+
module Source
|
23
|
+
class CocoaPodsRepository
|
24
|
+
attr_reader :url
|
25
|
+
|
26
|
+
def initialize(url:)
|
27
|
+
@url = url
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class GitRepository
|
32
|
+
VALID_TYPES = [:branch, :tag, :commit].freeze
|
33
|
+
|
34
|
+
attr_reader :url, :type, :label
|
35
|
+
|
36
|
+
def initialize(url:, type: nil, label: nil)
|
37
|
+
raise ArgumentError, "Invalid checkout information" if !type.nil? && !VALID_TYPES.include?(type)
|
38
|
+
@url, @type, @label = url, type, label
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class LocalPod
|
43
|
+
attr_reader :path
|
44
|
+
|
45
|
+
def initialize(path:)
|
46
|
+
@path = path
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Podspec
|
51
|
+
attr_reader :url
|
52
|
+
|
53
|
+
def initialize(url:)
|
54
|
+
@url = url
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|