nessus_client 0.1.0 → 0.1.6

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
  SHA256:
3
- metadata.gz: 1dda4cc05fbcee8eac14f8469f106944fa356ec74ecb31e3623c13338ec175f9
4
- data.tar.gz: db9270a96eb7866357754b65e1d3e17c0c67d9fdc98e5d507104a44fee49ef31
3
+ metadata.gz: 97155cc8a1019ff1a15883a8b3749b6702a4baf9aed454655a3f92ea49d06c22
4
+ data.tar.gz: 35b2dc0e7b0bc0ca9f32e92ffcadc8e01fed7de5140f8a9637081c716201438d
5
5
  SHA512:
6
- metadata.gz: d8c172bec3e3039644a3352dfa279e3799153351936b3d4b9ac86d90e87a31d9d151480e34ef7eb742258b77fdeb28066224216c07ae5a2cf31c9fb5771f8e8b
7
- data.tar.gz: 74dff229a778e97680bde8f690c845e84767b3da1847459577a377d2cfea2febc35f53c8fd1aac1befce5138c450cc7092c3b71d5f35cd840edd516dd4012461
6
+ metadata.gz: 3f4be80be45b124d81426674079fef9315366e91114854e7e20caa0090df53ea9aeb2f60e8f18ebd57ecb3e811846ba5e17cd4b02ffdd8f258d65f2bc27be75b
7
+ data.tar.gz: 6de44d65aae4427ca4ae24a098012fb2131cc7cd506de51d601a7d5cfe7f058144f0391bfc6234d6fae1d3da3701fe63f761a579980b1c07713618398415e7fb
@@ -0,0 +1,71 @@
1
+ # The NessusClient - Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ All complaints will be reviewed and investigated and will result in a response that
58
+ is deemed necessary and appropriate to the circumstances. The project team is
59
+ obligated to maintain confidentiality with regard to the reporter of an incident.
60
+ Further details of specific enforcement policies may be posted separately.
61
+
62
+ Project maintainers who do not follow or enforce the Code of Conduct in good
63
+ faith may face temporary or permanent repercussions as determined by other
64
+ members of the project's leadership.
65
+
66
+ ## Attribution
67
+
68
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
69
+ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
70
+
71
+ [homepage]: https://www.contributor-covenant.org
@@ -0,0 +1,7 @@
1
+
2
+ ## Contributing
3
+ 1. Fork it
4
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
5
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
6
+ 4. Push to the branch (`git push origin my-new-feature`)
7
+ 5. Create new Pull Request
data/README.md CHANGED
@@ -1,14 +1,20 @@
1
- NessusApi
1
+ NessusClient
2
2
  =========
3
- **Ruby wrapper for Nessus API**
4
3
 
5
- * [Source Code]
6
- * [API documentation]
7
- * [Changelog]
8
- * [Rubygem]
4
+ Usable, fast, simple Ruby gem for Tenable Nessus Pro from v7.0.1 to v8.3.1
5
+ NessusClient was designed to be simple, fast and performant through communication with Nessus over REST interface.
9
6
 
