nexpose_servicenow 0.6.2 → 0.7.1
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/README.md +1 -1
- data/lib/nexpose_servicenow.rb +58 -91
- data/lib/nexpose_servicenow/arg_parser.rb +80 -75
- data/lib/nexpose_servicenow/chunker.rb +0 -1
- data/lib/nexpose_servicenow/csv_compare.rb +17 -0
- data/lib/nexpose_servicenow/helpers/connection_helper.rb +79 -0
- data/lib/nexpose_servicenow/helpers/data_warehouse_helper.rb +134 -0
- data/lib/nexpose_servicenow/{nexpose_helper.rb → helpers/nexpose_console_helper.rb} +32 -85
- data/lib/nexpose_servicenow/historical_data.rb +46 -355
- data/lib/nexpose_servicenow/{queries.rb → queries/nexpose_queries.rb} +61 -90
- data/lib/nexpose_servicenow/queries/queries_base.rb +25 -0
- data/lib/nexpose_servicenow/queries/warehouse_queries.rb +330 -0
- data/lib/nexpose_servicenow/version.rb +1 -1
- data/nexpose_servicenow.gemspec +14 -11
- metadata +27 -6
@@ -1,5 +1,7 @@
|
|
1
|
+
require_relative './queries_base'
|
2
|
+
|
1
3
|
module NexposeServiceNow
|
2
|
-
class
|
4
|
+
class NexposeQueries < QueriesBase
|
3
5
|
def self.vulnerabilities(options={})
|
4
6
|
"SELECT
|
5
7
|
concat('R7_', vulnerability_id) as ID,
|
@@ -78,9 +80,9 @@ module NexposeServiceNow
|
|
78
80
|
def self.vulnerability_references(options={})
|
79
81
|
"SELECT concat('R7_', vulnerability_id) as ID, dvr.Source, dvr.Reference
|
80
82
|
FROM dim_vulnerability
|
81
|
-
|
82
|
-
|
83
|
-
|
83
|
+
JOIN
|
84
|
+
(SELECT vulnerability_id, dvr.Source, dvr.Reference
|
85
|
+
FROM dim_vulnerability_reference dvr) dvr USING (vulnerability_id)
|
84
86
|
WHERE date_modified >= '#{options[:vuln_query_date]}'"
|
85
87
|
end
|
86
88
|
|
@@ -125,7 +127,7 @@ module NexposeServiceNow
|
|
125
127
|
WHERE scan_id = lastScan(asset_id)"
|
126
128
|
end
|
127
129
|
|
128
|
-
|
130
|
+
def self.service_definition(options={})
|
129
131
|
"SELECT DISTINCT on(dsf.name, ds.name, dp.name, port)
|
130
132
|
dsf.name, ds.name as service_name, dp.name as protocol, port
|
131
133
|
|
@@ -268,63 +270,56 @@ module NexposeServiceNow
|
|
268
270
|
end
|
269
271
|
|
270
272
|
def self.vulnerable_new_items(options={})
|
271
|
-
standard_filter = if options[:id_type] == 'site'
|
272
|
-
"MIN(fasv.scan_id) > #{options[:delta]}"
|
273
|
-
else
|
274
|
-
"MIN(fasv.date) > '#{options[:delta]}'"
|
275
|
-
end
|
276
|
-
|
277
273
|
cve_filter = self.generate_cve_filter(options[:filters][:cve])
|
278
274
|
date_filter = self.generate_date_filter(options[:filters][:date], false)
|
279
275
|
cvss_filter = self.generate_cvss_filter(options[:filters][:cvss])
|
280
276
|
|
281
|
-
"SELECT
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
FROM fact_asset_scan_vulnerability_finding
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
ORDER BY fasva.asset_id, vulnerability"
|
277
|
+
"SELECT CAST(asset_id as text) Configuration_Item,
|
278
|
+
TRUE as Active,
|
279
|
+
concat('R7_', vulnerability_id) as Vulnerability,
|
280
|
+
first_discovered as First_Found,
|
281
|
+
most_recently_discovered as Last_Found,
|
282
|
+
vulnerability_instances as Times_Found,
|
283
|
+
ip_address as IP_Address,
|
284
|
+
proof as Proof,
|
285
|
+
status as Status,
|
286
|
+
ports as Ports,
|
287
|
+
protocol as Protocol,
|
288
|
+
array_to_string(dvsol.solution_ids, ',', '') as Solutions
|
289
|
+
FROM
|
290
|
+
(SELECT asset_id, scan_id, vulnerability_id, vulnerability_instances
|
291
|
+
FROM fact_asset_vulnerability_finding
|
292
|
+
WHERE (asset_id, vulnerability_id) NOT IN
|
293
|
+
(SELECT asset_id, vulnerability_id
|
294
|
+
FROM fact_asset_scan_vulnerability_finding
|
295
|
+
WHERE scan_id=#{options[:delta]})) favf
|
296
|
+
#{cve_filter}
|
297
|
+
#{cvss_filter}
|
298
|
+
JOIN (SELECT asset_id, vulnerability_id,
|
299
|
+
first_discovered, most_recently_discovered
|
300
|
+
FROM fact_asset_vulnerability_age #{date_filter}) fasva USING (asset_id, vulnerability_id)
|
301
|
+
JOIN (SELECT asset_id,
|
302
|
+
vulnerability_id,
|
303
|
+
string_agg(proof, E'\n') as proof,
|
304
|
+
array_to_string(array_agg(DISTINCT port), ' ', '') as ports,
|
305
|
+
string_agg(DISTINCT status, ',') as status,
|
306
|
+
string_agg(DISTINCT protocol, ',') as protocol
|
307
|
+
FROM (SELECT asset_id, vulnerability_id,
|
308
|
+
proofAsText(proof) as proof,
|
309
|
+
status_id as status_id,
|
310
|
+
port,
|
311
|
+
dp.description as protocol,
|
312
|
+
dvs.description as status
|
313
|
+
FROM fact_asset_vulnerability_instance
|
314
|
+
JOIN dim_protocol dp USING (protocol_id)
|
315
|
+
JOIN dim_vulnerability_status dvs USING (status_id)) favi
|
316
|
+
GROUP BY asset_id, vulnerability_id) favi USING (asset_id, vulnerability_id)
|
317
|
+
JOIN (SELECT asset_id, ip_address
|
318
|
+
FROM dim_asset) s USING (asset_id)
|
319
|
+
LEFT JOIN (SELECT asset_id, vulnerability_id,
|
320
|
+
array_agg(DISTINCT solution_id) as solution_ids
|
321
|
+
FROM dim_asset_vulnerability_solution
|
322
|
+
GROUP BY asset_id, vulnerability_id) dvsol USING (asset_id, vulnerability_id)"
|
328
323
|
end
|
329
324
|
|
330
325
|
def self.vulnerable_old_items(options={})
|
@@ -346,33 +341,31 @@ module NexposeServiceNow
|
|
346
341
|
'MIN(fasv.scan_id) as first_found,'
|
347
342
|
end
|
348
343
|
|
349
|
-
"SELECT
|
344
|
+
"SELECT
|
350
345
|
CAST(da.asset_id as text) Configuration_Item,
|
351
346
|
FALSE as Active,
|
352
|
-
concat('R7_', subq.vulnerability_id) as Vulnerability
|
353
|
-
da.ip_address as IP_Address
|
347
|
+
concat('R7_', subq.vulnerability_id) as Vulnerability
|
354
348
|
FROM (
|
355
349
|
SELECT fasv.asset_id, fasv.vulnerability_id,
|
356
350
|
#{date_field}
|
357
351
|
MAX(fasv.scan_id) as latest_found,
|
358
|
-
s.current_scan
|
359
|
-
|
352
|
+
s.current_scan
|
353
|
+
FROM fact_asset_scan_vulnerability_finding fasv
|
360
354
|
|
361
355
|
#{cve_filter}
|
362
356
|
#{cvss_filter}
|
363
357
|
|
364
358
|
JOIN (
|
365
|
-
SELECT asset_id,
|
359
|
+
SELECT asset_id, lastScan(asset_id) AS current_scan FROM dim_asset
|
366
360
|
) s ON s.asset_id = fasv.asset_id
|
367
|
-
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
361
|
+
GROUP BY fasv.asset_id, fasv.vulnerability_id, s.current_scan
|
368
362
|
|
369
363
|
HAVING MAX(fasv.scan_id) < current_scan
|
370
364
|
AND #{standard_filter}
|
371
365
|
) subq
|
372
366
|
|
373
367
|
JOIN dim_asset da ON subq.asset_id = da.asset_id
|
374
|
-
#{date_filter}
|
375
|
-
ORDER BY da.ip_address"
|
368
|
+
#{date_filter}"
|
376
369
|
|
377
370
|
end
|
378
371
|
|
@@ -424,31 +417,9 @@ module NexposeServiceNow
|
|
424
417
|
end
|
425
418
|
|
426
419
|
def self.latest_scans(options={})
|
427
|
-
'SELECT ds.site_id, ds.last_scan_id, dsc.finished
|
420
|
+
'SELECT ds.site_id, ds.name, ds.last_scan_id, dsc.finished
|
428
421
|
FROM dim_site ds
|
429
422
|
JOIN dim_scan dsc ON ds.last_scan_id = dsc.scan_id'
|
430
423
|
end
|
431
|
-
|
432
|
-
def self.multiple_reports?(query_name)
|
433
|
-
single_queries = %w(vulnerabilities vulnerability_category
|
434
|
-
asset_groups latest_scans
|
435
|
-
vulnerability_solutions vulnerability_references
|
436
|
-
)
|
437
|
-
return !(single_queries.include? query_name.to_s)
|
438
|
-
end
|
439
|
-
|
440
|
-
def self.csv_diff_required?(query_name)
|
441
|
-
diff_required = %w(asset_groups asset_group_memberships)
|
442
|
-
return (diff_required.include? query_name.to_s)
|
443
|
-
end
|
444
|
-
|
445
|
-
# TODO: Refactor this.
|
446
|
-
def self.query_keys(query_name)
|
447
|
-
if query_name.to_s == 'asset_groups'
|
448
|
-
[0]
|
449
|
-
else
|
450
|
-
[0,1]
|
451
|
-
end
|
452
|
-
end
|
453
424
|
end
|
454
425
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module NexposeServiceNow
|
2
|
+
class QueriesBase
|
3
|
+
def self.single_report?(query_name)
|
4
|
+
single_queries = %w(vulnerabilities vulnerability_category
|
5
|
+
asset_groups latest_scans
|
6
|
+
vulnerability_solutions vulnerability_references
|
7
|
+
)
|
8
|
+
return single_queries.include? query_name.to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.csv_diff_required?(query_name)
|
12
|
+
diff_required = %w(asset_groups asset_group_memberships)
|
13
|
+
return (diff_required.include? query_name.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO: Refactor this.
|
17
|
+
def self.query_keys(query_name)
|
18
|
+
if query_name.to_s == 'asset_groups'
|
19
|
+
[0]
|
20
|
+
else
|
21
|
+
[0,1]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,330 @@
|
|
1
|
+
require_relative './queries_base'
|
2
|
+
require_relative '../nx_logger'
|
3
|
+
|
4
|
+
module NexposeServiceNow
|
5
|
+
class WarehouseQueries < QueriesBase
|
6
|
+
def self.latest_scans(options={})
|
7
|
+
'SELECT ds.site_id, ds.name, ds.last_scan_id, dsc.finished
|
8
|
+
FROM dim_site ds
|
9
|
+
JOIN dim_scan dsc ON ds.last_scan_id = dsc.scan_id'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.vulnerabilities(options={})
|
13
|
+
"SELECT
|
14
|
+
concat('R7_', vulnerability_id) AS ID,
|
15
|
+
cve.ref AS CVE,
|
16
|
+
cwe.ref AS CWE,
|
17
|
+
concat('Rapid7 Nexpose') AS Source,
|
18
|
+
to_char(date_published, 'yyyy-MM-dd hh:mm:ss') AS date_published,
|
19
|
+
to_char(date_modified, 'yyyy-MM-dd hh:mm:ss') AS Last_Modified,
|
20
|
+
dvc.category,
|
21
|
+
severity AS Severity_Rating,
|
22
|
+
severity_score AS Severity,
|
23
|
+
pci_status,
|
24
|
+
pci_adjusted_cvss_score AS PCI_Severity,
|
25
|
+
coalesce(NULLIF(CONCAT('\"',regexp_replace(title, '\"','''', 'g'),'\"'), '\"\"'), 'None') AS Summary,
|
26
|
+
coalesce(NULLIF(CONCAT('\"',regexp_replace(htmltotext(description), '\"','''', 'g'),'\"'), '\"\"'), 'None') AS Threat,
|
27
|
+
ROUND(risk_score :: NUMERIC, 2) AS Riskscore,
|
28
|
+
cvss_vector,
|
29
|
+
ROUND(cvss_impact_score :: NUMERIC, 2) AS Impact_Score,
|
30
|
+
ROUND(cvss_exploit_score :: NUMERIC, 2) AS Exploit_Score,
|
31
|
+
cvss_access_complexity AS Access_Complexity,
|
32
|
+
cvss_access_vector AS Access_Vector,
|
33
|
+
cvss_authentication AS Authentication,
|
34
|
+
ROUND(cvss_score :: NUMERIC, 2) AS Vulnerability_Score,
|
35
|
+
cvss_integrity_impact AS Integrity_Impact,
|
36
|
+
cvss_confidentiality_impact AS Confidentiality_Impact,
|
37
|
+
cvss_availability_impact AS Availability_Impact,
|
38
|
+
(CASE
|
39
|
+
WHEN exploits > 0
|
40
|
+
THEN 'true'
|
41
|
+
ELSE 'false'
|
42
|
+
END) AS Exploitability,
|
43
|
+
(CASE
|
44
|
+
WHEN malware_kits > 0
|
45
|
+
THEN 'true'
|
46
|
+
ELSE 'false'
|
47
|
+
END) AS Malware_Kits,
|
48
|
+
NULLIF(CONCAT('\"', array_to_string(sol.solutions, ',', ''), '\"'), '\"\"') AS Solution
|
49
|
+
FROM
|
50
|
+
dim_vulnerability
|
51
|
+
LEFT OUTER JOIN
|
52
|
+
(SELECT DISTINCT ON (vulnerability_id)
|
53
|
+
vulnerability_id,
|
54
|
+
dvr.reference AS ref
|
55
|
+
FROM dim_vulnerability_reference dvr
|
56
|
+
WHERE source = 'CWE'
|
57
|
+
GROUP BY dvr.vulnerability_id, dvr.reference
|
58
|
+
) cwe USING (vulnerability_id)
|
59
|
+
LEFT OUTER JOIN
|
60
|
+
(SELECT DISTINCT ON (vulnerability_id)
|
61
|
+
vulnerability_id,
|
62
|
+
dvr.reference AS ref
|
63
|
+
FROM dim_vulnerability_reference dvr
|
64
|
+
WHERE source = 'CVE'
|
65
|
+
GROUP BY dvr.vulnerability_id, dvr.reference
|
66
|
+
) cve USING (vulnerability_id)
|
67
|
+
LEFT OUTER JOIN (SELECT DISTINCT ON (dvc.vulnerability_id)
|
68
|
+
dvc.vulnerability_id,
|
69
|
+
dvc.category_name AS category
|
70
|
+
FROM dim_vulnerability_category dvc
|
71
|
+
GROUP BY dvc.vulnerability_id, dvc.category_name) dvc USING (vulnerability_id)
|
72
|
+
LEFT OUTER JOIN (SELECT
|
73
|
+
dvr.vulnerability_id,
|
74
|
+
string_agg(dvr.source || ': ' || dvr.reference, '|') AS references
|
75
|
+
FROM dim_vulnerability_reference dvr
|
76
|
+
GROUP BY dvr.vulnerability_id) ref USING (vulnerability_id)
|
77
|
+
LEFT OUTER JOIN (SELECT
|
78
|
+
vulnerability_id,
|
79
|
+
array_agg(solution_id) AS solutions
|
80
|
+
FROM dim_vulnerability_solution
|
81
|
+
GROUP BY vulnerability_id) sol USING (vulnerability_id)
|
82
|
+
WHERE date_modified >= '#{options[:vuln_query_date]}'"
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.vulnerability_references(options={})
|
86
|
+
"SELECT concat('R7_', vulnerability_id) as ID,
|
87
|
+
Source,
|
88
|
+
Reference
|
89
|
+
FROM (SELECT vulnerability_id
|
90
|
+
FROM dim_vulnerability
|
91
|
+
WHERE date_modified >= '#{options[:vuln_query_date]}') dv
|
92
|
+
JOIN (SELECT vulnerability_id, Source, Reference
|
93
|
+
FROM dim_vulnerability_reference) dvr USING (vulnerability_id)"
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.vulnerability_category(options={})
|
97
|
+
"SELECT concat('R7_', vulnerability_id) as ID, dvc.Category
|
98
|
+
FROM dim_vulnerability
|
99
|
+
LEFT OUTER JOIN
|
100
|
+
(SELECT vulnerability_id, category_name as Category
|
101
|
+
FROM dim_vulnerability_category dvc) dvc USING (vulnerability_id)
|
102
|
+
WHERE date_modified >= '#{options[:vuln_query_date]}'"
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.assets(options={})
|
106
|
+
last_import_date = options[:delta]
|
107
|
+
site_id = options[:site_id]
|
108
|
+
|
109
|
+
"WITH scan_found AS (
|
110
|
+
SELECT DISTINCT (asset_id)
|
111
|
+
asset_id,
|
112
|
+
MIN(scan_id) AS scan_found
|
113
|
+
FROM fact_asset_event
|
114
|
+
WHERE type = 'SCAN'
|
115
|
+
GROUP BY asset_id
|
116
|
+
), new_assets AS (
|
117
|
+
SELECT
|
118
|
+
sf.asset_id
|
119
|
+
FROM dim_scan ds
|
120
|
+
JOIN scan_found sf ON (ds.scan_id = sf.scan_found)
|
121
|
+
WHERE ds.finished > '#{last_import_date}'
|
122
|
+
AND ds.site_id = #{site_id}
|
123
|
+
)
|
124
|
+
SELECT
|
125
|
+
coalesce(host_name, CAST(dim_asset.asset_id AS TEXT)) AS Name,
|
126
|
+
dim_asset.ip_address,
|
127
|
+
dim_asset.mac_address,
|
128
|
+
concat('Rapid7 Nexpose') AS Discovery_Source,
|
129
|
+
CAST(CASE
|
130
|
+
WHEN dim_asset.host_type = 'Virtual Machine' OR dim_asset.host_type = 'Hypervisor'
|
131
|
+
THEN 1
|
132
|
+
ELSE 0
|
133
|
+
END AS BIT) AS Is_Virtual,
|
134
|
+
CONCAT('\"', dim_asset.os_description, '\"') AS Operating_System,
|
135
|
+
dim_asset.last_assessed_for_vulnerabilities AS Most_Recent_Discovery,
|
136
|
+
dim_asset.asset_id AS Nexpose_ID,
|
137
|
+
fact_asset.pci_status,
|
138
|
+
dim_asset.credential_status
|
139
|
+
FROM dim_asset
|
140
|
+
JOIN fact_asset USING (asset_id)
|
141
|
+
WHERE dim_asset.asset_id IN (
|
142
|
+
SELECT asset_id
|
143
|
+
FROM new_assets
|
144
|
+
)"
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.generate_vulnerability_filter(cves, cvss_range)
|
148
|
+
return '' if cves.to_a.empty? && cvss_range.to_a.empty?
|
149
|
+
vuln_filter = ''
|
150
|
+
vuln_filter += cves.map { |c| "reference='#{c}'" }.join(' OR ') unless cves.nil?
|
151
|
+
|
152
|
+
cvss_min = cvss_range.first || '0'
|
153
|
+
cvss_max = cvss_range.last || '10'
|
154
|
+
unless cvss_min.to_s == '0' && cvss_max.to_s == '10'
|
155
|
+
vuln_filter += ' AND ' unless vuln_filter.empty?
|
156
|
+
vuln_filter += "cvss_score >= #{cvss_min} AND cvss_score <= #{cvss_max}"
|
157
|
+
end
|
158
|
+
return '' if vuln_filter.empty?
|
159
|
+
|
160
|
+
"AND favi.vulnerability_id IN (
|
161
|
+
SELECT dvr.vulnerability_id
|
162
|
+
FROM dim_vulnerability_reference dvr
|
163
|
+
JOIN dim_vulnerability dv ON dv.vulnerability_id = dvr.vulnerability_id
|
164
|
+
WHERE dvr.vulnerability_id IN (SELECT av.vulnerability_id FROM asset_vulns av)
|
165
|
+
AND #{vuln_filter})"
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.generate_date_filter(dates)
|
169
|
+
return '' if dates.to_a.empty?
|
170
|
+
filters = []
|
171
|
+
filters << "first_found > '#{dates.first}'" unless dates.first.nil?
|
172
|
+
filters << "first_found < '#{dates.last}'" unless dates.last.nil?
|
173
|
+
filters.empty? ? '' : " AND #{filters.join(' AND ')}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.vulnerable_new_items(options={})
|
177
|
+
site_id = options[:site_id]
|
178
|
+
last_import_date = options[:delta]
|
179
|
+
vuln_filter = self.generate_vulnerability_filter(
|
180
|
+
options[:filters][:cve], options[:filters][:cvss]
|
181
|
+
)
|
182
|
+
date_filters = self.generate_date_filter(options[:filters][:date])
|
183
|
+
|
184
|
+
"WITH asset_vulns AS (
|
185
|
+
SELECT DISTINCT ON (favf.asset_id, favf.vulnerability_id)
|
186
|
+
favf.asset_id,
|
187
|
+
favf.vulnerability_id,
|
188
|
+
favf.date AS first_found,
|
189
|
+
favf.vulnerability_instances,
|
190
|
+
ip_address,
|
191
|
+
(SELECT max(date)
|
192
|
+
FROM fact_asset_event fae
|
193
|
+
WHERE type = 'SCAN'
|
194
|
+
AND fae.asset_id = favf.asset_id
|
195
|
+
GROUP BY fae.asset_id) AS last_found
|
196
|
+
FROM fact_asset_vulnerability_finding favf
|
197
|
+
JOIN dim_site_asset dsa ON favf.asset_id = dsa.asset_id AND dsa.site_id = #{site_id}
|
198
|
+
JOIN dim_asset da ON favf.asset_id = da.asset_id
|
199
|
+
WHERE date > '#{last_import_date}'
|
200
|
+
), valid_vulns AS (
|
201
|
+
SELECT DISTINCT ON (favi.asset_id, favi.vulnerability_id)
|
202
|
+
favi.asset_id,
|
203
|
+
favi.vulnerability_id,
|
204
|
+
array_agg(DISTINCT favi.port) AS ports,
|
205
|
+
array_agg(DISTINCT davs.solution_id) AS solutions,
|
206
|
+
favi.protocol,
|
207
|
+
regexp_replace(htmltotext(favi.proof), '\"','''', 'g') AS proof,
|
208
|
+
favi.status
|
209
|
+
FROM fact_asset_vulnerability_instance favi
|
210
|
+
LEFT JOIN dim_asset_vulnerability_finding_solution davs ON
|
211
|
+
davs.asset_id = favi.asset_id AND davs.vulnerability_id = favi.vulnerability_id
|
212
|
+
WHERE (favi.vulnerability_id, favi.asset_id) IN (SELECT av.vulnerability_id, av.asset_id FROM asset_vulns av)
|
213
|
+
#{vuln_filter}
|
214
|
+
GROUP BY favi.asset_id, favi.vulnerability_id, protocol, proof, status
|
215
|
+
)
|
216
|
+
SELECT
|
217
|
+
CAST(nv.asset_id AS TEXT) AS Configuration_Item,
|
218
|
+
'true' AS Active,
|
219
|
+
concat('R7_', nv.vulnerability_id) AS Vulnerability,
|
220
|
+
nv.first_found AS First_Found,
|
221
|
+
nv.last_found AS Last_Found,
|
222
|
+
nv.vulnerability_instances AS Times_Found,
|
223
|
+
nv.ip_address AS IP_Address,
|
224
|
+
coalesce(NULLIF(CONCAT('\"', vv.proof, '\"'), '\"\"'), 'None') AS Proof,
|
225
|
+
vv.status AS Status,
|
226
|
+
array_to_string(vv.ports, ' ', '') AS Ports,
|
227
|
+
coalesce(vv.protocol, 'None') AS Protocol,
|
228
|
+
NULLIF(CONCAT('\"', array_to_string(vv.solutions, ',', ''), '\"'), '\"\"') AS Solutions
|
229
|
+
FROM asset_vulns nv
|
230
|
+
JOIN valid_vulns vv ON vv.asset_id = nv.asset_id AND vv.vulnerability_id = nv.vulnerability_id
|
231
|
+
#{date_filters}
|
232
|
+
ORDER BY nv.asset_id, Vulnerability"
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.vulnerable_old_items(options={})
|
236
|
+
site_id = options[:site_id]
|
237
|
+
last_import_date = options[:delta]
|
238
|
+
vuln_filter = self.generate_vulnerability_filter(
|
239
|
+
options[:filters][:cve], options[:filters][:cvss]
|
240
|
+
)
|
241
|
+
date_filters = self.generate_date_filter(options[:filters][:date])
|
242
|
+
|
243
|
+
"WITH site_assets AS (
|
244
|
+
SELECT asset_id
|
245
|
+
FROM dim_site_asset
|
246
|
+
WHERE site_id = #{site_id}
|
247
|
+
), asset_vulns AS (
|
248
|
+
-- Asset vulns at last import
|
249
|
+
SELECT asset_id, vulnerability_id, date AS first_found
|
250
|
+
FROM fact_asset_vulnerability_finding_date
|
251
|
+
WHERE asset_id IN (SELECT asset_id FROM site_assets)
|
252
|
+
AND DAY = (SELECT periodbefore('#{last_import_date}'))
|
253
|
+
), current_vulns AS (
|
254
|
+
SELECT asset_id, vulnerability_id
|
255
|
+
FROM fact_asset_vulnerability_finding
|
256
|
+
WHERE asset_id IN (SELECT asset_id FROM site_assets)
|
257
|
+
)
|
258
|
+
SELECT
|
259
|
+
CAST(da.asset_id AS TEXT) AS Configuration_Item,
|
260
|
+
'false' AS Active,
|
261
|
+
concat('R7_', favi.vulnerability_id) AS Vulnerability,
|
262
|
+
da.ip_address AS IP_Address
|
263
|
+
FROM dim_asset da
|
264
|
+
-- Name this table favi to use the vulnerability ID with the vuln filter.
|
265
|
+
JOIN asset_vulns favi ON (da.asset_id = favi.asset_id)
|
266
|
+
LEFT JOIN current_vulns cv ON (cv.asset_id = da.asset_id) AND (cv.vulnerability_id = favi.vulnerability_id)
|
267
|
+
WHERE da.asset_id IN (SELECT asset_id FROM site_assets)
|
268
|
+
AND cv.vulnerability_id IS NULL
|
269
|
+
#{vuln_filter}
|
270
|
+
#{date_filters}
|
271
|
+
ORDER BY da.asset_id, Vulnerability"
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.vulnerability_solutions(options={})
|
275
|
+
"SELECT DISTINCT (solution_id)
|
276
|
+
solution_id,
|
277
|
+
nexpose_id,
|
278
|
+
coalesce(NULLIF(CONCAT('\"',regexp_replace(htmltotext(fix), '\"','''', 'g'),'\"'), '\"\"'), 'None') as fix,
|
279
|
+
estimate,
|
280
|
+
coalesce(NULLIF(CONCAT('\"',regexp_replace(htmltotext(summary), '\"','''', 'g'),'\"'), '\"\"'), 'None') as summary,
|
281
|
+
solution_type,
|
282
|
+
coalesce(NULLIF(CONCAT('\"',regexp_replace(htmltotext(applies_to), '\"','''', 'g'),'\"'), '\"\"'), 'None') as applies_to,
|
283
|
+
coalesce(NULLIF(CONCAT('\"', url, '\"'), '\"\"'), 'None') as url,
|
284
|
+
coalesce(NULLIF(CONCAT('\"',regexp_replace(htmltotext(additional_data), '\"','''', 'g'),'\"'), '\"\"'), 'None') as additional_data,
|
285
|
+
CONCAT('\"', array_to_string(req_solutions, ',', ''), '\"') as required_solutions,
|
286
|
+
CONCAT('\"', array_to_string(super_solutions, ',', ''), '\"') as superceding_solutions
|
287
|
+
FROM dim_solution
|
288
|
+
RIGHT OUTER JOIN (
|
289
|
+
SELECT DISTINCT (solution_id) solution_id
|
290
|
+
FROM (
|
291
|
+
SELECT solution_id, vulnerability_id, date_modified
|
292
|
+
FROM dim_vulnerability
|
293
|
+
LEFT JOIN dim_vulnerability_solution idvs USING (vulnerability_id)
|
294
|
+
) dvs
|
295
|
+
WHERE date_modified >= '#{options[:vuln_query_date]}'
|
296
|
+
UNION
|
297
|
+
SELECT DISTINCT (solution_id) solution_id
|
298
|
+
FROM dim_solution
|
299
|
+
LEFT JOIN (
|
300
|
+
SELECT solution_id, vulnerability_id, date_modified
|
301
|
+
FROM dim_vulnerability
|
302
|
+
LEFT JOIN dim_vulnerability_solution idvs USING (vulnerability_id)
|
303
|
+
) ndvs USING (solution_id)
|
304
|
+
WHERE vulnerability_id IS NULL
|
305
|
+
) dvs USING (solution_id)
|
306
|
+
LEFT JOIN (
|
307
|
+
SELECT DISTINCT (solution_id) solution_id,
|
308
|
+
array_agg(required_solution_id) as req_solutions
|
309
|
+
FROM dim_solution_prerequisite
|
310
|
+
GROUP BY solution_id
|
311
|
+
) dsp USING (solution_id)
|
312
|
+
JOIN (
|
313
|
+
SELECT DISTINCT (solution_id) solution_id,
|
314
|
+
array_agg(superceding_solution_id) as super_solutions
|
315
|
+
FROM dim_solution_highest_supercedence
|
316
|
+
GROUP BY solution_id
|
317
|
+
) dshs USING (solution_id)
|
318
|
+
ORDER BY solution_id"
|
319
|
+
end
|
320
|
+
|
321
|
+
def self.method_missing(m, *args, &block)
|
322
|
+
msg = "Method #{m} is not yet available for data warehouse mode."
|
323
|
+
|
324
|
+
log = NexposeServiceNow::NxLogger.instance
|
325
|
+
log.log_message(msg)
|
326
|
+
puts "#{msg} Exiting..."
|
327
|
+
exit 0
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|