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 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: