tenable-ruby-sdk 0.2.1 → 0.2.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
  SHA256:
3
- metadata.gz: 86b71cea395f4634ebeee10f3a73541f012ecb41604c19cd4f8441d64a317ece
4
- data.tar.gz: 1dd6a81da536e32ebfdcbb6b908d0eeb502ac471ae2f9d7ba713f87c4d76b93b
3
+ metadata.gz: b6d43ebf62504ff612798c1da4e1b9ab3139fce82074ba5c45030d8369106cfd
4
+ data.tar.gz: 919847851066083b7cf95d137b0eb7b376b522333ca4866a004337cdf0d32cc1
5
5
  SHA512:
6
- metadata.gz: c83641303c4468e92e859611131006158aaa963e55d3c0c08ae687911748477a0c7327f98a2394bca6e40174b1ac37a5074c07545df7be0fb1fbd0e29a9c4f2d
7
- data.tar.gz: e4caa53b483ceef74f7279384d3a3da94c34350cbd92623c3365c220ff3e24b535f677d785d1b061df0060f622ff5657b223b4ef11f323dee03a2d6c4119b05e
6
+ metadata.gz: fa1c0b19a4921747e86d7e5a5eb884ac3842b1db46cf1b7289920b026f55328536fe3ba6fbaddecd7b0bafce920dc1e6268fc54fd660ab797c32d8a5d9eb8b31
7
+ data.tar.gz: d58116b3a1d534878c8dc0c10f6081bb686b64bb663744595472daee50429364cce5dd9221ab0edd83267485b7ac608bbeb90290c46ec2ef23c3dffe85636e1b
@@ -23,15 +23,24 @@ module Tenable
23
23
  def build_connection
24
24
  Faraday.new(url: @config.base_url) do |f|
25
25
  f.headers['Accept'] = 'application/json'
26
- f.use Middleware::Authentication,
27
- access_key: @config.access_key,
28
- secret_key: @config.secret_key
29
- f.use Middleware::Retry, max_retries: @config.max_retries
30
- f.use Middleware::Logging, logger: @config.logger
31
- f.options.timeout = @config.timeout
32
- f.options.open_timeout = @config.open_timeout
26
+ f.request :json
27
+ configure_middleware(f)
28
+ configure_timeouts(f)
33
29
  f.adapter Faraday.default_adapter
34
30
  end
35
31
  end
32
+
33
+ def configure_middleware(faraday)
34
+ faraday.use Middleware::Authentication,
35
+ access_key: @config.access_key,
36
+ secret_key: @config.secret_key
37
+ faraday.use Middleware::Retry, max_retries: @config.max_retries
38
+ faraday.use Middleware::Logging, logger: @config.logger
39
+ end
40
+
41
+ def configure_timeouts(faraday)
42
+ faraday.options.timeout = @config.timeout
43
+ faraday.options.open_timeout = @config.open_timeout
44
+ end
36
45
  end
37
46
  end
@@ -38,10 +38,7 @@ module Tenable
38
38
  # @raise [ApiError] on other non-2xx responses
39
39
  # @raise [ParseError] if the response is not valid JSON
40
40
  def post(path, body = nil)
41
- response = @connection.faraday.post(path) do |req|
42
- req.headers['Content-Type'] = 'application/json'
43
- req.body = JSON.generate(body) if body
44
- end
41
+ response = @connection.faraday.post(path, body)
45
42
  handle_response(response)
46
43
  end
47
44
 
@@ -51,10 +48,7 @@ module Tenable
51
48
  # @param body [Hash, nil] request body (serialized to JSON)
52
49
  # @return [Hash, Array, nil] parsed JSON response
53
50
  def put(path, body = nil)
54
- response = @connection.faraday.put(path) do |req|
55
- req.headers['Content-Type'] = 'application/json'
56
- req.body = JSON.generate(body) if body
57
- end
51
+ response = @connection.faraday.put(path, body)
58
52
  handle_response(response)
59
53
  end
60
54
 
@@ -64,10 +58,7 @@ module Tenable
64
58
  # @param body [Hash, nil] request body (serialized to JSON)
65
59
  # @return [Hash, Array, nil] parsed JSON response
66
60
  def patch(path, body = nil)
67
- response = @connection.faraday.patch(path) do |req|
68
- req.headers['Content-Type'] = 'application/json'
69
- req.body = JSON.generate(body) if body
70
- end
61
+ response = @connection.faraday.patch(path, body)
71
62
  handle_response(response)
72
63
  end
73
64
 
@@ -6,8 +6,17 @@ module Tenable
6
6
  class WebAppScans < Base
7
7
  TERMINAL_STATUSES = %w[completed failed cancelled error].freeze
8
8
 
9
+ # Supported scan export formats and their MIME types.
10
+ FORMAT_CONTENT_TYPES = {
11
+ 'json' => 'application/json',
12
+ 'csv' => 'text/csv',
13
+ 'xml' => 'application/xml',
14
+ 'html' => 'text/html',
15
+ 'pdf' => 'application/pdf'
16
+ }.freeze
17
+
9
18
  # Supported scan export formats.
