app_permission_statistics 0.1.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 AppPermissionStatistics
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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppPermissionStatistics
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "app_permission_statistics/version"
4
+ require_relative "app_permission_statistics/analyze"
5
+ require_relative "app_permission_statistics/extracter"
6
+
7
+ module AppPermissionStatistics
8
+
9
+ class Actuator
10
+ extend Forwardable
11
+ attr_reader :file
12
+ attr_reader :compare_file
13
+ attr_reader :store_path
14
+ attr_reader :report_path
15
+
16
+ def initialize(file,compare_file, report_path: nil,store_path: nil)
17
+ @file = file
18
+ @compare_file = compare_file
19
+ @report_path = report_path
20
+ @store_path = store_path
21
+ end
22
+
23
+ def run
24
+ compare_extracter = Extracter.new(@compare_file , @store_path)
25
+ if compare_extracter.plistInfo.identifier != extracter.plistInfo.identifier
26
+ abort("compared files have different plist identifier")
27
+ end
28
+ if Gem::Version.new(compare_extracter.plistInfo.version) == Gem::Version.new(extracter.plistInfo.version)
29
+ abort("compared files plist version is same")
30
+ end
31
+ compare_extracter.extract_update
32
+ extracter.extract_update
33
+ analyzer.analyze
34
+ extracter.clear!
35
+ compare_extracter.clear!
36
+ end
37
+
38
+ def extracter
39
+ @extracter ||= Extracter.new(@file , @store_path)
40
+ end
41
+
42
+ def analyzer
43
+ identifier = @extracter.plistInfo.identifier
44
+ @analyzer ||= Analyzer.new(identifier,@report_path,@store_path)
45
+ end
46
+
47
+ end
48
+
49
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: app_permission_statistics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.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
+ description: app permission statistics from ipas
76
+ email:
77
+ - tang.bin@olaola.chat
78
+ executables:
79
+ - app_permission_statistics
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - ".DS_Store"
84
+ - ".gitignore"
85
+ - Gemfile
86
+ - Gemfile.lock
87
+ - README.md
88
+ - Rakefile
89
+ - analyze_report
90
+ - app_permission_statistics.gemspec
91
+ - bin/app_permission_statistics
92
+ - bin/setup
93
+ - lib/app_permission_statistics.rb
94
+ - lib/app_permission_statistics/analyze.rb
95
+ - lib/app_permission_statistics/core_ext/inflector.rb
96
+ - lib/app_permission_statistics/core_ext/try.rb
97
+ - lib/app_permission_statistics/entitlements.rb
98
+ - lib/app_permission_statistics/extracter.rb
99
+ - lib/app_permission_statistics/helper.rb
100
+ - lib/app_permission_statistics/info_plist.rb
101
+ - lib/app_permission_statistics/mobile_provision.rb
102
+ - lib/app_permission_statistics/version.rb
103
+ homepage: https://github.com/olaola-chat/cli-app_permission_statistics.git
104
+ licenses: []
105
+ metadata:
106
+ homepage_uri: https://github.com/olaola-chat/cli-app_permission_statistics.git
107
+ source_code_uri: https://github.com/olaola-chat/cli-app_permission_statistics.git
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 2.3.0
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubygems_version: 3.2.3
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: app permission statistics
127
+ test_files: []