nessus_client 0.1.0 → 0.1.6

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