app-info 2.8.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +12 -7
  3. data/.github/workflows/ci.yml +7 -5
  4. data/.github/workflows/create_release.yml +15 -0
  5. data/.rubocop.yml +33 -11
  6. data/CHANGELOG.md +107 -1
  7. data/Gemfile +10 -5
  8. data/README.md +82 -15
  9. data/Rakefile +11 -0
  10. data/app_info.gemspec +14 -5
  11. data/lib/app_info/aab.rb +76 -110
  12. data/lib/app_info/android/signature.rb +114 -0
  13. data/lib/app_info/android/signatures/base.rb +53 -0
  14. data/lib/app_info/android/signatures/info.rb +158 -0
  15. data/lib/app_info/android/signatures/v1.rb +63 -0
  16. data/lib/app_info/android/signatures/v2.rb +121 -0
  17. data/lib/app_info/android/signatures/v3.rb +131 -0
  18. data/lib/app_info/android/signatures/v4.rb +18 -0
  19. data/lib/app_info/android.rb +181 -0
  20. data/lib/app_info/apk.rb +77 -112
  21. data/lib/app_info/apple.rb +192 -0
  22. data/lib/app_info/certificate.rb +176 -0
  23. data/lib/app_info/const.rb +76 -0
  24. data/lib/app_info/core_ext/object/try.rb +3 -1
  25. data/lib/app_info/core_ext/string/inflector.rb +2 -0
  26. data/lib/app_info/dsym/debug_info.rb +81 -0
  27. data/lib/app_info/dsym/macho.rb +62 -0
  28. data/lib/app_info/dsym.rb +35 -135
  29. data/lib/app_info/error.rb +3 -1
  30. data/lib/app_info/file.rb +49 -0
  31. data/lib/app_info/helper/archive.rb +37 -0
  32. data/lib/app_info/helper/file_size.rb +25 -0
  33. data/lib/app_info/helper/generate_class.rb +29 -0
  34. data/lib/app_info/helper/protobuf.rb +12 -0
  35. data/lib/app_info/helper/signatures.rb +229 -0
  36. data/lib/app_info/helper.rb +5 -128
  37. data/lib/app_info/info_plist.rb +66 -29
  38. data/lib/app_info/ipa/framework.rb +4 -4
  39. data/lib/app_info/ipa.rb +61 -135
  40. data/lib/app_info/macos.rb +54 -102
  41. data/lib/app_info/mobile_provision.rb +66 -48
  42. data/lib/app_info/pe.rb +322 -0
  43. data/lib/app_info/png_uncrush.rb +25 -5
  44. data/lib/app_info/proguard.rb +39 -22
  45. data/lib/app_info/protobuf/manifest.rb +22 -11
  46. data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
  47. data/lib/app_info/protobuf/models/README.md +8 -1
  48. data/lib/app_info/protobuf/models/Resources.proto +51 -0
  49. data/lib/app_info/protobuf/models/Resources_pb.rb +42 -0
  50. data/lib/app_info/protobuf/resources.rb +5 -5
  51. data/lib/app_info/version.rb +1 -1
  52. data/lib/app_info.rb +93 -43
  53. metadata +57 -37
@@ -7,63 +7,42 @@ require 'cfpropertylist'
7
7
 
8
8
  module AppInfo
9
9
  # MacOS App parser
