jira_scan 0.0.4 → 0.0.6

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 +5 -13
  2. data/bin/jira-scan +59 -15
  3. data/lib/jira_scan.rb +174 -4
  4. metadata +16 -18
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YjNmYTk1OWYwM2VjNzJlZjVmZGFlZGIyNzdlYmUyOGE3Mzg0NTIxNg==
5
- data.tar.gz: !binary |-
6
- NDA1MWUyMjE2ODIwODBhMThjYjU2ZWNlY2VhMjcxY2E0YWIyODYyZQ==
2
+ SHA256:
3
+ metadata.gz: 694f95d2a4df4f67588a35cce083c44568ab6fd6411cad9be7b778f86fdc74f7
4
+ data.tar.gz: a52b797b7810b69b20921a6ae539aebc070df18968ed41fb002319fce71db47b
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MjliM2EyYWE0MzFjNDllMWMyOTljMDYyOGRkYTU3ZDc2NTk2MDc0ZTg0ODJi
10
- Zjg3MTAwOGU5MjkzNmEzNmZkNGZkYWY4YTNhMDE3Mzg0YzEzNjVkOGUzMjMy
11
- YTI1MGQ0NDg1NzA4Y2YzNjE3ODM0MWQ2NTJiOTk1NDUzZDI1ZmU=
12
- data.tar.gz: !binary |-
13
- NDM2MTdiY2ViMzVlYmEwZTg5MTY4NGI0NWY0M2IwZmFjMGFjYmM5MzRhZmFi
14
- NmYxYTkzYWE4ODAzNDYwNzNjZTNmNGE1OGM1OGJjMzAyZDYwYWRkMzg0NWRl
15
- NzVmNTQ2NmY3NTNhZWQ3ODA0MWZhNDVjN2M0YzQ2OTQ5Y2M1MDg=
6
+ metadata.gz: c9f02c01c0b3aff58e99d484a09eef8c30c594706d08bbb0d3197411dc038ff07dfcd6bf0ae3074eec3d7e8ac40d375a51e2ab6f8b20c5bde986e3e63fffe5cf
7
+ data.tar.gz: 39cdb3fa320f6e3dca07bf9bb3b5926eb9a1c2adfbc5ca735a05fbcd89632443d9939c51b49f8657a6f5e37f1e9cfa2b1e4d78901cd10e22b6eec461448398df
data/bin/jira-scan CHANGED
@@ -17,7 +17,7 @@ def banner
17
17
  _ | | | '__/ _` |\\___ \\ / __/ _` | '_ \\
