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.
- checksums.yaml +5 -13
- data/bin/jira-scan +59 -15
- data/lib/jira_scan.rb +174 -4
- metadata +16 -18
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NDA1MWUyMjE2ODIwODBhMThjYjU2ZWNlY2VhMjcxY2E0YWIyODYyZQ==
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 694f95d2a4df4f67588a35cce083c44568ab6fd6411cad9be7b778f86fdc74f7
|
4
|
+
data.tar.gz: a52b797b7810b69b20921a6ae539aebc070df18968ed41fb002319fce71db47b
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
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
|
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
|
-
|
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
|
-
|
117
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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/
|
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/
|
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 (
|
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
|
+
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:
|
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: '
|
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:
|
@@ -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
|
-
|
70
|
-
|
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:
|