10
- class Macos
11
- include Helper::HumanFileSize
12
- include Helper::Archive
13
- extend Forwardable
14
-
15
- attr_reader :file
16
-
17
- # macOS Export types
18
- module ExportType
19
- DEBUG = 'Debug'
20
- RELEASE = 'Release'
21
- APPSTORE = 'AppStore'
22
- end
23
-
24
- def initialize(file)
25
- @file = file
26
- end
27
-
28
- def size(human_size: false)
29
- file_to_human_size(@file, human_size: human_size)
30
- end
31
-
32
- def os
33
- Platform::MACOS
34
- end
35
- alias file_type os
36
-
37
- def_delegators :info, :macos?, :iphone?, :ipad?, :universal?, :build_version, :name,
38
- :release_version, :identifier, :bundle_id, :display_name,
39
- :bundle_name, :min_system_version, :min_os_version, :device_type
40
-
41
- def_delegators :mobileprovision, :team_name, :team_identifier,
42
- :profile_name, :expired_date
43
-
44
- def distribution_name
45
- "#{profile_name} - #{team_name}" if profile_name && team_name
46
- end
47
-
48
- def release_type
49
- if stored?
50
- ExportType::APPSTORE
51
- elsif mobileprovision?
52
- ExportType::RELEASE
53
- else
54
- ExportType::DEBUG
55
- end
56
- end
57
-
58
- def stored?
59
- File.exist?(store_path)
60
- end
61
-
10
+ class Macos < Apple
11
+ # @!method min_sdk_version
12
+ # @see InfoPlist#min_sdk_version
13
+ def_delegators :info, :min_system_version
14
+
15
+ # Full icons metadata
16
+ # @param [Boolean] convert Convert .icons to .png format
17
+ # @example uncovert .icons
18
+ # macos.icons
19
+ # # => [
20
+ # # {
21
+ # # name: 'icon.icns',
22
+ # # file: '/path/to/icon.icns',
23
+ # # }
24
+ # # ]
25
+ #
26
+ # @example coverted .icons
27
+ # macos.icons(convert: true)
28
+ # # => [
29
+ # # {
30
+ # # name: 'converted_icon_32x32.png',
31
+ # # file: '/path/to/converted_icon_32x32.png',
32
+ # # dimensions: [32, 32]
33
+ # # },
34
+ # # {
35
+ # # name: 'converted_icon_120x120.png',
36
+ # # file: '/path/to/converted_icon_120x120.png',
37
+ # # dimensions: [120, 120]
38
+ # # },
39
+ # # ]
40
+ # @return [Array<Hash{Symbol => String, Array<Integer>}>] icons paths of icons
62
41
  def icons(convert: true)
63
42
  return unless icon_file
64
43
 
65
44
  data = {
66
- name: File.basename(icon_file),
45
+ name: ::File.basename(icon_file),
67
46
  file: icon_file
68
47
  }
69
48
 
@@ -71,63 +50,40 @@ module AppInfo
71
50
  data
72
51
  end
73
52
 
74
- def archs
75
- return unless File.exist?(binary_path)
76
-
77
- file = MachO.open(binary_path)
78
- case file
79
- when MachO::MachOFile
80
- [file.cpusubtype]
81
- else
82
- file.machos.each_with_object([]) do |arch, obj|
83
- obj << arch.cpusubtype
84
- end
85
- end
86
- end
87
- alias architectures archs
88
-
89
- def hide_developer_certificates
90
- mobileprovision.delete('DeveloperCertificates') if mobileprovision?
91
- end
92
-
93
- def mobileprovision
94
- return unless mobileprovision?
95
-
96
- @mobileprovision ||= MobileProvision.new(mobileprovision_path)
97
- end
98
-
99
- def mobileprovision?
100
- File.exist?(mobileprovision_path)
53
+ # @return [Boolean]
54
+ def stored?
55
+ ::File.exist?(store_path)
101
56
  end
102
57
 
58
+ # @return [String]
103
59
  def mobileprovision_path
104
- @mobileprovision_path ||= File.join(app_path, 'Contents', 'embedded.provisionprofile')
60
+ @mobileprovision_path ||= ::File.join(app_path, 'Contents', 'embedded.provisionprofile')
105
61
  end
106
62
 
63
+ # @return [String]
107
64
  def store_path
108
- @store_path ||= File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
65
+ @store_path ||= ::File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
109
66
  end
110
67
 
68
+ # @return [String]
111
69
  def binary_path
112
70
  return @binary_path if @binary_path
113
71
 
