openvas-cli 0.1.1 → 0.2.0

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.
Files changed (42) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +50 -0
  3. data/VERSION +1 -1
  4. data/lib/openvas-cli/configuration.rb +25 -0
  5. data/lib/openvas-cli/conn_addin.rb +27 -0
  6. data/lib/openvas-cli/immutable_children_validator.rb +15 -0
  7. data/lib/openvas-cli/vas_administrator.rb +7 -0
  8. data/lib/openvas-cli/vas_base.rb +20 -30
  9. data/lib/openvas-cli/vas_config.rb +127 -0
  10. data/lib/openvas-cli/vas_connection.rb +140 -0
  11. data/lib/openvas-cli/vas_exceptions.rb +9 -7
  12. data/lib/openvas-cli/vas_lsc_credential.rb +110 -0
  13. data/lib/openvas-cli/vas_nvt.rb +64 -64
  14. data/lib/openvas-cli/vas_nvt_family.rb +39 -30
  15. data/lib/openvas-cli/vas_override.rb +6 -4
  16. data/lib/openvas-cli/vas_period.rb +89 -0
  17. data/lib/openvas-cli/vas_preference.rb +139 -49
  18. data/lib/openvas-cli/vas_report.rb +110 -103
  19. data/lib/openvas-cli/vas_result.rb +90 -89
  20. data/lib/openvas-cli/vas_schedule.rb +163 -55
  21. data/lib/openvas-cli/vas_target.rb +200 -23
  22. data/lib/openvas-cli/vas_task.rb +229 -30
  23. data/lib/openvas-cli/vas_task_progress.rb +29 -0
  24. data/lib/openvas-cli/xml_addin.rb +34 -0
  25. data/lib/openvas_cli.rb +19 -0
  26. data/openvas-cli.gemspec +28 -6
  27. data/spec/openvas-cli/vas_administrator_spec.rb +6 -0
  28. data/spec/openvas-cli/vas_config_spec.rb +81 -0
  29. data/spec/openvas-cli/vas_lsc_credential_spec.rb +72 -0
  30. data/spec/openvas-cli/vas_nvt_family_spec.rb +7 -5
  31. data/spec/openvas-cli/vas_nvt_spec.rb +30 -26
  32. data/spec/openvas-cli/vas_period_spec.rb +7 -0
  33. data/spec/openvas-cli/vas_preference_spec.rb +23 -21
  34. data/spec/openvas-cli/vas_report_spec.rb +65 -63
  35. data/spec/openvas-cli/vas_result_spec.rb +94 -93
  36. data/spec/openvas-cli/vas_schedule_spec.rb +154 -57
  37. data/spec/openvas-cli/vas_target_spec.rb +140 -28
  38. data/spec/openvas-cli/vas_task_spec.rb +92 -11
  39. data/spec/spec_helper.rb +15 -5
  40. metadata +72 -24
  41. data/lib/openvas-cli/openvas-cli.rb +0 -273
  42. data/spec/openvas-cli/openvas-cli_spec.rb +0 -45
data/Gemfile CHANGED
@@ -3,7 +3,9 @@ source "http://rubygems.org"
3
3
  # Example:
4
4
  gem "activesupport", ">= 3.0.5"
5
5
  gem 'activerecord', '>= 3.0.5'
6
+ gem 'rails', '>=3.0.5'
6
7
  gem 'nokogiri', '>= 1.4.4'
8
+ gem 'ipaddress', '>=0.7.0'
7
9
 
8
10
  # Add dependencies to develop your gem here.
9
11
  # Include everything needed to run rake, tests, features, etc.
data/Gemfile.lock CHANGED
@@ -2,6 +2,20 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  ZenTest (4.5.0)
5
+ abstract (1.0.0)
6
+ actionmailer (3.0.5)
7
+ actionpack (= 3.0.5)
8
+ mail (~> 2.2.15)
9
+ actionpack (3.0.5)
10
+ activemodel (= 3.0.5)
11
+ activesupport (= 3.0.5)
12
+ builder (~> 2.1.2)
13
+ erubis (~> 2.6.6)
14
+ i18n (~> 0.4)
15
+ rack (~> 1.2.1)
16
+ rack-mount (~> 0.6.13)
17
+ rack-test (~> 0.5.7)
18
+ tzinfo (~> 0.3.23)
5
19
  activemodel (3.0.5)