10
- SUPPORTED_EXPORT_FORMATS = %w[json csv xml html pdf].freeze
19
+ SUPPORTED_EXPORT_FORMATS = FORMAT_CONTENT_TYPES.keys.freeze
11
20
 
12
21
  # @return [Integer] default seconds between status polls
13
22
  DEFAULT_POLL_INTERVAL = 2
@@ -34,8 +43,6 @@ module Tenable
34
43
  post('/was/v2/configs', { 'name' => name, 'target' => target })
35
44
  end
36
45
 
37
- # Retrieves a scan configuration by ID.
38
- #
39
46
  # @param config_id [String] the scan configuration ID
40
47
  # @return [Hash] the configuration data
41
48
  def get_config(config_id)
@@ -43,8 +50,6 @@ module Tenable
43
50
  get("/was/v2/configs/#{config_id}")
44
51
  end
45
52
 
46
- # Updates a scan configuration.
47
- #
48
53
  # @param config_id [String] the scan configuration ID
49
54
  # @param params [Hash] configuration parameters to update
50
55
  # @return [Hash] the updated configuration data
@@ -53,8 +58,6 @@ module Tenable
53
58
  put("/was/v2/configs/#{config_id}", params)
54
59
  end
55
60
 
56
- # Deletes a scan configuration.
57
- #
58
61
  # @param config_id [String] the scan configuration ID
59
62
  # @return [Hash, nil] parsed response or nil
60
63
  def delete_config(config_id)
@@ -62,8 +65,6 @@ module Tenable
62
65
  delete("/was/v2/configs/#{config_id}")
63
66
  end
64
67
 
65
- # Searches scan configurations.
66
- #
67
68
  # @param params [Hash] search parameters
68
69
  # @return [Hash] search results with items and pagination
69
70
  def search_configs(**params)
@@ -157,8 +158,6 @@ module Tenable
157
158
  post("/was/v2/configs/#{config_id}/scans/search", params)
158
159
  end
159
160
 
160
- # Searches WAS vulnerabilities.
161
- #
162
161
  # @param params [Hash] search parameters
163
162
  # @return [Hash] search results with items and pagination
164
163
  def search_vulnerabilities(**params)
@@ -176,18 +175,19 @@ module Tenable
176
175
 
177
176
  # Initiates a report export for a specific WAS scan.
178
177
  #
178
+ # The format is specified via the Content-Type header per the Tenable API.
179
+ #
179
180
  # @param scan_id [String] the scan ID
180
181
  # @param format [String] export format — one of "json", "csv", "xml", "html", or "pdf"
181
- # @param body [Hash] additional export parameters
182
182
  # @return [Hash] export initiation response
183
183
  # @raise [ArgumentError] if the format is not supported
184
- def export_scan(scan_id, format:, **body)
184
+ def export_scan(scan_id, format:)
185
185
  validate_path_segment!(scan_id, name: 'scan_id')
186
- unless SUPPORTED_EXPORT_FORMATS.include?(format)
187
- raise ArgumentError, "Unsupported format '#{format}'. Must be one of: #{SUPPORTED_EXPORT_FORMATS.join(', ')}"
186
+ content_type = validate_export_format!(format)
187
+ response = @connection.faraday.put("/was/v2/scans/#{scan_id}/report") do |req|
188
+ req.headers['Content-Type'] = content_type
188
189
  end
189
-
190
- put("/was/v2/scans/#{scan_id}/report", body.merge('format' => format))
190
+ handle_response(response)
191
191
  end
192
192
 
193
193
  # Checks the status of a WAS scan report by attempting to fetch it.
@@ -196,10 +196,15 @@ module Tenable
196
196
  # indicates the report is still being generated.
197
197
  #
198
198
  # @param scan_id [String] the scan ID
199
+ # @param format [String] export format — one of "json", "csv", "xml", "html", or "pdf"
199
200
  # @return [Hash] status data with +"status"+ key ("ready" or "loading")
200
- def export_scan_status(scan_id)
201
+ def export_scan_status(scan_id, format:)
201
202
  validate_path_segment!(scan_id, name: 'scan_id')
202
- response = @connection.faraday.get("/was/v2/scans/#{scan_id}/report")
203
+ content_type = validate_export_format!(format)
204
+ response = @connection.faraday.get("/was/v2/scans/#{scan_id}/report") do |req|
205
+ req.headers['Accept'] = content_type
206
+ req.headers['Content-Type'] = content_type
207
+ end
203
208
  if response.status == 404
204
209
  { 'status' => 'loading' }
205
210
  else
@@ -211,23 +216,32 @@ module Tenable
211
216
  # Downloads a completed WAS scan export as raw binary data.
212
217
  #
213
218
  # @param scan_id [String] the scan ID
