app-info 2.8.4 → 3.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +12 -7
- data/.github/workflows/ci.yml +3 -1
- data/.rubocop.yml +31 -11
- data/CHANGELOG.md +30 -2
- data/Gemfile +7 -1
- data/README.md +74 -9
- data/Rakefile +11 -0
- data/app_info.gemspec +12 -3
- data/lib/app_info/aab.rb +58 -39
- data/lib/app_info/android/signature.rb +114 -0
- data/lib/app_info/android/signatures/base.rb +49 -0
- data/lib/app_info/android/signatures/info.rb +152 -0
- data/lib/app_info/android/signatures/v1.rb +59 -0
- data/lib/app_info/android/signatures/v2.rb +117 -0
- data/lib/app_info/android/signatures/v3.rb +127 -0
- data/lib/app_info/android/signatures/v4.rb +14 -0
- data/lib/app_info/apk.rb +43 -46
- data/lib/app_info/certificate.rb +181 -0
- data/lib/app_info/const.rb +41 -0
- data/lib/app_info/core_ext/object/try.rb +3 -1
- data/lib/app_info/core_ext/string/inflector.rb +2 -0
- data/lib/app_info/dsym/debug_info.rb +72 -0
- data/lib/app_info/dsym/macho.rb +55 -0
- data/lib/app_info/dsym.rb +27 -134
- data/lib/app_info/error.rb +8 -0
- data/lib/app_info/file.rb +23 -0
- data/lib/app_info/helper/archive.rb +37 -0
- data/lib/app_info/helper/file_size.rb +25 -0
- data/lib/app_info/helper/generate_class.rb +29 -0
- data/lib/app_info/helper/protobuf.rb +12 -0
- data/lib/app_info/helper/signatures.rb +229 -0
- data/lib/app_info/helper.rb +5 -126
- data/lib/app_info/info_plist.rb +14 -6
- data/lib/app_info/ipa/framework.rb +4 -4
- data/lib/app_info/ipa.rb +41 -36
- data/lib/app_info/macos.rb +34 -26
- data/lib/app_info/mobile_provision.rb +19 -30
- data/lib/app_info/pe.rb +226 -0
- data/lib/app_info/png_uncrush.rb +5 -4
- data/lib/app_info/proguard.rb +11 -17
- data/lib/app_info/protobuf/manifest.rb +16 -9
- data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
- data/lib/app_info/protobuf/models/README.md +7 -0
- data/lib/app_info/protobuf/models/Resources_pb.rb +2 -0
- data/lib/app_info/protobuf/resources.rb +5 -5
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +91 -42
- metadata +46 -35
data/lib/app_info/info_plist.rb
CHANGED
@@ -6,11 +6,19 @@ require 'app_info/png_uncrush'
|
|
6
6
|
|
7
7
|
module AppInfo
|
8
8
|
# iOS Info.plist parser
|
9
|
-
class InfoPlist
|
9
|
+
class InfoPlist < File
|
10
10
|
extend Forwardable
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
# Icon Key
|
13
|
+
ICON_KEYS = {
|
14
|
+
Device::IPHONE => ['CFBundleIcons'],
|
15
|
+
Device::IPAD => ['CFBundleIcons~ipad'],
|
16
|
+
Device::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
|
17
|
+
Device::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def file_type
|
21
|
+
Format::INFOPLIST
|
14
22
|
end
|
15
23
|
|
16
24
|
def version
|
@@ -126,7 +134,7 @@ module AppInfo
|
|
126
134
|
private
|
127
135
|
|
128
136
|
def info
|
129
|
-
return unless File.file?(@file)
|
137
|
+
return unless ::File.file?(@file)
|
130
138
|
|
131
139
|
@info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: @file).value)
|
132
140
|
end
|
@@ -134,9 +142,9 @@ module AppInfo
|
|
134
142
|
def app_path
|
135
143
|
@app_path ||= case device_type
|
136
144
|
when Device::MACOS
|
137
|
-
File.dirname(@file)
|
145
|
+
::File.dirname(@file)
|
138
146
|
else
|
139
|
-
File.expand_path('../', @file)
|
147
|
+
::File.expand_path('../', @file)
|
140
148
|
end
|
141
149
|
end
|
142
150
|
end
|
@@ -8,7 +8,7 @@ module AppInfo
|
|
8
8
|
extend Forwardable
|
9
9
|
|
10
10
|
def self.parse(path, name = 'Frameworks')
|
11
|
-
files = Dir.glob(File.join(path, name.to_s, '*'))
|
11
|
+
files = Dir.glob(::File.join(path, name.to_s, '*'))
|
12
12
|
return [] if files.empty?
|
13
13
|
|
14
14
|
files.sort.each_with_object([]) do |file, obj|
|
@@ -26,7 +26,7 @@ module AppInfo
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def name
|
29
|
-
File.basename(file)
|
29
|
+
::File.basename(file)
|
30
30
|
end
|
31
31
|
|
32
32
|
def macho
|
@@ -37,11 +37,11 @@ module AppInfo
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def lib?
|
40
|
-
File.file?(file)
|
40
|
+
::File.file?(file)
|
41
41
|
end
|
42
42
|
|
43
43
|
def info
|
44
|
-
@info ||= InfoPlist.new(File.join(file, 'Info.plist'))
|
44
|
+
@info ||= InfoPlist.new(::File.join(file, 'Info.plist'))
|
45
45
|
end
|
46
46
|
|
47
47
|
def to_s
|
data/lib/app_info/ipa.rb
CHANGED
@@ -7,7 +7,7 @@ require 'cfpropertylist'
|
|
7
7
|
|
8
8
|
module AppInfo
|
9
9
|
# IPA parser
|
10
|
-
class IPA
|
10
|
+
class IPA < File
|
11
11
|
include Helper::HumanFileSize
|
12
12
|
include Helper::Archive
|
13
13
|
extend Forwardable
|
@@ -25,18 +25,26 @@ module AppInfo
|
|
25
25
|
INHOUSE = 'Enterprise' # Rename and Alias to enterprise
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
# return file size
|
29
|
+
# @example Read file size in integer
|
30
|
+
# aab.size # => 3618865
|
31
|
+
#
|
32
|
+
# @example Read file size in human readabale
|
33
|
+
# aab.size(human_size: true) # => '3.45 MB'
|
34
|
+
#
|
35
|
+
# @param [Boolean] human_size Convert integer value to human readable.
|
36
|
+
# @return [Integer, String]
|
32
37
|
def size(human_size: false)
|
33
38
|
file_to_human_size(@file, human_size: human_size)
|
34
39
|
end
|
35
40
|
|
36
|
-
def
|
41
|
+
def file_type
|
42
|
+
Format::IPA
|
43
|
+
end
|
44
|
+
|
45
|
+
def platform
|
37
46
|
Platform::IOS
|
38
47
|
end
|
39
|
-
alias file_type os
|
40
48
|
|
41
49
|
def_delegators :info, :iphone?, :ipad?, :universal?, :build_version, :name,
|
42
50
|
:release_version, :identifier, :bundle_id, :display_name,
|
@@ -70,7 +78,7 @@ module AppInfo
|
|
70
78
|
end
|
71
79
|
|
72
80
|
def archs
|
73
|
-
return unless File.exist?(bundle_path)
|
81
|
+
return unless ::File.exist?(bundle_path)
|
74
82
|
|
75
83
|
file = MachO.open(bundle_path)
|
76
84
|
case file
|
@@ -114,14 +122,14 @@ module AppInfo
|
|
114
122
|
end
|
115
123
|
|
116
124
|
def mobileprovision?
|
117
|
-
File.exist?(mobileprovision_path)
|
125
|
+
::File.exist?(mobileprovision_path)
|
118
126
|
end
|
119
127
|
|
120
128
|
def mobileprovision_path
|
121
129
|
filename = 'embedded.mobileprovision'
|
122
|
-
@mobileprovision_path ||= File.join(@file, filename)
|
123
|
-
unless File.exist?(@mobileprovision_path)
|
124
|
-
@mobileprovision_path = File.join(app_path, filename)
|
130
|
+
@mobileprovision_path ||= ::File.join(@file, filename)
|
131
|
+
unless ::File.exist?(@mobileprovision_path)
|
132
|
+
@mobileprovision_path = ::File.join(app_path, filename)
|
125
133
|
end
|
126
134
|
|
127
135
|
@mobileprovision_path
|
@@ -134,15 +142,15 @@ module AppInfo
|
|
134
142
|
end
|
135
143
|
|
136
144
|
def metadata?
|
137
|
-
File.exist?(metadata_path)
|
145
|
+
::File.exist?(metadata_path)
|
138
146
|
end
|
139
147
|
|
140
148
|
def metadata_path
|
141
|
-
@metadata_path ||= File.join(contents, 'iTunesMetadata.plist')
|
149
|
+
@metadata_path ||= ::File.join(contents, 'iTunesMetadata.plist')
|
142
150
|
end
|
143
151
|
|
144
152
|
def bundle_path
|
145
|
-
@bundle_path ||= File.join(app_path, info.bundle_name)
|
153
|
+
@bundle_path ||= ::File.join(app_path, info.bundle_name)
|
146
154
|
end
|
147
155
|
|
148
156
|
def info
|
@@ -150,35 +158,32 @@ module AppInfo
|
|
150
158
|
end
|
151
159
|
|
152
160
|
def info_path
|
153
|
-
@info_path ||= File.join(app_path, 'Info.plist')
|
161
|
+
@info_path ||= ::File.join(app_path, 'Info.plist')
|
154
162
|
end
|
155
163
|
|
156
164
|
def app_path
|
157
|
-
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
|
165
|
+
@app_path ||= Dir.glob(::File.join(contents, 'Payload', '*.app')).first
|
158
166
|
end
|
159
167
|
|
160
168
|
IPHONE_KEY = 'CFBundleIcons'
|
161
169
|
IPAD_KEY = 'CFBundleIcons~ipad'
|
162
170
|
|
163
171
|
def icons_path
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
@icons_path << file
|
172
|
+
@icons_path ||= lambda {
|
173
|
+
icon_keys.each_with_object([]) do |name, icons|
|
174
|
+
filenames = info.try(:[], name)
|
175
|
+
.try(:[], 'CFBundlePrimaryIcon')
|
176
|
+
.try(:[], 'CFBundleIconFiles')
|
177
|
+
|
178
|
+
next if filenames.nil? || filenames.empty?
|
179
|
+
|
180
|
+
filenames.each do |filename|
|
181
|
+
Dir.glob(::File.join(app_path, "#{filename}*")).find_all.each do |file|
|
182
|
+
icons << file
|
183
|
+
end
|
177
184
|
end
|
178
185
|
end
|
179
|
-
|
180
|
-
|
181
|
-
@icons_path
|
186
|
+
}.call
|
182
187
|
end
|
183
188
|
|
184
189
|
def clear!
|
@@ -197,7 +202,7 @@ module AppInfo
|
|
197
202
|
end
|
198
203
|
|
199
204
|
def contents
|
200
|
-
@contents ||= unarchive(@file,
|
205
|
+
@contents ||= unarchive(@file, prefix: 'ios')
|
201
206
|
end
|
202
207
|
|
203
208
|
private
|
@@ -206,7 +211,7 @@ module AppInfo
|
|
206
211
|
uncrushed_file = uncrush ? uncrush_png(file) : nil
|
207
212
|
|
208
213
|
{
|
209
|
-
name: File.basename(file),
|
214
|
+
name: ::File.basename(file),
|
210
215
|
file: file,
|
211
216
|
uncrushed_file: uncrushed_file,
|
212
217
|
dimensions: PngUncrush.dimensions(file)
|
@@ -217,7 +222,7 @@ module AppInfo
|
|
217
222
|
def uncrush_png(src_file)
|
218
223
|
dest_file = tempdir(src_file, prefix: 'uncrushed')
|
219
224
|
PngUncrush.decompress(src_file, dest_file)
|
220
|
-
File.exist?(dest_file) ? dest_file : nil
|
225
|
+
::File.exist?(dest_file) ? dest_file : nil
|
221
226
|
end
|
222
227
|
|
223
228
|
def icon_keys
|
data/lib/app_info/macos.rb
CHANGED
@@ -7,7 +7,7 @@ require 'cfpropertylist'
|
|
7
7
|
|
8
8
|
module AppInfo
|
9
9
|
# MacOS App parser
|
10
|
-
class Macos
|
10
|
+
class Macos < File
|
11
11
|
include Helper::HumanFileSize
|
12
12
|
include Helper::Archive
|
13
13
|
extend Forwardable
|
@@ -21,18 +21,26 @@ module AppInfo
|
|
21
21
|
APPSTORE = 'AppStore'
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
# return file size
|
25
|
+
# @example Read file size in integer
|
26
|
+
# aab.size # => 3618865
|
27
|
+
#
|
28
|
+
# @example Read file size in human readabale
|
29
|
+
# aab.size(human_size: true) # => '3.45 MB'
|
30
|
+
#
|
31
|
+
# @param [Boolean] human_size Convert integer value to human readable.
|
32
|
+
# @return [Integer, String]
|
28
33
|
def size(human_size: false)
|
29
34
|
file_to_human_size(@file, human_size: human_size)
|
30
35
|
end
|
31
36
|
|
32
|
-
def
|
37
|
+
def file_type
|
38
|
+
Format::MACOS
|
39
|
+
end
|
40
|
+
|
41
|
+
def platform
|
33
42
|
Platform::MACOS
|
34
43
|
end
|
35
|
-
alias file_type os
|
36
44
|
|
37
45
|
def_delegators :info, :macos?, :iphone?, :ipad?, :universal?, :build_version, :name,
|
38
46
|
:release_version, :identifier, :bundle_id, :display_name,
|
@@ -56,14 +64,14 @@ module AppInfo
|
|
56
64
|
end
|
57
65
|
|
58
66
|
def stored?
|
59
|
-
File.exist?(store_path)
|
67
|
+
::File.exist?(store_path)
|
60
68
|
end
|
61
69
|
|
62
70
|
def icons(convert: true)
|
63
71
|
return unless icon_file
|
64
72
|
|
65
73
|
data = {
|
66
|
-
name: File.basename(icon_file),
|
74
|
+
name: ::File.basename(icon_file),
|
67
75
|
file: icon_file
|
68
76
|
}
|
69
77
|
|
@@ -72,7 +80,7 @@ module AppInfo
|
|
72
80
|
end
|
73
81
|
|
74
82
|
def archs
|
75
|
-
return unless File.exist?(binary_path)
|
83
|
+
return unless ::File.exist?(binary_path)
|
76
84
|
|
77
85
|
file = MachO.open(binary_path)
|
78
86
|
case file
|
@@ -97,25 +105,25 @@ module AppInfo
|
|
97
105
|
end
|
98
106
|
|
99
107
|
def mobileprovision?
|
100
|
-
File.exist?(mobileprovision_path)
|
108
|
+
::File.exist?(mobileprovision_path)
|
101
109
|
end
|
102
110
|
|
103
111
|
def mobileprovision_path
|
104
|
-
@mobileprovision_path ||= File.join(app_path, 'Contents', 'embedded.provisionprofile')
|
112
|
+
@mobileprovision_path ||= ::File.join(app_path, 'Contents', 'embedded.provisionprofile')
|
105
113
|
end
|
106
114
|
|
107
115
|
def store_path
|
108
|
-
@store_path ||= File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
|
116
|
+
@store_path ||= ::File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
|
109
117
|
end
|
110
118
|
|
111
119
|
def binary_path
|
112
120
|
return @binary_path if @binary_path
|
113
121
|
|
114
|
-
base_path = File.join(app_path, 'Contents', 'MacOS')
|
122
|
+
base_path = ::File.join(app_path, 'Contents', 'MacOS')
|
115
123
|
binary = info['CFBundleExecutable']
|
116
|
-
return File.join(base_path, binary) if binary
|
124
|
+
return ::File.join(base_path, binary) if binary
|
117
125
|
|
118
|
-
@binary_path ||= Dir.glob(File.join(base_path, '*')).first
|
126
|
+
@binary_path ||= Dir.glob(::File.join(base_path, '*')).first
|
119
127
|
end
|
120
128
|
|
121
129
|
def info
|
@@ -123,11 +131,11 @@ module AppInfo
|
|
123
131
|
end
|
124
132
|
|
125
133
|
def info_path
|
126
|
-
@info_path ||= File.join(app_path, 'Contents', 'Info.plist')
|
134
|
+
@info_path ||= ::File.join(app_path, 'Contents', 'Info.plist')
|
127
135
|
end
|
128
136
|
|
129
137
|
def app_path
|
130
|
-
@app_path ||= Dir.glob(File.join(contents, '*.app')).first
|
138
|
+
@app_path ||= Dir.glob(::File.join(contents, '*.app')).first
|
131
139
|
end
|
132
140
|
|
133
141
|
def clear!
|
@@ -137,14 +145,14 @@ module AppInfo
|
|
137
145
|
|
138
146
|
@contents = nil
|
139
147
|
@app_path = nil
|
140
|
-
@
|
148
|
+
@binary_path = nil
|
141
149
|
@info_path = nil
|
142
150
|
@info = nil
|
143
151
|
@icons = nil
|
144
152
|
end
|
145
153
|
|
146
154
|
def contents
|
147
|
-
@contents ||= unarchive(@file,
|
155
|
+
@contents ||= unarchive(@file, prefix: 'macos')
|
148
156
|
end
|
149
157
|
|
150
158
|
private
|
@@ -155,8 +163,8 @@ module AppInfo
|
|
155
163
|
info.icons.each do |key|
|
156
164
|
next unless value = info[key]
|
157
165
|
|
158
|
-
file = File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
|
159
|
-
next unless File.file?(file)
|
166
|
+
file = ::File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
|
167
|
+
next unless ::File.file?(file)
|
160
168
|
|
161
169
|
return @icon_file = file
|
162
170
|
end
|
@@ -173,14 +181,14 @@ module AppInfo
|
|
173
181
|
file = data[:file]
|
174
182
|
reader = Icns::Reader.new(file)
|
175
183
|
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')
|
184
|
+
dest_filename = "#{::File.basename(file, '.icns')}_#{size}x#{size}.png"
|
185
|
+
dest_file = tempdir(::File.join(::File.dirname(file), dest_filename), prefix: 'converted')
|
178
186
|
next unless icon_data = reader.image(size: size)
|
179
187
|
|
180
|
-
File.write(dest_file, icon_data, encoding: Encoding::BINARY)
|
188
|
+
::File.write(dest_file, icon_data, encoding: Encoding::BINARY)
|
181
189
|
|
182
190
|
data[:sets] << {
|
183
|
-
name: File.basename(dest_filename),
|
191
|
+
name: ::File.basename(dest_filename),
|
184
192
|
file: dest_file,
|
185
193
|
dimensions: ImageSize.path(dest_file).size
|
186
194
|
}
|
@@ -5,9 +5,9 @@ require 'cfpropertylist'
|
|
5
5
|
|
6
6
|
module AppInfo
|
7
7
|
# .mobileprovision file parser
|
8
|
-
class MobileProvision
|
9
|
-
def
|
10
|
-
|
8
|
+
class MobileProvision < File
|
9
|
+
def file_type
|
10
|
+
Format::MOBILEPROVISION
|
11
11
|
end
|
12
12
|
|
13
13
|
def name
|
@@ -66,12 +66,22 @@ module AppInfo
|
|
66
66
|
mobileprovision.try(:[], 'Entitlements')
|
67
67
|
end
|
68
68
|
|
69
|
+
# return developer certificates.
|
70
|
+
#
|
71
|
+
# @deprecated Use {#certificates} instead of this method.
|
69
72
|
def developer_certs
|
73
|
+
certificates
|
74
|
+
end
|
75
|
+
|
76
|
+
# return developer certificates.
|
77
|
+
#
|
78
|
+
# @return [Array<Certificate>]
|
79
|
+
def certificates
|
70
80
|
certs = mobileprovision.try(:[], 'DeveloperCertificates')
|
71
81
|
return if certs.empty?
|
72
82
|
|
73
|
-
certs.each_with_object([]) do |
|
74
|
-
obj <<
|
83
|
+
certs.each_with_object([]) do |cert_data, obj|
|
84
|
+
obj << Certificate.parse(cert_data)
|
75
85
|
end
|
76
86
|
end
|
77
87
|
|
@@ -142,10 +152,10 @@ module AppInfo
|
|
142
152
|
when 'com.apple.developer.networking.vpn.api'
|
143
153
|
capabilities << 'Personal VPN'
|
144
154
|
when 'com.apple.developer.healthkit',
|
145
|
-
|
155
|
+
'com.apple.developer.healthkit.access'
|
146
156
|
capabilities << 'HealthKit' unless capabilities.include?('HealthKit')
|
147
157
|
when 'com.apple.developer.icloud-services',
|
148
|
-
|
158
|
+
'com.apple.developer.icloud-container-identifiers'
|
149
159
|
capabilities << 'iCloud' unless capabilities.include?('iCloud')
|
150
160
|
when 'com.apple.developer.in-app-payments'
|
151
161
|
capabilities << 'Apple Pay'
|
@@ -201,9 +211,9 @@ module AppInfo
|
|
201
211
|
end
|
202
212
|
|
203
213
|
def mobileprovision
|
204
|
-
return @mobileprovision = nil unless File.exist?(@
|
214
|
+
return @mobileprovision = nil unless ::File.exist?(@file)
|
205
215
|
|
206
|
-
data = File.read(@
|
216
|
+
data = ::File.read(@file)
|
207
217
|
data = strip_plist_wrapper(data) unless bplist?(data)
|
208
218
|
list = CFPropertyList::List.new(data: data).value
|
209
219
|
@mobileprovision = CFPropertyList.native_types(list)
|
@@ -235,26 +245,5 @@ module AppInfo
|
|
235
245
|
end_point = raw.index(end_tag) + end_tag.size - 1
|
236
246
|
raw[start_point..end_point]
|
237
247
|
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
248
|
end
|
260
249
|
end
|