6
20
  activesupport (= 3.0.5)
7
21
  builder (~> 2.1.2)
@@ -11,18 +25,49 @@ GEM
11
25
  activesupport (= 3.0.5)
12
26
  arel (~> 2.0.2)
13
27
  tzinfo (~> 0.3.23)
28
+ activeresource (3.0.5)
29
+ activemodel (= 3.0.5)
30
+ activesupport (= 3.0.5)
14
31
  activesupport (3.0.5)
15
32
  arel (2.0.9)
16
33
  builder (2.1.2)
17
34
  diff-lcs (1.1.2)
35
+ erubis (2.6.6)
36
+ abstract (>= 1.0.0)
18
37
  git (1.2.5)
19
38
  i18n (0.5.0)
39
+ ipaddress (0.7.0)
20
40
  jeweler (1.5.2)
21
41
  bundler (~> 1.0.0)
22
42
  git (>= 1.2.5)
23
43
  rake
24
44
  log4r (1.1.9)
45
+ mail (2.2.15)
46
+ activesupport (>= 2.3.6)
47
+ i18n (>= 0.4.0)
48
+ mime-types (~> 1.16)
49
+ treetop (~> 1.4.8)
50
+ mime-types (1.16)
25
51
  nokogiri (1.4.4)
52
+ polyglot (0.3.1)
53
+ rack (1.2.2)
54
+ rack-mount (0.6.13)
55
+ rack (>= 1.0.0)
56
+ rack-test (0.5.7)
57
+ rack (>= 1.0)
58
+ rails (3.0.5)
59
+ actionmailer (= 3.0.5)
60
+ actionpack (= 3.0.5)
61
+ activerecord (= 3.0.5)
62
+ activeresource (= 3.0.5)
63
+ activesupport (= 3.0.5)
64
+ bundler (~> 1.0)
65
+ railties (= 3.0.5)
66
+ railties (3.0.5)
67
+ actionpack (= 3.0.5)
68
+ activesupport (= 3.0.5)
69
+ rake (>= 0.8.7)
70
+ thor (~> 0.14.4)
26
71
  rake (0.8.7)
27
72
  rcov (0.9.9)
28
73
  rspec (2.3.0)
@@ -33,6 +78,9 @@ GEM
33
78
  rspec-expectations (2.3.0)
34
79
  diff-lcs (~> 1.1.2)
35
80
  rspec-mocks (2.3.0)
81
+ thor (0.14.6)
82
+ treetop (1.4.9)
83
+ polyglot (>= 0.3.1)
36
84
  tzinfo (0.3.24)
37
85
 
38
86
  PLATFORMS
@@ -43,8 +91,10 @@ DEPENDENCIES
43
91
  activerecord (>= 3.0.5)
44
92
  activesupport (>= 3.0.5)
45
93
  bundler (~> 1.0.0)
94
+ ipaddress (>= 0.7.0)
46
95
  jeweler (~> 1.5.2)
47
96
  log4r
48
97
  nokogiri (>= 1.4.4)
98
+ rails (>= 3.0.5)
49
99
  rcov
50
100
  rspec (~> 2.3.0)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
@@ -0,0 +1,25 @@
1
+ module OpenvasCli
2
+ class Configuration
3
+ def initialize
4
+ config_opts = {
5
+ :username => "admin",
6
+ :password => "",
7
+ :host => "localhost",
8
+ :port => 9390,
9
+ :buffer_size => 512,
10
+ :time_out => 60,
11
+ :auto_login => true,
12
+ :max_tries => 3
13
+ }
14
+
15
+ config_opts.each { |key, value|
16
+ add_configuration(key, value)
17
+ }
18
+ end
19
+
20
+ def add_configuration(name, default)
21
+ Configuration.send :attr_accessor, name.to_sym
22
+ instance_variable_set("@#{name.to_s}".to_sym, default)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ require 'vas_connection'
2
+ module OpenvasCli
3
+ module ConnAddin
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def has_vas_connection(connection_type=:manager)
10
+ @@config = {:password => "Password"}
11
+ if connection_type == :manager
12
+ @@config[:port] = 9390
13
+ elsif connection_type == :administrator
14
+ @@config[:port] = 9393
15
+ end
16
+ end
17
+
18
+ def establish_connection
19
+ VasConnection.new(@@config)
20
+ end
21
+
22
+ def connection
23
+ @@connection ||= establish_connection
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_model'
2
+
3
+ module OpenvasCli
4
+ class ImmutableChildrenValidator < ActiveModel::Validator
5
+ def validate(record)
6
+ if record.id
7
+ record.errors[:base] << "Configuration cannot change after creation." \
8
+ if record.config_id_changed?
9
+
10
+ record.errors[:base] << "Target cannot change after creation." \
11
+ if record.target_id_changed? || record.target.changed?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ require 'vas_base'
2
+
3
+ module OpenvasCli
4
+ class VasAdministrator < VasBase
5
+
6
+ end
7
+ end
@@ -1,40 +1,30 @@
1
1
  require 'active_model'
2
+ require 'base64'
2
3
  Dir["#{File.dirname(__FILE__)}/*_validator.rb"].each {|f| require f}
4
+ require 'vas_exceptions'
5
+ require 'xml_addin'
6
+ require 'conn_addin'
3
7
 
4
8
  # Provides a base from which to build objects that utilize the OpenvasCli object.
5
9
  # Includes support for ActiveModel validations and a helper method (extract_value_from)
6
10
  # to assist in OpenVAS object implementations.
7
- class VasBase
8
- include ActiveModel::Validations
9
-
10
- # OpenvasCli object to be used for communications. Initializes a new instance
11
- # if one does not already exist.
12
- def self.client
13
- @@client ||= OpenvasCli.new
14
- end
15
-
16
- # Helper method to extract a value from a Nokogiri::XML::Node object. If the
17
- # xpath provided contains an @, then the method assumes that the value resides
18
- # in an attribute, otherwise it pulls the text of the last +text+ node.
19
- def self.extract_value_from(x_str, n)
20
- ret = ""
21
- if x_str =~ /@/
22
- ret = n.at_xpath(x_str).value if n.at_xpath(x_str)
23
- else
24
- tn = n.at_xpath(x_str)
25
- if tn
26
- if tn.children.count > 0
27
- tn.children.each { |tnc|
28
- if tnc.text?
29
- ret = tnc.text
30
- end
31
- }
32
- else
33
- ret = tn.text
34
- end
35
- end
11
+ module OpenvasCli
12
+ class VasBase
13
+ include ActiveModel::Validations
14
+ include ActiveModel::Dirty
15
+ include XmlAddin
16
+ include ConnAddin
17
+
18
+ has_vas_connection(:manager)
19
+
20
+ attr_accessor :id
21
+
22
+ def new_record?
23
+ @id == nil || @id.empty?
36
24
  end
37
25
 
38
- ret
26
+ def reset_changes
27
+ @changed_attributes.clear if @changed_attributes
28
+ end
39
29
  end
40
30
  end