219
+ # @param format [String] export format — one of "json", "csv", "xml", "html", or "pdf"
214
220
  # @return [String] raw binary content of the export
215
- def download_scan_export(scan_id)
221
+ def download_scan_export(scan_id, format:)
216
222
  validate_path_segment!(scan_id, name: 'scan_id')
217
- get_raw("/was/v2/scans/#{scan_id}/report")
223
+ content_type = validate_export_format!(format)
224
+ response = @connection.faraday.get("/was/v2/scans/#{scan_id}/report") do |req|
225
+ req.headers['Accept'] = content_type
226
+ req.headers['Content-Type'] = content_type
227
+ end
228
+ raise_for_status(response)
229
+ response.body
218
230
  end
219
231
 
220
232
  # Polls until a WAS scan export is ready for download.
221
233
  #
222
234
  # @param scan_id [String] the scan ID
235
+ # @param format [String] export format — one of "json", "csv", "xml", "html", or "pdf"
223
236
  # @param timeout [Integer] maximum seconds to wait (default: 600)
224
237
  # @param poll_interval [Integer] seconds between status checks (default: 5)
225
238
  # @return [Hash] the final status data when export is ready
226
239
  # @raise [Tenable::TimeoutError] if the export does not become ready within the timeout
227
- def wait_for_scan_export(scan_id, timeout: DEFAULT_EXPORT_TIMEOUT, poll_interval: DEFAULT_EXPORT_POLL_INTERVAL)
240
+ def wait_for_scan_export(scan_id, format:, timeout: DEFAULT_EXPORT_TIMEOUT,
241
+ poll_interval: DEFAULT_EXPORT_POLL_INTERVAL)
228
242
  validate_path_segment!(scan_id, name: 'scan_id')
229
243
  poll_until(timeout: timeout, poll_interval: poll_interval, label: "WAS scan export for #{scan_id}") do
230
- status_data = export_scan_status(scan_id)
244
+ status_data = export_scan_status(scan_id, format: format)
231
245
  status_data if status_data['status'] == 'ready'
232
246
  end
233
247
  end
@@ -235,26 +249,25 @@ module Tenable
235
249
  # Convenience method: requests an export, waits for completion, and downloads the result.
236
250
  #
237
251
  # @param scan_id [String] the scan ID
238
- # @param format [String] export format — one of "pdf", "csv", or "nessus"
252
+ # @param format [String] export format — one of "json", "csv", "xml", "html", or "pdf"
239
253
  # @param save_path [String, nil] if provided, writes binary content to this file path.
240
254
  # The caller is responsible for ensuring the path is safe and writable.
241
255
  # This value is used as-is with +File.binwrite+ — no sanitization is performed.
242
256
  # @param timeout [Integer] maximum seconds to wait (default: 600)
243
257
  # @param poll_interval [Integer] seconds between status checks (default: 5)
244
- # @param body [Hash] additional export parameters
245
258
  # @return [String] the save_path if given, otherwise the raw binary content
246
259
  #
247
260
  # @example Download PDF to disk
248
261
  # client.web_app_scans.export('scan-123', format: 'pdf', save_path: '/tmp/report.pdf')
249
262
  #
250
263
  # @example Get raw binary content
251
- # binary = client.web_app_scans.export('scan-123', format: 'nessus')
264
+ # binary = client.web_app_scans.export('scan-123', format: 'csv')
252
265
  def export(scan_id, format:, save_path: nil, timeout: DEFAULT_EXPORT_TIMEOUT,
253
- poll_interval: DEFAULT_EXPORT_POLL_INTERVAL, **body)
266
+ poll_interval: DEFAULT_EXPORT_POLL_INTERVAL)
254
267
  validate_path_segment!(scan_id, name: 'scan_id')
255
- export_scan(scan_id, format: format, **body)
256
- wait_for_scan_export(scan_id, timeout: timeout, poll_interval: poll_interval)
257
- content = download_scan_export(scan_id)
268
+ export_scan(scan_id, format: format)
269
+ wait_for_scan_export(scan_id, format: format, timeout: timeout, poll_interval: poll_interval)
270
+ content = download_scan_export(scan_id, format: format)
258
271
 
259
272
  if save_path
260
273
  File.binwrite(save_path, content)
@@ -300,6 +313,14 @@ module Tenable
300
313
  validate_path_segment!(export_uuid, name: 'export_uuid')
301
314
  post("/was/v1/export/vulns/#{export_uuid}/cancel")
302
315
  end
316
+
317
+ private
318
+
319
+ def validate_export_format!(format)
320
+ FORMAT_CONTENT_TYPES.fetch(format) do
321
+ raise ArgumentError, "Unsupported format '#{format}'. Must be one of: #{SUPPORTED_EXPORT_FORMATS.join(', ')}"
322
+ end
323
+ end
303
324
  end
304
325
  end
305
326
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tenable
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tenable-ruby-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - vudx00