7
+ [![Gem Version](https://badge.fury.io/rb/nessus_client.svg)](https://badge.fury.io/rb/nessus_client)
8
+ [![Maintainability](https://api.codeclimate.com/v1/badges/9cca9e4260cadd8ab98d/maintainability)](https://codeclimate.com/github/heyder/nessus_client/maintainability)
9
+ [![codecov](https://codecov.io/gh/heyder/nessus_client/branch/master/graph/badge.svg)](https://codecov.io/gh/heyder/nessus_client)
10
+ [![Inline docs](http://inch-ci.org/github/heyder/nessus_client.svg?branch=master)](http://inch-ci.org/github/heyder/nessus_client)
11
+
12
+ **Ruby gem for Nessus API**
13
+
14
+ * [Source Code](https://github.com/heyder/nessus_client)
15
+ * [API documentation](https://rubydoc.info/github/heyder/nessus_client/master)
16
+ * [Rubygem](https://rubygems.org/gems/nessus_client)
10
17
 
11
- Ruby wrapper for Nessus API (all verions)
12
18
 
13
19
  ## Contact
14
20
 
@@ -22,17 +28,75 @@ with some common problems to check out before creating an issue.
22
28
  Getting started
23
29
  ---------------
24
30
 
31
+ ```ruby
32
+ require 'nessus_client'
33
+
34
+ nc = NessusClient.new( { :uri=>'https://localhost:8834', :username=>'username',:password=> 'password'} )
35
+ status = nc.server_status
36
+ puts status
37
+ puts nc.server_properties
38
+
39
+ if status['status'] == 'ready'
40
+ scan_id = nc.get_scan_by_name('Monthly Scan')
41
+ scan_uuid = nc.launch_by_name( 'Monthly Scan', ['127.0.0.1'])['scan_uuid']
42
+
43
+ loop do
44
+ puts `clear`
45
+ scan_status = nc.scan_details( scan_id )["info"]["status"]
46
+ puts " #{scan_id} - #{scan_uuid} - #{scan_status} "
47
+ sleep 5
48
+ if ["completed","canceled"].include? scan_status
49
+ export_request = nc.export_request(scan_id, "nessus" )
50
+ puts " export request: #{export_request}"
51
+ while true do
52
+ puts `clear`
53
+ export_status = nc.token_status( export_request['token'])["status"]
54
+ puts " export status: #{export_status}"
55
+ sleep 5
56
+ if export_status == "ready"
57
+ puts " downloading..."
58
+ open("scan_report", "wb") do |file|
59
+ file.write(nc.token_download( export_request['token'] ))
60
+ end
61
+ exit 0
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ ```
68
+
69
+ ## Installation
70
+
71
+ Add this line to your application's Gemfile:
72
+
73
+ gem 'nessus_client'
74
+
75
+ And then execute:
76
+
77
+ $ bundle
78
+
79
+ Or install it yourself as:
80
+
81
+ $ gem install nessus_client
82
+
83
+ ## Requirements
25
84
 
85
+ Requirements are Exon for HTTP(S) and Oj parsing:
26
86
 
87
+ ```ruby
88
+ require 'excon'
89
+ require 'oj'
90
+ ```
27
91
 
28
92
  ## Code of Conduct
29
93
 
30
94
  Everyone participating in this project's development, issue trackers and other channels is expected to follow our
31
- [Code of Conduct](./CODE_OF_CONDUCT.md)
95
+ [Code of Conduct](./CODE_OF_CONDUCT.md).
32
96
 
33
97
  ## Contributing
34
98
 
35
- See the [contributing guide](https://github.com/heyder/nessus_client/blob/master/CONTRIBUTING.md).
99
+ See the [contributing guide](./CONTRIBUTING.md).
36
100
 
37
101
  ## Copyright
38
102
 
@@ -1,15 +1,37 @@
1
- # require_relative '../nessus_client/request'
1
+ # frozen_string_literal: true
2
2
 
3
- module NessusClient::Exports
4
- # export scans
5
- def export_request( scan_id, format )
6
- params = {:format => format }
7
- self.request.post("/scans/#{scan_id}/export", params)
3
+ # Namespace for Exports resource.
4
+ module Resource::Exports
5
+ # Export the given scan. Once requested, the file can be downloaded using the Resource::Tokens.token_download method upon receiving a "ready" status from the Resource::Tokens#token_status method. You can also use the older Resource::Exports#export_status and Resource::Exports#export_download methods.
6
+ # @param [String] scan_id The export uuid string.
7
+ # @param [String] format The file format to use (Nessus, HTML, PDF, CSV, or DB).
8
+ # @return [JSON]
9
+ def export_request(scan_id, format = 'nessus')
10
+ payload = { format: format }
11
+ request.post({ path: "/scans/#{scan_id}/export", payload: payload, headers: headers })
8
12
  end
9
- def export_status( export_id )
10
- self.request.get("/tokens/#{export_id}/status")
13
+
14
+ # Check the file status of an exported scan. When an export has been requested, it is necessary to poll this resource until a "ready" status is returned, at which point the file is complete and can be downloaded using the export download resource.
15
+ # @param [String] scan_id The identifier for the scan. This identifier can be the either the 'schedule_uuid' or the numeric 'id' attribute for the scan. We recommend that you use 'schedule_uuid'.
16
+ # @param [String] file_id The ID of the file to poll (Included in response from #export_request).
17
+ # @return [JSON]
18
+ # @example Checking the status of a export.
19
+ # export_status = nc.export_status( "15", "cd956" )
20
+ # return true if export_status["status"] == "ready"
21
+ def export_status(scan_id, file_id)
22
+ request.get({ path: "/scans/#{scan_id}/export/#{file_id}/status", headers: headers })
11
23
  end
12
- def export_download( export_id )
13
- self.request.get("/tokens/#{export_id}/download")
24
+
25
+ # Download exported scan.
26
+ # @param [String] scan_id The identifier for the scan. This identifier can be the either the 'schedule_uuid' or the numeric 'id' attribute for the scan. We recommend that you use 'schedule_uuid'.
27
+ # @param [String] file_id The ID of the file to poll (Included in response from #export_request).
28
+ # @return [JSON]
29
+ # @example Download a ready export.
30
+ # export = nc.export_download( '17', '46b78587')
31
+ # open("scan_report", "wb") do |file|
32
+ # file.write( export )
33
+ # end
34
+ def export_download(scan_id, file_id)
35
+ request.get({ path: "/scans/#{scan_id}/export/#{file_id}/download", headers: headers })
14
36
  end
15
- end
37
+ end
@@ -1,12 +1,18 @@
1
- # require_relative '../nessus_client/request'
1
+ # frozen_string_literal: true
2
2
 
3
- module NessusClient::Folders
4
- # folders
3
+ # Namespace for Folders resource.
4
+ module Resource::Folders
5
+ # Get the list of folders from the resource.
6
+ # @return [JSON]
5
7
  def list_folders
6
- self.request.get("/folders")
8
+ request.get({ path: '/folders', headers: headers })
7
9
  end
8
- def create_folder( folder_name )
9
- params = {:name => folder_name }.to_json
10
- self.request.post("/folders", params)
10
+
11
+ # Create a folder into the resource.
12
+ # @param [String] folder_name The name of the folder the will be created.
13
+ # @return [JSON]
14
+ def create_folder(folder_name)
15
+ payload = { name: folder_name }
16
+ request.post({ path: '/folders', payload: payload, headers: headers })
11
17
  end
12
- end
18
+ end
@@ -1,7 +1,43 @@
1
- # require_relative '../nessus_client/request'
1
+ # frozen_string_literal: true
2
2
 
3
- module NessusClient::Policies
3
+ # Namespace for Policies resource.
4
+ module Resource::Policies
5
+ # List the scan polices.
6
+ # @return [JSON] list of policies.
4
7
  def policies
5
- self.request.get( "/policies" )
8
+ request.get({ path: '/policies', headers: headers })
9
+ end
10
+
11
+ # Get id of a policy by its name.
12
+ # @param [String] policy name.
13
+ # @return [Integer] ID of a policy.
14
+ def get_policy_id_by_name(policy_name)
15
+ policies['policies'].each do |policy|
16
+ return policy['id'] if policy['name'] == policy_name
17
+ end
18
+ end
19
+
20
+ # Get a policy by its name.
21
+ # @param [String] policy name.
22
+ # @return [JSON] policy object.
23
+ def get_policy_by_name(policy_name)
24
+ policy_id = get_policy_id_by_name(policy_name)
25
+ request.get({ path: "/policies/#{policy_id}", headers: headers })
26
+ end
27
+
28
+ # Get a list of credentials from a policy.
29
+ # @param [String] policy name.
30
+ # @return [JSON] credential list
31
+ def list_credentials_by_policy_name(policy_name)
32
+ get_policy_by_name(policy_name)['credentials']
33
+ end
34
+
35
+ # update a scan policy.
36
+ # @param [String] policy name.
37
+ # @param [String] history_id (nil) The `history_id` of a scan.
38
+ # @return nil
39
+ def update_policy_by_name(policy_name, payload)
40
+ id = get_policy_id_by_name(policy_name)
41
+ request.put({ path: "/policies/#{id}", headers: headers, payload: payload })
6
42
  end
7
43
  end
@@ -1,27 +1,50 @@
1
+ # frozen_string_literal: true
1
2
 
2
- module NessusClient::Scans
3
+ # Namespace for Scans resource.
4
+ module Resource::Scans
5
+ # List scans from the resource.
6
+ # @param [String] folder_id (nil) The name of a alredy created scan.
7
+ # @return [JSON]
8
+ def list_scans(folder_id = nil)
9
+ query = folder_id.nil? ? nil : { 'folder_id' => folder_id }
10
+ request.get({ path: '/scans', query: query, headers: headers })
11
+ end
12
+ alias scans list_scans
3
13
 
4
- def list_scans( folder_id=nil )
5
- query = folder_id.nil? ? nil : { "folder_id" => folder_id }
6
- self.request.get( "/scans", nil, query )
14
+ # See details of a scan.
15
+ # @param [String] scan_id The `uuid` of a scan.
16
+ # @param [String] history_id (nil) The `history_id` of a scan.
17
+ # @return [JSON]
18
+ def scan_details(scan_id, history_id = nil)
19
+ query = history_id.nil? ? nil : { 'history_id' => history_id }
20
+ request.get({ path: "/scans/#{scan_id}", query: query, headers: headers })
7
21
  end
8
- alias_method :scans, :list_scans
9
22
 
10
- def scan_details( scan_id, history_id=nil )
11
- query = history_id.nil? ? nil : { "history_id" => history_id }
12
- self.request.get( "/scans/#{scan_id}", nil, query )
23
+ # Lauch a scan by its id
24
+ # @param [Integer] scan_id The ID of a alredy created scan.
25
+ # @param [Array<String>] targets comma separeted new target to be scanned.
26
+ # @return [JSON]
27
+ def launch(scan_id, targets = [])
28
+ payload = { alt_targets: targets } unless targets.empty?
29
+ request.post({ path: "/scans/#{scan_id}/launch", payload: payload, headers: headers })
13
30
  end
14
31
 
15
- def launch_by_name( scan_name, targets=[] )
16
- scan_id = get_scan_by_name( scan_name )
17
- params = { :alt_targets => targets } unless targets.empty?
18
- self.request.post( "/scans/#{scan_id}/launch", params )
32
+ # Lauch a scan by its name
33
+ # @param [String] scan_name The name of a alredy created scan.
34
+ # @param [Array<String>] targets comma separeted new target to be scanned.
35
+ # @return [JSON]
36
+ def launch_by_name(scan_name, targets = [])
37
+ scan_id = get_scan_by_name(scan_name)
38
+ launch(scan_id, targets)
19
39
  end
20
40
 
21
- def get_scan_by_name( folder_id=nil, name )
22
- Oj.load(list_scans( folder_id ))["scans"].each do |scan|
23
- return scan['id'] if scan['name'] == name
41
+ # Get a scan by its name
42
+ # @param [String] folder_id The id of the folder to look into.
43
+ # @param [String] scan_name The name of the scan to look for.
44
+ # @return [String, nil] The uuid of the scan.
45
+ def get_scan_by_name(scan_name, folder_id = nil)
46
+ list_scans(folder_id)['scans'].each do |scan|
47
+ return scan['id'] if scan['name'] == scan_name
24
48
  end
25
49
  end
26
-
27
- end
50
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Namespace for Server resource.
4
+ module Resource::Server
5
+ # Returns the server status.
6
+ # @return [JSON] Returns the server status
7
+ def server_status
8
+ request.get({ path: '/server/status', headers: headers })
9
+ end
10
+
11
+ # Returns the server version and other properties.
12
+ # @return [JSON] Returns the server properties
13
+ def server_properties
14
+ request.get({ path: '/server/properties', headers: headers })
15
+ end
16
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Namespace for Session resource.
4
+ module Resource::Session
5
+ # @return [Boolean] whether has a session.
6
+ attr_reader :session
7
+
8
+ @session = false
9
+
10
+ # Autenticate into Nessus resource.
11
+ # @param [String] username
12
+ # @param [String] password
13
+ # @return [nil]
14
+ # @raise [NessusClient::Error] Unable to authenticate.
15
+ # @todo Validate response token format
16
+ def set_session(username, password)
17
+ payload = {
18
+ username: username,
19
+ password: password
20
+ }
21
+
22
+ resp = request.post({ path: '/session', payload: payload, headers: headers })
23
+ # binding.pry
24
+ if !resp.key?('token')
25
+ raise NessusClient::Error, 'Unable to authenticate.'
26
+ elsif !resp['token'].match(/(?<token>[a-z0-9]{48})/)
27
+ raise NessusClient::Error, 'The token doesnt match with the pattern.'
28
+ end
29
+
30
+ headers.update('X-Cookie' => 'token=' + resp['token'])
31
+ @session = true
32
+ api_token = set_api_token
33
+ headers.update('X-API-Token' => api_token) if api_token
34
+ rescue NessusClient::Error => e
35
+ raise e
36
+ end
37
+ alias session_create set_session
38
+
39
+ # Destroy the current session.
40
+ def destroy
41
+ request.delete({ path: '/session', headers: headers })
42
+ @session = false
43
+ end
44
+ alias logout destroy
45
+
46
+ private
47
+
48
+ # Set the API Token from legacy Nessus version
49
+ # @raise [NessusClient::Error] Unable to get API Token.
50
+ # @todo To get it direct from the session authentication on v6.x
51
+ def set_api_token
52
+ response = request.get({ path: '/nessus6.js', headers: headers })
53
+ response.match(/return"(\w{8}-(?:\w{4}-){3}\w{12})"\}/)
54
+ unless Regexp.last_match(1)
55
+ raise NessusClient::Error, "Unable to get API Token. Some features won't work."
56
+ end
57
+ rescue NessusClient::Error => e
58
+ puts e.message
59
+ else
60
+ Regexp.last_match(1)
61
+ end
62
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Namespace for tokens resource.
4
+ module Resource::Tokens
5
+ # Check the status of a export request
6
+ # @param [String] export_uuid The export uuid string.
7
+ # @return [JSON]
8
+ # @example Checking the status of a export.
9
+ # export_status = nc.export_status( "73376c41-1508-46b7-8587-483d159cd956" )
10
+ # return true if export_status["status"] == "ready"
11
+ def token_status(export_uuid)
12
+ request.get({ path: "/tokens/#{export_uuid}/status", headers: headers })
13
+ end
14
+
15
+ # Check the download of a export request
16
+ # @param [String] export_uuid The export uuid string.
17
+ # @return [JSON] (@see #format)
18
+ # @example Download a ready export.
19
+ # export = nc.export_download( '73376c41-1508-46b7-8587-483d159cd956')
20
+ # open("scan_report", "wb") do |file|
21
+ # file.write( export )
22
+ # end
23
+ def token_download(export_uuid)
24
+ request.get({ path: "/tokens/#{export_uuid}/download", headers: headers })
25
+ end
26
+ end
@@ -1,53 +1,54 @@
1
- # require 'pry'
1
+ # frozen_string_literal: true
2
+
2
3
  require_relative 'nessus_client/version'
3
4
  require_relative 'nessus_client/exception'
5
+ require_relative 'nessus_client/resource'
4
6
 
5
- Dir[File.join(__dir__, 'modules', '*.rb')].each { |file| require file }
7
+ Dir[File.join(__dir__, 'modules', '*.rb')].sort.each { |file| require file }
6
8
 
9
+ # Nessus resource abstraction.
7
10
  class NessusClient
8
-
9
- attr_reader :request, :session
10
-
11
- include NessusClient::Scans
12
- include NessusClient::Exports
13
- include NessusClient::Folders
14
- include NessusClient::Policies
15
-
16
- autoload :Request, "nessus_client/request"
17
- autoload :Session, "nessus_client/session"
18
-
19
- def initialize( params={uri: nil, username: nil, password: nil, :ssl_verify_peer => false} )
20
- @has_session = false
21
- req_params = params.select {|key, value| [:uri, :ssl_verify_peer].include?(key) }
22
- # session_params = params.select {|key, value| [:username, :password].include?(key) }
23
-
24
- @request = NessusClient::Request.new( req_params )
25
- @session = NessusClient::Session.create( params.fetch(:username), params.fetch(:password) )
26
-
27
- if @session.token
28
- begin
29
- @has_session = true
30
- # NessusClient::Request.headers.update( 'X-Cookie' => 'token=' + api_session.token )
31
- @request.headers.update( 'X-Cookie' => 'token=' + @session.token )
32
- @session.set_api_token
33
- rescue NessusClient::Error => err
34
- puts err.message
35
- else
36
- request.headers.update( 'X-API-Token' => @session.api_token )
37
- ensure
38
- return
39
- end
40
-
41
- end
42
-
11
+ # @return [NessusClient::Request] Instance HTTP request object.
12
+ # @see NessusClient::Request
13
+ attr_reader :request
14
+ # @return [Boolean] whether has a session.
15
+ attr_reader :session
16
+ # @return [Hash] Instance current HTTP headers.
17
+ attr_reader :headers
18
+
19
+ include Resource::Exports
20
+ include Resource::Folders
21
+ include Resource::Policies
22
+ include Resource::Scans
23
+ include Resource::Server
24
+ include Resource::Session
25
+ include Resource::Tokens
26
+
27
+ autoload :Request, 'nessus_client/request'
28
+
29
+ # @param [Hash] params the options to create a NessusClient with.
30
+ # @option params [String] :uri ('https://localhost:8834/') Nessus resource to connect with
31
+ # @option params [String] :username (nil) Username to use in the connection
32
+ # @option params [String] :password (nil) Password to use in the connection
33
+ # @option params [String] :ssl_verify_peer (true) Whether should check valid SSL certificate
34
+ def initialize(params = {})
35
+ default_params = {
36
+ uri: 'https://localhost:8834/',
37
+ username: nil,
38
+ password: nil,
39
+ ssl_verify_peer: true
40
+ }
41
+ params = default_params.merge(params)
42
+ req_params = params.select { |key, _value| %i[uri ssl_verify_peer].include?(key) }
43
+
44
+ @request = NessusClient::Request.new(req_params)
45
+ @headers = NessusClient::Request::DEFAULT_HEADERS.dup
46
+ set_session(params.fetch(:username), params.fetch(:password))
43
47
  end
44
48
 
49
+ # Gets NessusClient::Session authentication status.
50
+ # @return [Boolean] whether NessusClient has successfully authenticated.
45
51
  def has_session?
46
- @has_session
52
+ session
47
53
  end
48
-
49
- def status
50
- self.request.get( "/server/status" )
51
- end
52
-
53
- end
54
+ end
@@ -1,7 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class NessusClient
4
+ # Abstract Error class for NessusClient.
2
5
  class Error < ::StandardError
3
- def initialize(msg="message")
6
+ # Raise a custom error namespace.
7
+ # @param [String] msg The exception message.
8
+ # @example
9
+ # NessusClient::Error.new('This is a custom error.')
10
+ def initialize(msg)
4
11
  super
5
12
  end
6
13
  end
7
- end
14
+ end
@@ -1,76 +1,108 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'excon'
2
- require 'json'
3
- # require 'pry'
4
- class NessusClient
4
+ require 'oj'
5
5
 
6
- # Excon.defaults[:ssl_verify_peer] = false
7
- # This class should be used to in all requests classes
8
-
6
+ class NessusClient
7
+ # Abstract http request class for NessusClient.
8
+ # Provides some helper methods for perform HTTP requests.
9
9
  class Request
10
- # attr_accessor :headers
11
- attr_reader :url, :headers
10
+ # @return [String] The base url of the API.
11
+ attr_reader :url
12
12
 
13
+ # Default HTTP header to be used on the requests.
13
14
  DEFAULT_HEADERS = {
14
- "User-Agent" => "Mozilla/5.0 (Linux x86_64)",
15
- "Content-Type" => "application/json"
16
- }
15
+ 'User-Agent' => 'NessusClient::Request - rubygems.org nessus_client',
16
+ 'Content-Type' => 'application/json'
17
+ }.freeze
17
18
 
18
- def initialize( params )
19
- params = {:uri => nil, :ssl_verify_peer => false, :headers => {} }.merge( params )
20
- @@ssl_verify_peer = params.fetch(:ssl_verify_peer)
21
- @url = @@url = NessusClient::Request.uri_parse( params.fetch(:uri) )
22
- @headers = params.fetch( :headers ).merge( DEFAULT_HEADERS )
23
- end
19
+ # @param [Hash] params the options to create a NessusClient::Request with.
20
+ # @option params [String] :uri ('https://localhost:8834/') Nessus server to connect with
21
+ # @option params [String] :ssl_verify_peer (true) Whether should check valid SSL certificate
22
+ def initialize(params = {})
23
+ params = { uri: nil }.merge(params)
24
+ @ssl_verify_peer = params[:ssl_verify_peer] ? true : false
25
+ @url = NessusClient::Request.uri_parse(params.fetch(:uri))
26
+ end
24
27
 
25
- # def self.headers
26
- # @@headers
27
- # end
28
- def headers=(value)
29
- raise NotImplementedError.new("Use update from Hash insted.")
28
+ # Perform a HTTP GET request.
29
+ # @param [Hash] opts to use in the request.
30
+ # @option opts [String] path The URI path to perform the request.
31
+ # @option opts [String] payload The HTTP body to send.
32
+ # @option opts [String] query The URI query to send.
33
+ # @return [Hash, String] The body of the resposnse if there is any.
34
+ def get(opts = {})
35
+ http_request(opts, :get)
30
36
  end
31
-
32
- def get( path=nil, payload=nil, query=nil )
33
- http_request( :get, path, payload, query )
37
+
38
+ # Perform a HTTP POST request.
39
+ # @param [Hash] opts to use in the request.
40
+ # @option opts [String] path The URI path to perform the request.
41
+ # @option opts [String] payload The HTTP body to send.
42
+ # @option opts [String] query The URI query to send.
43
+ # @return [Hash, String] The body of the resposnse if there is any.
44
+ def post(opts = {})
45
+ http_request(opts, :post)
34
46
  end
35
47
 
36
- def post( path=nil, payload=nil, query=nil )
37
- http_request( :post, path, payload, query )
48
+ # Perform a HTTP PUT request.
49
+ # @param [Hash] opts to use in the request.
50
+ # @option opts [String] path The URI path to perform the request.
51
+ # @option opts [String] payload The HTTP body to send.
52
+ # @option opts [String] query The URI query to send.
53
+ # @return [Hash, String] The body of the resposnse if there is any.
54
+ def put(opts = {})
55
+ http_request(opts, :put)
38
56
  end
39
57
 
40
- def delete( path=nil, payload=nil, query=nil )
41
- http_request( :delete, path, payload, query )
58
+ # Perform a HTTP DELETE request.
59
+ # @param [Hash] opts to use in the request.
60
+ # @option opts [String] path The URI path to perform the request.
61
+ # @option opts [String] payload The HTTP body to send.
62
+ # @option opts [String] query The URI query to send.
63
+ # @return [Hash, String] The body of the resposnse if there is any.
64
+ def delete(opts = {})
65
+ http_request(opts, :delete)
42
66
  end
43
67
 
44
- def self.uri_parse( uri )
45
- url = URI.parse( uri )
68
+ # Parse a receiveid string against the URI stantard.
69
+ # @param [String] uri A string to be validate URI.
70
+ # @return [String] A string uri.
71
+ def self.uri_parse(uri)
72
+ url = URI.parse(uri)
46
73
  raise URI::InvalidURIError unless url.scheme
47
- return url.to_s
74
+
75
+ url.to_s
48
76
  end
49
77
 
50
78
  private
51
79
 
52
- def http_request( method=:get, path, payload, query )
53
- # binding.pry
54
- connection = Excon.new( @@url )
55
-
56
- body = payload ? payload.to_json : ''
80
+ # @private HTTP request abstraction to be used.
81
+ # @param [Symbol] method The HTTP method to be used on the request.
82
+ # @param [Hash] args Parameters to use in the request.
83
+ # @option args [String] path (nil) The URI path to perform the request.
84
+ # @option args [String] payload (nil) The HTTP body to send.
85
+ # @option args [String] query (nil) The URI query to send.
86
+ # @option args [String] headers (nil) The headers to send.
87
+ # @return [Hash, String] The body of the resposnse if there is any.
88
+ def http_request(args, method = :get)
89
+ opts = { path: nil, payload: nil, query: nil, headers: nil }.merge(args)
90
+ connection = Excon.new(@url, { ssl_verify_peer: @ssl_verify_peer })
91
+ body = opts[:payload] ? Oj.dump(opts[:payload], mode: :compat) : ''
57
92
  options = {
58
93
  method: method,
59
- path: path,
94
+ path: opts.fetch(:path),
60
95
  body: body,
61
- query: query,
62
- headers: @headers,
63
- ssl_verify_peer: @@ssl_verify_peer,
64
- #idempotent: true,
65
- #proxy: "http://127.0.0.1:8080",
96
+ query: opts.fetch(:query),
97
+ headers: opts.fetch(:headers),
66
98
  expects: [200, 201]
67
99
  }
68
- response = connection.request( options )
69
-
70
- return response.body if response.body.length > 0
71
-
100
+ response = connection.request(options)
101
+ ret = Oj.load(response.body) # if response.body.length > 0
102
+ rescue Oj::ParseError
103
+ response.body
104
+ else
105
+ ret
72
106
  end
73
-
74
107
  end
75
-
76
108
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Namespace for endpoints
4
+ module Resource end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class NessusClient
2
- VERSION = '0.1.0'
4
+ # The current version of the libary.
5
+ VERSION = '0.1.6'
3
6
  end
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nessus_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Heyder
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2018-11-28 00:00:00.000000000 Z
@@ -14,16 +14,22 @@ dependencies:
14
14
  name: excon
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.73.0
17
20
  - - "~>"
18
21
  - !ruby/object:Gem::Version
19
- version: '0.62'
22
+ version: '0.73'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.73.0
24
30
  - - "~>"
25
31
  - !ruby/object:Gem::Version
26
- version: '0.62'
32
+ version: '0.73'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: oj
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -39,47 +45,53 @@ dependencies:
39
45
  - !ruby/object:Gem::Version
40
46
  version: '3.7'
41
47
  - !ruby/object:Gem::Dependency
42
- name: json
48
+ name: bundler
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '2.1'
48
- type: :runtime
53
+ version: '1.12'
54
+ type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '2.1'
60
+ version: '1.12'
55
61
  - !ruby/object:Gem::Dependency
56
- name: rspec
62
+ name: codecov
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '3.2'
67
+ version: 0.1.14
62
68
  type: :development
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '3.2'
74
+ version: 0.1.14
69
75
  - !ruby/object:Gem::Dependency
70
- name: bundler
76
+ name: nokogiri
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 1.8.5
73
82
  - - "~>"
74
83
  - !ruby/object:Gem::Version
75
- version: '1.12'
84
+ version: 1.10.10
76
85
  type: :development
77
86
  prerelease: false
78
87
  version_requirements: !ruby/object:Gem::Requirement
79
88
  requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 1.8.5
80
92
  - - "~>"
81
93
  - !ruby/object:Gem::Version
82
- version: '1.12'
94
+ version: 1.10.10
83
95
  - !ruby/object:Gem::Dependency
84
96
  name: pry
85
97
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +106,40 @@ dependencies:
94
106
  - - "~>"
95
107
  - !ruby/object:Gem::Version
96
108
  version: 0.12.2
109
+ - !ruby/object:Gem::Dependency
110
+ name: regexp-examples
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 1.5.0
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '1.5'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 1.5.0
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '1.5'
129
+ - !ruby/object:Gem::Dependency
130
+ name: rspec
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '3.2'
136
+ type: :development
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '3.2'
97
143
  - !ruby/object:Gem::Dependency
98
144
  name: simplecov
99
145
  requirement: !ruby/object:Gem::Requirement
@@ -108,31 +154,59 @@ dependencies:
108
154
  - - "~>"
109
155
  - !ruby/object:Gem::Version
110
156
  version: 0.17.0
111
- description: Ruby wrapper for Nessus API (all verions)
157
+ - !ruby/object:Gem::Dependency
158
+ name: yard
159
+ requirement: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - "~>"
162
+ - !ruby/object:Gem::Version
163
+ version: '0.9'
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: 0.9.20
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.9'
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: 0.9.20
177
+ description: "\n Usable, fast, simple Ruby gem for Tenable Nessus Pro from v7.0.1
178
+ to v8.3.1.\n NessusClient was designed to be simple, fast and performant through
179
+ communication with Nessus\n over REST interface.\n "
112
180
  email: eu@heyderandrade.org
113
181
  executables: []
114
182
  extensions: []
115
183
  extra_rdoc_files:
116
184
  - README.md
117
185
  - CONTRIBUTING.md
186
+ - CODE_OF_CONDUCT.md
118
187
  files:
188
+ - CODE_OF_CONDUCT.md
119
189
  - CONTRIBUTING.md
120
190
  - README.md
121
191
  - lib/modules/exports.rb
122
192
  - lib/modules/folders.rb
123
193
  - lib/modules/policies.rb
124
194
  - lib/modules/scans.rb
195
+ - lib/modules/server.rb
196
+ - lib/modules/session.rb
197
+ - lib/modules/tokens.rb
125
198
  - lib/nessus_client.rb
126
199
  - lib/nessus_client/exception.rb
127
200
  - lib/nessus_client/request.rb
128
- - lib/nessus_client/session.rb
201
+ - lib/nessus_client/resource.rb
129
202
  - lib/nessus_client/version.rb
130
- homepage: https://rubygemspec.org/gems/nessus_client
203
+ homepage: https://github.com/heyder/nessus_client
131
204
  licenses:
132
205
  - MIT
133
206
  metadata:
207
+ documentation_uri: https://rubydoc.info/github/heyder/nessus_client/
134
208
  source_code_uri: https://github.com/heyder/nessus_client
135
- post_install_message:
209
+ post_install_message:
136
210
  rdoc_options: []
137
211
  require_paths:
138
212
  - lib
@@ -147,8 +221,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
221
  - !ruby/object:Gem::Version
148
222
  version: '0'
149
223
  requirements: []
150
- rubygems_version: 3.0.3
151
- signing_key:
224
+ rubygems_version: 3.0.6
225
+ signing_key:
152
226
  specification_version: 4
153
- summary: Ruby wrapper for Nessus API
227
+ summary: Usable, fast, simple Ruby gem for Tenable Nessus Pro from v7.0.1 to v8.3.1.
154
228
  test_files: []
@@ -1,56 +0,0 @@
1
- require 'oj'
2
- require_relative 'request'
3
- require_relative 'exception'
4
-
5
- class NessusClient
6
-
7
- # This class should be used to get an access token
8
- # for use with the main client class.
9
- class Session
10
- attr_reader :token, :api_token
11
-
12
- @token = @api_token = nil
13
-
14
- # @param [String] username
15
- # @param [String] password
16
- def self.create( username, password )
17
-
18
- payload = {
19
- username: username,
20
- password: password,
21
- }
22
-
23
- response = NessusClient::Request.post( '/session', payload )
24
- response = Oj.load(response) if response.length > 0
25
-
26
- if response['token']
27
- return self.new( response['token'] )
28
- else
29
- raise NessusClient::Error.new "#{__method__}::Response did not include a session token."
30
- end
31
-
32
- end
33
-
34
- def initialize( token )
35
- @token = token
36
- end
37
-
38
- def set_api_token
39
- response = NessusClient::Request.get( "/nessus6.js" )
40
- response.match( %r{return"(\w{8}-(?:\w{4}-){3}\w{12})"\}} )
41
-
42
- raise NessusClient::Error.new( "Unable to get API Token. Some features won't work." ) unless $1#.nil?
43
-
44
- @api_token = $1
45
-
46
- end
47
-
48
- def destroy
49
- NessusClient::Request.delete( '/session', nil )
50
- @token = nil
51
- end
52
- alias_method :logout , :destroy
53
-
54
- end
55
-
56
- end