jira_scan 0.0.4 → 0.0.5
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 +8 -8
- data/bin/jira-scan +49 -1
- data/lib/jira_scan.rb +153 -4
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzE1OTJiNTg5NzIxMzdhYjgwZGU1NzQzZDY2ZTJhOWYzNjA1NTgzZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YzBkOGJlMzkwNzlkMjU5OWJhN2RmYTIwNzA2YTNlMzQ4OTI3NGE2Ng==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTA2NjY0NjdiYzY0YjQzNGE0OTgzZDNkYjM4MzA0OGFhY2VkNGIzNmYzN2Y1
|
10
|
+
YjNmZGE2MjRhNzZhZDUzMTZmMjA3YjkwMDEyZThiYWRlYzRhOTA5MGEyMTRj
|
11
|
+
Y2ZkN2U0NWM0YjFlNDRhMDI5NzJmNTJiNGVhNGU3MGMxZDA4Yzc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MTQxMzE2ODI2MzAxZDA5MjA3N2QxNzkyNGNjMWI1NGE0ZWQ5Y2EwN2I2NzUy
|
14
|
+
ZmIxYzZmZTgyYmQ4MTcxYjNmZDY1YTQ2ZjA0ZDNmYjkwMzU0ZTRlM2FjNWJk
|
15
|
+
ODRjMTM0ZmVhMzg2Yjk4MDJkOTRiMzBhYmYwYmU0OGEyYjNkZWQ=
|
data/bin/jira-scan
CHANGED
@@ -17,7 +17,7 @@ def banner
|
|
17
17
|
_ | | | '__/ _` |\\___ \\ / __/ _` | '_ \\
|
18
18
|
| |__| | | | | (_| |____) | (_| (_| | | | |
|
19
19
|
\\____/|_|_| \\__,_|_____/ \\___\\__,_|_| |_|
|
20
|
-
version 0.0.
|
20
|
+
version 0.0.5"
|
21
21
|
puts
|
22
22
|
puts '-' * 60
|
23
23
|
end
|
@@ -108,6 +108,14 @@ def scan(url, check: true, insecure: false, verbose: false)
|
|
108
108
|
version = JiraScan::getVersionFromLogin(url) unless version
|
109
109
|
puts "+ Version: #{version}" if version
|
110
110
|
|
111
|
+
# Retrieve Jira software information
|
112
|
+
info = JiraScan::getServerInfo(url)
|
113
|
+
unless info.empty?
|
114
|
+
puts "+ Server info:"
|
115
|
+
table = Terminal::Table.new :rows => info
|
116
|
+
puts table
|
117
|
+
end
|
118
|
+
|
111
119
|
# Dev mode enabled
|
112
120
|
dev_mode = JiraScan::devMode(url)
|
113
121
|
puts '+ Dev mode is enabled' if dev_mode
|
@@ -161,6 +169,46 @@ def scan(url, check: true, insecure: false, verbose: false)
|
|
161
169
|
puts table
|
162
170
|
end
|
163
171
|
|
172
|
+
# Retrieve list of installed gadgets
|
173
|
+
gadgets = JiraScan::getGadgets(url)
|
174
|
+
unless gadgets.empty?
|
175
|
+
puts "+ Found gadgets (#{gadgets.length}):"
|
176
|
+
table = Terminal::Table.new :headings => ['Title', 'Author Name', 'Author Email', 'Description'], :rows => gadgets
|
177
|
+
puts table
|
178
|
+
end
|
179
|
+
|
180
|
+
# Retrieve list of resolutions
|
181
|
+
resolutions = JiraScan::getResolutions(url)
|
182
|
+
unless resolutions.empty?
|
183
|
+
puts "+ Found resolutions (#{resolutions.length}):"
|
184
|
+
table = Terminal::Table.new :headings => ['ID', 'Name', 'Description'], :rows => resolutions
|
185
|
+
puts table
|
186
|
+
end
|
187
|
+
|
188
|
+
# Retrieve list of projects
|
189
|
+
projects = JiraScan::getProjects(url)
|
190
|
+
unless projects.empty?
|
191
|
+
puts "+ Found projects (#{projects.length}):"
|
192
|
+
table = Terminal::Table.new :headings => ['ID', 'Key', 'Name'], :rows => projects
|
193
|
+
puts table
|
194
|
+
end
|
195
|
+
|
196
|
+
# Retrieve list of project categories
|
197
|
+
project_categories = JiraScan::getProjectCategories(url)
|
198
|
+
unless project_categories.empty?
|
199
|
+
puts "+ Found project categories (#{project_categories.length}):"
|
200
|
+
table = Terminal::Table.new :headings => ['ID', 'Name', 'Description'], :rows => project_categories
|
201
|
+
puts table
|
202
|
+
end
|
203
|
+
|
204
|
+
# Retrieve list of linked applications
|
205
|
+
apps = JiraScan::getLinkedApps(url)
|
206
|
+
unless apps.empty?
|
207
|
+
puts "+ Found linked applications (#{apps.length}):"
|
208
|
+
table = Terminal::Table.new :headings => ['Link', 'Label', 'Application Type'], :rows => apps
|
209
|
+
puts table
|
210
|
+
end
|
211
|
+
|
164
212
|
# Retrieve list of field names
|
165
213
|
field_names = JiraScan::getFieldNamesQueryComponentDefault(url)
|
166
214
|
unless field_names.empty?
|
data/lib/jira_scan.rb
CHANGED
@@ -11,7 +11,7 @@ require 'net/http'
|
|
11
11
|
require 'openssl'
|
12
12
|
|
13
13
|
class JiraScan
|
14
|
-
VERSION = '0.0.
|
14
|
+
VERSION = '0.0.5'.freeze
|
15
15
|
|
16
16
|
def self.logger
|
17
17
|
@logger
|
@@ -115,6 +115,26 @@ class JiraScan
|
|
115
115
|
"#{version}-##{build}"
|
116
116
|
end
|
117
117
|
|
118
|
+
#
|
119
|
+
# Retrieve Jira software information
|
120
|
+
#
|
121
|
+
# @param [String] URL
|
122
|
+
#
|
123
|
+
# @return [Array] Jira software information
|
124
|
+
#
|
125
|
+
def self.getServerInfo(url)
|
126
|
+
url += '/' unless url.to_s.end_with? '/'
|
127
|
+
res = sendHttpRequest("#{url}rest/api/latest/serverInfo")
|
128
|
+
|
129
|
+
return [] unless res
|
130
|
+
return [] unless res.code.to_i == 200
|
131
|
+
return [] unless res.body.to_s.start_with?('{"baseUrl"')
|
132
|
+
|
133
|
+
JSON.parse(res.body.to_s, symbolize_names: true)
|
134
|
+
rescue
|
135
|
+
[]
|
136
|
+
end
|
137
|
+
|
118
138
|
#
|
119
139
|
# Check if dev mode is enabled
|
120
140
|
#
|
@@ -198,6 +218,7 @@ class JiraScan
|
|
198
218
|
|
199
219
|
#
|
200
220
|
# Check if unauthenticated access to REST UserPicker is allowed (CVE-2019-3403)
|
221
|
+
# https://jira.atlassian.com/browse/JRASERVER-69242
|
201
222
|
#
|
202
223
|
# @param [String] URL
|
203
224
|
#
|
@@ -205,7 +226,7 @@ class JiraScan
|
|
205
226
|
#
|
206
227
|
def self.restUserPicker(url)
|
207
228
|
url += '/' unless url.to_s.end_with? '/'
|
208
|
-
res = sendHttpRequest("#{url}rest/api/
|
229
|
+
res = sendHttpRequest("#{url}rest/api/2/user/picker")
|
209
230
|
|
210
231
|
return false unless res
|
211
232
|
return false unless res.code.to_i == 400
|
@@ -215,6 +236,7 @@ class JiraScan
|
|
215
236
|
|
216
237
|
#
|
217
238
|
# Check if unauthenticated access to REST GroupUserPicker is allowed (CVE-2019-8449)
|
239
|
+
# https://jira.atlassian.com/browse/JRASERVER-69796
|
218
240
|
#
|
219
241
|
# @param [String] URL
|
220
242
|
#
|
@@ -222,7 +244,7 @@ class JiraScan
|
|
222
244
|
#
|
223
245
|
def self.restGroupUserPicker(url)
|
224
246
|
url += '/' unless url.to_s.end_with? '/'
|
225
|
-
res = sendHttpRequest("#{url}rest/api/
|
247
|
+
res = sendHttpRequest("#{url}rest/api/2/groupuserpicker")
|
226
248
|
|
227
249
|
return false unless res
|
228
250
|
return false unless res.code.to_i == 400
|
@@ -230,8 +252,33 @@ class JiraScan
|
|
230
252
|
res.body.to_s.include?('The username query parameter was not provided')
|
231
253
|
end
|
232
254
|
|
255
|
+
#
|
256
|
+
# Retrieve list of installed gadgets
|
257
|
+
# https://jira.atlassian.com/browse/JRASERVER-72613
|
258
|
+
#
|
259
|
+
# @param [String] URL
|
260
|
+
#
|
261
|
+
# @return [Array] list of installed gadgets
|
262
|
+
#
|
263
|
+
def self.getGadgets(url)
|
264
|
+
url += '/' unless url.to_s.end_with? '/'
|
265
|
+
res = sendHttpRequest("#{url}rest/config/1.0/directory.json")
|
266
|
+
|
267
|
+
return [] unless res
|
268
|
+
return [] unless res.code.to_i == 200
|
269
|
+
return [] unless res.body.to_s.start_with?('{"categories"')
|
270
|
+
|
271
|
+
gadgets = JSON.parse(res.body.to_s)['gadgets']
|
272
|
+
return [] if gadgets.empty?
|
273
|
+
|
274
|
+
JSON.parse(gadgets.to_json, symbolize_names: true).map { |g| [g[:title], g[:authorName], g[:authorEmail], g[:description]] }
|
275
|
+
rescue
|
276
|
+
[]
|
277
|
+
end
|
278
|
+
|
233
279
|
#
|
234
280
|
# Check if unauthenticated access to ViewUserHover.jspa is allowed (CVE-2020-14181)
|
281
|
+
# https://jira.atlassian.com/browse/JRASERVER-71560
|
235
282
|
#
|
236
283
|
# @param [String] URL
|
237
284
|
#
|
@@ -249,6 +296,7 @@ class JiraScan
|
|
249
296
|
|
250
297
|
#
|
251
298
|
# Check if META-INF contents are accessible (CVE-2019-8442)
|
299
|
+
# https://jira.atlassian.com/browse/JRASERVER-69241
|
252
300
|
#
|
253
301
|
# @param [String] URL
|
254
302
|
#
|
@@ -266,6 +314,7 @@ class JiraScan
|
|
266
314
|
|
267
315
|
#
|
268
316
|
# Retrieve list of popular filters
|
317
|
+
# https://jira.atlassian.com/browse/JRASERVER-23255
|
269
318
|
#
|
270
319
|
# @param [String] URL
|
271
320
|
#
|
@@ -302,14 +351,113 @@ class JiraScan
|
|
302
351
|
return [] unless res
|
303
352
|
return [] unless res.code.to_i == 200
|
304
353
|
return [] unless res.body.to_s.start_with?('{"startAt"')
|
354
|
+
return [] unless res.body.to_s.include?('id')
|
355
|
+
return [] unless res.body.to_s.include?('name')
|
305
356
|
|
306
357
|
JSON.parse(res.body.to_s, symbolize_names: true)[:dashboards].map { |d| [d[:id], d[:name]] }
|
307
358
|
rescue
|
308
359
|
[]
|
309
360
|
end
|
310
361
|
|
362
|
+
#
|
363
|
+
# Retrieve list of resolutions
|
364
|
+
#
|
365
|
+
# @param [String] URL
|
366
|
+
#
|
367
|
+
# @return [Array] list of resolutions
|
368
|
+
#
|
369
|
+
def self.getResolutions(url)
|
370
|
+
url += '/' unless url.to_s.end_with? '/'
|
371
|
+
res = sendHttpRequest("#{url}rest/api/2/resolution")
|
372
|
+
|
373
|
+
return [] unless res
|
374
|
+
return [] unless res.code.to_i == 200
|
375
|
+
return [] unless res.body.to_s.start_with?('[{"self"')
|
376
|
+
return [] unless res.body.to_s.include?('id')
|
377
|
+
return [] unless res.body.to_s.include?('name')
|
378
|
+
return [] unless res.body.to_s.include?('description')
|
379
|
+
|
380
|
+
JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:id], r[:name], r[:description]] }
|
381
|
+
rescue
|
382
|
+
[]
|
383
|
+
end
|
384
|
+
|
385
|
+
#
|
386
|
+
# Retrieve list of projects
|
387
|
+
#
|
388
|
+
# @param [String] URL
|
389
|
+
#
|
390
|
+
# @return [Array] list of projects
|
391
|
+
#
|
392
|
+
def self.getProjects(url)
|
393
|
+
url += '/' unless url.to_s.end_with? '/'
|
394
|
+
max = 1_000
|
395
|
+
res = sendHttpRequest("#{url}rest/api/2/project?maxResults=#{max}")
|
396
|
+
|
397
|
+
return [] unless res
|
398
|
+
return [] unless res.code.to_i == 200
|
399
|
+
return [] unless res.body.to_s.start_with?('[{"expand"')
|
400
|
+
return [] unless res.body.to_s.include?('id')
|
401
|
+
return [] unless res.body.to_s.include?('key')
|
402
|
+
return [] unless res.body.to_s.include?('name')
|
403
|
+
|
404
|
+
JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:id], r[:key], r[:name]] }
|
405
|
+
rescue
|
406
|
+
[]
|
407
|
+
end
|
408
|
+
|
409
|
+
#
|
410
|
+
# Retrieve list of project categories
|
411
|
+
#
|
412
|
+
# @param [String] URL
|
413
|
+
#
|
414
|
+
# @return [Array] list of project categories
|
415
|
+
#
|
416
|
+
def self.getProjectCategories(url)
|
417
|
+
url += '/' unless url.to_s.end_with? '/'
|
418
|
+
res = sendHttpRequest("#{url}rest/api/2/projectCategory")
|
419
|
+
|
420
|
+
return [] unless res
|
421
|
+
return [] unless res.code.to_i == 200
|
422
|
+
return [] unless res.body.to_s.start_with?('[{"self"')
|
423
|
+
return [] unless res.body.to_s.include?('id')
|
424
|
+
return [] unless res.body.to_s.include?('name')
|
425
|
+
return [] unless res.body.to_s.include?('description')
|
426
|
+
|
427
|
+
JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:id], r[:name], r[:description]] }
|
428
|
+
rescue
|
429
|
+
[]
|
430
|
+
end
|
431
|
+
|
432
|
+
#
|
433
|
+
# Retrieve list of linked applications
|
434
|
+
# https://jira.atlassian.com/browse/JRASERVER-64963
|
435
|
+
# https://jira.atlassian.com/browse/JRACLOUD-64963
|
436
|
+
#
|
437
|
+
# @param [String] URL
|
438
|
+
#
|
439
|
+
# @return [Array] list of linked applications
|
440
|
+
#
|
441
|
+
def self.getLinkedApps(url)
|
442
|
+
url += '/' unless url.to_s.end_with? '/'
|
443
|
+
res = sendHttpRequest("#{url}rest/menu/latest/admin")
|
444
|
+
|
445
|
+
return [] unless res
|
446
|
+
return [] unless res.code.to_i == 200
|
447
|
+
return [] unless res.body.to_s.start_with?('[{"key"')
|
448
|
+
return [] unless res.body.to_s.include?('link')
|
449
|
+
return [] unless res.body.to_s.include?('label')
|
450
|
+
return [] unless res.body.to_s.include?('applicationType')
|
451
|
+
|
452
|
+
JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:link], r[:label], r[:applicationType]] }
|
453
|
+
rescue
|
454
|
+
[]
|
455
|
+
end
|
456
|
+
|
311
457
|
#
|
312
458
|
# Retrieve list of field names from QueryComponent!Default.jspa (CVE-2020-14179)
|
459
|
+
# https://jira.atlassian.com/browse/JRASERVER-71536
|
460
|
+
# https://jira.atlassian.com/browse/JRACLOUD-75661
|
313
461
|
#
|
314
462
|
# @param [String] URL
|
315
463
|
#
|
@@ -342,7 +490,8 @@ class JiraScan
|
|
342
490
|
end
|
343
491
|
|
344
492
|
#
|
345
|
-
# Retrieve list of field names from QueryComponent!Jql.jspa (
|
493
|
+
# Retrieve list of field names from QueryComponent!Jql.jspa (CVE-2020-14179)
|
494
|
+
# https://jira.atlassian.com/browse/JRASERVER-71536
|
346
495
|
#
|
347
496
|
# @param [String] URL
|
348
497
|
#
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jira_scan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brendan Coles
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: terminal-table
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
19
|
+
version: '3.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
26
|
+
version: '3.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: logger
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.4'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.4'
|
41
41
|
description: A simple remote scanner for Atlassian Jira
|
42
42
|
email: bcoles@gmail.com
|
43
43
|
executables:
|