18
18
  | |__| | | | | (_| |____) | (_| (_| | | | |
19
19
  \\____/|_|_| \\__,_|_____/ \\___\\__,_|_| |_|
20
- version 0.0.4"
20
+ version #{JiraScan::VERSION}"
21
21
  puts
22
22
  puts '-' * 60
23
23
  end
@@ -108,17 +108,25 @@ 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
- dev_mode = JiraScan::devMode(url)
113
- puts '+ Dev mode is enabled' if dev_mode
120
+ puts '+ Dev mode is enabled' if JiraScan::devMode(url)
114
121
 
115
122
  # User registration enabled
116
- register = JiraScan::userRegistration(url)
117
- puts '+ User registration is enabled' if register
123
+ puts '+ User registration is enabled' if JiraScan::userRegistration(url)
124
+
125
+ # Service Desk user registration enabled
126
+ puts '+ Service Desk user registration is enabled' if JiraScan::userServiceDeskRegistration(url)
118
127
 
119
128
  # Check if User Picker Browser is accessible
120
- user_picker = JiraScan::userPickerBrowser(url)
121
- if user_picker
129
+ if JiraScan::userPickerBrowser(url)
122
130
  puts '+ User Picker Browser is available'
123
131
  # Retrieve list of first 1,000 users
124
132
  users = JiraScan::getUsersFromUserPickerBrowser(url)
@@ -130,20 +138,16 @@ def scan(url, check: true, insecure: false, verbose: false)
130
138
  end
131
139
 
132
140
  # Check if REST User Picker is accessible
133
- rest_user_picker = JiraScan::restUserPicker(url)
134
- puts "+ REST UserPicker is available" if rest_user_picker
141
+ puts "+ REST UserPicker is available" if JiraScan::restUserPicker(url)
135
142
 
136
143
  # Check if REST Group User Picker is accessible
137
- rest_group_user_picker = JiraScan::restGroupUserPicker(url)
138
- puts "+ REST GroupUserPicker is available" if rest_group_user_picker
144
+ puts "+ REST GroupUserPicker is available" if JiraScan::restGroupUserPicker(url)
139
145
 
140
146
  # Check if ViewUserHover.jspa is accessible
141
- view_user_hover = JiraScan::viewUserHover(url)
142
- puts "+ ViewUserHover.jspa is available" if view_user_hover
147
+ puts "+ ViewUserHover.jspa is available" if JiraScan::viewUserHover(url)
143
148
 
144
149
  # Check if META-INF contents are accessible
145
- meta_inf = JiraScan::metaInf(url)
146
- puts '+ META-INF directory contents are accessible' if meta_inf
150
+ puts '+ META-INF directory contents are accessible' if JiraScan::metaInf(url)
147
151
 
148
152
  # Retrieve list of dashboards
149
153
  dashboards = JiraScan::getDashboards(url)
@@ -161,6 +165,46 @@ def scan(url, check: true, insecure: false, verbose: false)
161
165
  puts table
162
166
  end
163
167
 
168
+ # Retrieve list of installed gadgets
169
+ gadgets = JiraScan::getGadgets(url)
170
+ unless gadgets.empty?
171
+ puts "+ Found gadgets (#{gadgets.length}):"
172
+ table = Terminal::Table.new :headings => ['Title', 'Author Name', 'Author Email', 'Description'], :rows => gadgets
173
+ puts table
174
+ end
175
+
176
+ # Retrieve list of resolutions
177
+ resolutions = JiraScan::getResolutions(url)
178
+ unless resolutions.empty?
179
+ puts "+ Found resolutions (#{resolutions.length}):"
180
+ table = Terminal::Table.new :headings => ['ID', 'Name', 'Description'], :rows => resolutions
181
+ puts table
182
+ end
183
+
184
+ # Retrieve list of projects
185
+ projects = JiraScan::getProjects(url)
186
+ unless projects.empty?
187
+ puts "+ Found projects (#{projects.length}):"
188
+ table = Terminal::Table.new :headings => ['ID', 'Key', 'Name'], :rows => projects
189
+ puts table
190
+ end
191
+
192
+ # Retrieve list of project categories
193
+ project_categories = JiraScan::getProjectCategories(url)
194
+ unless project_categories.empty?
195
+ puts "+ Found project categories (#{project_categories.length}):"
196
+ table = Terminal::Table.new :headings => ['ID', 'Name', 'Description'], :rows => project_categories
197
+ puts table
198
+ end
199
+
200
+ # Retrieve list of linked applications
201
+ apps = JiraScan::getLinkedApps(url)
202
+ unless apps.empty?
203
+ puts "+ Found linked applications (#{apps.length}):"
204
+ table = Terminal::Table.new :headings => ['Link', 'Label', 'Application Type'], :rows => apps
205
+ puts table
206
+ end
207
+
164
208
  # Retrieve list of field names
165
209
  field_names = JiraScan::getFieldNamesQueryComponentDefault(url)
166
210
  unless field_names.empty?
data/lib/jira_scan.rb CHANGED
@@ -9,9 +9,10 @@ require 'json'
9
9
  require 'logger'
10
10
  require 'net/http'
11
11
  require 'openssl'
12
+ require 'stringio'
12
13
 
13
14
  class JiraScan
14
- VERSION = '0.0.4'.freeze
15
+ VERSION = '0.0.6'.freeze
15
16
 
16
17
  def self.logger
17
18
  @logger
@@ -115,6 +116,26 @@ class JiraScan
115
116
  "#{version}-##{build}"
116
117
  end
117
118
 
119
+ #
120
+ # Retrieve Jira software information
121
+ #
122
+ # @param [String] URL
123
+ #
124
+ # @return [Array] Jira software information
125
+ #
126
+ def self.getServerInfo(url)
127
+ url += '/' unless url.to_s.end_with? '/'
128
+ res = sendHttpRequest("#{url}rest/api/latest/serverInfo")
129
+
130
+ return [] unless res
131
+ return [] unless res.code.to_i == 200
132
+ return [] unless res.body.to_s.start_with?('{"baseUrl"')
133
+
134
+ JSON.parse(res.body.to_s, symbolize_names: true)
135
+ rescue
136
+ []
137
+ end
138
+
118
139
  #
119
140
  # Check if dev mode is enabled
120
141
  #
@@ -134,6 +155,7 @@ class JiraScan
134
155
 
135
156
  #
136
157
  # Check if account registration is enabled
158
+ # https://docs.atlassian.com/jira/jsd-docs-045/Configuring+public+signup
137
159
  #
138
160
  # @param [String] URL
139
161
  #
@@ -149,6 +171,25 @@ class JiraScan
149
171
  res.body.to_s.include?('<h1>Sign up</h1>')
150
172
  end
151
173
 
174
+ #
175
+ # Check if Jira Service Desk (part of Jira Service Management) account registration is enabled
176
+ # https://docs.atlassian.com/jira/jsd-docs-045/Configuring+public+signup
177
+ # https://support.atlassian.com/jira-service-management-cloud/docs/customer-permissions-for-your-service-project-and-jira-site/
178
+ #
179
+ # @param [String] URL
180
+ #
181
+ # @return [Boolean]
182
+ #
183
+ def self.userServiceDeskRegistration(url)
184
+ url += '/' unless url.to_s.end_with? '/'
185
+ res = sendHttpRequest("#{url}servicedesk/customer/user/signup")
186
+
187
+ return false unless res
188
+ return false unless res.code.to_i == 200
189
+
190
+ res.body.to_s.include?('serviceDeskVersion') || res.body.to_s.include?('com.atlassian.servicedesk')
191
+ end
192
+
152
193
  #
153
194
  # Check if unauthenticated access to UserPickerBrowser.jspa is allowed
154
195
  #
@@ -198,6 +239,7 @@ class JiraScan
198
239
 
199
240
  #
200
241
  # Check if unauthenticated access to REST UserPicker is allowed (CVE-2019-3403)
242
+ # https://jira.atlassian.com/browse/JRASERVER-69242
201
243
  #
202
244
  # @param [String] URL
203
245
  #
@@ -205,7 +247,7 @@ class JiraScan
205
247
  #
206
248
  def self.restUserPicker(url)
207
249
  url += '/' unless url.to_s.end_with? '/'
208
- res = sendHttpRequest("#{url}rest/api/latest/user/picker")
250
+ res = sendHttpRequest("#{url}rest/api/2/user/picker")
209
251
 
210
252
  return false unless res
211
253
  return false unless res.code.to_i == 400
@@ -215,6 +257,7 @@ class JiraScan
215
257
 
216
258
  #
217
259
  # Check if unauthenticated access to REST GroupUserPicker is allowed (CVE-2019-8449)
260
+ # https://jira.atlassian.com/browse/JRASERVER-69796
218
261
  #
219
262
  # @param [String] URL
220
263
  #
@@ -222,7 +265,7 @@ class JiraScan
222
265
  #
223
266
  def self.restGroupUserPicker(url)
224
267
  url += '/' unless url.to_s.end_with? '/'
225
- res = sendHttpRequest("#{url}rest/api/latest/groupuserpicker")
268
+ res = sendHttpRequest("#{url}rest/api/2/groupuserpicker")
226
269
 
227
270
  return false unless res
228
271
  return false unless res.code.to_i == 400
@@ -230,8 +273,33 @@ class JiraScan
230
273
  res.body.to_s.include?('The username query parameter was not provided')
231
274
  end
232
275
 
276
+ #
277
+ # Retrieve list of installed gadgets
278
+ # https://jira.atlassian.com/browse/JRASERVER-72613
279
+ #
280
+ # @param [String] URL
281
+ #
282
+ # @return [Array] list of installed gadgets
283
+ #
284
+ def self.getGadgets(url)
285
+ url += '/' unless url.to_s.end_with? '/'
286
+ res = sendHttpRequest("#{url}rest/config/1.0/directory.json")
287
+
288
+ return [] unless res
289
+ return [] unless res.code.to_i == 200
290
+ return [] unless res.body.to_s.start_with?('{"categories"')
291
+
292
+ gadgets = JSON.parse(res.body.to_s)['gadgets']
293
+ return [] if gadgets.empty?
294
+
295
+ JSON.parse(gadgets.to_json, symbolize_names: true).map { |g| [g[:title], g[:authorName], g[:authorEmail], g[:description]] }
296
+ rescue
297
+ []
298
+ end
299
+
233
300
  #
234
301
  # Check if unauthenticated access to ViewUserHover.jspa is allowed (CVE-2020-14181)
302
+ # https://jira.atlassian.com/browse/JRASERVER-71560
235
303
  #
236
304
  # @param [String] URL
237
305
  #
@@ -249,6 +317,7 @@ class JiraScan
249
317
 
250
318
  #
251
319
  # Check if META-INF contents are accessible (CVE-2019-8442)
320
+ # https://jira.atlassian.com/browse/JRASERVER-69241
252
321
  #
253
322
  # @param [String] URL
254
323
  #
@@ -266,6 +335,7 @@ class JiraScan
266
335
 
267
336
  #
268
337
  # Retrieve list of popular filters
338
+ # https://jira.atlassian.com/browse/JRASERVER-23255
269
339
  #
270
340
  # @param [String] URL
271
341
  #
@@ -302,14 +372,113 @@ class JiraScan
302
372
  return [] unless res
303
373
  return [] unless res.code.to_i == 200
304
374
  return [] unless res.body.to_s.start_with?('{"startAt"')
375
+ return [] unless res.body.to_s.include?('id')
376
+ return [] unless res.body.to_s.include?('name')
305
377
 
306
378
  JSON.parse(res.body.to_s, symbolize_names: true)[:dashboards].map { |d| [d[:id], d[:name]] }
307
379
  rescue
308
380
  []
309
381
  end
310
382
 
383
+ #
384
+ # Retrieve list of resolutions
385
+ #
386
+ # @param [String] URL
387
+ #
388
+ # @return [Array] list of resolutions
389
+ #
390
+ def self.getResolutions(url)
391
+ url += '/' unless url.to_s.end_with? '/'
392
+ res = sendHttpRequest("#{url}rest/api/2/resolution")
393
+
394
+ return [] unless res
395
+ return [] unless res.code.to_i == 200
396
+ return [] unless res.body.to_s.start_with?('[{"self"')
397
+ return [] unless res.body.to_s.include?('id')
398
+ return [] unless res.body.to_s.include?('name')
399
+ return [] unless res.body.to_s.include?('description')
400
+
401
+ JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:id], r[:name], r[:description]] }
402
+ rescue
403
+ []
404
+ end
405
+
406
+ #
407
+ # Retrieve list of projects
408
+ #
409
+ # @param [String] URL
410
+ #
411
+ # @return [Array] list of projects
412
+ #
413
+ def self.getProjects(url)
414
+ url += '/' unless url.to_s.end_with? '/'
415
+ max = 1_000
416
+ res = sendHttpRequest("#{url}rest/api/2/project?maxResults=#{max}")
417
+
418
+ return [] unless res
419
+ return [] unless res.code.to_i == 200
420
+ return [] unless res.body.to_s.start_with?('[{"expand"')
421
+ return [] unless res.body.to_s.include?('id')
422
+ return [] unless res.body.to_s.include?('key')
423
+ return [] unless res.body.to_s.include?('name')
424
+
425
+ JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:id], r[:key], r[:name]] }
426
+ rescue
427
+ []
428
+ end
429
+
430
+ #
431
+ # Retrieve list of project categories
432
+ #
433
+ # @param [String] URL
434
+ #
435
+ # @return [Array] list of project categories
436
+ #
437
+ def self.getProjectCategories(url)
438
+ url += '/' unless url.to_s.end_with? '/'
439
+ res = sendHttpRequest("#{url}rest/api/2/projectCategory")
440
+
441
+ return [] unless res
442
+ return [] unless res.code.to_i == 200
443
+ return [] unless res.body.to_s.start_with?('[{"self"')
444
+ return [] unless res.body.to_s.include?('id')
445
+ return [] unless res.body.to_s.include?('name')
446
+ return [] unless res.body.to_s.include?('description')
447
+
448
+ JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:id], r[:name], r[:description]] }
449
+ rescue
450
+ []
451
+ end
452
+
453
+ #
454
+ # Retrieve list of linked applications
455
+ # https://jira.atlassian.com/browse/JRASERVER-64963
456
+ # https://jira.atlassian.com/browse/JRACLOUD-64963
457
+ #
458
+ # @param [String] URL
459
+ #
460
+ # @return [Array] list of linked applications
461
+ #
462
+ def self.getLinkedApps(url)
463
+ url += '/' unless url.to_s.end_with? '/'
464
+ res = sendHttpRequest("#{url}rest/menu/latest/admin")
465
+
466
+ return [] unless res
467
+ return [] unless res.code.to_i == 200
468
+ return [] unless res.body.to_s.start_with?('[{"key"')
469
+ return [] unless res.body.to_s.include?('link')
470
+ return [] unless res.body.to_s.include?('label')
471
+ return [] unless res.body.to_s.include?('applicationType')
472
+
473
+ JSON.parse(res.body.to_s, symbolize_names: true).map { |r| [r[:link], r[:label], r[:applicationType]] }
474
+ rescue
475
+ []
476
+ end
477
+
311
478
  #
