tenable-ruby 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a7f7c335ff236793a5687cc416a23398aec7264e
4
+ data.tar.gz: 50be6ad12e2f3f4bd6a75c43d83af180eca6483a
5
+ SHA512:
6
+ metadata.gz: 9cdcd8832b724c65bc9217497d77c501e669a9f35bff4d9935943a91fecde7d43bd1908e55da271d82301e13b32679ac79bbf4d2e1d54dd4cac176d0d76a98e4
7
+ data.tar.gz: 8d34cc3920773f68f329ce96fe137e723a1f8b8344cd7262610a30a8f0041362073687cbb88d14f427663adc8274a29558f4e6f3b2d93e832c0629dd47235080
@@ -0,0 +1,6 @@
1
+ module TenableRuby
2
+ module Error
3
+ class AuthenticationError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,765 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ # = tenable-ruby.rb: Unofficial Ruby library for communicating with the tenable.io API
4
+ #
5
+ # Authors:: Vlatko Kosturjak, Patrick Craston
6
+ #
7
+ # (C) Vlatko Kosturjak, Kost. Distributed under MIT license.
8
+ #
9
+ # == What is this library?
10
+ #
11
+ # Unofficial Ruby library for communicating with the tenable.io API (also works with Nessus 6 API).
12
+ # You can start, stop, pause and resume scans. Get status of scans, download reports, create policies, etc.
13
+ # Based on the excellent library for interacting with Nessus
14
+ # https://github.com/kost/nessus_rest-ruby by https://github.com/kost.
15
+ #
16
+ # == Requirements
17
+ #
18
+ # Standard Ruby libraries: uri, net/https and json.
19
+ #
20
+
21
+ require 'openssl'
22
+ require 'uri'
23
+ require 'net/http'
24
+ require 'net/https'
25
+ require 'json'
26
+ require 'error/authentication_error'
27
+
28
+ module TenableRuby
29
+ class Client
30
+ attr_accessor :quick_defaults
31
+ attr_accessor :defsleep, :httpsleep, :httpretry, :ssl_use, :ssl_verify, :autologin
32
+ attr_reader :header
33
+
34
+ class << self
35
+ @connection
36
+ end
37
+
38
+ # initialize quick scan defaults: these will be used when not specifying defaults
39
+ #
40
+ # Usage:
41
+ #
42
+ # n.init_quick_defaults()
43
+ def init_quick_defaults
44
+ @quick_defaults = Hash.new
45
+ @quick_defaults['enabled'] = false
46
+ @quick_defaults['launch'] = 'ONETIME'
47
+ @quick_defaults['launch_now'] = true
48
+ @quick_defaults['description'] = 'Created with tenable-ruby https//gitlab.com/intruder/tenable-ruby'
49
+ end
50
+
51
+ # initialize object: try to connect to tenable.io
52
+ # Usage:
53
+ #
54
+ # TenableRuby::Client.new (:credentials => {username: 'user', password: 'password'})
55
+ # or
56
+ # TenableRuby::Client.new (:credentials => {access_key: 'XXX', secret_key: 'XXX'})
57
+ #
58
+ # default url is set to tenable.io, change to Nessus appliance url if required, e.g.
59
+ # TenableRuby::Client.new (:url => 'https://nessus_url:8834',
60
+ # :credentials => {access_key: 'XXX', secret_key: 'XXX'})
61
+ def initialize(params = {})
62
+ # defaults
63
+ @tenable_url = params.fetch(:url, 'https://cloud.tenable.com')
64
+ @credentials = params.fetch(:credentials)
65
+ @ssl_verify = params.fetch(:ssl_verify, false)
66
+ @ssl_use = params.fetch(:ssl_use, true)
67
+ @autologin = params.fetch(:autologin, true)
68
+ @defsleep = params.fetch(:defsleep, 1)
69
+ @httpretry = params.fetch(:httpretry, 3)
70
+ @httpsleep = params.fetch(:httpsleep, 1)
71
+
72
+ init_quick_defaults
73
+
74
+ uri = URI.parse(@tenable_url)
75
+ @connection = Net::HTTP.new(uri.host, uri.port)
76
+ @connection.use_ssl = @ssl_use
77
+
78
+ if @ssl_verify
79
+ @connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
80
+ else
81
+ @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
82
+ end
83
+
84
+ yield @connection if block_given?
85
+ authenticate if @autologin
86
+ end
87
+
88
+ # Tries to authenticate to the tenable.io REST JSON interface using username/password or API keys
89
+ def authenticate
90
+ if @credentials[:username] and @credentials[:password]
91
+ payload = {
92
+ :username => @credentials[:username],
93
+ :password => @credentials[:password],
94
+ :json => 1,
95
+ :authenticationmethod => true
96
+ }
97
+ res = http_post(:uri => "/session", :data => payload)
98
+ if res['token']
99
+ @token = "token=#{res['token']}"
100
+ @header = {'X-Cookie' => @token}
101
+ else
102
+ fail NessusREST::Error::AuthenticationError, "Authentication failed. Could not authenticate using
103
+ username/password."
104
+ end
105
+ elsif @credentials[:access_key] and @credentials[:secret_key]
106
+ @header = {'X-ApiKeys' => "accessKey=#{@credentials[:access_key]}; secretKey=#{@credentials[:secret_key]}"}
107
+ else
108
+ fail NessusREST::Error::AuthenticationError, "Authentication credentials were not provided. You must provide" \
109
+ " either a username and password or an API access key and secret key (these can be generated at " \
110
+ "https://cloud.tenable.com/app.html#/settings/my-account/api-keys."
111
+ end
112
+ end
113
+
114
+ # Returns the server version and other properties
115
+ #
116
+ # Reference:
117
+ # https://cloud.tenable.com/api#/resources/server/properties
118
+ def get_server_properties
119
+ http_get(:uri => "/server/properties", :fields => header)
120
+ end
121
+
122
+ # Creates a new user
123
+ #
124
+ # Reference:
125
+ # https://cloud.tenable.com/api#/resources/users/create
126
+ def user_add(username, password, permissions, type)
127
+ payload = {
128
+ :username => username,
129
+ :password => password,
130
+ :permissions => permissions,
131
+ :type => type,
132
+ :json => 1
133
+ }
134
+ http_post(:uri => "/users", :fields => header, :data => payload)
135
+ end
136
+
137
+ # Deletes a user
138
+ #
139
+ # Reference:
140
+ # https://cloud.tenable.com/api#/resources/users/delete
141
+ def user_delete(user_id)
142
+ res = http_delete(:uri => "/users/#{user_id}", :fields => header)
143
+ res.code
144
+ end
145
+
146
+ # Changes the password for the given user
147
+ #
148
+ # Reference:
149
+ # https://cloud.tenable.com/api#/resources/users/password
150
+ def user_chpasswd(user_id, password)
151
+ payload = {
152
+ :password => password,
153
+ :json => 1
154
+ }
155
+ res = http_put(:uri => "/users/#{user_id}/chpasswd", :data => payload, :fields => header)
156
+ res.code
157
+ end
158
+
159
+ # Logs the current user out and destroys the session
160
+ #
161
+ # Reference:
162
+ # https://cloud.tenable.com/api#/resources/session/destroy
163
+ def user_logout
164
+ res = http_delete(:uri => "/session", :fields => header)
165
+ res.code
166
+ end
167
+
168
+ # Returns the policy list
169
+ #
170
+ # Reference:
171
+ # https://cloud.tenable.com/api#/resources/policies/list
172
+ def list_policies
173
+ http_get(:uri => "/policies", :fields => header)
174
+ end
175
+
176
+ # Returns the user list
177
+ #
178
+ # Reference:
179
+ # https://cloud.tenable.com/api#/resources/users/list
180
+ def list_users
181
+ http_get(:uri => "/users", :fields => header)
182
+ end
183
+
184
+ # Returns the current user's scan folders
185
+ #
186
+ # Reference:
187
+ # https://cloud.tenable.com/api#/resources/folders/list
188
+ def list_folders
189
+ http_get(:uri => "/folders", :fields => header)
190
+ end
191
+
192
+ # Returns the scanner list
193
+ #
194
+ # Reference:
195
+ # https://cloud.tenable.com/api#/resources/scanners/list
196
+ def list_scanners
197
+ http_get(:uri => "/scanners", :fields => header)
198
+ end
199
+
200
+ # Returns the list of plugin families
201
+ #
202
+ # Reference:
203
+ # https://cloud.tenable.com/api#/resources/plugins/families
204
+ def list_families
205
+ http_get(:uri => "/plugins/families", :fields => header)
206
+ end
207
+
208
+ # Returns the list of plugins in a family
209
+ #
210
+ # Reference:
211
+ # https://cloud.tenable.com/api#/resources/plugins/family-details
212
+ def list_plugins(family_id)
213
+ http_get(:uri => "/plugins/families/#{family_id}", :fields => header)
214
+ end
215
+
216
+ # Returns the template list
217
+ #
218
+ # Reference:
219
+ # https://cloud.tenable.com/api#/resources/editor/list
220
+ def list_templates(type)
221
+ http_get(:uri => "/editor/#{type}/templates", :fields => header)
222
+ end
223
+
224
+ # Returns details for the given template
225
+ #
226
+ # Reference:
227
+ # https://cloud.tenable.com/api#/resources/editor/template-details
228
+ def editor_templates (type, uuid)
229
+ http_get(:uri => "/editor/#{type}/templates/#{uuid}", :fields => header)
230
+ end
231
+
232
+ # Returns details for a given plugin
233
+ #
234
+ # Reference:
235
+ # https://cloud.tenable.com/api#/resources/plugins/plugin-details
236
+ def plugin_details(plugin_id)
237
+ http_get(:uri => "/plugins/plugin/#{plugin_id}", :fields => header)
238
+ end
239
+
240
+ # Returns the server status
241
+ #
242
+ # Reference:
243
+ # https://cloud.tenable.com/api#/resources/server/status
244
+ def server_status
245
+ http_get(:uri => "/server/status", :fields => header)
246
+ end
247
+
248
+ # Creates a scan
249
+ #
250
+ # Reference:
251
+ # https://cloud.tenable.com/api#/resources/scans/create
252
+ def scan_create(uuid, settings)
253
+ payload = {
254
+ :uuid => uuid,
255
+ :settings => settings,
256
+ :json => 1
257
+ }.to_json
258
+ http_post(:uri => "/scans", :body => payload, :fields => header, :ctype => 'application/json')
259
+ end
260
+
261
+ # Launches a scan
262
+ #
263
+ # Reference:
264
+ # https://cloud.tenable.com/api#/resources/scans/launch
265
+ def scan_launch(scan_id)
266
+ http_post(:uri => "/scans/#{scan_id}/launch", :fields => header)
267
+ end
268
+
269
+ # Get List of Scans
270
+ #
271
+ # Reference:
272
+ # https://cloud.tenable.com/api#/resources/scans/list
273
+ def scan_list
274
+ http_get(:uri => "/scans", :fields => header)
275
+ end
276
+
277
+ # Returns details for the given scan
278
+ #
279
+ # Reference:
280
+ # https://cloud.tenable.com/api#/resources/scans/details
281
+ def scan_details(scan_id)
282
+ http_get(:uri => "/scans/#{scan_id}", :fields => header)
283
+ end
284
+
285
+ # Pauses a scan
286
+ #
287
+ # Reference:
288
+ # https://cloud.tenable.com/api#/resources/scans/pause
289
+ def scan_pause(scan_id)
290
+ http_post(:uri => "/scans/#{scan_id}/pause", :fields => header)
291
+ end
292
+
293
+ # Resumes a scan
294
+ #
295
+ # Reference:
296
+ # https://cloud.tenable.com/api#/resources/scans/resume
297
+ def scan_resume(scan_id)
298
+ http_post(:uri => "/scans/#{scan_id}/resume", :fields => header)
299
+ end
300
+
301
+ # Stops a scan
302
+ #
303
+ # Reference:
304
+ # https://cloud.tenable.com/api#/resources/scans/stop
305
+ def scan_stop(scan_id)
306
+ http_post(:uri => "/scans/#{scan_id}/stop", :fields => header)
307
+ end
308
+
309
+ # Export the given scan. Once requested, the file can be downloaded using the export download method
310
+ # upon receiving a "ready" status from the export status method.
311
+ #
312
+ # Reference:
313
+ # https://cloud.tenable.com/api#/resources/scans/export-request
314
+ def scan_export(scan_id, format)
315
+ payload = {
316
+ :format => format
317
+ }.to_json
318
+ http_post(:uri => "/scans/#{scan_id}/export", :body => payload, :ctype => 'application/json', :fields => header)
319
+ end
320
+
321
+ # Check the file status of an exported scan. When an export has been requested, it is necessary to poll this
322
+ # endpoint until a "ready" status is returned, at which point the file is complete and can be downloaded
323
+ # using the export download endpoint.
324
+ #
325
+ # Reference:
326
+ # https://cloud.tenable.com/api#/resources/scans/export-status
327
+ def scan_export_status(scan_id, file_id)
328
+ http_get(:uri => "/scans/#{scan_id}/export/#{file_id}/status", :fields => header)
329
+ end
330
+
331
+ # Deletes a scan. NOTE: Scans in running, paused or stopping states can not be deleted.
332
+ #
333
+ # Reference:
334
+ # https://cloud.tenable.com/api#/resources/scans/delete
335
+ def scan_delete(scan_id)
336
+ res = http_delete(:uri => "/scans/#{scan_id}", :fields => header)
337
+ if res.code == 200
338
+ true
339
+ else
340
+ false
341
+ end
342
+ end
343
+
344
+ # Returns details for the given host
345
+ #
346
+ # Reference:
347
+ # https://cloud.tenable.com/api#/resources/scans/host-details
348
+ def host_details(scan_id, host_id, history_id: nil)
349
+ uri = "/scans/#{scan_id}/hosts/#{host_id}"
350
+ unless history_id.nil?
351
+ uri += "?history_id=#{history_id}"
352
+ end
353
+ http_get(:uri => uri, :fields => header)
354
+ end
355
+
356
+ # Download an exported scan
357
+ #
358
+ # Reference:
359
+ # https://cloud.tenable.com/api#/resources/scans/export-download
360
+ def report_download(scan_id, file_id)
361
+ http_get(:uri => "/scans/#{scan_id}/export/#{file_id}/download", :raw_content => true, :fields => header)
362
+ end
363
+
364
+ # Returns details for the given policy
365
+ #
366
+ # Reference:
367
+ # https://cloud.tenable.com/api#/resources/scans/host-details
368
+ def policy_details(policy_id)
369
+ http_get(:uri => "/policies/#{policy_id}", :fields => header)
370
+ end
371
+
372
+ # Creates a policy
373
+ #
374
+ # Reference:
375
+ # https://cloud.tenable.com/api#/resources/policies/create
376
+ def policy_create(template_id, plugins, settings)
377
+ options = {
378
+ :uri => "/policies/",
379
+ :fields => header,
380
+ :ctype => 'application/json',
381
+ :body => {
382
+ :uuid => template_id,
383
+ :audits => {},
384
+ :credentials => {delete: []},
385
+ :plugins => plugins,
386
+ :settings => settings
387
+ }.to_json
388
+ }
389
+ http_post(options)
390
+ end
391
+
392
+ # Copy a policy
393
+ #
394
+ # Reference:
395
+ # https://cloud.tenable.com/api#/resources/policies/copy
396
+ def policy_copy(policy_id)
397
+ options = {
398
+ :uri => "/policies/#{policy_id}/copy",
399
+ :fields => header,
400
+ :ctype => 'application/json'
401
+ }
402
+ http_post(options)
403
+ end
404
+
405
+ # Changes the parameters of a policy
406
+ #
407
+ # Reference:
408
+ # https://cloud.tenable.com/api#/resources/policies/configure
409
+ def policy_configure(policy_id, template_id, plugins, settings)
410
+ options = {
411
+ :uri => "/policies/#{policy_id}",
412
+ :fields => header,
413
+ :ctype => 'application/json',
414
+ :body => {
415
+ :uuid => template_id,
416
+ :audits => {},
417
+ :credentials => {delete: []},
418
+ :plugins => plugins,
419
+ :settings => settings
420
+ }.to_json
421
+ }
422
+ http_put(options)
423
+ end
424
+
425
+ # Delete a policy
426
+ #
427
+ # Reference:
428
+ # https://cloud.tenable.com/api#/resources/policies/delete
429
+ def policy_delete(policy_id)
430
+ res = http_delete(:uri => "/policies/#{policy_id}", :fields => header)
431
+ res.code
432
+ end
433
+
434
+ # Schedules a software update for all components (only Nessus 6)
435
+ #
436
+ def software_update
437
+ if @tenable_url == 'https://cloud.tenable.com'
438
+ return "software_update only works on a Nessus 6 appliance"
439
+ end
440
+ http_post(:uri => "/settings/software-update", :fields => header)
441
+ end
442
+
443
+ # Performs scan with templatename provided (name, title or uuid of scan).
444
+ # Name is your scan name and targets are targets for scan
445
+ #
446
+ # returns: JSON parsed object with scan info
447
+ def scan_quick_template (templatename, name, targets)
448
+ templates = list_templates('scan')['templates'].select do |temp|
449
+ temp['uuid'] == templatename or temp['name'] == templatename or temp['title'] == templatename
450
+ end
451
+ if templates.nil?
452
+ return nil
453
+ end
454
+ template_uuid = templates.first['uuid']
455
+ settings = editor_templates('scan', template_uuid)
456
+ settings.merge!(@quick_defaults)
457
+ settings['name'] = name
458
+ settings['text_targets'] = targets
459
+ scan_create(template_uuid, settings)
460
+ end
461
+
462
+ # Performs scan with scan policy provided (uuid of policy or policy name).
463
+ # Name is your scan name and targets are targets for scan
464
+ # (foldername is optional - folder where to save the scan (if that folder exists))
465
+ # (scanner_id is optional - ID of the scanner/cloud scanner you want to run this scan on)
466
+ #
467
+ # returns: JSON parsed object with scan info
468
+ def scan_quick_policy (policyname, name, targets, foldername = nil, scanner_id = nil)
469
+ policies = list_policies['policies'].select do |pol|
470
+ pol['id'] == policyname or pol['name'] == policyname
471
+ end
472
+ if policies.nil?
473
+ return nil
474
+ end
475
+ policy = policies.first
476
+ template_uuid = policy['template_uuid']
477
+ settings = Hash.new
478
+ settings.merge!(@quick_defaults)
479
+ settings['name'] = name
480
+ settings['policy_id'] = policy['id']
481
+ settings['text_targets'] = targets
482
+ unless foldername.nil?
483
+ folders = list_folders['folders'].select do |folder|
484
+ folder['name'] == foldername
485
+ end
486
+ unless folders.empty?
487
+ settings['folder_id'] = folders.first['id']
488
+ end
489
+ end
490
+ unless scanner_id.nil?
491
+ settings['scanner_id'] = scanner_id
492
+ end
493
+ scan_create(template_uuid, settings)
494
+ end
495
+
496
+ # Returns scan status by performing a 'scan_details' API call
497
+ def scan_status(scan_id)
498
+ sd = scan_details(scan_id)
499
+ unless sd['error'].nil?
500
+ return 'error'
501
+ end
502
+ if sd.nil?
503
+ return 'error'
504
+ end
505
+ sd['info']['status']
506
+ end
507
+
508
+ # Returns the status of the latest history object of a scan by performing a 'scan_details' API call.
509
+ # Note this is currently updated more frequently than the scan status in the tenable.io API
510
+ def scan_latest_history_status(scan_id)
511
+ sd = scan_details(scan_id)
512
+ unless sd['error'].nil?
513
+ return 'error'
514
+ end
515
+ if sd.nil?
516
+ return 'error'
517
+ end
518
+ history = sd['history']
519
+ if history.nil? or history.length == 0
520
+ 'error'
521
+ else
522
+ sd['history'].last['status']
523
+ end
524
+ end
525
+
526
+ # Parse the scan status command to determine if a scan has finished
527
+ def scan_finished?(scan_id)
528
+ ss = scan_status(scan_id)
529
+ if ss == 'completed' or ss == 'canceled' or ss == 'imported'
530
+ true
531
+ else
532
+ false
533
+ end
534
+ end
535
+
536
+ # use download scan API call to download a report in raw format
537
+ def report_download_quick(scan_id, format)
538
+ se = scan_export(scan_id, format)
539
+ # ready, loading
540
+ while (status = scan_export_status(scan_id, se['file'])['status']) != "ready" do
541
+ # puts status
542
+ if status.nil? or status == ''
543
+ return nil
544
+ end
545
+ sleep @defsleep
546
+ end
547
+ report_download(scan_id, se['file'])
548
+ end
549
+
550
+ # use download scan API call to save a report as file
551
+ def report_download_file(scan_id, format, outputfn)
552
+ report_content = report_download_quick(scan_id, format)
553
+ File.open(outputfn, 'w') do |f|
554
+ f.write(report_content)
555
+ end
556
+ end
557
+
558
+
559
+ private
560
+
561
+ # Perform HTTP put method with uri, data and fields
562
+ #
563
+ # returns: HTTP result object
564
+ def http_put(opts = {})
565
+ ret = http_put_low(opts)
566
+ if ret.is_a?(Hash) and ret.has_key?('error') and ret['error'] == 'Invalid Credentials'
567
+ authenticate
568
+ http_put_low(opts)
569
+ else
570
+ ret
571
+ end
572
+ end
573
+
574
+ def http_put_low(opts = {})
575
+ uri = opts[:uri]
576
+ data = opts[:data]
577
+ fields = opts[:fields] || {}
578
+ res = nil
579
+ tries = @httpretry
580
+
581
+ req = Net::HTTP::Put.new(uri)
582
+ req.set_form_data(data) unless (data.nil? || data.empty?)
583
+ fields.each_pair do |name, value|
584
+ req.add_field(name, value)
585
+ end
586
+
587
+ begin
588
+ tries -= 1
589
+ res = @connection.request(req)
590
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
591
+ if tries > 0
592
+ sleep @httpsleep
593
+ retry
594
+ else
595
+ return res
596
+ end
597
+ rescue URI::InvalidURIError
598
+ return res
599
+ end
600
+ end
601
+
602
+ # Perform HTTP delete method with uri, data and fields
603
+ #
604
+ # returns: HTTP result object
605
+ def http_delete(opts = {})
606
+ ret = http_delete_low(opts)
607
+ if ret.is_a?(Hash) and ret.has_key?('error') and ret['error'] == 'Invalid Credentials'
608
+ authenticate
609
+ http_delete_low(opts)
610
+ ret
611
+ else
612
+ ret
613
+ end
614
+ end
615
+
616
+ def http_delete_low(opts = {})
617
+ uri = opts[:uri]
618
+ fields = opts[:fields] || {}
619
+ res = nil
620
+ tries = @httpretry
621
+
622
+ req = Net::HTTP::Delete.new(uri)
623
+
624
+ fields.each_pair do |name, value|
625
+ req.add_field(name, value)
626
+ end
627
+
628
+ begin
629
+ tries -= 1
630
+ res = @connection.request(req)
631
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
632
+ if tries > 0
633
+ sleep @httpsleep
634
+ retry
635
+ else
636
+ return res
637
+ end
638
+ rescue URI::InvalidURIError
639
+ return res
640
+ end
641
+ end
642
+
643
+ # Perform HTTP get method with uri and fields
644
+ #
645
+ # returns: JSON parsed object (if JSON parseable)
646
+ def http_get(opts = {})
647
+ raw_content = opts[:raw_content] || false
648
+ ret = http_get_low(opts)
649
+ if !raw_content
650
+ if ret.is_a?(Hash) and ret.has_key?('error') and ret['error'] == 'Invalid Credentials'
651
+ authenticate
652
+ ret = http_get_low(opts)
653
+ return ret
654
+ else
655
+ return ret
656
+ end
657
+ else
658
+ ret
659
+ end
660
+ end
661
+
662
+ def http_get_low(opts = {})
663
+ uri = opts[:uri]
664
+ fields = opts[:fields] || {}
665
+ raw_content = opts[:raw_content] || false
666
+ json = {}
667
+ tries = @httpretry
668
+
669
+ req = Net::HTTP::Get.new(uri)
670
+ fields.each_pair do |name, value|
671
+ req.add_field(name, value)
672
+ end
673
+
674
+ begin
675
+ tries -= 1
676
+ res = @connection.request(req)
677
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
678
+ if tries > 0
679
+ sleep @httpsleep
680
+ retry
681
+ else
682
+ return json
683
+ end
684
+ rescue URI::InvalidURIError
685
+ return json
686
+ end
687
+ if !raw_content
688
+ parse_json(res.body)
689
+ else
690
+ res.body
691
+ end
692
+ end
693
+
694
+ # Perform HTTP post method with uri, data, body and fields
695
+ #
696
+ # returns: JSON parsed object (if JSON parseable)
697
+ def http_post(opts = {})
698
+ if opts.has_key?(:authenticationmethod)
699
+ # i know authzmethod = opts.delete(:authorizationmethod) is short, but not readable
700
+ authzmethod = opts[:authenticationmethod]
701
+ opts.delete(:authenticationmethod)
702
+ end
703
+ ret = http_post_low(opts)
704
+ if ret.is_a?(Hash) and ret.has_key?('error') and ret['error'] == 'Invalid Credentials'
705
+ unless authzmethod
706
+ authenticate
707
+ ret = http_post_low(opts)
708
+ return ret
709
+ end
710
+ else
711
+ ret
712
+ end
713
+ end
714
+
715
+ def http_post_low(opts = {})
716
+ uri = opts[:uri]
717
+ data = opts[:data]
718
+ fields = opts[:fields] || {}
719
+ body = opts[:body]
720
+ ctype = opts[:ctype]
721
+ json = {}
722
+ tries = @httpretry
723
+
724
+ req = Net::HTTP::Post.new(uri)
725
+ req.set_form_data(data) unless (data.nil? || data.empty?)
726
+ req.body = body unless (body.nil? || body.empty?)
727
+ req['Content-Type'] = ctype unless (ctype.nil? || ctype.empty?)
728
+ fields.each_pair do |name, value|
729
+ req.add_field(name, value)
730
+ end
731
+
732
+ begin
733
+ tries -= 1
734
+ res = @connection.request(req)
735
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
736
+ if tries > 0
737
+ sleep @httpsleep
738
+ retry
739
+ else
740
+ return json
741
+ end
742
+ rescue URI::InvalidURIError
743
+ return json
744
+ end
745
+
746
+ parse_json(res.body)
747
+ end
748
+
749
+ # Perform JSON parsing of body
750
+ #
751
+ # returns: JSON parsed object (if JSON parseable)
752
+ def parse_json(body)
753
+ buf = {}
754
+
755
+ begin
756
+ buf = JSON.parse(body)
757
+ rescue JSON::ParserError
758
+ end
759
+
760
+ buf
761
+ end
762
+
763
+ end
764
+ end
765
+
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tenable-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ platform: ruby
6
+ authors:
7
+ - Vlatko Kosturjak
8
+ - Patrick Craston
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-06-20 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: "Unofficial Ruby library for communicating with the tenable.io API (also
15
+ works with Nessus 6).\n You can start, stop, pause and resume scan. Get status
16
+ of scans, download reports, create policies, etc.\n Based on the excellent library
17
+ for interacting with Nessus https://github.com/kost/nessus_rest-ruby by https://github.com/kost. "
18
+ email: patrick.craston@intruder.io
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - lib/error/authentication_error.rb
24
+ - lib/tenable-ruby.rb
25
+ homepage: https://gitlab.com/intruder/tenable-ruby
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.5.2
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: Unofficial Ruby library for communicating with the tenable.io API
49
+ test_files: []