nexpose_ticketing 0.8.3 → 1.0.0
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 +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)
|