312
479
  # Retrieve list of field names from QueryComponent!Default.jspa (CVE-2020-14179)
480
+ # https://jira.atlassian.com/browse/JRASERVER-71536
481
+ # https://jira.atlassian.com/browse/JRACLOUD-75661
313
482
  #
314
483
  # @param [String] URL
315
484
  #
@@ -342,7 +511,8 @@ class JiraScan
342
511
  end
343
512
 
344
513
  #
345
- # Retrieve list of field names from QueryComponent!Jql.jspa (EDB-49924)
514
+ # Retrieve list of field names from QueryComponent!Jql.jspa (CVE-2020-14179)
515
+ # https://jira.atlassian.com/browse/JRASERVER-71536
346
516
  #
347
517
  # @param [String] URL
348
518
  #
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.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brendan Coles
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-12 00:00:00.000000000 Z
11
+ date: 2023-01-30 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:
@@ -51,25 +51,23 @@ homepage: https://github.com/bcoles/jira_scan
51
51
  licenses:
52
52
  - MIT
53
53
  metadata: {}
54
- post_install_message:
54
+ post_install_message:
55
55
  rdoc_options: []
56
56
  require_paths:
57
57
  - lib
58
58
  required_ruby_version: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ! '>='
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
62
  version: 2.0.0
63
63
  required_rubygems_version: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ! '>='
65
+ - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
68
  requirements: []
69
- rubyforge_project:
70
- rubygems_version: 2.2.2
71
- signing_key:
69
+ rubygems_version: 3.3.15
70
+ signing_key:
72
71
  specification_version: 4
73
72
  summary: Jira scanner
74
73
  test_files: []
75
- has_rdoc: