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.
Files changed (4) hide show
  1. checksums.yaml +8 -8
  2. data/bin/jira-scan +49 -1
  3. data/lib/jira_scan.rb +153 -4
  4. metadata +10 -10
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjNmYTk1OWYwM2VjNzJlZjVmZGFlZGIyNzdlYmUyOGE3Mzg0NTIxNg==
4
+ YzE1OTJiNTg5NzIxMzdhYjgwZGU1NzQzZDY2ZTJhOWYzNjA1NTgzZA==
5
5
  data.tar.gz: !binary |-
6
- NDA1MWUyMjE2ODIwODBhMThjYjU2ZWNlY2VhMjcxY2E0YWIyODYyZQ==
6
+ YzBkOGJlMzkwNzlkMjU5OWJhN2RmYTIwNzA2YTNlMzQ4OTI3NGE2Ng==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MjliM2EyYWE0MzFjNDllMWMyOTljMDYyOGRkYTU3ZDc2NTk2MDc0ZTg0ODJi
10
- Zjg3MTAwOGU5MjkzNmEzNmZkNGZkYWY4YTNhMDE3Mzg0YzEzNjVkOGUzMjMy
11
- YTI1MGQ0NDg1NzA4Y2YzNjE3ODM0MWQ2NTJiOTk1NDUzZDI1ZmU=
9
+ OTA2NjY0NjdiYzY0YjQzNGE0OTgzZDNkYjM4MzA0OGFhY2VkNGIzNmYzN2Y1
10
+ YjNmZGE2MjRhNzZhZDUzMTZmMjA3YjkwMDEyZThiYWRlYzRhOTA5MGEyMTRj
11
+ Y2ZkN2U0NWM0YjFlNDRhMDI5NzJmNTJiNGVhNGU3MGMxZDA4Yzc=
12
12
  data.tar.gz: !binary |-
13
- NDM2MTdiY2ViMzVlYmEwZTg5MTY4NGI0NWY0M2IwZmFjMGFjYmM5MzRhZmFi
14
- NmYxYTkzYWE4ODAzNDYwNzNjZTNmNGE1OGM1OGJjMzAyZDYwYWRkMzg0NWRl
15
- NzVmNTQ2NmY3NTNhZWQ3ODA0MWZhNDVjN2M0YzQ2OTQ5Y2M1MDg=
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.4"
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.4'.freeze
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/latest/user/picker")
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/latest/groupuserpicker")
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 (EDB-49924)
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
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-07-12 00:00:00.000000000 Z
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: '0'
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: '0'
40
+ version: '1.4'
41
41
  description: A simple remote scanner for Atlassian Jira
42
42
  email: bcoles@gmail.com
43
43
  executables: