nexpose_scan_manager 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/scan_manager.rb +165 -0
  2. metadata +69 -0
@@ -0,0 +1,165 @@
1
+ require 'rubygems'
2
+ require 'eventmachine'
3
+ require 'thread'
4
+ require 'observer'
5
+ require 'nexpose'
6
+
7
+ #------------------------------------------------------------------------------------------------------
8
+ # Used to start site device scans where a user is able to specify the maximum amount of scans that
9
+ # should be running at a time. This class does not guarantee that there will be no more than the
10
+ # maximum amount of scans specified will be running BUT scans will not be started from this class
11
+ # until the current amount of scans running is less than or equal to the maximum.
12
+ #
13
+ # This class is also used to
14
+ #------------------------------------------------------------------------------------------------------
15
+ class ScanManager
16
+ include Observable
17
+
18
+ # Used to determine if the poller thread is running
19
+ attr_accessor :is_poller_thread_running
20
+
21
+ # Determines how often the poller thread is executed
22
+ attr_reader :period
23
+
24
+ # Synchronize calls that modify open data structs
25
+ @semaphore = nil
26
+
27
+ # The NeXpose API object.
28
+ @nexpose_conn = nil
29
+
30
+ # A Hash of scan-ids to an array of operations for that scan
31
+ # All tasks associated with a scan id must implement #scan_update
32
+ @conditional_device_scans = nil
33
+ @excution_cycle_started = nil
34
+ @poler_exit_on_completion = nil
35
+
36
+ private
37
+ def start_poller
38
+ @is_poller_thread_running = true
39
+ operation = proc {
40
+ puts "Scan Manager poller thread executing ..."
41
+ while true do
42
+ sleep @period.to_i
43
+ check_and_execute_op
44
+ if @execution_cycle_started and @conditional_device_scans.empty? and @scan_listeners.empty? and @poler_exit_on_completion
45
+ break
46
+ end
47
+ end
48
+
49
+ @is_poller_thread_running = false
50
+ puts "Poller exiting ..."
51
+ }
52
+
53
+ EM.defer operation
54
+ end
55
+
56
+ def check_and_execute_op
57
+ @semaphore.synchronize do
58
+
59
+ @scans_observed.each do |scan_id|
60
+ status = @nexpose_conn.scan_status scan_id
61
+ scan_stats = @nexpose_conn.scan_statistics scan_id
62
+ message = scan_stats[:message]
63
+
64
+
65
+ content = {
66
+ :scan_id => scan_id,
67
+ :status => status,
68
+ :message => message
69
+ }
70
+
71
+ changed
72
+ notify_observers content, self
73
+ end
74
+
75
+ # We start scans one per thread execution loop.
76
+ # Then we start the scan if conditions are met.
77
+ # NOTE: the only condition implemented here is the maximum amount of scans.
78
+ if @conditional_device_scans.length > 0
79
+ scan_count = 0
80
+ stats = @nexpose_conn.scan_activity
81
+
82
+ # Get a count of all the running scans
83
+ stats.each do |stat|
84
+ if (stat[:status].eql?('running') or stat[:status].eql?('dispatched'))
85
+ scan_count = scan_count + 1
86
+ end
87
+ end
88
+
89
+ for i in 0..(@conditional_device_scans.length-1)
90
+ scan_condition = @conditional_device_scans[i]
91
+ if scan_condition[:max_scans] > scan_count
92
+ res = @nexpose_conn.site_device_scan_start scan_condition[:site_id], scan_condition[:devices], scan_condition[:hosts]
93
+ if res
94
+ puts "Scan started scan ID: #{res[:scan_id]}, on engine ID: #{res[:engine_id]}"
95
+ @conditional_device_scans.delete_at i
96
+ else
97
+ put "Scan start failed for site #{site}"
98
+ end
99
+
100
+ break # Only one scan per loop can be started
101
+ end
102
+ end
103
+ end
104
+ end # End of synchronize block
105
+ end
106
+
107
+ public
108
+
109
+ #------------------------------------------------------------------------------------------------------
110
+ # The poller thread used within this class is initialized here.
111
+ #
112
+ # nexpose_conn: The NeXpose API object
113
+ # poler_exit_on_completion: 'true', if the poller thread should exit when
114
+ # when there is nothing left to process.
115
+ # period: The frequency at which the poller thread executes
116
+ #------------------------------------------------------------------------------------------------------
117
+ def initialize nexpose_conn, poler_exit_on_completion, period
118
+ @nexpose_conn = nexpose_conn
119
+ @period = period
120
+ @poler_exit_on_completion = poler_exit_on_completion
121
+ @semaphore = Mutex.new
122
+ @conditional_device_scans = []
123
+ @scans_observed = []
124
+ @execution_cycle_started = false
125
+
126
+ start_poller
127
+ end
128
+
129
+ #------------------------------------------------------------------------------------------------------
130
+ # Adds a scan to be observed
131
+ # scan_id: The ID of the scan to be observed.
132
+ #------------------------------------------------------------------------------------------------------
133
+ def add_scan_observed scan_id
134
+ puts "Obeserving scan #{scan_id}"
135
+ @scans_observed << scan_id
136
+ end
137
+
138
+ #------------------------------------------------------------------------------------------------------
139
+ # Removes a currently observed scan
140
+ # scan_id: The ID of the scan to be removed.
141
+ #------------------------------------------------------------------------------------------------------
142
+ def remover_scan_observed scan_id
143
+ @scans_observed.delete scan_id
144
+ end
145
+
146
+ #------------------------------------------------------------------------------------------------------
147
+ # Starts device site scans based on a particular condition, for now this is if the max amount of scans
148
+ # specified is greater than the amount of currently running scans.
149
+ #
150
+ # conditional_scan: A hash of informations used to start scanning
151
+ # ie : @[0] -> :site_id => 1 :devices => [192.168.1.1] :max_scans => 5 :listeners => [listerner_objects]
152
+ #-------------------------------------------------------------------------------------------------------
153
+ def add_cond_scan conditional_scan
154
+ if conditional_scan.nil?
155
+ raise ArgumentError 'Condtional scan arguement is null'
156
+ end
157
+
158
+ @semaphore.synchronize do
159
+ @conditional_device_scans << conditional_scan
160
+ if not @execution_cycle_started
161
+ @execution_cycle_started = true
162
+ end
163
+ end
164
+ end
165
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nexpose_scan_manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christopher Lee
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-06-16 00:00:00.000000000 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nexpose
17
+ requirement: &27094380 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.3
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *27094380
26
+ - !ruby/object:Gem::Dependency
27
+ name: eventmachine-eventmachine
28
+ requirement: &27093600 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.12.9
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *27093600
37
+ description:
38
+ email: christopher_lee@rapid7.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - lib/scan_manager.rb
44
+ has_rdoc: true
45
+ homepage:
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.5.2
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Used for to start a managed scan and monitor their activity
69
+ test_files: []