cocoapods-entitlements-statistics 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,317 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core_ext/inflector'
4
+ require_relative 'core_ext/try'
5
+
6
+ require 'openssl'
7
+ require 'cfpropertylist'
8
+
9
+ module AppEntitlementsStatistics
10
+
11
+ # .mobileprovision file parser
12
+ class MobileProvision
13
+ def initialize(path)
14
+ @path = path
15
+ end
16
+
17
+ def name
18
+ mobileprovision.try(:[], 'Name')
19
+ end
20
+
21
+ def app_name
22
+ mobileprovision.try(:[], 'AppIDName')
23
+ end
24
+
25
+ def type
26
+ return :development if development?
27
+ return :adhoc if adhoc?
28
+ return :appstore if appstore?
29
+ return :enterprise if enterprise?
30
+ end
31
+
32
+ def platforms
33
+ return unless platforms = mobileprovision.try(:[], 'Platform')
34
+
35
+ platforms.map do |v|
36
+ v = 'macOS' if v == 'OSX'
37
+ v.downcase.to_sym
38
+ end
39
+ end
40
+
41
+ def platform
42
+ platforms[0]
43
+ end
44
+
45
+ def devices
46
+ mobileprovision.try(:[], 'ProvisionedDevices')
47
+ end
48
+
49
+ def team_identifier
50
+ mobileprovision.try(:[], 'TeamIdentifier')
51
+ end
52
+
53
+ def team_name
54
+ mobileprovision.try(:[], 'TeamName')
55
+ end
56
+
57
+ def profile_name
58
+ mobileprovision.try(:[], 'Name')
59
+ end
60
+
61
+ def created_date
62
+ mobileprovision.try(:[], 'CreationDate')
63
+ end
64
+
65
+ def expired_date
66
+ mobileprovision.try(:[], 'ExpirationDate')
67
+ end
68
+
69
+ def entitlements
70
+ mobileprovision.try(:[], 'Entitlements')
71
+ end
72
+
73
+ def developer_certs
74
+ certs = mobileprovision.try(:[], 'DeveloperCertificates')
75
+ return if certs.empty?
76
+
77
+ certs.each_with_object([]) do |cert, obj|
78
+ obj << DeveloperCertificate.new(cert)
79
+ end
80
+ end
81
+
82
+ # Detect is development type of mobileprovision
83
+ #
84
+ # related link: https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
85
+ def development?
86
+ case platform.downcase.to_sym
87
+ when :ios
88
+ entitlements['get-task-allow'] == true
89
+ when :macos
90
+ !devices.nil?
91
+ else
92
+ raise Error, "Not implement with platform: #{platform}"
93
+ end
94
+ end
95
+
96
+ # Detect app store type
97
+ #
98
+ # related link: https://developer.apple.com/library/archive/qa/qa1830/_index.html
99
+ def appstore?
100
+ case platform.downcase.to_sym
101
+ when :ios
102
+ !development? && entitlements.key?('beta-reports-active')
103
+ when :macos
104
+ !development?
105
+ else
106
+ raise Error, "Not implement with platform: #{platform}"
107
+ end
108
+ end
109
+
110
+ def adhoc?
111
+ return false if platform == :macos # macOS no need adhoc
112
+
113
+ !development? && !devices.nil?
114
+ end
115
+
116
+ def enterprise?
117
+ return false if platform == :macos # macOS no need adhoc
118
+
119
+ !development? && !adhoc? && !appstore?
120
+ end
121
+ alias inhouse? enterprise?
122
+
123
+ # Enabled Capabilites
124
+ #
125
+ # Related link: https://developer.apple.com/help/account/reference/supported-capabilities-ios
126
+ def enabled_capabilities
127
+ capabilities = Hash.new
128
+ entitlements.each do |key, value|
129
+ case key
130
+ when 'com.apple.developer.game-center'
131
+ capabilities['Game Center'] = {
132
+ key => value
133
+ }
134
+ when 'keychain-access-groups'
135
+ capabilities['Keychain sharing'] = {
136
+ key => value
137
+ }
138
+ when 'aps-environment'
139
+ capabilities['Push Notifications'] = {
140
+ key => value
141
+ }
142
+ when 'com.apple.developer.applesignin'
143
+ capabilities['Sign In with Apple'] = {
144
+ key => value
145
+ }
146
+ when 'com.apple.developer.siri'
147
+ capabilities['SiriKit'] = {
148
+ key => value
149
+ }
150
+ when 'com.apple.security.application-groups'
151
+ capabilities['App Groups'] = {
152
+ key => value
153
+ }
154
+ when 'com.apple.developer.associated-domains'
155
+ capabilities['Associated Domains'] = {
156
+ key => value
157
+ }
158
+ when 'com.apple.developer.default-data-protection'
159
+ capabilities['Data Protection'] = {
160
+ key => value
161
+ }
162
+ when 'com.apple.developer.networking.networkextension'
163
+ capabilities ['Network Extensions'] = {
164
+ key => value
165
+ }
166
+ when 'com.apple.developer.networking.vpn.api'
167
+ capabilities ['Personal VPN'] = {
168
+ key => value
169
+ }
170
+ when 'com.apple.developer.healthkit',
171
+ 'com.apple.developer.healthkit.access'
172
+ capabilities['HealthKit'] = {
173
+ 'com.apple.developer.healthkit' => entitlements['com.apple.developer.healthkit'],
174
+ 'com.apple.developer.healthkit.access' => entitlements['com.apple.developer.healthkit.access'],
175
+ } unless capabilities.include?('HealthKit')
176
+ when 'com.apple.developer.icloud-services',
177
+ 'com.apple.developer.icloud-container-identifiers'
178
+ capabilities['iCloud'] = {
179
+ 'com.apple.developer.icloud-services' => entitlements['com.apple.developer.icloud-services'],
180
+ 'com.apple.developer.icloud-container-identifiers' => entitlements['com.apple.developer.icloud-container-identifiers'],
181
+ } unless capabilities.include?('iCloud')
182
+ when 'com.apple.developer.in-app-payments'
183
+ capabilities['Apple Pay'] = {
184
+ key => value
185
+ }
186
+ when 'com.apple.developer.homekit'
187
+ capabilities['HomeKit'] = {
188
+ key => value
189
+ }
190
+ when 'com.apple.developer.user-fonts'
191
+ capabilities['Fonts'] = {
192
+ key => value
193
+ }
194
+ when 'com.apple.developer.pass-type-identifiers'
195
+ capabilities['Wallet'] = {
196
+ key => value
197
+ }
198
+ when 'inter-app-audio'
199
+ capabilities['Inter-App Audio'] = {
200
+ key => value
201
+ }
202
+ when 'com.apple.developer.networking.multipath'
203
+ capabilities['Multipath'] = {
204
+ key => value
205
+ }
206
+ when 'com.apple.developer.authentication-services.autofill-credential-provider'
207
+ capabilities['AutoFill Credential Provider'] = {
208
+ key => value
209
+ }
210
+ when 'com.apple.developer.networking.wifi-info'
211
+ capabilities['Access WiFi Information'] = {
212
+ key => value
213
+ }
214
+ when 'com.apple.external-accessory.wireless-configuration'
215
+ capabilities['Wireless Accessory Configuration'] = {
216
+ key => value
217
+ }
218
+ when 'com.apple.developer.kernel.extended-virtual-addressing'
219
+ capabilities['Extended Virtual Address Space'] = {
220
+ key => value
221
+ }
222
+ when 'com.apple.developer.nfc.readersession.formats'
223
+ capabilities['NFC Tag Reading'] = {
224
+ key => value
225
+ }
226
+ when 'com.apple.developer.ClassKit-environment'
227
+ capabilities['ClassKit'] = {
228
+ key => value
229
+ }
230
+ when 'com.apple.developer.networking.HotspotConfiguration'
231
+ capabilities['Hotspot'] = {
232
+ key => value
233
+ }
234
+ when 'com.apple.developer.devicecheck.appattest-environment'
235
+ capabilities['App Attest'] = {
236
+ key => value
237
+ }
238
+ when 'com.apple.developer.coremedia.hls.low-latency'
239
+ capabilities['Low Latency HLS'] = {
240
+ key => value
241
+ }
242
+ when 'com.apple.developer.associated-domains.mdm-managed'
243
+ capabilities['MDM Managed Associated Domains'] = {
244
+ key => value
245
+ }
246
+ end
247
+ end
248
+ capabilities
249
+ end
250
+
251
+
252
+ def [](key)
253
+ mobileprovision.try(:[], key.to_s)
254
+ end
255
+
256
+ def empty?
257
+ mobileprovision.nil?
258
+ end
259
+
260
+ def mobileprovision
261
+ return @mobileprovision = nil unless File.exist?(@path)
262
+
263
+ data = File.read(@path)
264
+ data = strip_plist_wrapper(data) unless bplist?(data)
265
+ list = CFPropertyList::List.new(data: data).value
266
+ @mobileprovision = CFPropertyList.native_types(list)
267
+ rescue CFFormatError
268
+ @mobileprovision = nil
269
+ end
270
+
271
+ def method_missing(method_name, *args, &block)
272
+ mobileprovision.try(:[], method_name.to_s.ai_camelcase) ||
273
+ mobileprovision.send(method_name) ||
274
+ super
275
+ end
276
+
277
+ def respond_to_missing?(method_name, *args)
278
+ mobileprovision.key?(method_name.to_s.ai_camelcase) ||
279
+ mobileprovision.respond_to?(method_name) ||
280
+ super
281
+ end
282
+
283
+ private
284
+
285
+ def bplist?(raw)
286
+ raw[0..5] == 'bplist'
287
+ end
288
+
289
+ def strip_plist_wrapper(raw)
290
+ end_tag = '</plist>'
291
+ start_point = raw.index('<?xml version=')
292
+ end_point = raw.index(end_tag) + end_tag.size - 1
293
+ raw[start_point..end_point]
294
+ end
295
+
296
+ # Developer Certificate
297
+ class DeveloperCertificate
298
+ attr_reader :raw
299
+
300
+ def initialize(data)
301
+ @raw = OpenSSL::X509::Certificate.new(data)
302
+ end
303
+
304
+ def name
305
+ @raw.subject.to_a.find { |name, _, _| name == 'CN' }[1].force_encoding('UTF-8')
306
+ end
307
+
308
+ def created_date
309
+ @raw.not_after
310
+ end
311
+
312
+ def expired_date
313
+ @raw.not_before
314
+ end
315
+ end
316
+ end
317
+ end
@@ -0,0 +1,3 @@
1
+ module CocoapodsEntitlementsStatistics
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,82 @@
1
+
2
+ require 'cocoapods'
3
+
4
+ module CocoapodsEntitlementsStatistics
5
+
6
+ class ProjectInfo
7
+ include Xcodeproj::Project::Object
8
+
9
+ attr_reader :project
10
+ attr_reader :load_success
11
+ attr_reader :tagret_name
12
+
13
+ def initialize
14
+ load_xcodeproj
15
+ end
16
+
17
+ def load_xcodeproj_success?
18
+ return @load_success
19
+ end
20
+
21
+ def load_xcodeproj
22
+ xcodeprojects = Pathname.glob('**/*.xcodeproj').reject { |path| path.to_s.start_with?('Pods/') }
23
+ if xcodeprojects.length == 1
24
+ @tagret_name = Pathname(xcodeprojects.first).basename('.xcodeproj')
25
+ @project = Xcodeproj::Project.open(xcodeprojects.first.realpath)
26
+ @load_success = true
27
+ else
28
+ @load_success = false
29
+ error_message = xcodeprojects.length > 1 ? 'found too many' : "couldn't find any"
30
+ projects = xcodeprojects.map(&:basename).join(' ')
31
+ Pod::UI.notice 'CocoaPods-Keys ' + error_message + ' Xcode projects (' + projects + '). Please give a name for this project.'
32
+ end
33
+ end
34
+
35
+ def info_plist
36
+ info_plists = Pathname.glob('**/info.plist').reject { |path| !path.to_s.start_with?("#{@tagret_name}/") }
37
+ info_plist_path = info_plists.length > 0 ? info_plists.first.realpath : ""
38
+ info_plist_path
39
+ end
40
+
41
+ def entitlements_plist
42
+ entitlements_files = Pathname.glob("**/#{@tagret_name}.entitlements").reject { |path| !path.to_s.start_with?("#{@tagret_name}/") }
43
+ entitlements_path = entitlements_files.length > 0 ? entitlements_files.first.realpath : ""
44
+ entitlements_path
45
+ end
46
+
47
+ def project_release_config
48
+ return @release_configurations unless @release_configurations.nil?
49
+ @project.targets[0].build_configurations.each do |config|
50
+ if config.name == 'Release'
51
+ @release_configurations = config
52
+ end
53
+ end
54
+ @release_configurations
55
+ end
56
+
57
+ def marketing_version
58
+ return nil unless !project_release_config.nil?
59
+ marketing_version = project_release_config.build_settings["MARKETING_VERSION"]
60
+ marketing_version
61
+ end
62
+
63
+ def product_bundle_identifier
64
+ return nil unless !project_release_config.nil?
65
+ product_bundle_identifier = project_release_config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
66
+ product_bundle_identifier
67
+ end
68
+
69
+ def system_capabilities
70
+ target_attr_hash = @project.root_object.attributes["TargetAttributes"]
71
+ system_capabilities = Hash.new
72
+ target_attr_hash.each do |_,value|
73
+ if value.class == Hash && value.keys.include?("SystemCapabilities")
74
+ system_capabilities = value["SystemCapabilities"]
75
+ end
76
+ end
77
+ system_capabilities
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1 @@
1
+ require 'cocoapods-entitlements-statistics/gem_version'
@@ -0,0 +1,34 @@
1
+ require "cocoapods-entitlements-statistics/app_entitlements_statistics/extracter"
2
+ require "cocoapods-entitlements-statistics/app_entitlements_statistics/analyze"
3
+ require "cocoapods-entitlements-statistics/project_info"
4
+ require 'cocoapods'
5
+
6
+ module CocoapodsEntitlementsStatistics
7
+
8
+ Pod::HooksManager.register('cocoapods-entitlements-statistics', :post_install) do |context,options|
9
+
10
+ project_info = ProjectInfo.new()
11
+
12
+ if project_info.load_xcodeproj_success?
13
+ args = {
14
+ "cur_version" => project_info.marketing_version,
15
+ "product_bundle_identifier" => project_info.product_bundle_identifier,
16
+ "info_plist_path" => project_info.info_plist,
17
+ "entitlements_path" => project_info.entitlements_plist,
18
+ "system_capabilities" => project_info.system_capabilities
19
+ }
20
+
21
+ # extract_update 、analyze
22
+ extracter = AppEntitlementsStatistics::Extracter.new(args)
23
+ extracter.extract_update
24
+
25
+ report_path = (!options.nil? && options.has_key?("report_path")) ? options[:report_path] : nil
26
+ analyzer = AppEntitlementsStatistics::Analyzer.new(project_info.product_bundle_identifier, report_path: report_path )
27
+
28
+ report_real_path = analyzer.analyze
29
+ Pod::UI.notice "Entitlements Statistics Report : #{File.expand_path(report_real_path)}" unless report_real_path.empty?
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ module Pod
4
+ describe Command::Statistics do
5
+ describe 'CLAide' do
6
+ it 'registers it self' do
7
+ Command.parse(%w{ statistics }).should.be.instance_of Command::Statistics
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,50 @@
1
+ require 'pathname'
2
+ ROOT = Pathname.new(File.expand_path('../../', __FILE__))
3
+ $:.unshift((ROOT + 'lib').to_s)
4
+ $:.unshift((ROOT + 'spec').to_s)
5
+
6
+ require 'bundler/setup'
7
+ require 'bacon'
8
+ require 'mocha-on-bacon'
9
+ require 'pretty_bacon'
10
+ require 'pathname'
11
+ require 'cocoapods'
12
+
13
+ Mocha::Configuration.prevent(:stubbing_non_existent_method)
14
+
15
+ require 'cocoapods_plugin'
16
+
17
+ #-----------------------------------------------------------------------------#
18
+
19
+ module Pod
20
+
21
+ # Disable the wrapping so the output is deterministic in the tests.
22
+ #
23
+ UI.disable_wrap = true
24
+
25
+ # Redirects the messages to an internal store.
26
+ #
27
+ module UI
28
+ @output = ''
29
+ @warnings = ''
30
+
31
+ class << self
32
+ attr_accessor :output
33
+ attr_accessor :warnings
34
+
35
+ def puts(message = '')
36
+ @output << "#{message}\n"
37
+ end
38
+
39
+ def warn(message = '', actions = [])
40
+ @warnings << "#{message}\n"
41
+ end
42
+
43
+ def print(message)
44
+ @output << message
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ #-----------------------------------------------------------------------------#
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cocoapods-entitlements-statistics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - bin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yaml
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: crimp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: CFPropertyList
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.1.0
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 2.3.4
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "<"
56
+ - !ruby/object:Gem::Version
57
+ version: 3.1.0
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.3.4
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '1.12'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '1.12'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubyzip
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '1.2'
82
+ - - "<"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.0'
85
+ type: :runtime
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '1.2'
92
+ - - "<"
93
+ - !ruby/object:Gem::Version
94
+ version: '3.0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rake
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ description: A short description of cocoapods-entitlements-statistics.
110
+ email:
111
+ - tang.bin@olaola.chat
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - ".gitignore"
117
+ - Gemfile
118
+ - LICENSE.txt
119
+ - README.md
120
+ - Rakefile
121
+ - cocoapods-entitlements-statistics.gemspec
122
+ - lib/cocoapods-entitlements-statistics.rb
123
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/analyze.rb
124
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/core_ext/inflector.rb
125
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/core_ext/try.rb
126
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/entitlements.rb
127
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/extracter.rb
128
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/helper.rb
129
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/info_plist.rb
130
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/mobile_provision.rb
131
+ - lib/cocoapods-entitlements-statistics/gem_version.rb
132
+ - lib/cocoapods-entitlements-statistics/project_info.rb
133
+ - lib/cocoapods_plugin.rb
134
+ - spec/command/statistics_spec.rb
135
+ - spec/spec_helper.rb
136
+ homepage: https://github.com/EXAMPLE/cocoapods-entitlements-statistics
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubygems_version: 3.2.3
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: A longer description of cocoapods-entitlements-statistics.
159
+ test_files:
160
+ - spec/command/statistics_spec.rb
161
+ - spec/spec_helper.rb