nexpose_scan_manager 0.0.1
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.
- data/lib/scan_manager.rb +165 -0
- metadata +69 -0
data/lib/scan_manager.rb
ADDED
|
@@ -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: []
|