nexpose_ticketing 0.8.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/nexpose_jira +7 -0
- data/bin/nexpose_remedy +6 -0
- data/bin/nexpose_servicedesk +6 -0
- data/bin/nexpose_servicenow +6 -0
- data/lib/nexpose_ticketing/common_helper.rb +344 -0
- data/lib/nexpose_ticketing/config/servicedesk.config +3 -3
- data/lib/nexpose_ticketing/config/ticket_service.config +2 -0
- data/lib/nexpose_ticketing/helpers/jira_helper.rb +53 -107
- data/lib/nexpose_ticketing/helpers/remedy_helper.rb +208 -594
- data/lib/nexpose_ticketing/helpers/servicedesk_helper.rb +302 -289
- data/lib/nexpose_ticketing/helpers/servicenow_helper.rb +74 -165
- data/lib/nexpose_ticketing/nx_logger.rb +139 -30
- data/lib/nexpose_ticketing/queries.rb +148 -59
- data/lib/nexpose_ticketing/ticket_repository.rb +6 -0
- data/lib/nexpose_ticketing/ticket_service.rb +28 -16
- data/lib/nexpose_ticketing/version.rb +3 -0
- metadata +14 -6
- data/Gemfile.lock +0 -67
@@ -64,8 +64,13 @@ module NexposeTicketing
|
|
64
64
|
# |url| |summary| |fix|
|
65
65
|
#
|
66
66
|
def self.all_new_vulns(options = {})
|
67
|
-
|
68
|
-
|
67
|
+
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
68
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
69
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
70
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
71
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
72
|
+
fa.riskscore, dv.cvss_score,
|
73
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references,
|
69
74
|
fasva.first_discovered, fasva.most_recently_discovered
|
70
75
|
FROM (SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
71
76
|
FROM fact_asset_scan_vulnerability_finding fasv
|
@@ -77,15 +82,17 @@ module NexposeTicketing
|
|
77
82
|
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan, fasv.scan_id
|
78
83
|
HAVING NOT baselineComparison(fasv.scan_id, current_scan) = 'Old'
|
79
84
|
) subs
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
85
|
+
JOIN dim_vulnerability dv USING (vulnerability_id)
|
86
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
87
|
+
JOIN dim_asset_vulnerability_solution davs USING (vulnerability_id)
|
88
|
+
JOIN fact_asset_vulnerability_age fasva ON subs.vulnerability_id = fasva.vulnerability_id AND subs.asset_id = fasva.asset_id
|
89
|
+
JOIN dim_solution ds USING (solution_id)
|
90
|
+
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
91
|
+
JOIN fact_asset fa ON fa.asset_id = da.asset_id
|
92
|
+
#{createRiskString( options[:riskScore])}
|
93
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
94
|
+
fa.riskscore, dv.cvss_score, fasva.first_discovered, fasva.most_recently_discovered
|
95
|
+
ORDER BY da.ip_address, subs.vulnerability_id"
|
89
96
|
end
|
90
97
|
|
91
98
|
# Gets all delta vulns for all sites sorted by vuln ID.
|
@@ -95,25 +102,38 @@ module NexposeTicketing
|
|
95
102
|
# |url| |summary| |fix|
|
96
103
|
#
|
97
104
|
def self.all_new_vulns_by_vuln_id(options = {})
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
105
|
+
"SELECT DISTINCT on (subs.vulnerability_id) subs.vulnerability_id, dv.title, MAX(dv.cvss_score) as cvss_score,
|
106
|
+
string_agg(DISTINCT subs.asset_id ||
|
107
|
+
'|' || da.ip_address ||
|
108
|
+
'|' || coalesce(da.host_name, '') ||
|
109
|
+
'|' || fa.riskscore, '~') as assets,
|
110
|
+
|
111
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
112
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
113
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix)) ||
|
114
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
115
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references
|
116
|
+
FROM (SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
117
|
+
FROM fact_asset_scan_vulnerability_finding fasv
|
118
|
+
JOIN
|
119
|
+
(
|
120
|
+
SELECT asset_id, previousScan(asset_id) AS baseline_scan, lastScan(asset_id) AS current_scan
|
121
|
+
FROM dim_asset #{createAssetString(options)}) s
|
122
|
+
ON s.asset_id = fasv.asset_id AND (fasv.scan_id = s.baseline_scan OR fasv.scan_id = s.current_scan)
|
123
|
+
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan, fasv.scan_id
|
124
|
+
HAVING NOT baselineComparison(fasv.scan_id, current_scan) = 'Old'
|
125
|
+
) subs
|
126
|
+
JOIN dim_asset_vulnerability_solution davs USING (vulnerability_id)
|
127
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
128
|
+
JOIN dim_solution ds USING (solution_id)
|
129
|
+
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
130
|
+
JOIN dim_vulnerability dv ON subs.vulnerability_id = dv.vulnerability_id
|
131
|
+
JOIN fact_asset fa ON fa.asset_id = da.asset_id
|
132
|
+
JOIN fact_asset_vulnerability_age fasva ON subs.vulnerability_id = fasva.vulnerability_id AND subs.asset_id = fasva.asset_id
|
133
|
+
#{createRiskString(options[:riskScore])}
|
134
|
+
|
135
|
+
GROUP BY subs.vulnerability_id, dv.title
|
136
|
+
ORDER BY subs.vulnerability_id"
|
117
137
|
end
|
118
138
|
|
119
139
|
# Gets all new vulnerabilities happening after a reported scan id.
|
@@ -126,25 +146,36 @@ module NexposeTicketing
|
|
126
146
|
# |url| |summary| |fix|
|
127
147
|
#
|
128
148
|
def self.new_vulns_since_scan(options = {})
|
129
|
-
"SELECT subs.asset_id, da.ip_address, subs.current_scan, subs.vulnerability_id,
|
130
|
-
|
149
|
+
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
150
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
151
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
152
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
153
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
154
|
+
fa.riskscore, dv.cvss_score,
|
155
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references,
|
156
|
+
fasva.first_discovered, fasva.most_recently_discovered
|
131
157
|
FROM (SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
132
158
|
FROM fact_asset_scan_vulnerability_finding fasv
|
133
159
|
JOIN
|
134
160
|
(
|
135
161
|
SELECT asset_id, previousScan(asset_id) AS baseline_scan, lastScan(asset_id) AS current_scan
|
136
162
|
FROM dim_asset #{createAssetString(options)}) s
|
137
|
-
ON s.asset_id = fasv.asset_id AND (fasv.scan_id >=
|
163
|
+
ON s.asset_id = fasv.asset_id AND (fasv.scan_id >= #{options[:scan_id]} OR fasv.scan_id = s.current_scan)
|
138
164
|
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
139
165
|
HAVING baselineComparison(fasv.scan_id, current_scan) = 'New'
|
140
166
|
) subs
|
167
|
+
JOIN dim_vulnerability dv USING (vulnerability_id)
|
168
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
141
169
|
JOIN dim_asset_vulnerability_solution davs USING (vulnerability_id)
|
170
|
+
JOIN fact_asset_vulnerability_age fasva ON subs.vulnerability_id = fasva.vulnerability_id AND subs.asset_id = fasva.asset_id
|
142
171
|
JOIN dim_solution ds USING (solution_id)
|
143
172
|
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
144
173
|
AND subs.current_scan > #{options[:scan_id]}
|
145
174
|
JOIN fact_asset fa ON fa.asset_id = da.asset_id
|
146
175
|
#{createRiskString(options[:riskScore])}
|
147
|
-
|
176
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
177
|
+
fa.riskscore, dv.cvss_score, fasva.first_discovered, fasva.most_recently_discovered
|
178
|
+
ORDER BY da.ip_address, subs.vulnerability_id"
|
148
179
|
end
|
149
180
|
|
150
181
|
|
@@ -158,8 +189,17 @@ module NexposeTicketing
|
|
158
189
|
# |url| |summary| |fix|
|
159
190
|
#
|
160
191
|
def self.new_vulns_by_vuln_id_since_scan(options = {})
|
161
|
-
"SELECT DISTINCT on (subs.vulnerability_id
|
162
|
-
|
192
|
+
"SELECT DISTINCT on (subs.vulnerability_id) subs.vulnerability_id, dv.title, MAX(dv.cvss_score) as cvss_score,
|
193
|
+
string_agg(DISTINCT subs.asset_id ||
|
194
|
+
'|' || da.ip_address ||
|
195
|
+
'|' || coalesce(da.host_name, '') ||
|
196
|
+
'|' || fa.riskscore, '~') as assets,
|
197
|
+
|
198
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
199
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
200
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix)) ||
|
201
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
202
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references
|
163
203
|
FROM (SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
164
204
|
FROM fact_asset_scan_vulnerability_finding fasv
|
165
205
|
JOIN
|
@@ -170,13 +210,17 @@ module NexposeTicketing
|
|
170
210
|
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
171
211
|
HAVING baselineComparison(fasv.scan_id, current_scan) = 'New'
|
172
212
|
) subs
|
213
|
+
JOIN dim_vulnerability dv USING (vulnerability_id)
|
214
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
173
215
|
JOIN dim_asset_vulnerability_solution davs USING (vulnerability_id)
|
216
|
+
JOIN fact_asset_vulnerability_age fasva ON subs.vulnerability_id = fasva.vulnerability_id AND subs.asset_id = fasva.asset_id
|
174
217
|
JOIN dim_solution ds USING (solution_id)
|
175
218
|
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
176
219
|
AND subs.current_scan > #{options[:scan_id]}
|
177
220
|
JOIN fact_asset fa ON fa.asset_id = da.asset_id
|
178
221
|
#{createRiskString(options[:riskScore])}
|
179
|
-
|
222
|
+
GROUP BY subs.vulnerability_id, dv.title
|
223
|
+
ORDER BY vulnerability_id"
|
180
224
|
end
|
181
225
|
|
182
226
|
|
@@ -190,7 +234,7 @@ module NexposeTicketing
|
|
190
234
|
# |url| |summary| |fix|
|
191
235
|
#
|
192
236
|
def self.old_vulns_since_scan(options = {})
|
193
|
-
"SELECT subs.asset_id, da.ip_address, subs.current_scan, subs.vulnerability_id, dvs.solution_id, ds.nexpose_id, ds.url,
|
237
|
+
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id, dvs.solution_id, ds.nexpose_id, ds.url,
|
194
238
|
proofAsText(ds.summary) as summary, proofAsText(ds.fix) as fix, subs.comparison, fa.riskscore
|
195
239
|
FROM (
|
196
240
|
SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan, baselineComparison(fasv.scan_id, s.current_scan) as comparison
|
@@ -223,8 +267,14 @@ module NexposeTicketing
|
|
223
267
|
# |url| |summary| |fix| |comparison|
|
224
268
|
#
|
225
269
|
def self.all_vulns_since_scan(options = {})
|
226
|
-
"SELECT
|
227
|
-
|
270
|
+
"SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
271
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
272
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
273
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
274
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
275
|
+
subs.comparison, fa.riskscore, dv.cvss_score,
|
276
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references,
|
277
|
+
null as first_discovered, null as most_recently_discovered
|
228
278
|
FROM (
|
229
279
|
SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan, baselineComparison(fasv.scan_id, s.current_scan) as comparison
|
230
280
|
FROM fact_asset_scan_vulnerability_finding fasv
|
@@ -235,17 +285,27 @@ module NexposeTicketing
|
|
235
285
|
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
236
286
|
HAVING baselineComparison(fasv.scan_id, current_scan) = 'Old'
|
237
287
|
) subs
|
288
|
+
JOIN dim_vulnerability dv USING (vulnerability_id)
|
289
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
238
290
|
JOIN dim_vulnerability_solution dvs USING (vulnerability_id)
|
239
291
|
JOIN dim_solution ds USING (solution_id)
|
240
292
|
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
241
|
-
|
242
|
-
|
293
|
+
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
294
|
+
#{createRiskString(options[:riskScore])}
|
243
295
|
AND subs.current_scan > #{options[:scan_id]}
|
296
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
297
|
+
fa.riskscore, dv.cvss_score, subs.comparison
|
244
298
|
|
245
299
|
UNION
|
246
300
|
|
247
|
-
SELECT
|
248
|
-
|
301
|
+
SELECT DISTINCT on (da.ip_address, subs.vulnerability_id) subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
302
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
303
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
304
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix), 'None') ||
|
305
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
306
|
+
subs.comparison, fa.riskscore, dv.cvss_score,
|
307
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references,
|
308
|
+
fasva.first_discovered, fasva.most_recently_discovered
|
249
309
|
FROM
|
250
310
|
(
|
251
311
|
SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan, baselineComparison(fasv.scan_id, s.current_scan) as comparison
|
@@ -258,18 +318,23 @@ module NexposeTicketing
|
|
258
318
|
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
259
319
|
HAVING baselineComparison(fasv.scan_id, current_scan) IN ('Same','New')
|
260
320
|
) subs
|
321
|
+
JOIN dim_vulnerability dv USING (vulnerability_id)
|
322
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
261
323
|
JOIN dim_asset_vulnerability_solution davs USING (vulnerability_id)
|
324
|
+
JOIN fact_asset_vulnerability_age fasva ON subs.vulnerability_id = fasva.vulnerability_id AND subs.asset_id = fasva.asset_id
|
262
325
|
JOIN dim_solution ds USING (solution_id)
|
263
326
|
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
264
327
|
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
265
|
-
|
328
|
+
#{createRiskString(options[:riskScore])}
|
266
329
|
AND subs.current_scan > #{options[:scan_id]}
|
330
|
+
GROUP BY subs.asset_id, da.ip_address, da.host_name, subs.current_scan, subs.vulnerability_id,
|
331
|
+
fa.riskscore, dv.cvss_score, fasva.first_discovered, fasva.most_recently_discovered, subs.comparison
|
267
332
|
|
268
333
|
ORDER BY ip_address, comparison"
|
269
334
|
end
|
270
335
|
|
271
336
|
# Gets all vulnerabilities happening after a reported scan id. Sorted by vuln ID. This result set also includes the
|
272
|
-
# baseline comparision ("Old", "New", or "Same") allowing for
|
337
|
+
# baseline comparision ("Old", "New", or "Same") allowing for vulnerability-based ticket updating.
|
273
338
|
#
|
274
339
|
# * *Args* :
|
275
340
|
# - +reported_scan+ - Last reported scan id.
|
@@ -279,8 +344,18 @@ module NexposeTicketing
|
|
279
344
|
# |url| |summary| |fix| |comparison|
|
280
345
|
#
|
281
346
|
def self.all_vulns_by_vuln_id_since_scan(options = {})
|
282
|
-
"SELECT subs.
|
283
|
-
|
347
|
+
"SELECT DISTINCT on (subs.vulnerability_id, subs.comparison) subs.vulnerability_id, dv.title, MAX(dv.cvss_score) as cvss_score,
|
348
|
+
string_agg(DISTINCT subs.asset_id ||
|
349
|
+
'|' || da.ip_address ||
|
350
|
+
'|' || coalesce(da.host_name, '') ||
|
351
|
+
'|' || fa.riskscore, '~') as assets,
|
352
|
+
|
353
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
354
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
355
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix)) ||
|
356
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
357
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references,
|
358
|
+
subs.comparison
|
284
359
|
FROM (
|
285
360
|
SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan, baselineComparison(fasv.scan_id, s.current_scan) as comparison
|
286
361
|
FROM fact_asset_scan_vulnerability_finding fasv
|
@@ -291,19 +366,31 @@ module NexposeTicketing
|
|
291
366
|
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
292
367
|
HAVING baselineComparison(fasv.scan_id, current_scan) = 'Old'
|
293
368
|
) subs
|
369
|
+
JOIN dim_vulnerability dv USING (vulnerability_id)
|
370
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
294
371
|
JOIN dim_vulnerability_solution dvs USING (vulnerability_id)
|
295
372
|
JOIN dim_solution ds USING (solution_id)
|
296
373
|
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
297
|
-
JOIN
|
298
|
-
|
299
|
-
#{createRiskString(options[:riskScore])}
|
374
|
+
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
375
|
+
#{createRiskString(options[:riskScore])}
|
300
376
|
AND subs.current_scan > #{options[:scan_id]}
|
377
|
+
GROUP BY subs.vulnerability_id, dv.title, subs.comparison
|
301
378
|
|
302
379
|
UNION
|
303
380
|
|
304
|
-
SELECT subs.
|
305
|
-
|
306
|
-
|
381
|
+
SELECT DISTINCT on (subs.vulnerability_id, subs.comparison) subs.vulnerability_id, dv.title, MAX(dv.cvss_score) as cvss_score,
|
382
|
+
string_agg(DISTINCT subs.asset_id ||
|
383
|
+
'|' || da.ip_address ||
|
384
|
+
'|' || coalesce(da.host_name, '') ||
|
385
|
+
'|' || fa.riskscore, '~') as assets,
|
386
|
+
|
387
|
+
string_agg(DISTINCT 'Summary: ' || coalesce(ds.summary, 'None') ||
|
388
|
+
'|Nexpose ID: ' || ds.nexpose_id ||
|
389
|
+
'|Fix: ' || coalesce(proofAsText(ds.fix)) ||
|
390
|
+
'|URL: ' || coalesce(ds.url, 'None'), '~') as solutions,
|
391
|
+
string_agg(DISTINCT dvr.source || ': ' || dvr.reference, ', ') as references,
|
392
|
+
subs.comparison
|
393
|
+
FROM
|
307
394
|
(
|
308
395
|
SELECT fasv.asset_id, fasv.vulnerability_id, s.current_scan, baselineComparison(fasv.scan_id, s.current_scan) as comparison
|
309
396
|
FROM fact_asset_scan_vulnerability_finding fasv
|
@@ -315,15 +402,17 @@ module NexposeTicketing
|
|
315
402
|
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
316
403
|
HAVING baselineComparison(fasv.scan_id, current_scan) IN ('Same','New')
|
317
404
|
) subs
|
405
|
+
JOIN dim_vulnerability dv USING (vulnerability_id)
|
406
|
+
LEFT JOIN dim_vulnerability_reference dvr USING (vulnerability_id)
|
318
407
|
JOIN dim_asset_vulnerability_solution davs USING (vulnerability_id)
|
319
408
|
JOIN dim_solution ds USING (solution_id)
|
320
409
|
JOIN dim_asset da ON subs.asset_id = da.asset_id
|
321
|
-
JOIN
|
322
|
-
|
323
|
-
#{createRiskString(options[:riskScore])}
|
410
|
+
JOIN fact_asset fa ON fa.asset_id = subs.asset_id
|
411
|
+
#{createRiskString(options[:riskScore])}
|
324
412
|
AND subs.current_scan > #{options[:scan_id]}
|
413
|
+
GROUP BY subs.vulnerability_id, dv.title, subs.comparison
|
325
414
|
|
326
|
-
ORDER BY vulnerability_id, comparison
|
415
|
+
ORDER BY vulnerability_id, comparison"
|
327
416
|
end
|
328
417
|
|
329
418
|
|
@@ -336,7 +425,7 @@ module NexposeTicketing
|
|
336
425
|
# - Returns |asset_id| |ip_address| |current_scan| |vulnerability_id| |comparison|
|
337
426
|
#
|
338
427
|
def self.old_tickets_by_ip(options = {})
|
339
|
-
"SELECT subs.asset_id, subs.ip_address, subs.current_scan, subs.vulnerability_id, subs.comparison
|
428
|
+
"SELECT DISTINCT on(subs.ip_address) subs.asset_id, subs.ip_address, subs.current_scan, subs.vulnerability_id, subs.comparison
|
340
429
|
FROM (
|
341
430
|
SELECT fasv.asset_id, s.ip_address, fasv.vulnerability_id, s.current_scan, baselineComparison(fasv.scan_id, s.current_scan) as comparison
|
342
431
|
FROM fact_asset_scan_vulnerability_finding fasv
|
@@ -5,6 +5,8 @@ module NexposeTicketing
|
|
5
5
|
require 'nexpose'
|
6
6
|
require 'nexpose_ticketing/queries'
|
7
7
|
require 'nexpose_ticketing/report_helper'
|
8
|
+
require 'nexpose_ticketing/nx_logger'
|
9
|
+
require 'nexpose_ticketing/version'
|
8
10
|
|
9
11
|
@timeout = 10800
|
10
12
|
|
@@ -13,8 +15,12 @@ module NexposeTicketing
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def nexpose_login(nexpose_data)
|
18
|
+
|
16
19
|
@nsc = Nexpose::Connection.new(nexpose_data[:nxconsole], nexpose_data[:nxuser], nexpose_data[:nxpasswd])
|
17
20
|
@nsc.login
|
21
|
+
@log = NexposeTicketing::NxLogger.instance
|
22
|
+
@log.on_connect(nexpose_data[:nxconsole], 3780, @nsc.session_id, "{}")
|
23
|
+
|
18
24
|
#After login, create the report helper
|
19
25
|
@report_helper = NexposeReportHelper::ReportOps.new(@nsc, @timeout)
|
20
26
|
end
|
@@ -52,9 +52,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
52
52
|
require 'yaml'
|
53
53
|
require 'fileutils'
|
54
54
|
require 'nexpose_ticketing/ticket_repository'
|
55
|
+
require 'nexpose_ticketing'
|
56
|
+
require 'nexpose_ticketing/nx_logger'
|
57
|
+
require 'nexpose_ticketing/version'
|
55
58
|
|
56
59
|
TICKET_SERVICE_CONFIG_PATH = File.join(File.dirname(__FILE__), '/config/ticket_service.config')
|
57
|
-
LOGGER_FILE = File.join(File.dirname(__FILE__), '/
|
60
|
+
LOGGER_FILE = File.join(File.dirname(__FILE__), '/logs/ticket_service.log')
|
58
61
|
|
59
62
|
attr_accessor :helper_data, :nexpose_data, :options, :ticket_repository, :first_time, :nexpose_item_histories
|
60
63
|
|
@@ -90,14 +93,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
90
93
|
end
|
91
94
|
|
92
95
|
def setup_logging(enabled = false)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
96
|
+
helper_log = NexposeTicketing::NxLogger.instance
|
97
|
+
helper_log.setup_logging(@options[:logging_enabled],
|
98
|
+
@options[:log_level])
|
99
|
+
|
100
|
+
return unless enabled
|
101
|
+
require 'logger'
|
102
|
+
directory = File.dirname(LOGGER_FILE)
|
103
|
+
FileUtils.mkdir_p(directory) unless File.directory?(directory)
|
104
|
+
@log = Logger.new(LOGGER_FILE, 'monthly')
|
105
|
+
@log.level = Logger::INFO
|
106
|
+
log_message('Logging enabled, starting service.')
|
101
107
|
end
|
102
108
|
|
103
109
|
# Logs a message if logging is enabled.
|
@@ -222,6 +228,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
222
228
|
# There's a new scan with possibly new vulnerabilities.
|
223
229
|
def delta_site_new_scan(ticket_repository, nexpose_item, options, helper, file_site_histories, tag_id=nil)
|
224
230
|
log_message("New scan detected for nexpose id: #{nexpose_item}. Generating report.")
|
231
|
+
item = options[:tag_run] ? 'asset' : 'site'
|
225
232
|
|
226
233
|
if options[:ticket_mode] == 'I' || options[:ticket_mode] == 'V'
|
227
234
|
# I-mode and V-mode tickets require updating the tickets in the target system.
|
@@ -270,16 +277,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
270
277
|
tag_run: options[:tag_run],
|
271
278
|
tag: tag_id)
|
272
279
|
|
273
|
-
preparse = CSV.
|
280
|
+
preparse = CSV.open(new_scan_vuln_file.path, headers: :first_row)
|
274
281
|
empty_report = preparse.shift.nil?
|
275
|
-
|
276
|
-
|
282
|
+
preparse.close
|
283
|
+
|
284
|
+
|
285
|
+
log_message("No new vulnerabilities found in new scan for #{item}: #{nexpose_item}.") if empty_report
|
286
|
+
log_message("New vulnerabilities found in new scan for #{item} #{nexpose_item}, preparing tickets.") unless empty_report
|
277
287
|
unless empty_report
|
278
288
|
ticket_rate_limiter(options, new_scan_vuln_file, Proc.new {|ticket_batch| helper.prepare_create_tickets(ticket_batch, tag_id.nil? ? nexpose_item : "T#{tag_id}")}, Proc.new {|tickets| helper.create_tickets(tickets)})
|
279
289
|
end
|
280
290
|
|
281
291
|
if helper.respond_to?('prepare_close_tickets') && helper.respond_to?('close_tickets')
|
282
292
|
old_scan_vuln_file = ticket_repository.old_vulns(scan_id: file_site_histories[nexpose_item],
|
293
|
+
nexpose_item: nexpose_item,
|
283
294
|
site_id: nexpose_item,
|
284
295
|
severity: options[:severity],
|
285
296
|
riskScore: options[:riskScore],
|
@@ -287,10 +298,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
287
298
|
tag_run: options[:tag_run],
|
288
299
|
tag: tag_id)
|
289
300
|
|
290
|
-
preparse = CSV.
|
301
|
+
preparse = CSV.open(old_scan_vuln_file.path, headers: :first_row, :skip_blanks => true)
|
291
302
|
empty_report = preparse.shift.nil?
|
292
|
-
|
293
|
-
log_message("
|
303
|
+
preparse.close
|
304
|
+
log_message("No old (closed) vulnerabilities found in new scan for #{item}: #{nexpose_item}.") if empty_report
|
305
|
+
log_message("Old vulnerabilities found in new scan for #{item} #{nexpose_item}, preparing closures.") unless empty_report
|
294
306
|
unless empty_report
|
295
307
|
ticket_rate_limiter(options, old_scan_vuln_file, Proc.new {|ticket_batch| helper.prepare_close_tickets(ticket_batch, tag_id.nil? ? nexpose_item : "T#{tag_id}")}, Proc.new {|tickets| helper.close_tickets(tickets)})
|
296
308
|
end
|
@@ -366,7 +378,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
366
378
|
# Prep the batch of tickets
|
367
379
|
log_message('Creating tickets.')
|
368
380
|
tickets = ticket_prepare_method.call(ticket_batch.join(''))
|
369
|
-
log_message("
|
381
|
+
log_message("Parsed rows: #{ticket_batch.size}")
|
370
382
|
# Sent them off
|
371
383
|
log_message('Sending tickets.')
|
372
384
|
ticket_send_method.call(tickets)
|