nexpose_servicenow 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/lib/nexpose_servicenow.rb +2 -0
- data/lib/nexpose_servicenow/arg_parser.rb +7 -0
- data/lib/nexpose_servicenow/csv_compare.rb +27 -7
- data/lib/nexpose_servicenow/helpers/connection_helper.rb +5 -0
- data/lib/nexpose_servicenow/helpers/data_warehouse_helper.rb +6 -0
- data/lib/nexpose_servicenow/helpers/nexpose_console_helper.rb +7 -0
- data/lib/nexpose_servicenow/queries/nexpose_queries.rb +29 -14
- data/lib/nexpose_servicenow/queries/warehouse_queries.rb +19 -8
- data/lib/nexpose_servicenow/version.rb +1 -1
- data/nexpose_servicenow.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84febd71cae74e5f410a05a4bc1a862ba29508e4
|
4
|
+
data.tar.gz: d37e0a05cdc1c1276ab23cbd50a5b7e80ccce0c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90a4f106d54bd9995ebbd7afd457301b92f1d4bd5272d1ef650a8f11019d51e0265b03ea551a885241d59de49f3ab2538c101a6e753da2ee32512a72590f67fb
|
7
|
+
data.tar.gz: 13598b6b64bb63094f31f9ff43d3ec7e315530f749bfc3f02779c18123fad814b486981df4697be492c54d6fda703802e7d3fdeca2930fc4008972de3a3ada10
|
data/README.md
CHANGED
@@ -13,11 +13,13 @@ Alternatively, it is also possible to call the following to see a list of parame
|
|
13
13
|
|
14
14
|
|
15
15
|
## Support
|
16
|
-
Please
|
16
|
+
Please visit the following address for support queries:
|
17
17
|
[Rapid7 Support Portal](https://rapid7support.force.com/customers/login)
|
18
18
|
|
19
19
|
Please attach both the gem logs and relevant snippets from the agent logs.
|
20
20
|
|
21
|
+
Documentation is available from the ServiceNow Store Portal.
|
22
|
+
|
21
23
|
## License
|
22
24
|
|
23
25
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/nexpose_servicenow.rb
CHANGED
@@ -107,6 +107,8 @@ module NexposeServiceNow
|
|
107
107
|
query_options[:filters] = options[:filters]
|
108
108
|
query_options[:page_size] = options[:row_limit]
|
109
109
|
query_options[:row_limit] = options[:row_limit]
|
110
|
+
query_options[:cvss_v3] = options[:cvss_v3]
|
111
|
+
|
110
112
|
report_helper = get_helper(options)
|
111
113
|
@log.log_message("Querying using the #{report_helper.class}.")
|
112
114
|
report_helper.generate_report(options[:query],
|
@@ -77,6 +77,12 @@ module NexposeServiceNow
|
|
77
77
|
options[:vuln_query_date] = vulnt
|
78
78
|
end
|
79
79
|
|
80
|
+
opts.on('-y', '--cvss-version VERSION',
|
81
|
+
'The CVSS version to import ' \
|
82
|
+
'(2 or 3 where available)') do |version|
|
83
|
+
options[:cvss_v3] = version == '3'
|
84
|
+
end
|
85
|
+
|
80
86
|
opts.separator ''
|
81
87
|
opts.separator 'Connection options:'
|
82
88
|
|
@@ -218,6 +224,7 @@ module NexposeServiceNow
|
|
218
224
|
options[:conn_type] ||= :nexpose_console
|
219
225
|
options[:nexpose_ids] ||= {}
|
220
226
|
options[:filters] ||= {}
|
227
|
+
options[:cvss_v3] ||= false
|
221
228
|
|
222
229
|
options[:query] = 'latest_scans' if options[:mode] == 'latest_scans'
|
223
230
|
|
@@ -108,15 +108,35 @@ module NexposeServiceNow
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
+
temp.close
|
111
112
|
FileUtils.mv(temp, target_file, :force => true)
|
112
113
|
end
|
113
114
|
|
114
115
|
def self.create_csv_diff(old_file, new_file, target_file, key_fields=[0])
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
old_temp = Tempfile.new("#{File.basename old_file}tmp")
|
117
|
+
new_temp = Tempfile.new("#{File.basename new_file}tmp")
|
118
|
+
|
119
|
+
File.open(old_file) do |f|
|
120
|
+
IO.copy_stream(f, old_temp)
|
121
|
+
end
|
122
|
+
File.open(new_file) do |f|
|
123
|
+
IO.copy_stream(f, new_temp)
|
124
|
+
end
|
125
|
+
|
126
|
+
begin
|
127
|
+
diff = CSVDiff.new(
|
128
|
+
File.expand_path(old_temp),
|
129
|
+
File.expand_path(new_temp),
|
130
|
+
ignore_moves: true,
|
131
|
+
key_fields: key_fields
|
132
|
+
)
|
133
|
+
rescue Exception => e
|
134
|
+
file_name = File.basename target_file
|
135
|
+
raise "Unable to diff file: #{file_name}. \nError received: #{e}"
|
136
|
+
ensure
|
137
|
+
old_temp.close!
|
138
|
+
new_temp.close!
|
139
|
+
end
|
120
140
|
|
121
141
|
columns = self.get_columns(new_file)
|
122
142
|
|
@@ -126,7 +146,7 @@ module NexposeServiceNow
|
|
126
146
|
diff.deletes.each_value { |v| csv << get_delete(v, columns) }
|
127
147
|
diff.adds.each_value { |v| csv << get_add(v, columns) }
|
128
148
|
|
129
|
-
if
|
149
|
+
if key_fields.count == 1
|
130
150
|
# If only a single key field, we don't need the old values
|
131
151
|
# Just grab the row and let ServiceNow coalesce and update the row
|
132
152
|
update_rows = diff.updates.each_value.map { |u| u.row }
|
@@ -142,7 +162,7 @@ module NexposeServiceNow
|
|
142
162
|
update_row_num = update_rows.shift
|
143
163
|
break if update_row_num == nil
|
144
164
|
end
|
145
|
-
elsif
|
165
|
+
elsif key_fields.count == 2
|
146
166
|
# Multiple key fields result in row "updates"
|
147
167
|
diff.updates.each_value do |v|
|
148
168
|
csv << self.update_to_old(v, columns)
|
@@ -37,6 +37,7 @@ module NexposeServiceNow
|
|
37
37
|
options[:site_id] = nexpose_id
|
38
38
|
options[:id_type] = 'site'
|
39
39
|
options[:filters] = query_options[:filters] || {}
|
40
|
+
options[:cvss_v] = query_options[:cvss_v]
|
40
41
|
|
41
42
|
# Without a nexpose ID, we don't have a specific delta
|
42
43
|
return options if [nil, -1].include? nexpose_id
|
@@ -75,5 +76,9 @@ module NexposeServiceNow
|
|
75
76
|
def save_report(report_name, report_id, output_dir)
|
76
77
|
raise NOT_IMPL
|
77
78
|
end
|
79
|
+
|
80
|
+
def get_cvss_version_strings(use_v3)
|
81
|
+
raise NOT_IMPL
|
82
|
+
end
|
78
83
|
end
|
79
84
|
end
|
@@ -29,6 +29,8 @@ module NexposeServiceNow
|
|
29
29
|
#A single report doesn't use site filters
|
30
30
|
ids = [-1] if WarehouseQueries.single_report?(query_name)
|
31
31
|
|
32
|
+
query_options[:cvss_v] = get_cvss_version_strings(query_options[:cvss_v3])
|
33
|
+
|
32
34
|
page_size = query_options[:page_size]
|
33
35
|
row_limit = query_options[:row_limit]
|
34
36
|
|
@@ -130,5 +132,9 @@ module NexposeServiceNow
|
|
130
132
|
result.map { |r| r['site_id'] }
|
131
133
|
connection.finish
|
132
134
|
end
|
135
|
+
|
136
|
+
def get_cvss_version_strings(use_v3)
|
137
|
+
use_v3 ? { choice: '_v3', fallback: '' } : { choice: '', fallback: '' }
|
138
|
+
end
|
133
139
|
end
|
134
140
|
end
|
@@ -24,6 +24,8 @@ module NexposeServiceNow
|
|
24
24
|
@log.on_connect(@url, @port, @nsc.session_id, '{}')
|
25
25
|
end
|
26
26
|
|
27
|
+
query_options[:cvss_v] = get_cvss_version_strings(query_options[:cvss_v3])
|
28
|
+
|
27
29
|
ids.each do |id|
|
28
30
|
report_name = self.class.get_report_name(query_name, id)
|
29
31
|
clean_up_reports(report_name)
|
@@ -153,5 +155,10 @@ module NexposeServiceNow
|
|
153
155
|
def collection_ids(collection_type)
|
154
156
|
@nsc.send("#{collection_type}s").map { |s| s.id }.sort
|
155
157
|
end
|
158
|
+
|
159
|
+
def get_cvss_version_strings(use_v3)
|
160
|
+
return { choice: '_v3', vector: '_v3', fallback: '' } if use_v3
|
161
|
+
{ choice: '_v2', vector: '', fallback: '' }
|
162
|
+
end
|
156
163
|
end
|
157
164
|
end
|
@@ -18,13 +18,20 @@ module NexposeServiceNow
|
|
18
18
|
title as Summary,
|
19
19
|
proofAsText(description) as Threat,
|
20
20
|
ROUND(riskscore::numeric, 2) as Riskscore,
|
21
|
-
|
22
|
-
|
23
|
-
ROUND(
|
21
|
+
coalesce(cvss#{options[:cvss_v][:vector]}_vector,
|
22
|
+
cvss#{options[:cvss_v][:fallback]}_vector) as cvss_vector,
|
23
|
+
ROUND(coalesce(cvss#{options[:cvss_v][:choice]}_impact_score::numeric,
|
24
|
+
cvss#{options[:cvss_v][:fallback]}_impact_score::numeric),
|
25
|
+
2) as Impact_Score,
|
26
|
+
ROUND(coalesce(cvss#{options[:cvss_v][:choice]}_exploit_score::numeric,
|
27
|
+
cvss#{options[:cvss_v][:fallback]}_exploit_score::numeric),
|
28
|
+
2) as Exploit_Score,
|
24
29
|
cvss_access_complexity_id as Access_Complexity,
|
25
30
|
cvss_access_vector_id as Access_Vector,
|
26
31
|
cvss_authentication_id as Authentication,
|
27
|
-
ROUND(
|
32
|
+
ROUND(coalesce(cvss#{options[:cvss_v][:choice]}_score::numeric,
|
33
|
+
cvss#{options[:cvss_v][:fallback]}_score::numeric),
|
34
|
+
2) as Vulnerability_Score,
|
28
35
|
cvss_integrity_impact_id as Integrity_Impact,
|
29
36
|
cvss_confidentiality_impact_id as Confidentiality_Impact,
|
30
37
|
cvss_availability_impact_id as Availability_Impact,
|
@@ -120,9 +127,9 @@ module NexposeServiceNow
|
|
120
127
|
end
|
121
128
|
|
122
129
|
def self.software_instance(options={})
|
123
|
-
"SELECT asset_id as Nexpose_ID,
|
130
|
+
"SELECT asset_id as Nexpose_ID, CAST(da.asset_id as text) as Installed_On, ds.name, ds.vendor, ds.family, ds.version, ds.cpe
|
124
131
|
FROM fact_asset_scan_software
|
125
|
-
LEFT OUTER JOIN (SELECT software_id, name, vendor
|
132
|
+
LEFT OUTER JOIN (SELECT software_id, name, vendor, family, version, cpe FROM dim_software) ds USING (software_id)
|
126
133
|
LEFT OUTER JOIN (SELECT asset_id, host_name FROM dim_asset) da USING (asset_id)
|
127
134
|
WHERE scan_id = lastScan(asset_id)"
|
128
135
|
end
|
@@ -254,7 +261,7 @@ module NexposeServiceNow
|
|
254
261
|
fas.scan_id = first_found"
|
255
262
|
end
|
256
263
|
|
257
|
-
def self.generate_cvss_filter(cvss_range)
|
264
|
+
def self.generate_cvss_filter(cvss_range, cvss_strings)
|
258
265
|
return '' if cvss_range.nil? || cvss_range.last.nil?
|
259
266
|
|
260
267
|
cvss_min = cvss_range.first
|
@@ -263,13 +270,16 @@ module NexposeServiceNow
|
|
263
270
|
# No need to join if not applying a filter
|
264
271
|
return '' if cvss_min.to_s == '0' && cvss_max.to_s == '10'
|
265
272
|
|
266
|
-
"
|
273
|
+
cvss_score = "(coalesce(cvss#{cvss_strings[:choice]}_score,
|
274
|
+
cvss#{cvss_strings[:fallback]}_score))"
|
275
|
+
|
276
|
+
"JOIN (SELECT vulnerability_id
|
267
277
|
FROM dim_vulnerability
|
268
|
-
WHERE cvss_score >= #{cvss_min} AND cvss_score <= #{cvss_max}) dv
|
269
|
-
|
278
|
+
WHERE #{cvss_score} >= #{cvss_min} AND #{cvss_score} <= #{cvss_max}) dv
|
279
|
+
USING (vulnerability_id)"
|
270
280
|
end
|
271
281
|
|
272
|
-
def self.generate_cvss_table(cvss_range)
|
282
|
+
def self.generate_cvss_table(cvss_range, cvss_strings)
|
273
283
|
return '' if cvss_range.nil? || cvss_range.last.nil?
|
274
284
|
|
275
285
|
cvss_min = cvss_range.first
|
@@ -277,15 +287,19 @@ module NexposeServiceNow
|
|
277
287
|
|
278
288
|
return '' if cvss_min.to_s == '0' && cvss_max.to_s == '10'
|
279
289
|
|
290
|
+
cvss_score = "(coalesce(cvss#{cvss_strings[:choice]}_score,
|
291
|
+
cvss#{cvss_strings[:fallback]}_score))"
|
292
|
+
|
280
293
|
"vulns_cvss AS (
|
281
294
|
SELECT vulnerability_id FROM dim_vulnerability
|
282
|
-
WHERE cvss_score >= #{cvss_min} AND cvss_score <= #{cvss_max})"
|
295
|
+
WHERE #{cvss_score} >= #{cvss_min} AND #{cvss_score} <= #{cvss_max})"
|
283
296
|
end
|
284
297
|
|
285
298
|
def self.vulnerable_new_items(options={})
|
286
299
|
date_filter = self.generate_date_filter(options[:filters][:date], false)
|
287
300
|
|
288
|
-
cvss_table = self.generate_cvss_table(options[:filters][:cvss]
|
301
|
+
cvss_table = self.generate_cvss_table(options[:filters][:cvss],
|
302
|
+
options[:cvss_v])
|
289
303
|
cvss_filter = ''
|
290
304
|
if cvss_table != ''
|
291
305
|
cvss_table = ",#{cvss_table}"
|
@@ -355,7 +369,8 @@ module NexposeServiceNow
|
|
355
369
|
|
356
370
|
cve_filter = self.generate_cve_filter(options[:filters][:cve])
|
357
371
|
date_filter = self.generate_date_filter(options[:filters][:date])
|
358
|
-
cvss_filter = self.generate_cvss_filter(options[:filters][:cvss]
|
372
|
+
cvss_filter = self.generate_cvss_filter(options[:filters][:cvss],
|
373
|
+
options[:cvss_v])
|
359
374
|
|
360
375
|
# Only perform this operation is necessary
|
361
376
|
date_field = if date_filter.nil? || date_filter == ''
|
@@ -25,13 +25,20 @@ module NexposeServiceNow
|
|
25
25
|
coalesce(NULLIF(CONCAT('\"',regexp_replace(title, '\"','''', 'g'),'\"'), '\"\"'), 'None') AS Summary,
|
26
26
|
coalesce(NULLIF(CONCAT('\"',regexp_replace(htmltotext(description), '\"','''', 'g'),'\"'), '\"\"'), 'None') AS Threat,
|
27
27
|
ROUND(risk_score :: NUMERIC, 2) AS Riskscore,
|
28
|
-
|
29
|
-
|
30
|
-
ROUND(
|
28
|
+
coalesce(cvss#{options[:cvss_v][:choice]}_vector,
|
29
|
+
cvss#{options[:cvss_v][:fallback]}_vector) as cvss_vector,
|
30
|
+
ROUND(coalesce(cvss#{options[:cvss_v][:choice]}_impact_score::NUMERIC,
|
31
|
+
cvss#{options[:cvss_v][:fallback]}_impact_score::NUMERIC),
|
32
|
+
2) AS Impact_Score,
|
33
|
+
ROUND(coalesce(cvss#{options[:cvss_v][:choice]}_exploit_score::NUMERIC,
|
34
|
+
cvss#{options[:cvss_v][:fallback]}_exploit_score::NUMERIC),
|
35
|
+
2) AS Exploit_Score,
|
31
36
|
cvss_access_complexity AS Access_Complexity,
|
32
37
|
cvss_access_vector AS Access_Vector,
|
33
38
|
cvss_authentication AS Authentication,
|
34
|
-
ROUND(
|
39
|
+
ROUND(coalesce(cvss#{options[:cvss_v][:choice]}_score::NUMERIC,
|
40
|
+
cvss#{options[:cvss_v][:fallback]}_score::NUMERIC),
|
41
|
+
2) as Vulnerability_Score,
|
35
42
|
cvss_integrity_impact AS Integrity_Impact,
|
36
43
|
cvss_confidentiality_impact AS Confidentiality_Impact,
|
37
44
|
cvss_availability_impact AS Availability_Impact,
|
@@ -144,16 +151,20 @@ module NexposeServiceNow
|
|
144
151
|
)"
|
145
152
|
end
|
146
153
|
|
147
|
-
def self.generate_vulnerability_filter(cves, cvss_range)
|
154
|
+
def self.generate_vulnerability_filter(cves, cvss_range, cvss_strings)
|
148
155
|
return '' if cves.to_a.empty? && cvss_range.to_a.empty?
|
149
156
|
vuln_filter = ''
|
150
157
|
vuln_filter += cves.map { |c| "reference='#{c}'" }.join(' OR ') unless cves.nil?
|
151
158
|
|
152
159
|
cvss_min = cvss_range.first || '0'
|
153
160
|
cvss_max = cvss_range.last || '10'
|
161
|
+
|
162
|
+
cvss_score = "(coalesce(cvss#{cvss_strings[:choice]}_score,
|
163
|
+
cvss#{cvss_strings[:fallback]}_score))"
|
164
|
+
|
154
165
|
unless cvss_min.to_s == '0' && cvss_max.to_s == '10'
|
155
166
|
vuln_filter += ' AND ' unless vuln_filter.empty?
|
156
|
-
vuln_filter += "cvss_score >= #{cvss_min} AND cvss_score <= #{cvss_max}"
|
167
|
+
vuln_filter += "#{cvss_score} >= #{cvss_min} AND #{cvss_score} <= #{cvss_max}"
|
157
168
|
end
|
158
169
|
return '' if vuln_filter.empty?
|
159
170
|
|
@@ -177,7 +188,7 @@ module NexposeServiceNow
|
|
177
188
|
site_id = options[:site_id]
|
178
189
|
last_import_date = options[:delta]
|
179
190
|
vuln_filter = self.generate_vulnerability_filter(
|
180
|
-
options[:filters][:cve], options[:filters][:cvss]
|
191
|
+
options[:filters][:cve], options[:filters][:cvss], options[:cvss_v]
|
181
192
|
)
|
182
193
|
date_filters = self.generate_date_filter(options[:filters][:date])
|
183
194
|
|
@@ -236,7 +247,7 @@ module NexposeServiceNow
|
|
236
247
|
site_id = options[:site_id]
|
237
248
|
last_import_date = options[:delta]
|
238
249
|
vuln_filter = self.generate_vulnerability_filter(
|
239
|
-
options[:filters][:cve], options[:filters][:cvss]
|
250
|
+
options[:filters][:cve], options[:filters][:cvss], options[:cvss_v]
|
240
251
|
)
|
241
252
|
date_filters = self.generate_date_filter(options[:filters][:date])
|
242
253
|
|
data/nexpose_servicenow.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'nexpose_servicenow/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'nexpose_servicenow'
|
8
8
|
spec.version = NexposeServiceNow::VERSION
|
9
|
-
spec.authors = ['David Valente']
|
9
|
+
spec.authors = ['David Valente', 'Adam Robinson']
|
10
10
|
spec.email = ['david_valente@rapid7.com']
|
11
11
|
|
12
12
|
spec.require_paths = ['lib']
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexpose_servicenow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Valente
|
8
|
+
- Adam Robinson
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2018-
|
12
|
+
date: 2018-05-23 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: bundler
|
@@ -133,9 +134,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
134
|
version: '0'
|
134
135
|
requirements: []
|
135
136
|
rubyforge_project:
|
136
|
-
rubygems_version: 2.
|
137
|
+
rubygems_version: 2.6.8
|
137
138
|
signing_key:
|
138
139
|
specification_version: 4
|
139
140
|
summary: Gem for Nexpose-ServiceNow integration.
|
140
141
|
test_files: []
|
141
|
-
has_rdoc:
|