114
- base_path = File.join(app_path, 'Contents', 'MacOS')
72
+ base_path = ::File.join(app_path, 'Contents', 'MacOS')
115
73
  binary = info['CFBundleExecutable']
116
- return File.join(base_path, binary) if binary
74
+ return ::File.join(base_path, binary) if binary
117
75
 
118
- @binary_path ||= Dir.glob(File.join(base_path, '*')).first
119
- end
120
-
121
- def info
122
- @info ||= InfoPlist.new(info_path)
76
+ @binary_path ||= Dir.glob(::File.join(base_path, '*')).first
123
77
  end
124
78
 
79
+ # @return [String]
125
80
  def info_path
126
- @info_path ||= File.join(app_path, 'Contents', 'Info.plist')
81
+ @info_path ||= ::File.join(app_path, 'Contents', 'Info.plist')
127
82
  end
128
83
 
84
+ # @return [String]
129
85
  def app_path
130
- @app_path ||= Dir.glob(File.join(contents, '*.app')).first
86
+ @app_path ||= Dir.glob(::File.join(contents, '*.app')).first
131
87
  end
132
88
 
133
89
  def clear!
@@ -137,16 +93,12 @@ module AppInfo
137
93
 
138
94
  @contents = nil
139
95
  @app_path = nil
140
- @binrary_path = nil
96
+ @binary_path = nil
141
97
  @info_path = nil
142
98
  @info = nil
143
99
  @icons = nil
144
100
  end
145
101
 
146
- def contents
147
- @contents ||= unarchive(@file, path: 'macos')
148
- end
149
-
150
102
  private
151
103
 
152
104
  def icon_file
@@ -155,8 +107,8 @@ module AppInfo
155
107
  info.icons.each do |key|
156
108
  next unless value = info[key]
157
109
 
158
- file = File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
159
- next unless File.file?(file)
110
+ file = ::File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
111
+ next unless ::File.file?(file)
160
112
 
161
113
  return @icon_file = file
162
114
  end
@@ -173,14 +125,14 @@ module AppInfo
173
125
  file = data[:file]
174
126
  reader = Icns::Reader.new(file)
175
127
  Icns::SIZE_TO_TYPE.each do |size, _|
176
- dest_filename = "#{File.basename(file, '.icns')}_#{size}x#{size}.png"
177
- dest_file = tempdir(File.join(File.dirname(file), dest_filename), prefix: 'converted')
128
+ dest_filename = "#{::File.basename(file, '.icns')}_#{size}x#{size}.png"
129
+ dest_file = tempdir(::File.join(::File.dirname(file), dest_filename), prefix: 'converted')
178
130
  next unless icon_data = reader.image(size: size)
179
131
 
180
- File.write(dest_file, icon_data, encoding: Encoding::BINARY)
132
+ ::File.write(dest_file, icon_data, encoding: Encoding::BINARY)
181
133
 
182
134
  data[:sets] << {
183
- name: File.basename(dest_filename),
135
+ name: ::File.basename(dest_filename),
184
136
  file: dest_file,
185
137
  dimensions: ImageSize.path(dest_file).size
186
138
  }
@@ -4,20 +4,37 @@ require 'openssl'
4
4
  require 'cfpropertylist'
5
5
 
6
6
  module AppInfo
7
- # .mobileprovision file parser
8
- class MobileProvision
9
- def initialize(path)
10
- @path = path
7
+ # Apple code signing: provisioning profile parser
8
+ # @see https://developer.apple.com/documentation/technotes/tn3125-inside-code-signing-provisioning-profiles
9
+ class MobileProvision < File
10
+ # @return [Symbol] {Manufacturer}
11
+ def manufacturer
12
+ Manufacturer::APPLE
11
13
  end
12
14
 
