nexpose_servicenow 0.7.2 → 0.7.3
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 +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:
|