@@ -0,0 +1,127 @@
1
+ require 'vas_base'
2
+ require 'vas_task'
3
+ require 'vas_nvt_family'
4
+ require 'vas_preference'
5
+ require 'vas_exceptions'
6
+
7
+ module OpenvasCli
8
+ class VasConfig < VasBase
9
+ attr_accessor :name
10
+ attr_accessor :comment
11
+ attr_accessor :families_grow
12
+ attr_accessor :rules_grow
13
+ attr_accessor :in_use
14
+
15
+ define_attribute_methods [:name, :comment, :families_grow, :in_use, :tasks,
16
+ :preferences, :families]
17
+
18
+ validates :id, :UUID => true, :if => Proc.new { |o| o.id }
19
+ validates :name, :presence=>true, :length => {:minimum=>1, :maximum=>80}
20
+
21
+ def initialize(attributes = {})
22
+ @id = attributes[:id] if attributes[:id]
23
+ @name = attributes[:name] if attributes[:name]
24
+ @comment = attributes[:comment] if attributes[:comment]
25
+ @families_grow = attributes[:families_grow] if attributes[:families_grow]
26
+ @rules_grow = attributes[:rules_grow] if attributes[:rules_grow]
27
+ end
28
+
29
+ def tasks
30
+ @tasks ||= []
31
+ end
32
+
33
+ def preferences
34
+ @preferences ||= []
35
+ end
36
+
37
+ def families
38
+ @families ||= []
39
+ end
40
+
41
+ def self.get_all(options = {})
42
+ params = {:sort_field => "name"}
43
+ if options[:show_details] && options[:show_details] == true
44
+ params.merge!({:families => "1", :preferences => "1"})
45
+ end
46
+ params[:config_id] = options[:id] if options[:id]
47
+ req = Nokogiri::XML::Builder.new { |xml|
48
+ xml.get_configs(params)
49
+ }
50
+
51
+ resp = connection.send_receive(req.doc)
52
+
53
+ ret = []
54
+ resp.xpath("/get_configs_response/config").each { |xml|
55
+ cfg = VasConfig.new
56
+ cfg.id = extract_value_from("@id", xml)
57
+ cfg.name = extract_value_from("name", xml)
58
+ cfg.comment = extract_value_from("comment", xml)
59
+ cfg.families_grow = extract_value_from("family_count/growing", xml).to_i > 0
60
+ cfg.rules_grow = extract_value_from("nvt_count/growing", xml).to_i > 0
61
+ cfg.in_use = extract_value_from("in_use", xml).to_i > 0
62
+
63
+ xml.xpath("tasks/task").each { |t| cfg.tasks << VasTask.from_xml_node(t) }
64
+ xml.xpath("families/family").each { |f| cfg.families << VasNVTFamily.from_xml_node(f) }
65
+ xml.xpath("preferences/preference").each { |p|
66
+ p = VasPreference.from_xml_node(p)
67
+ p.config_id = cfg.id
68
+ p.reset_changes
69
+ cfg.preferences << p
70
+ }
71
+
72
+ cfg.reset_changes
73
+
74
+ ret << cfg
75
+ }
76
+
77
+ ret
78
+ end
79
+
80
+ def self.copy_config(id, name)
81
+ begin
82
+ req = Nokogiri::XML::Builder.new { |xml|
83
+ xml.create_config {
84
+ xml.copy { xml.text(id) }
85
+ xml.name { xml.text(name) }
86
+ }
87
+ }
88
+
89
+ resp = connection.send_receive(req.doc)
90
+ new_id = extract_value_from("/create_config_response/@id", resp)
91
+
92
+ get_all(:id => new_id, :show_details => true)[0]
93
+ rescue VasExceptions::CommandException => e
94
+ nil
95
+ end
96
+ end
97
+
98
+ def save!
99
+ @previously_changed = changes
100
+ preferences.each{ |p| p.save! if p.changed? }
101
+ @changed_attributes.clear
102
+ end
103
+
104
+ def destroy
105
+ begin
106
+ destroy!
107
+
108
+ true
109
+ rescue Exception => e
110
+ # logger = OpenvasCli.logger
111
+ # logger.error("Could Not Destroy Configuration: (#{e.class})#{e.message}") if logger
112
+
113
+ false
114
+ end
115
+ end
116
+
117
+ def destroy!
118
+ tasks.each { |t| t.destroy! }
119
+
120
+ req = Nokogiri::XML::Builder.new { |xml|
121
+ xml.delete_config(:config_id => @id)
122
+ }
123
+
124
+ VasConfig.connection.send_receive(req.doc)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,140 @@
1
+ require 'nokogiri'
2
+ require 'vas_exceptions'
3
+ require 'xml_addin'
4
+ require 'openssl'
5
+ require 'timeout'
6
+
7
+ module OpenvasCli
8
+ class VasConnection
9
+ include XmlAddin
10
+ # Initializes the client, connectes to the OpenVas Managment service specified
11
+ # by host & port, and unless auto_login is set to +false+, loggs in using
12
+ # username and password.
13
+ def initialize(config={})
14
+ @logger = OpenvasCli.logger
15
+ @config = OpenvasCli.configuration
16
+
17
+ connect
18
+ if @config.auto_login == true
19
+ login
20
+ end
21
+ end
22
+
23
+ # Closes the active connection and sets it up for re-connection.
24
+ def close
25
+ @socket.close if @socket
26
+ @socket = nil
27
+ end
28
+
29
+ def config
30
+ @config ||= {}
31
+ end
32
+
33
+ # Logs into the OpenVAS Management service using the specified username and
34
+ # passoword. By default, this method is called by new unless auto_login is
35
+ # set to +false+.
36
+ def login
37
+ log_message("Logging in: :user => #{@config.username}", :info)
38
+ areq = Nokogiri::XML::Builder.new { |xml|
39
+ xml.authenticate {
40
+ xml.credentials {
41
+ xml.username { xml.text(@config.username) }
42
+ xml.password { xml.text(@config.password) }
43
+ }
44
+ }
45
+ }
46
+
47
+ send_receive(areq.doc)
48
+ end
49
+
50
+ def state
51
+ @socket ? @socket.state : "closed"
52
+ end
53
+
54
+ def send_receive(request)
55
+ if request.kind_of? String
56
+ tosend = request
57
+ else
58
+ tosend = request.to_xml
59
+ end
60
+
61
+ tries = 0
62
+
63
+ begin
64
+ unless @socket && @socket.state =~ /sslok/i
65
+ log_message("Socket closed, Reconnecting", :info)
66
+ connect
67
+ login
68
+ end
69
+ log_message("Sending: #{tosend}", :debug)
70
+
71
+ @socket.puts(tosend)
72
+
73
+ rbuf=''
74
+ size=0
75
+ begin
76
+ timeout(@config.time_out) {
77
+ a = @socket.sysread(@config.buffer_size)
78
+ size = a.length
79
+ rbuf << a
80
+ }
81
+ end while size >= @config.buffer_size
82
+ rescue Timeout::Error
83
+ msg = "Command Timed Out [#{tries}] (#{$!})\nCommand: #{tosend}"
84
+ log_message msg, :error
85
+ tries += 1
86
+ @socket = nil
87
+ retry if tries < @config.max_tries
88
+ raise VasExceptions::CommunicationException.new(msg)
89
+ rescue EOFError
90
+ msg = "EOFError [#{tries}] (#{$!})\nReceived: #{rbuf}\nCommand: #{tosend}"
91
+ log_message msg, :error
92
+ tries += 1
93
+ @socket = nil
94
+ retry if tries < @config.max_tries
95
+ raise VasExceptions::CommunicationException.new(msg)
96
+ end
97
+
98
+ response = Nokogiri::XML(rbuf)
99
+
100
+ log_message "RECEIVED: #{response.to_xml}", :debug
101
+
102
+ unless VasConnection.extract_value_from("//@status", response) =~ /20\d/
103
+ msg = "Command Failed: #{VasConnection.extract_value_from("//@status_text", response)}\n" +
104
+ "Command Status: #{VasConnection.extract_value_from("//@status", response)}\n" +
105
+ "Command: #{tosend}\n" +
106
+ "Response: #{response.to_xml}"
107
+ log_message msg, :error
108
+ raise VasExceptions::CommandException.new(msg)
109
+ end
110
+
111
+ response
112
+ end
113
+ private
114
+ def log_message(msg, level)
115
+ if @logger
116
+ case level
117
+ when :debug
118
+ @logger.debug msg
119
+ when :info
120
+ @logger.info msg
121
+ when :warn
122
+ @logger.warn msg
123
+ when :error
124
+ @logger.error msg
125
+ when :fatal
126
+ @logger.fatal msg
127
+ end
128
+ end
129
+ end
130
+
131
+ def connect
132
+ log_message("Connecting: :host => #{@config.host}, :port => #{@config.port}", :info)
133
+ plain_socket = TCPSocket.open(@config.host, @config.port)
134
+ ssl_context = OpenSSL::SSL::SSLContext.new
135
+ @socket = OpenSSL::SSL::SSLSocket.new(plain_socket, ssl_context)
136
+ @socket.sync_close = true
137
+ @socket.connect
138
+ end
139
+ end
140
+ end