15
+ # @return [Symbol] {Platform}
16
+ def platform
17
+ case platforms[0]
18
+ when :macos
19
+ Platform::MACOS
20
+ when :ios
21
+ Platform::IOS
22
+ else
23
+ raise NotImplementedError, "Unkonwn platform: #{platforms[0]}"
24
+ end
25
+ end
26
+
27
+ # @return [String, nil]
13
28
  def name
14
29
  mobileprovision.try(:[], 'Name')
15
30
  end
16
31
 
32
+ # @return [String, nil]
17
33
  def app_name
18
34
  mobileprovision.try(:[], 'AppIDName')
19
35
  end
20
36
 
37
+ # @return [Symbol, nil]
21
38
  def type
22
39
  return :development if development?
23
40
  return :adhoc if adhoc?
@@ -25,6 +42,7 @@ module AppInfo
25
42
  return :enterprise if enterprise?
26
43
  end
27
44
 
45
+ # @return [Array<Symbol>]
28
46
  def platforms
29
47
  return unless platforms = mobileprovision.try(:[], 'Platform')
30
48
 
@@ -34,83 +52,100 @@ module AppInfo
34
52
  end
35
53
  end
36
54
 
37
- def platform
38
- platforms[0]
39
- end
40
-
55
+ # @return [Array<String>, nil]
41
56
  def devices
42
57
  mobileprovision.try(:[], 'ProvisionedDevices')
43
58
  end
44
59
 
60
+ # @return [String, nil]
45
61
  def team_identifier
46
62
  mobileprovision.try(:[], 'TeamIdentifier')
47
63
  end
48
64
 
65
+ # @return [String, nil]
49
66
  def team_name
50
67
  mobileprovision.try(:[], 'TeamName')
51
68
  end
52
69
 
70
+ # @return [String, nil]
53
71
  def profile_name
54
72
  mobileprovision.try(:[], 'Name')
55
73
  end
56
74
 
75
+ # @return [String, nil]
57
76
  def created_date
58
77
  mobileprovision.try(:[], 'CreationDate')
59
78
  end
60
79
 
80
+ # @return [String, nil]
61
81
  def expired_date
62
82
  mobileprovision.try(:[], 'ExpirationDate')
63
83
  end
64
84
 
85
+ # @return [Array<String>, nil]
65
86
  def entitlements
66
87
  mobileprovision.try(:[], 'Entitlements')
67
88
  end
68
89
 
90
+ # return developer certificates.
91
+ #
92
+ # @deprecated Use {#certificates} instead of this method.
69
93
  def developer_certs
94
+ certificates
95
+ end
96
+
97
+ # return developer certificates.
98
+ #
99
+ # @return [Array<Certificate>]
100
+ def certificates
70
101
  certs = mobileprovision.try(:[], 'DeveloperCertificates')
71
102
  return if certs.empty?
72
103
 
73
- certs.each_with_object([]) do |cert, obj|
74
- obj << DeveloperCertificate.new(cert)
104
+ certs.each_with_object([]) do |cert_data, obj|
105
+ obj << Certificate.parse(cert_data)
75
106
  end
76
107
  end
77
108
 
78
109
  # Detect is development type of mobileprovision
79
110
  #
80
- # related link: https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
111
+ # @see https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
112
+ # @return [Boolea]
81
113
  def development?
82
- case platform.downcase.to_sym
83
- when :ios
114
+ case platform
115
+ when Platform::IOS
84
116
  entitlements['get-task-allow'] == true
85
- when :macos
117
+ when Platform::MACOS
86
118
  !devices.nil?
87
119
  else
88
- raise Error, "Not implement with platform: #{platform}"
120
+ raise NotImplementedError, "Unknown platform: #{platform}"
89
121
  end
90
122
  end
91
123
 
92
124
  # Detect app store type
93
125
  #
94
- # related link: https://developer.apple.com/library/archive/qa/qa1830/_index.html
126
+ # @see https://developer.apple.com/library/archive/qa/qa1830/_index.html
127
+ # @return [Boolea]
95
128
  def appstore?
