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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5cf867dd52b399b8e111b8d36c404e133c663c31
4
- data.tar.gz: 5d2e5338b7ec21c7216dd1837ade8cf17921e000
3
+ metadata.gz: 84febd71cae74e5f410a05a4bc1a862ba29508e4
4
+ data.tar.gz: d37e0a05cdc1c1276ab23cbd50a5b7e80ccce0c2
5
5
  SHA512:
6
- metadata.gz: 803f91eaed2e6b4e8ad728588546cbd04a35a9fc511c41e8a5b2a1c6452ba1fa5ee5a91857525908fa86735d0d8d78d8e21e72362e6e865d15ab9a5e72f6dab4
7
- data.tar.gz: a80e9eaa45e6bffea2c5167736b9f52848bae488dcdfc2dfec10b902c4cef0daca1bca54e39f3c15ff931369f6a7d07557fe9b52fb88be797f9e38a429aad73b
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 contact the following address for support queries:
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).
@@ -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
- diff = CSVDiff.new(old_file,
116
- new_file,
117
- ignore_moves: true,
118
- key_fields: key_fields
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(key_fields.count == 1)
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(key_fields.count == 2)
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
- cvss_vector,
22
- ROUND(cvss_impact_score::numeric, 2) as Impact_Score,
23
- ROUND(cvss_exploit_score::numeric, 2) as Exploit_Score,
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(cvss_score::numeric, 2) as Vulnerability_Score,
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, coalesce(da.host_name, CAST(da.asset_id as text)) as Installed_On, ds.name, ds.Product_Name, ds.version, ds.cpe
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 || ' ' || family as Product_Name, version, cpe FROM dim_software) ds USING (software_id)
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
- "JOIN (SELECT vulnerability_id, cvss_score
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
- USING (vulnerability_id)"
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
- cvss_vector,
29
- ROUND(cvss_impact_score :: NUMERIC, 2) AS Impact_Score,
30
- ROUND(cvss_exploit_score :: NUMERIC, 2) AS Exploit_Score,
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(cvss_score :: NUMERIC, 2) AS Vulnerability_Score,
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
 
@@ -1,5 +1,5 @@
1
1
  module NexposeServiceNow
2
- VERSION = '0.7.2'
2
+ VERSION = '0.7.3'
3
3
  VENDOR = 'ServiceNow'
4
4
  PRODUCT = 'CMDB'
5
5
  end
@@ -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.2
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-04-30 00:00:00.000000000 Z
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.4.3
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: