excl_site_asset_scan 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/bin/nexpose_api_util +51 -0
- data/lib/api-options.rb +41 -0
- data/lib/extended-api.rb +117 -0
- data/lib/util.rb +33 -0
- metadata +75 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/extended-api.rb'))
|
4
|
+
|
5
|
+
#------------------------------------------------------------------------------------------------------
|
6
|
+
# == Synopsis
|
7
|
+
# Main script to execute specified API commands against a specified NSC
|
8
|
+
#
|
9
|
+
# == Usage
|
10
|
+
# ruby nexpose_api_util.rb --help
|
11
|
+
#
|
12
|
+
# == Author
|
13
|
+
# Christopher Lee, Rapid7 LLC
|
14
|
+
#------------------------------------------------------------------------------------------------------
|
15
|
+
|
16
|
+
begin
|
17
|
+
if ARGV.length > 0
|
18
|
+
$is_command_line = false
|
19
|
+
begin
|
20
|
+
options = Options.parse ARGV
|
21
|
+
extended_api = ::ExtendedAPI.new options.user, options.password, options.host
|
22
|
+
extended_api.do_login
|
23
|
+
case options.key
|
24
|
+
when 1 then
|
25
|
+
extended_api.print_asset_groups
|
26
|
+
when 2 then
|
27
|
+
extended_api.print_asset_group_info options.args
|
28
|
+
when 3 then
|
29
|
+
extended_api.start_excluded_scan options.args
|
30
|
+
else
|
31
|
+
puts "Invalid Input!"
|
32
|
+
end
|
33
|
+
|
34
|
+
rescue Exception => e
|
35
|
+
puts e
|
36
|
+
ensure
|
37
|
+
begin
|
38
|
+
if extended_api
|
39
|
+
extended_api.do_logout
|
40
|
+
end
|
41
|
+
rescue Nexpose::APIError => e
|
42
|
+
# Don't care, maybe session already dead
|
43
|
+
end
|
44
|
+
end
|
45
|
+
exit 0
|
46
|
+
else
|
47
|
+
puts 'Input required'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
data/lib/api-options.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
#------------------------------------------------------------------------------------------------------
|
5
|
+
# Defines options to be executed against the NeXpose API
|
6
|
+
#------------------------------------------------------------------------------------------------------
|
7
|
+
class Options
|
8
|
+
def self.parse(args)
|
9
|
+
options = OpenStruct.new
|
10
|
+
options.verbose = false
|
11
|
+
|
12
|
+
option_parser = OptionParser.new do |option_parser|
|
13
|
+
option_parser.on("-h HOST", "The network address of the NeXpose instance - Required") {|arg| options.host=arg}
|
14
|
+
option_parser.on("-u USER", "The user name - Required") {|arg| options.user=arg}
|
15
|
+
option_parser.on("-p PASSWORD", "The password - Required") {|arg| options.password=arg}
|
16
|
+
option_parser.on("--n1", "List all the asset groups") {|arg| options.key=1}
|
17
|
+
option_parser.on("--n2 GI", "List an asset groups config. The GI (group id or group name is required)") do |arg|
|
18
|
+
options.key=2
|
19
|
+
options.args=arg
|
20
|
+
end
|
21
|
+
option_parser.on("--ses params", "Start an asset group excluded scan where params are a comma separated list of the site-id to scan followed by
|
22
|
+
asset group id(s) to exclude") do |arg|
|
23
|
+
options.key=3
|
24
|
+
options.args=arg.chomp
|
25
|
+
end
|
26
|
+
option_parser.on_tail("--help", "Help") do
|
27
|
+
puts option_parser
|
28
|
+
exit 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
option_parser.parse!(args)
|
34
|
+
rescue OptionParser::ParseError => e
|
35
|
+
puts "#{e}\n\n#{option_parser}"
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
options
|
40
|
+
end
|
41
|
+
end
|
data/lib/extended-api.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'util.rb'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'api-options.rb'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'hirb'
|
6
|
+
require 'nexpose'
|
7
|
+
|
8
|
+
#------------------------------------------------------------------------------------------------------
|
9
|
+
# Extends the base API functionality by combining common API commands to produce a simplified
|
10
|
+
# user command.
|
11
|
+
#------------------------------------------------------------------------------------------------------
|
12
|
+
class ExtendedAPI
|
13
|
+
attr_accessor :user_name, :password, :host, :nexpose_api, :scan_manager
|
14
|
+
@@connected = false
|
15
|
+
|
16
|
+
def initialize user_name, password, host
|
17
|
+
@user_name = user_name
|
18
|
+
@password = password
|
19
|
+
@host = host
|
20
|
+
@nexpose_api = Nexpose::Connection.new @host, @user_name, @password
|
21
|
+
end
|
22
|
+
|
23
|
+
#-------------------------------------------------------------------
|
24
|
+
# Logs in to NeXpose and sets a session key on the connector object.
|
25
|
+
#-------------------------------------------------------------------
|
26
|
+
def do_login
|
27
|
+
if not @@connected
|
28
|
+
begin
|
29
|
+
if @nexpose_api.login
|
30
|
+
@@connected = true
|
31
|
+
end
|
32
|
+
rescue Exception => e
|
33
|
+
puts e.message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#------------------------------------------------------
|
39
|
+
# Prints all asset group information in tabular format:
|
40
|
+
# | site_id | device_id | address | riskfactor |
|
41
|
+
#------------------------------------------------------
|
42
|
+
def print_asset_group_info group_id
|
43
|
+
group_configs = @nexpose_api.asset_group_config group_id
|
44
|
+
puts "\nASSET GROUP INFO (id: #{group_id})"
|
45
|
+
puts Hirb::Helpers::AutoTable.render group_configs, :fields => [:site_id, :device_id, :address, :riskfactor]
|
46
|
+
rescue Exception => e
|
47
|
+
if e.message =~ /Invalid groupID/
|
48
|
+
puts 'Group ID does not exist'
|
49
|
+
else
|
50
|
+
puts e.message
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#----------------------------------------------------------------
|
55
|
+
# Prints asset group configuration information in tabular format:
|
56
|
+
# | site_id | device_id | address | riskfactor |
|
57
|
+
#----------------------------------------------------------------
|
58
|
+
def print_asset_groups
|
59
|
+
res = @nexpose_api.asset_groups_listing
|
60
|
+
puts "\nASSET GROUPS:"
|
61
|
+
puts Hirb::Helpers::AutoTable.render res, :fields => [:asset_group_id, :name, :description, :risk_score]
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
#
|
66
|
+
#
|
67
|
+
def start_excluded_scan scan_info
|
68
|
+
# Parse the scan_info object to get site
|
69
|
+
# to be scanned and the asset group(s) to exclude
|
70
|
+
parsed_string = scan_info.to_s.split ','
|
71
|
+
site_id = parsed_string[0]
|
72
|
+
unless Util.is_number? site_id
|
73
|
+
raise ArgumentError.new 'The site-id must be a number'
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get all the device_ids for the site
|
77
|
+
device_listing_hash = @nexpose_api.site_device_listing site_id
|
78
|
+
device_ids = []
|
79
|
+
device_listing_hash.each do |device_listing|
|
80
|
+
device_ids << device_listing[:device_id].to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get all the devices associated with the group(s)
|
84
|
+
device_ids_excluded = []
|
85
|
+
parsed_string.delete_at(0)
|
86
|
+
parsed_string.each do |group_id|
|
87
|
+
group_infos = @nexpose_api.asset_group_config group_id
|
88
|
+
group_infos.each do |group_info|
|
89
|
+
device_ids_excluded << group_info[:device_id].to_i
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Remove all the devices in the group
|
94
|
+
devices_to_scan = device_ids - device_ids_excluded
|
95
|
+
|
96
|
+
# Hopefully this is not an empty set
|
97
|
+
if not devices_to_scan or devices_to_scan.empty?
|
98
|
+
raise "There are no devices left to scan after devices in groups: #{parsed_string.inspect} are removed from site: #{site_id}"
|
99
|
+
end
|
100
|
+
|
101
|
+
# Start an ad-hoc scan.
|
102
|
+
res = @nexpose_api.site_device_scan_start site_id, devices_to_scan, nil
|
103
|
+
if res
|
104
|
+
puts "Scan started scan ID: #{res[:scan_id]}, on engine ID: #{res[:engine_id]}"
|
105
|
+
else
|
106
|
+
put "Scan start failed for site #{site}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
#
|
112
|
+
#
|
113
|
+
def do_logout
|
114
|
+
@nexpose_api.logout
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
data/lib/util.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class Util
|
2
|
+
|
3
|
+
#------------------------------------------------------------------------------------------------------
|
4
|
+
# Checks if the input is a number
|
5
|
+
#
|
6
|
+
# input: The object that will be tested
|
7
|
+
#------------------------------------------------------------------------------------------------------
|
8
|
+
def self.is_number? input
|
9
|
+
true if Float input rescue false
|
10
|
+
end
|
11
|
+
|
12
|
+
#------------------------------------------------------------------------------------------------------
|
13
|
+
# Retrieves numeric input only from the command line
|
14
|
+
#
|
15
|
+
# mssg: The message to display that requests the input
|
16
|
+
#------------------------------------------------------------------------------------------------------
|
17
|
+
def self.get_numeric_input mssg
|
18
|
+
begin
|
19
|
+
while true
|
20
|
+
print mssg
|
21
|
+
input = gets().chomp()
|
22
|
+
if Util.is_number? input
|
23
|
+
return input.to_i
|
24
|
+
else
|
25
|
+
puts "Input is not a number"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
rescue Exception => e
|
29
|
+
puts e.message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: excl_site_asset_scan
|
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-07-20 00:00:00.000000000 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: nexpose
|
17
|
+
requirement: &23552100 !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: *23552100
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: hirb
|
28
|
+
requirement: &23551800 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.4.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *23551800
|
37
|
+
description: ! " \tList asset groups, list information about certain asset groups,
|
38
|
+
start a site scan with certain asset groups excluded.\n"
|
39
|
+
email: christopher_lee@rapid7.com
|
40
|
+
executables:
|
41
|
+
- nexpose_api_util
|
42
|
+
extensions: []
|
43
|
+
extra_rdoc_files: []
|
44
|
+
files:
|
45
|
+
- lib/api-options.rb
|
46
|
+
- lib/extended-api.rb
|
47
|
+
- lib/util.rb
|
48
|
+
- bin/nexpose_api_util
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: https://github.com/chrlee/NeXpose-ScanManager-Interface/tree/master/examples/excluded_asset_scan
|
51
|
+
licenses: []
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.5.2
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: List asset groups, list information about certain asset groups, start a site
|
74
|
+
scan with certain asset groups excluded.
|
75
|
+
test_files: []
|