headdesk 0.1.0

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.
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Headdesk
4
+ module Checks
5
+ #
6
+ # Check the version of the Facebook SDK
7
+ #
8
+ # Facebook deprecates SDKs 2 years after they are released.
9
+ #
10
+ class FacebookSDK
11
+ include Check::APK
12
+
13
+ describe 'Facebook SDK version'
14
+ def call
15
+ skip_check unless: -> { apk.class?('com/facebook/FacebookSdk') }
16
+ facebook_sdk = apk.find_class('com/facebook/FacebookSdk')
17
+
18
+ # TODO: Parse https://developers.facebook.com/docs/android/change-log-4x
19
+ # and fail if > 2 years old, warn if < 3 months remaining
20
+ get_sdk_version = facebook_sdk.method('getSdkVersion').code
21
+ describe 'com.facebook.FacebookSdk contains getSdkVersion method'
22
+ fail_check if: -> { !get_sdk_version }
23
+
24
+ facebook_sdks = YAML.load_file(Headdesk::FACEBOOK_SDK_VERSIONS_YAML)
25
+ major, minor, patch = get_sdk_version.match(/const-string v0, "(\d+)\.(\d+)\.(\d+)"/).captures.map(&:to_i)
26
+
27
+ sdk_in_use = (facebook_sdks.select do |sdk|
28
+ sdk[:major] == major && sdk[:minor] == minor && sdk[:patch] == patch
29
+ end).first
30
+
31
+ describe "Found Facebook SDK version #{sdk_in_use[:version]}"
32
+ fail_check if: -> { !sdk_in_use }
33
+
34
+ export facebook_sdk: sdk_in_use
35
+
36
+ describe "Facebook SDK was released in the last 2 years (using #{sdk_in_use[:version]}, released #{sdk_in_use[:date]})"
37
+ fail_check if: -> { sdk_in_use[:date] < (Date.today - (365 * 2)) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Headdesk
4
+ module Checks
5
+ #
6
+ # Make sure all <reciever> blocks in AndroidManifest.xml point to a Java class
7
+ # that exists in the APK.
8
+ #
9
+ class Receiver
10
+ include Check::APK
11
+
12
+ describe 'All <receiver> blocks in AndroidManifest.xml point to valid Java classes'
13
+ def call
14
+ receivers = []
15
+ apk.android_manifest.xpath('//receiver').each do |receiver|
16
+ receiver_name = receiver.attributes['name'].to_s
17
+ fail_check unless: -> { apk.class?(receiver_name) }
18
+ klass = apk.find_class(receiver_name)
19
+
20
+ describe "#{receiver_name} has onReceive method"
21
+ fail_check unless: -> { klass.method?('onReceive') }
22
+
23
+ receivers << {
24
+ name: receiver_name
25
+ }
26
+ end
27
+ export receivers: receivers
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Headdesk
4
+ module Checks
5
+ #
6
+ # Module for Teak checks
7
+ #
8
+ module Teak
9
+ #
10
+ # Module for Teak APK checks
11
+ #
12
+ module APK
13
+ def self.included(klass)
14
+ klass.include(Check::APK)
15
+ klass.extend(Preconditions)
16
+ klass.include(Utility)
17
+ end
18
+
19
+ #
20
+ # Precondition tests for presence of Teak class SDK in APK
21
+ #
22
+ module Preconditions
23
+ def preconditions?
24
+ false unless apk.class?('io.teak.sdk.Teak')
25
+ end
26
+ end
27
+
28
+ #
29
+ # Utility methods for Teak based checks
30
+ #
31
+ module Utility
32
+ def teak_sdk
33
+ return @teak_sdk if @teak_sdk
34
+
35
+ major, minor, revision = apk.find_class('io.teak.sdk.Teak')
36
+ .field('SDKVersion')
37
+ .value
38
+ .to_version
39
+ @teak_sdk = OpenStruct.new(
40
+ version: "#{major}.#{minor}.#{revision}",
41
+ major: major, minor: minor, revision: revision
42
+ )
43
+
44
+ @teak_sdk
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ Dir[File.dirname(__FILE__) + '/teak/*.rb'].each { |file| require file }
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Headdesk
4
+ module Checks
5
+ module Teak
6
+ #
7
+ # Check to make sure that an APK, which uses Teak, has an API 21+ icon.
8
+ #
9
+ class Api21Icon
10
+ include Checks::Teak::APK
11
+
12
+ describe 'Check for API 21+ Teak icons'
13
+ # :reek:UncommunicativeVariableName { accept: ['icon_v21'] }
14
+ def call
15
+ skip_check if: -> { apk.min_sdk 21 }
16
+
17
+ icon = apk.resources
18
+ .values(v: 20)
19
+ .drawable.io_teak_small_notification_icon
20
+ icon_v21 = apk.resources
21
+ .values(v: 21)
22
+ .drawable.io_teak_small_notification_icon
23
+ export icon: icon, icon_v21: icon_v21
24
+
25
+ describe "APK contains a drawable for 'io_teak_small_notification_icon'"
26
+ fail_check unless: -> { icon && icon_v21 }
27
+
28
+ describe "'io_teak_small_notification_icon' for v21 is different from < v21"
29
+ fail_check unless: -> { icon != icon_v21 }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Headdesk
4
+ module Checks
5
+ module Teak
6
+ #
7
+ # Check to make sure that an APK, which uses Teak, has caching enabled.
8
+ #
9
+ class Caching
10
+ include Checks::Teak::APK
11
+
12
+ describe 'Check for io_teak_enable_caching'
13
+ def call
14
+ describe 'Teak SDK version is lower than 2.0.0'
15
+ major, = apk.find_class('io.teak.sdk.Teak')
16
+ .field('SDKVersion')
17
+ .value
18
+ .to_version
19
+ skip_check if: major.to_i >= 2
20
+
21
+ describe "APK enables caching of Teak notification content (via 'io_teak_enable_caching')"
22
+ fail_check unless: apk.resources
23
+ .values(v: 21)
24
+ .bool
25
+ .io_teak_enable_caching
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Headdesk
4
+ module Checks
5
+ module Teak
6
+ #
7
+ # Check to make sure that an APK, which uses Teak, has caching enabled.
8
+ #
9
+ class Configuration
10
+ include Checks::Teak::APK
11
+
12
+ describe 'Check Teak configuration'
13
+ # :reek:UncommunicativeVariableName { accept: ['gcm_defaultSenderId'] }
14
+ def call
15
+ # App Id
16
+ teak_app_id = apk.resources
17
+ .values
18
+ .string
19
+ .io_teak_app_id
20
+ # TODO: Manifest meta-data
21
+ describe 'Teak App Id configured'
22
+ fail_check if: !teak_app_id
23
+ export teak_app_id: teak_app_id
24
+
25
+ # Api Key
26
+ teak_api_key = apk.resources
27
+ .values
28
+ .string
29
+ .io_teak_api_key
30
+ # TODO: Manifest meta-data
31
+ describe 'Teak API Key configured'
32
+ fail_check if: !teak_api_key
33
+ export teak_api_key: teak_api_key
34
+
35
+ # GCM Sender Id
36
+ io_teak_gcm_sender_id = apk.resources
37
+ .values
38
+ .string
39
+ .io_teak_gcm_sender_id
40
+ # TODO: Manifest meta-data
41
+ gcm_defaultSenderId ||= apk.resources
42
+ .values
43
+ .string
44
+ .gcm_defaultSenderId
45
+ describe "'gcm_defaultSenderId' is specified, and different from 'io_teak_gcm_sender_id'"
46
+ fail_check if: gcm_defaultSenderId != io_teak_gcm_sender_id if gcm_defaultSenderId
47
+
48
+ gcm_sender_id = io_teak_gcm_sender_id || gcm_defaultSenderId
49
+ describe "Either 'io_teak_gcm_sender_id' or 'gcm_defaultSenderId' configured"
50
+ fail_check if: !gcm_sender_id
51
+ export gcm_sender_id: gcm_sender_id
52
+
53
+ # Firebase App Id
54
+ io_teak_firebase_app_id = apk.resources
55
+ .values
56
+ .string
57
+ .io_teak_firebase_app_id
58
+ # TODO: Manifest meta-data
59
+ google_app_id = apk.resources
60
+ .values
61
+ .string
62
+ .google_app_id
63
+ describe "'google_app_id' is specified, and different from 'io_teak_firebase_app_id'"
64
+ fail_check if: google_app_id != io_teak_firebase_app_id if google_app_id
65
+
66
+ firebase_app_id = google_app_id || io_teak_firebase_app_id
67
+ describe "Either 'io_teak_firebase_app_id' or 'google_app_id' configured"
68
+ fail_check if: !firebase_app_id, skip_if: teak_sdk.major < 2
69
+ export gcm_sender_id: gcm_sender_id
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tmpdir'
4
+ require 'thor'
5
+ require 'headdesk'
6
+ require 'awesome_print'
7
+
8
+ module Headdesk
9
+ #
10
+ # headdesk CLI
11
+ #
12
+ # :reek:TooManyStatements
13
+ class CLI < Thor
14
+ desc 'unpack FILE [DESTINATION]', 'Unpack an APK or IPA to [DESTINATION] or to the current directory'
15
+ method_option :analize, type: :boolean, aliases: '-a'
16
+ def unpack(file, destination = nil)
17
+ # Make sure the input file exists
18
+ unless File.exist?(file)
19
+ STDERR.puts "Could not find: #{file}"
20
+ CLI.command_help(Thor::Base.shell.new, 'unpack')
21
+ return 1
22
+ end
23
+
24
+ # Make sure destination exists, if specified
25
+ unless !destination || Dir.exist?(destination)
26
+ STDERR.puts "Could not find destination path: #{destination}"
27
+ CLI.command_help(Thor::Base.shell.new, 'unpack')
28
+ return 1
29
+ end
30
+
31
+ begin
32
+ stdout = nil
33
+ output_path = destination
34
+
35
+ if !destination
36
+ # Output to tempdir, then copy to cwd if no destination specified
37
+ Dir.mktmpdir do |tmp_dir|
38
+ output_path = tmp_dir
39
+ stdout = Headdesk::ApkTool.unpack_to(file, tmp_dir)
40
+ FileUtils.cp_r("#{tmp_dir}/.", Dir.pwd)
41
+ end
42
+ else
43
+ stdout = Headdesk::ApkTool.unpack_to(file, destination)
44
+ end
45
+
46
+ # Analize if requested
47
+ Headdesk::Analize.at(output_path) if options[:analize]
48
+ rescue CliError => cli_err
49
+ STDERR.puts cli_err.message
50
+ CLI.command_help(Thor::Base.shell.new, 'unpack')
51
+ return 1
52
+ rescue StandardError => rb_err
53
+ STDERR.puts err.message.red
54
+ STDERR.puts err.backtrace.ai
55
+ return 1
56
+ end
57
+ end
58
+
59
+ desc 'analize [FILE]', 'Analize an APK or IPA'
60
+ method_option :path, type: :string
61
+ method_option :json, type: :boolean
62
+ def analize(file = nil)
63
+ # Make sure input file exsts, if specified
64
+ unless !file || File.exist?(file)
65
+ STDERR.puts "Could not find input file: #{file}"
66
+ CLI.command_help(Thor::Base.shell.new, 'analize')
67
+ return 1
68
+ end
69
+
70
+ # Unpack APK if needed
71
+ path = options[:path]
72
+ tmp_dir = nil
73
+ if file
74
+ path = tmp_dir = Dir.mktmpdir
75
+ Headdesk::ApkTool.unpack_to(file, tmp_dir)
76
+ end
77
+
78
+ # Make sure path exists
79
+ unless Dir.exist?(path)
80
+ STDERR.puts "Could not find path: #{path}"
81
+ CLI.command_help(Thor::Base.shell.new, 'analize')
82
+ return 1
83
+ end
84
+
85
+ # Analize
86
+ begin
87
+ report = Headdesk::Analize.at(path)
88
+
89
+ if options[:json]
90
+ STDOUT.puts report.to_json
91
+ else
92
+ STDOUT.puts report.to_s
93
+ end
94
+ rescue CliError => cli_err
95
+ STDERR.puts cli_err.message
96
+ CLI.command_help(Thor::Base.shell.new, 'analize')
97
+ return 1
98
+ rescue StandardError => err
99
+ STDERR.puts err.message.red
100
+ STDERR.puts err.backtrace.ai
101
+ return 1
102
+ end
103
+ ensure
104
+ FileUtils.remove_entry(tmp_dir) if tmp_dir
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,281 @@
1
+ ---
2
+ - :major: 4
3
+ :minor: 38
4
+ :patch: 1
5
+ :version: 4.38.1
6
+ :date: 2018-11-01
7
+ - :major: 4
8
+ :minor: 38
9
+ :patch: 0
10
+ :version: 4.38.0
11
+ :date: 2018-10-23
12
+ - :major: 4
13
+ :minor: 37
14
+ :patch: 0
15
+ :version: 4.37.0
16
+ :date: 2018-09-27
17
+ - :major: 4
18
+ :minor: 36
19
+ :patch: 1
20
+ :version: 4.36.1
21
+ :date: 2018-09-17
22
+ - :major: 4
23
+ :minor: 36
24
+ :patch: 0
25
+ :version: 4.36.0
26
+ :date: 2018-08-29
27
+ - :major: 4
28
+ :minor: 35
29
+ :patch: 0
30
+ :version: 4.35.0
31
+ :date: 2018-07-26
32
+ - :major: 4
33
+ :minor: 34
34
+ :patch: 0
35
+ :version: 4.34.0
36
+ :date: 2018-06-18
37
+ - :major: 4
38
+ :minor: 33
39
+ :patch: 0
40
+ :version: 4.33.0
41
+ :date: 2018-05-01
42
+ - :major: 4
43
+ :minor: 32
44
+ :patch: 0
45
+ :version: 4.32.0
46
+ :date: 2018-04-11
47
+ - :major: 4
48
+ :minor: 31
49
+ :patch: 0
50
+ :version: 4.31.0
51
+ :date: 2018-02-28
52
+ - :major: 4
53
+ :minor: 30
54
+ :patch: 0
55
+ :version: 4.30.0
56
+ :date: 2018-01-24
57
+ - :major: 4
58
+ :minor: 29
59
+ :patch: 0
60
+ :version: 4.29.0
61
+ :date: 2017-12-05
62
+ - :major: 4
63
+ :minor: 28
64
+ :patch: 0
65
+ :version: 4.28.0
66
+ :date: 2017-11-07
67
+ - :major: 4
68
+ :minor: 27
69
+ :patch: 0
70
+ :version: 4.27.0
71
+ :date: 2017-09-26
72
+ - :major: 4
73
+ :minor: 26
74
+ :patch: 0
75
+ :version: 4.26.0
76
+ :date: 2017-08-24
77
+ - :major: 4
78
+ :minor: 25
79
+ :patch: 0
80
+ :version: 4.25.0
81
+ :date: 2017-07-26
82
+ - :major: 4
83
+ :minor: 24
84
+ :patch: 0
85
+ :version: 4.24.0
86
+ :date: 2017-06-26
87
+ - :major: 4
88
+ :minor: 23
89
+ :patch: 0
90
+ :version: 4.23.0
91
+ :date: 2017-05-25
92
+ - :major: 4
93
+ :minor: 22
94
+ :patch: 1
95
+ :version: 4.22.1
96
+ :date: 2017-05-11
97
+ - :major: 4
98
+ :minor: 22
99
+ :patch: 0
100
+ :version: 4.22.0
101
+ :date: 2017-04-18
102
+ - :major: 4
103
+ :minor: 21
104
+ :patch: 1
105
+ :version: 4.21.1
106
+ :date: 2017-04-06
107
+ - :major: 4
108
+ :minor: 21
109
+ :patch: 0
110
+ :version: 4.21.0
111
+ :date: 2017-04-04
112
+ - :major: 4
113
+ :minor: 20
114
+ :patch: 0
115
+ :version: 4.20.0
116
+ :date: 2017-03-01
117
+ - :major: 4
118
+ :minor: 19
119
+ :patch: 0
120
+ :version: 4.19.0
121
+ :date: 2017-01-25
122
+ - :major: 4
123
+ :minor: 18
124
+ :patch: 0
125
+ :version: 4.18.0
126
+ :date: 2016-11-30
127
+ - :major: 4
128
+ :minor: 17
129
+ :patch: 0
130
+ :version: 4.17.0
131
+ :date: 2016-10-26
132
+ - :major: 4
133
+ :minor: 16
134
+ :patch: 1
135
+ :version: 4.16.1
136
+ :date: 2016-10-07
137
+ - :major: 4
138
+ :minor: 16
139
+ :patch: 0
140
+ :version: 4.16.0
141
+ :date: 2016-09-27
142
+ - :major: 4
143
+ :minor: 15
144
+ :patch: 0
145
+ :version: 4.15.0
146
+ :date: 2016-08-23
147
+ - :major: 4
148
+ :minor: 14
149
+ :patch: 1
150
+ :version: 4.14.1
151
+ :date: 2016-08-04
152
+ - :major: 4
153
+ :minor: 14
154
+ :patch: 0
155
+ :version: 4.14.0
156
+ :date: 2016-07-13
157
+ - :major: 4
158
+ :minor: 13
159
+ :patch: 2
160
+ :version: 4.13.2
161
+ :date: 2016-07-01
162
+ - :major: 4
163
+ :minor: 13
164
+ :patch: 1
165
+ :version: 4.13.1
166
+ :date: 2016-06-17
167
+ - :major: 4
168
+ :minor: 13
169
+ :patch: 0
170
+ :version: 4.13.0
171
+ :date: 2016-06-15
172
+ - :major: 4
173
+ :minor: 12
174
+ :patch: 1
175
+ :version: 4.12.1
176
+ :date: 2016-05-26
177
+ - :major: 4
178
+ :minor: 12
179
+ :patch: 0
180
+ :version: 4.12.0
181
+ :date: 2016-05-20
182
+ - :major: 4
183
+ :minor: 11
184
+ :patch: 0
185
+ :version: 4.11.0
186
+ :date: 2016-04-12
187
+ - :major: 4
188
+ :minor: 10
189
+ :patch: 1
190
+ :version: 4.10.1
191
+ :date: 2016-03-18
192
+ - :major: 4
193
+ :minor: 10
194
+ :patch: 0
195
+ :version: 4.10.0
196
+ :date: 2016-02-10
197
+ - :major: 4
198
+ :minor: 9
199
+ :patch: 0
200
+ :version: 4.9.0
201
+ :date: 2016-01-13
202
+ - :major: 4
203
+ :minor: 8
204
+ :patch: 2
205
+ :version: 4.8.2
206
+ :date: 2015-11-23
207
+ - :major: 4
208
+ :minor: 8
209
+ :patch: 1
210
+ :version: 4.8.1
211
+ :date: 2015-11-11
212
+ - :major: 4
213
+ :minor: 8
214
+ :patch: 0
215
+ :version: 4.8.0
216
+ :date: 2015-11-11
217
+ - :major: 4
218
+ :minor: 7
219
+ :patch: 0
220
+ :version: 4.7.0
221
+ :date: 2015-10-07
222
+ - :major: 4
223
+ :minor: 6
224
+ :patch: 0
225
+ :version: 4.6.0
226
+ :date: 2015-09-10
227
+ - :major: 4
228
+ :minor: 5
229
+ :patch: 1
230
+ :version: 4.5.1
231
+ :date: 2015-08-13
232
+ - :major: 4
233
+ :minor: 5
234
+ :patch: 0
235
+ :version: 4.5.0
236
+ :date: 2015-08-10
237
+ - :major: 4
238
+ :minor: 4
239
+ :patch: 1
240
+ :version: 4.4.1
241
+ :date: 2015-07-13
242
+ - :major: 4
243
+ :minor: 4
244
+ :patch: 0
245
+ :version: 4.4.0
246
+ :date: 2015-07-08
247
+ - :major: 4
248
+ :minor: 3
249
+ :patch: 0
250
+ :version: 4.3.0
251
+ :date: 2015-06-25
252
+ - :major: 4
253
+ :minor: 2
254
+ :patch: 0
255
+ :version: 4.2.0
256
+ :date: 2015-05-28
257
+ - :major: 4
258
+ :minor: 1
259
+ :patch: 2
260
+ :version: 4.1.2
261
+ :date: 2015-05-14
262
+ - :major: 4
263
+ :minor: 1
264
+ :patch: 1
265
+ :version: 4.1.1
266
+ :date: 2015-05-06
267
+ - :major: 4
268
+ :minor: 1
269
+ :patch: 0
270
+ :version: 4.1.0
271
+ :date: 2015-04-30
272
+ - :major: 4
273
+ :minor: 0
274
+ :patch: 1
275
+ :version: 4.0.1
276
+ :date: 2015-04-02
277
+ - :major: 4
278
+ :minor: 0
279
+ :patch: 0
280
+ :version: 4.0.0
281
+ :date: 2015-03-25