96
- case platform.downcase.to_sym
97
- when :ios
129
+ case platform
130
+ when Platform::IOS
98
131
  !development? && entitlements.key?('beta-reports-active')
99
- when :macos
132
+ when Platform::MACOS
100
133
  !development?
101
134
  else
102
- raise Error, "Not implement with platform: #{platform}"
135
+ raise NotImplementedError, "Unknown platform: #{platform}"
103
136
  end
104
137
  end
105
138
 
139
+ # @return [Boolea]
106
140
  def adhoc?
107
- return false if platform == :macos # macOS no need adhoc
141
+ return false if platform == Platform::MACOS # macOS no need adhoc
108
142
 
109
143
  !development? && !devices.nil?
110
144
  end
111
145
 
146
+ # @return [Boolea]
112
147
  def enterprise?
113
- return false if platform == :macos # macOS no need adhoc
148
+ return false if platform == Platform::MACOS # macOS no need adhoc
114
149
 
115
150
  !development? && !adhoc? && !appstore?
116
151
  end
@@ -118,7 +153,8 @@ module AppInfo
118
153
 
119
154
  # Enabled Capabilites
120
155
  #
121
- # Related link: https://developer.apple.com/support/app-capabilities/
156
+ # @see https://developer.apple.com/support/app-capabilities/
157
+ # @return [Array<String>]
122
158
  def enabled_capabilities
123
159
  capabilities = []
124
160
  capabilities << 'In-App Purchase' << 'GameKit' if adhoc? || appstore?
@@ -142,10 +178,10 @@ module AppInfo
142
178
  when 'com.apple.developer.networking.vpn.api'
143
179
  capabilities << 'Personal VPN'
144
180
  when 'com.apple.developer.healthkit',
145
- 'com.apple.developer.healthkit.access'
181
+ 'com.apple.developer.healthkit.access'
146
182
  capabilities << 'HealthKit' unless capabilities.include?('HealthKit')
147
183
  when 'com.apple.developer.icloud-services',
148
- 'com.apple.developer.icloud-container-identifiers'
184
+ 'com.apple.developer.icloud-container-identifiers'
149
185
  capabilities << 'iCloud' unless capabilities.include?('iCloud')
150
186
  when 'com.apple.developer.in-app-payments'
151
187
  capabilities << 'Apple Pay'
@@ -192,18 +228,21 @@ module AppInfo
192
228
  capabilities
193
229
  end
194
230
 
231
+ # @return [String, nil]
195
232
  def [](key)
196
233
  mobileprovision.try(:[], key.to_s)
197
234
  end
198
235
 
236
+ # @return [Boolea]
199
237
  def empty?
200
238
  mobileprovision.nil?
201
239
  end
202
240
 
241
+ # @return [CFPropertyList]
203
242
  def mobileprovision
204
- return @mobileprovision = nil unless File.exist?(@path)
243
+ return @mobileprovision = nil unless ::File.exist?(@file)
205
244
 
206
- data = File.read(@path)
245
+ data = ::File.read(@file)
207
246
  data = strip_plist_wrapper(data) unless bplist?(data)
208
247
  list = CFPropertyList::List.new(data: data).value
209
248
  @mobileprovision = CFPropertyList.native_types(list)
@@ -235,26 +274,5 @@ module AppInfo
235
274
  end_point = raw.index(end_tag) + end_tag.size - 1
236
275
  raw[start_point..end_point]
237
276
  end
238
-
239
- # Developer Certificate
240
- class DeveloperCertificate
241
- attr_reader :raw
242
-
243
- def initialize(data)
244
- @raw = OpenSSL::X509::Certificate.new(data)
245
- end
246
-
247
- def name
248
- @raw.subject.to_a.find { |name, _, _| name == 'CN' }[1].force_encoding('UTF-8')
249
- end
250
-
251
- def created_date
252
- @raw.not_after
253
- end
254
-
255
- def expired_date
256
- @raw.not_before
257
- end
258
- end
259
277
  end
260
278
  end