nexposecli 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/bin/nexposecli ADDED
@@ -0,0 +1,1723 @@
1
+ #!/usr/bin/env ruby
2
+ ##############################################################################
3
+ #
4
+ # File: evm.rb
5
+ #
6
+ # Author: Erik Gomez <gomeze@pobox.com>
7
+ # Erik Gomez <erik_gomez@rapid7.com>
8
+ #
9
+ # Purpose: UF ISC EVM Administrative tasks via cli
10
+ #
11
+ # Base Revision: $Id:$ (20141030@1227.01)
12
+ # Revision: $Id:$ (20160426@1315.01)
13
+ #
14
+ # Usage: ./evm.rb <action> <target> [<args>]
15
+ #
16
+ # -v verbose
17
+ # --help help
18
+ # ***NOTE*** This script is being refactored!!!
19
+ # It is currently a shameless copy of my UF code and
20
+ # argparse.rb class code from Jim Hranicky (jfh@ufl.edu)
21
+ #
22
+ ##############################################################################
23
+ require 'rubygems'
24
+ require 'nexpose'
25
+ require 'nexposecli'
26
+ require 'socket'
27
+ require 'digest'
28
+ require 'securerandom'
29
+ require 'netaddr'
30
+ require 'logger'
31
+ require 'yaml'
32
+ require 'csv'
33
+ require 'set'
34
+ # for debug, this dumps the ruby objects to STDOUT
35
+ require 'pp'
36
+
37
+ ##############################################################################
38
+ # Set Const
39
+
40
+ # Allowed Ops by field, in Set form
41
+ CVSS_SCORE_OPS = Set["IS", "IS_NOT", "IN_RANGE", "GREATER_THAN", "LESS_THAN"]
42
+ IP_RANGE_OPS = Set["IN", "NOT_IN"]
43
+ OS_OPS = Set["CONTAINS", "NOT_CONTAINS", "IS_EMPTY", "IS_NOT_EMPTY"]
44
+ RISK_SCORE_OPS = Set["IS", "IS_NOT", "IN_RANGE", "GREATER_THAN", "LESS_THAN"]
45
+ SITE_ID_OPS = Set["IN", "NOT_IN"]
46
+ SCAN_DATE_OPS = Set["ON_OR_BEFORE", "ON_OR_AFTER", "BETWEEN", "EARLIER_THAN", "WITHIN_THE_LAST"]
47
+
48
+ # Set default var values
49
+ $debug = false
50
+ uf_scanners = ''
51
+
52
+ @logpath = "./logs/"
53
+ @scanpath = "./"
54
+ # Attempting to use logfile per month: @logfile = "evm" + Time.now.strftime("%Y%m%d_%H%M%S") + ".log"
55
+ @logfile = "evm" + Time.now.strftime("%Y%m") + ".log"
56
+ @evm_reqid = SecureRandom.hex
57
+ @nsc_server = "<server>"
58
+ @nsc_user = "<user>"
59
+ @nsc_passwd = "<pass>"
60
+ @nsc_sites = Hash.new
61
+
62
+ scan_task_sleep = 300
63
+ max_scan_task_attempts = 3
64
+
65
+ ##############################################################################
66
+ # Base procs
67
+ # wrapped puts for debug control
68
+ def uputs( facility, ulog )
69
+ # add string validation
70
+ @logger.info( @nsc_server + "," + @evm_reqid + " [" + facility.to_s + "] " + ulog.to_s)
71
+ if $debug
72
+ puts "[" + facility.to_s + "]" + ulog.to_s
73
+ end
74
+ end
75
+
76
+ # wrapped pp for debug control
77
+ def upp( uobj )
78
+ if $debug
79
+ pp uobj
80
+ end
81
+ end
82
+
83
+ # UF bail vs exit
84
+ def ubail(retval, msg)
85
+ uputs("BAIL", msg.to_s)
86
+ exit(retval)
87
+ end
88
+
89
+ # download, needs lots of work to ensure write path is clean and no overwrites
90
+ def download(url, filename, nsc)
91
+ return nil if url.nil? or url.empty?
92
+ uri = URI.parse(url)
93
+ http = Net::HTTP.new(uri.host, uri.port)
94
+ http.use_ssl = true
95
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE # XXX: security issue
96
+ headers = {'Cookie' => "nexposeCCSessionID=#{nsc.session_id}"}
97
+ resp = http.get(uri.path, headers)
98
+
99
+ if filename == "NONE"
100
+ resp.body
101
+ else
102
+ file = File.open(filename, "w")
103
+ file.write(resp.body)
104
+ file.close
105
+
106
+ filename
107
+ end
108
+ end
109
+
110
+ def read_config(conf)
111
+ # add check for file existence and perms
112
+ config = YAML.load_file(conf)
113
+
114
+ @nsc_server = config["config"]["server"]
115
+ @nsc_user = config["config"]["user"]
116
+ @nsc_passwd = config["config"]["password"]
117
+ @nsc_sites = config["sites"]
118
+ end
119
+
120
+ def scan_activity()
121
+ scanner_activity = Hash.new
122
+ # scan activity
123
+ scan_activity = @nsc.scan_activity
124
+ upp scan_activity
125
+ if scan_activity.length > 0
126
+ scan_activity.each do |scan|
127
+ timeDiff = DateTime.now.strftime('%s').to_i - scan.start_time.strftime('%s').to_i
128
+ tdSecs = timeDiff
129
+ tdHours = tdSecs / 3600
130
+ tdSecs -= tdHours * 3600
131
+ tdMins = tdSecs / 60
132
+ tdSecs -= tdMins * 60
133
+ timeDiffStr = "#{tdHours} hours, #{tdMins} minutes and #{tdSecs} seconds"
134
+ act_str = "Scan Id: #{scan.scan_id.to_s.ljust(5)} site: #{scan.site_id.to_s.ljust(4)} status: [#{scan.status.to_s.ljust(10)}]" +
135
+ " engine: #{scan.engine_id.to_s.ljust(3)} start time:#{scan.start_time} run time:#{timeDiffStr}"
136
+ if scanner_activity.has_key?scan.engine_id.to_s
137
+ scanner_activity[scan.scan_id.to_s] = { :site_id => scan.site_id.to_s, :status => scan.status.to_s, :scan_engine_id => scan.engine_id.to_s, :timeDiff => timeDiff, :act_str => act_str }
138
+ else
139
+ scanner_activity[scan.scan_id.to_s] = Hash.new
140
+ scanner_activity[scan.scan_id.to_s] = { :site_id => scan.site_id.to_s, :status => scan.status.to_s, :scan_engine_id => scan.engine_id.to_s, :timeDiff => timeDiff, :act_str => act_str }
141
+ end
142
+ end
143
+ end
144
+ scanner_activity
145
+ end
146
+
147
+ def valid_cidrv4(cstr)
148
+ if /^[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\/([1-2]?[0-9]|3[0-2])$/ =~ cstr
149
+ true
150
+ else
151
+ false
152
+ end
153
+ end
154
+
155
+ def valid_ipv4(ipstr)
156
+ if /^[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]$/ =~ ipstr
157
+ true
158
+ else
159
+ false
160
+ end
161
+ end
162
+
163
+ def valid_ipv4_dottedcsv(ipstr)
164
+ if /^[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9],[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]$/ =~ ipstr
165
+ true
166
+ else
167
+ false
168
+ end
169
+ end
170
+
171
+ def valid_ipv4_dottedcolon(ipstr)
172
+ if /^[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]:[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]$/ =~ ipstr
173
+ true
174
+ else
175
+ false
176
+ end
177
+ end
178
+
179
+ def valid_ipv4_dotteddashed(ipstr)
180
+ if /^[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]-[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]$/ =~ ipstr
181
+ true
182
+ else
183
+ false
184
+ end
185
+ end
186
+
187
+ def valid_ipv4_dotteddashedshort(ipstr)
188
+ if /^[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]-[1-2]?[0-9]?[0-9]$/ =~ ipstr
189
+ true
190
+ else
191
+ false
192
+ end
193
+ end
194
+
195
+ def ip2rangev4(cstr)
196
+ rangev4 = cstr.to_s + "," + cstr.to_s
197
+ end
198
+
199
+ def cidr2rangev4(cstr)
200
+ rangev4 = ""
201
+ netblock = NetAddr::CIDR.create(cstr)
202
+ rangev4 = netblock.first.to_s + "," + netblock.last.to_s
203
+ end
204
+
205
+ def dotteddashed2rangev4(cstr)
206
+ # consider validating the low,high values in order
207
+ rangev4 = cstr.split('-').first.to_s + "," + cstr.split('-').last.to_s
208
+ end
209
+
210
+ def dotteddashedshort2rangev4(cstr)
211
+ # consider validating the low,high values in order
212
+ ipv4l = cstr.split('-').first.to_s
213
+ ipv4h4th = cstr.split('-').last.to_s
214
+ ipv4h = ipv4l.sub(/\d+\Z/) {|x| x = ipv4h4th }
215
+ rangev4 = ipv4l.to_s + "," + ipv4h.to_s
216
+ end
217
+
218
+ def is_numeric(object)
219
+ true if Float(object) rescue false
220
+ end
221
+
222
+ def validate_searchstring(sfstr)
223
+ # Expectng field:op format for sfstr
224
+ valid_searchstring = nil
225
+ valid_search_field = nil
226
+ valid_search_op = nil
227
+
228
+ # Valid search fields and operators:
229
+ # CVSS_SCORE = IS, IS_NOT, IN_RANGE, GREATER_THAN, LESS_THAN (Float 0.0-10.0)
230
+ # IP_RANGE = IN, NOT_IN (IPv4 dotted notation)
231
+ # OS = CONTAINS, NOT_CONTAINS, IS_EMPTY, IS_NOT_EMPTY
232
+ # RISK_SCORE = IS, IS_NOT, IN_RANGE, GREATER_THAN, LESS_THAN (Fixnum)
233
+ # SITE_ID = IN, NOT_IN (Fixnum)
234
+ # SCAN_DATE = ON_OR_BEFORE, ON_OR_AFTER, BETWEEN (Value::ScanDate::FORMAT dates)
235
+ # SCAN_DATE = EARLIER_THAN, WITHIN_THE_LAST (Fixnum days)
236
+
237
+ # Grab search field and op
238
+ search_field = sfstr.split(':').first.to_s
239
+ search_op = sfstr.split(':').last.to_s
240
+
241
+ # Case by supported Search fields
242
+ isValid = false
243
+ case search_field
244
+ when "CVSS_SCORE"
245
+ isValid = true if CVSS_SCORE_OPS.include?(search_op)
246
+ when "IP_RANGE"
247
+ isValid = true if IP_RANGE_OPS.include?(search_op)
248
+ when "OS"
249
+ isValid = true if OS_OPS.include?(search_op)
250
+ when "RISK_SCORE"
251
+ isValid = true if RISK_SCORE_OPS.include?(search_op)
252
+ when "SITE_ID"
253
+ isValid = true if SITE_ID_OPS.include?(search_op)
254
+ when "SCAN_DATE"
255
+ isValid = true if SCAN_DATE_OPS.include?(search_op)
256
+ else
257
+ # Unsupported search field
258
+ end
259
+ if isValid
260
+ valid_search_field = search_field
261
+ valid_search_op = search_op
262
+ valid_searchstring = valid_search_field + "," + valid_search_op
263
+ else
264
+ valid_searchstring = "ERROR"
265
+ end
266
+ end
267
+
268
+ ##############################################################################
269
+ #
270
+ # Conf
271
+ #
272
+ ##############################################################################
273
+ # Parse cli and config options passed
274
+ ARGS = %q{
275
+ - comment : General Options
276
+
277
+ - name : help
278
+ desc : Print help
279
+
280
+ - name : verbose
281
+ short : v
282
+ desc : Run verbosely
283
+
284
+ - comment : EVM Administrative Actions
285
+
286
+ - name : create
287
+ short : c
288
+ desc : The create action is used for new objects
289
+
290
+ - name : list
291
+ short : l
292
+ desc : The list action is used to list of objects of the same type
293
+
294
+ - name : show
295
+ short : s
296
+ desc : The show action is used to display details of a single object
297
+
298
+ - name : update
299
+ short : u
300
+ desc : The update action is used to change properties of a single object
301
+
302
+ - name : delete
303
+ short : d
304
+ desc : The delete action is used to delete a single object
305
+
306
+ - name : run
307
+ desc : The run action is only used to issue commands to the COMMAND object
308
+
309
+ - comment : EVM Action Targets
310
+
311
+ - name : USER
312
+ short : U
313
+ desc : The USER target is used to alter or create the USER object
314
+
315
+ - name : ROLE
316
+ short : L
317
+ desc : The ROLE target is used to alter or create the ROLE object
318
+
319
+ - name : ENGINE
320
+ short : E
321
+ desc : The ENGINE target is used to alter or create the SCAN ENGINE object
322
+
323
+ - name : POOL
324
+ short : P
325
+ desc : The POOL target is used to alter or create the POOL object
326
+
327
+ - name : SCAN
328
+ short : S
329
+ desc : The SCAN target is used to alter or create the SCAN object
330
+
331
+ - name : SITE
332
+ short : T
333
+ desc : The SITE target is used to alter or create the SITE object
334
+
335
+ - name : ASSET
336
+ short : A
337
+ desc : The ASSET target is used to alter or create the ASSET object
338
+
339
+ - name : DASSET
340
+ short : D
341
+ desc : The DASSET target is used to alter or create the DASSET object
342
+
343
+ - name : TAG
344
+ short : G
345
+ desc : The TAG target is used to alter or create the TAG object
346
+
347
+ - name : REPORT
348
+ short : R
349
+ desc : The REPORT target is used to alter or create the REPORT object
350
+
351
+ - name : VULN
352
+ short : V
353
+ desc : The VULN target is used to alter or create the VULN object
354
+
355
+ - name : CONSOLE
356
+ desc : The CONSOLE target is used to alter the CONSOLE nsc connection object
357
+
358
+ - name : COMMAND
359
+ short : C
360
+ desc : The COMMAND target is only used in conjunction with the --run action
361
+ required : true
362
+
363
+ - comment : EVM Action Argument Values
364
+
365
+ - name : host
366
+ short : h
367
+ desc : The target ip or host to be acted upon by the action
368
+ required : true
369
+
370
+ - name : port
371
+ short : p
372
+ desc : The target port to be acted upon by the action
373
+ required : true
374
+
375
+ - name : name
376
+ short : n
377
+ desc : The target object name
378
+ required : true
379
+
380
+ - name : fullname
381
+ desc : The target object full name
382
+ required : true
383
+
384
+ - name : newname
385
+ desc : The target object new name
386
+ required : true
387
+
388
+ - name : description
389
+ short : t
390
+ desc : The text based description of the object being acted upon
391
+ required : true
392
+
393
+ - name : id
394
+ short : i
395
+ desc : The object id being acted upon
396
+ required : true
397
+
398
+ - name : site
399
+ desc : The site id of the object being acted upon
400
+ required : true
401
+
402
+ - name : range
403
+ short : r
404
+ desc : The comma separated (begin,end) range of ip addresses to be acted upon
405
+ required : true
406
+
407
+ - name : targets
408
+ desc : The network block or ip addresses to be acted upon, in CIDRv4, dotted dashed, or ip format
409
+ required : true
410
+
411
+ - name : argv
412
+ short : g
413
+ desc : Argument vector for the action, in the form key:value pairs
414
+ required : true
415
+
416
+ - name : filter
417
+ short : f
418
+ desc : Filters which are applied to the action, in the form key:value pairs
419
+ required : true
420
+
421
+ - name : filterv
422
+ desc : Filter value which are applied to the action. Formate varies by filter type
423
+ required : true
424
+
425
+ - name : action
426
+ short : a
427
+ desc : The subaction to be performed within the target action
428
+ required : true
429
+
430
+ - name : attempts
431
+ desc : The max number of attempts for iterative actions
432
+ required : true
433
+
434
+ - name : loop_sleep
435
+ desc : The sleep interval in seconds between action iterations
436
+ required : true
437
+
438
+ - comment : Nexpose Console credentials
439
+
440
+ - name : config
441
+ desc : The config yaml file containing the connection details of the Nexpose Console Server
442
+ required : true
443
+
444
+ - name : nsc_server
445
+ desc : The ip or hostname of the Nexpose Console Server
446
+ required : true
447
+
448
+ - name : nsc_user
449
+ desc : The username to login to the Nexpose Console Server
450
+ required : true
451
+
452
+ - name : nsc_pass
453
+ desc : The password to login to the Nexpose Console Server
454
+ required : true
455
+
456
+ - name : logpath
457
+ desc : The path for writing the logs
458
+ required : true
459
+
460
+ - name : scanpath
461
+ desc : The path for exported/imported scans
462
+ required : true
463
+ }
464
+
465
+
466
+ ##############################################################################
467
+ #
468
+ # Main
469
+ #
470
+ ##############################################################################
471
+ # Args parsing
472
+ ap = Nexposecli::ArgParse.new( ARGS )
473
+
474
+ begin
475
+ args = ap.parse
476
+ rescue Nexposecli::ArgParse::InvalidOption => e
477
+ STDERR.puts e
478
+ STDERR.puts ap.usage
479
+ exit(-1)
480
+ end
481
+
482
+ # start the logger
483
+ if args.logpath
484
+ # consider input validation, to avoid sec issues
485
+ @logpath = args.logpath.to_s
486
+ end
487
+ @logger = Logger.new(@logpath + @logfile)
488
+ @logger.level = Logger::INFO
489
+ uputs("LOG", "Automation tasks being run from: " + Socket.gethostname.to_s)
490
+ uputs("LOG", "Automation tasks being logged to: #{@logpath.to_s + @logfile.to_s}")
491
+
492
+ if args.scanpath
493
+ # consider input validation, to avoid sec issues
494
+ @scanpath = args.scanpath.to_s
495
+ end
496
+
497
+ $debug = TRUE if args.verbose
498
+ uputs("CLI", "Command-line args parsed for #{$0}")
499
+ uputs("CLI", "Args: #{args.inspect}")
500
+
501
+ if args.help
502
+ uputs("CLI", "Help was requested, displaying usage and exiting")
503
+ puts ap.usage("#{$0} [options] (v #{Nexposecli::VERSION})")
504
+ exit(0)
505
+ end
506
+
507
+ # test for a single action
508
+ uputs("CLI", "Checking for the requested action")
509
+ @action = 0
510
+ @action |= 1 if args.create
511
+ @action |= 2 if args.list
512
+ @action |= 4 if args.show
513
+ @action |= 8 if args.update
514
+ @action |= 16 if args.delete
515
+ @action |= 32 if args.run
516
+ uputs("ACTION", "The requested action value is: #{@action.to_s}")
517
+ raise "You can only submit one action per task, see --help (action submitted: #{@action.to_s})" unless [1,2,4,8,16,32].include?(@action)
518
+
519
+ uputs("TARGET", "Checking for the requested target")
520
+ @target = 0
521
+ @target |= 1 if args.USER
522
+ @target |= 2 if args.ENGINE
523
+ @target |= 4 if args.POOL
524
+ @target |= 8 if args.SCAN
525
+ @target |= 16 if args.SITE
526
+ @target |= 32 if args.ASSET
527
+ @target |= 64 if args.REPORT
528
+ @target |= 128 if args.VULN
529
+ @target |= 256 if args.COMMAND
530
+ @target |= 512 if args.DASSET
531
+ @target |= 1024 if args.TAG
532
+ @target |= 2048 if args.CONSOLE
533
+ @target |= 4096 if args.TEMPLATE
534
+ @target |= 8192 if args.ROLE
535
+ uputs("TARGET", "The requested target value is: #{@target.to_s}")
536
+ raise "You can only submit one target per task, see --help (#{@target})" unless [1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192].include?(@target)
537
+
538
+ # nsc conn vars
539
+ unless (
540
+ (args.nsc_server && args.nsc_user && args.nsc_pass) || args.config
541
+ )
542
+ raise 'Please supply Nexpose Console connection parameters listed under --help'
543
+ end
544
+ # nsc connection details
545
+ if args.config
546
+ uputs("CONF", "Reading config file: #{args.config.to_s}")
547
+ read_config(args.config.to_s)
548
+ else
549
+ uputs("CONF", "Using NSC creds from CLI args, be sure to clear shell history to avoid creds disclosure")
550
+ @nsc_server = args.nsc_server.to_s
551
+ @nsc_user = args.nsc_user.to_s
552
+ @nsc_passwd = args.nsc_pass.to_s
553
+ end
554
+
555
+ # Make the initial connection to the NSC server
556
+ uputs("CONNECT", "Attempting connection to NSC: #{@nsc_server.to_s} with user: #{@nsc_user.to_s}")
557
+ begin
558
+ @nsc = Nexpose::Connection.new( @nsc_server.to_s, @nsc_user.to_s, @nsc_passwd.to_s)
559
+ rescue SystemCallError => e
560
+ # EJG add conditional api error handling...
561
+ uputs("CONNECT", "An error occurred while attempting to connect to the specified server: #{e.to_s}")
562
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
563
+ exit(-1)
564
+ end
565
+ uputs("CONNECT", "Connection to NSC: #{@nsc_server.to_s} with user: #{@nsc_user.to_s} successful")
566
+
567
+ uputs("LOGIN", "Attempting login to NSC: #{@nsc_server.to_s} with user: #{@nsc_user.to_s}")
568
+ begin
569
+ @nsc.login
570
+ rescue Nexpose::APIError => e
571
+ # EJG add conditional api error handling...
572
+ uputs("LOGIN", "An error occurred while attempting to connect to the specified server: #{e.to_s}")
573
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
574
+ exit(-1)
575
+ end
576
+
577
+ if @nsc.session_id
578
+ uputs("CONNECT", 'NSC Login Successful')
579
+ else
580
+ uputs("CONNECT", 'NSC Login Failed')
581
+ exit
582
+ end
583
+
584
+ # opted to keep actions grouped by the target
585
+ case @target
586
+ when 1 # TARGET USER
587
+ case @action
588
+ when 1 # create
589
+ uputs("ACTION", 'create USER action requested')
590
+ name = args.name
591
+ full_name = "#{args.fullname}"
592
+ password = "nxpassword"
593
+
594
+ user = Nexpose::User.new(name,
595
+ full_name,
596
+ password,
597
+ role_name = 'user',
598
+ id = -1,
599
+ enabled = 1,
600
+ email = nil,
601
+ all_sites = false,
602
+ all_groups = false,
603
+ token = nil)
604
+ pp user
605
+ puts 'Not yet saved'
606
+ user.save(@nsc)
607
+ pp user
608
+ when 2 # list
609
+ uputs("ACTION", 'list USER action requested')
610
+ user_listing = @nsc.list_users
611
+ pp user_listing
612
+ when 4 # show
613
+ uputs("ACTION", 'show USER action requested')
614
+ userid = args.id.to_str
615
+ user = Nexpose::User.load(@nsc, userid)
616
+ pp user
617
+ when 8 # update
618
+ uputs("ACTION", 'update USER action requested')
619
+ puts 'Not yet implemented'
620
+ when 16 # delete
621
+ uputs("ACTION", 'delete USER action requested')
622
+ puts 'Not yet implemented'
623
+ else
624
+ uputs("ACTION", 'The action requested is not implemented for target')
625
+ puts 'The action requested is not implemented for target'
626
+ end
627
+ when 2 # TARGET ENGINE
628
+ case @action
629
+ when 1 # create
630
+ uputs("ACTION", 'create ENGINE action requested')
631
+ puts 'Not yet implemented'
632
+ when 2 # list
633
+ uputs("ACTION", 'list ENGINE action requested')
634
+ uf_scanners = @nsc.engines
635
+ if uf_scanners.length > 0
636
+ if args.action
637
+ case args.action
638
+ when "summary", "status"
639
+ scanner_activity = scan_activity()
640
+ nseSynopsis = Nexpose::DataTable._get_json_table(@nsc, '/data/engines', { 'sort' => -1, 'dir' => -1, 'startIndex' => -1, 'results' => -1, 'table-id' => 'engine-listing'})
641
+ upp nseSynopsis
642
+ nseSynopsis.each do |eng|
643
+ puts "\t-Id: #{eng['id']}\tStatus: #{eng['status'].to_s.ljust(10)} Last Update(id): #{eng['lastUpdateDate']}(#{eng['lastUpdateID']})\t Name: #{eng['name']}\n"
644
+ # EJG NEEDS work
645
+ if scanner_activity.has_key?(eng['Engine ID'].to_i)
646
+ scanner_activity[eng['Engine ID'].to_i].each do |scan| puts "\t " + scan end
647
+ end
648
+ end
649
+ else
650
+ end
651
+ else
652
+ uf_scanners.each do |scanner|
653
+ puts("\t- status: #{scanner.status}\t id:#{scanner.id}\t name:'#{scanner.name}'\n")
654
+ end
655
+ end
656
+ puts("\nNOTE: The Scan Engine Ids are used for Scan Engine Pool create, update, and delete.\n")
657
+ else
658
+ puts("This Nexpose Security Console has no defined Scan Engines.\n")
659
+ end
660
+ when 4 # show
661
+ uputs("ACTION", 'show ENGINE action requested')
662
+ puts 'Not yet implemented'
663
+ when 8 # update
664
+ uputs("ACTION", 'update ENGINE action requested')
665
+ puts 'Not yet implemented'
666
+ when 16 # delete
667
+ uputs("ACTION", 'delete ENGINE action requested')
668
+ puts 'Not yet implemented'
669
+ else
670
+ uputs("ACTION", 'The action requested is not implemented for target')
671
+ puts 'The action requested is not implemented for target'
672
+ end
673
+ when 4 # TARGET POOL
674
+ case @action
675
+ when 1 # create
676
+ uputs("ACTION", 'create POOL action requested')
677
+ raise 'Please submit the Scan Engine Ids for the Pool definition, see --help' unless ( args.id )
678
+ raise 'Please submit the Scan Engine Pool name, see --help' unless ( args.name )
679
+ uf_scanners = @nsc.engines # get the scan engine list to map name by id
680
+ # consider sanitizing the args.name
681
+ pool_name = args.name
682
+ pool = Nexpose::EnginePool.new(pool_name, 'silo')
683
+ engine_ids = args.id
684
+ engine_ids.each_line(',') {|s| engine = uf_scanners.find{ |scanner| scanner.id == s.to_i }; uputs("ACTION", "Adding Scan Engine: [#{engine.name}]"); pool.add(engine.name)}
685
+ pool.save(@nsc)
686
+ puts("The scan engine pool [ #{pool_name} ] was created and has the id: #{pool.id}")
687
+ uputs("ACTION", 'create POOL action completed for pool name: #{pool_name}, id: #{pool.id}')
688
+ when 2 # list
689
+ uputs("ACTION", 'list POOL action requested')
690
+ pool_listing = @nsc.list_engine_pools
691
+ if pool_listing.length > 0
692
+ pool_listing.each do |pool|
693
+ puts("Pool: id:#{pool.id}\tname:#{pool.name}\n")
694
+ upp pool
695
+ end
696
+ else
697
+ puts("This Nexpose Security Console has no defined Scan Engine Pools.\n")
698
+ end
699
+ when 4 # show
700
+ uputs("ACTION", 'show POOL action requested')
701
+ raise 'Please submit the Scan Engine Pool Id and Pool Name, see --help' unless ( args.id && args.name )
702
+ pool_name = args.name
703
+ pool_id = args.id
704
+ pool = Nexpose::EnginePool.load(@nsc, pool_name)
705
+ puts "\nScan Engine Pool name: [#{pool.name}] and id: [#{pool.id}] has the following Scan Engines as members:\n\n"
706
+ pool.engines.each do |engine|
707
+ puts "\t- Scan Engine name: [#{engine.name}] id: [#{engine.id}] at: [#{engine.address}]\n"
708
+ end
709
+ puts "\n"
710
+ upp pool
711
+ when 8 # update
712
+ uputs("ACTION", 'update POOL action requested')
713
+ puts 'Not yet implemented'
714
+ when 16 # delete
715
+ uputs("ACTION", 'delete POOL action requested')
716
+ raise 'Please submit the Scan Engine Pool Id and Pool Name, see --help' unless ( args.id && args.name )
717
+ pool_name = args.name
718
+ pool_id = args.id
719
+ pool = Nexpose::EnginePool.new(pool_name, 'silo', pool_id)
720
+ pool.delete(@nsc)
721
+ uputs("POOL", "The scan engine pool [ #{pool_name} ] with the id: #{pool.id} was deleted\n")
722
+ else
723
+ uputs("ACTION", 'The action requested is not implemented for target')
724
+ puts 'The action requested is not implemented for target'
725
+ end
726
+ when 8 # TARGET SCAN
727
+ case @action
728
+ when 1 # create
729
+ uputs("SCAN", 'create SCAN action requested')
730
+ unless (
731
+ args.id != nil && ( args.range || args.targets )
732
+ )
733
+ raise 'Please supply the site id and ip range, or targets to scan, see --help'
734
+ end
735
+
736
+ # CLI args for scan task attempts and sleep interval
737
+ if args.attempts
738
+ max_scan_task_attempts = args.attempts.to_i
739
+ end
740
+ if args.loop_sleep
741
+ scan_task_sleep = args.loop_sleep.to_i
742
+ end
743
+
744
+ # target arrays
745
+ targets_sha2 = false
746
+ target_cursor = 0
747
+ targets = []
748
+ target_ranges = []
749
+
750
+ # read all targets into an array, from cli arg or file
751
+ if args.targets
752
+ # expecting fields: target,...
753
+ uputs("SCAN", "Targets provided within: #{args.targets} file")
754
+ # look for .<sha2 digest> of targets state file
755
+ targets_sha2 = Digest::SHA2.file(args.targets).hexdigest
756
+ if File.file?("." + targets_sha2)
757
+ # read the state file
758
+ target_cursor = File.open("." + targets_sha2).first.to_i
759
+ uputs("SCAN", "A state file was found for #{args.targets}, the last completed task [#{target_cursor.to_s}]")
760
+ end
761
+
762
+ targets = CSV.read(args.targets, :headers=>true)
763
+ targets.each do |target|
764
+ target_ranges.push(target['target'].to_s)
765
+ end
766
+ elsif args.range
767
+ targeth = { "type" => "cli_range", "subnetid" => "0", "target" => args.range.to_s }
768
+ targets.push(targeth)
769
+ target_ranges.push(args.range.to_s)
770
+ else
771
+ STDERR.puts "No targets defined, see --help"
772
+ exit(-1)
773
+ end
774
+
775
+ ## BEGIN targets loop
776
+ num_ranges = target_ranges.length
777
+ range_ctr = 0
778
+ target_ranges.each do |target_range|
779
+ range_ctr += 1
780
+ if range_ctr <= target_cursor
781
+ puts "Skipping scan task #{range_ctr}, a state file existed.\n"
782
+ next
783
+ end
784
+ target_complete = false
785
+ uputs("SCAN:#{range_ctr}", "Preparing SiteDevicesScan Type:#{targets[range_ctr - 1]['type']} Subnet Id:#{targets[range_ctr - 1]['subnetid']} Target:#{targets[range_ctr - 1]['target']}")
786
+ uputs("SCAN:#{range_ctr}", "Target details Type:#{targets[range_ctr - 1]['type']} Subnet Id:#{targets[range_ctr - 1]['subnetid']} Resource Id:#{targets[range_ctr - 1]['resourceid']} Description:#{targets[range_ctr - 1]['description']}")
787
+
788
+ # Make a connection and login to the NSC server
789
+ uputs("CONNECT", "Attempting connection to NSC: #{@nsc_server.to_s} with user: #{@nsc_user.to_s}")
790
+ begin
791
+ @nsc = Nexpose::Connection.new( @nsc_server.to_s, @nsc_user.to_s, @nsc_passwd.to_s)
792
+ @nsc.login
793
+ rescue SystemCallError => e
794
+ # EJG add conditional api error handling and consider wrapping coonect/login into a method...
795
+ uputs("LOGIN", "An error occurred while attempting to connect to the specified server: #{e.to_s}")
796
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
797
+ exit(-1)
798
+ end
799
+
800
+ if @nsc.session_id
801
+ uputs("SCAN CONNECT", 'NSC Login Successful')
802
+ else
803
+ uputs("SCAN CONNECT", 'NSC Login Failed')
804
+ exit
805
+ end
806
+
807
+ # grab the site id
808
+ scan_site_id = args.id.to_i
809
+ if scan_site_id == 0 && targets[range_ctr - 1].has_key?("site")
810
+ scan_site_id = @nsc_sites[targets[range_ctr - 1]['site']]
811
+ uputs("SCAN:#{range_ctr}", "Scan Site Id from conf and targets file is being used: #{scan_site_id}")
812
+ end
813
+
814
+ site_engine_id = 0
815
+ scan_hosts = Array.new
816
+ site = ""
817
+
818
+ # Obtain the Site details based on the provided scan site id
819
+ begin
820
+ site = Nexpose::Site.load(@nsc, scan_site_id)
821
+ site_engine_id = site.engine_id
822
+ rescue Nexpose::APIError => e
823
+ # EJG add conditional api error handling...
824
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
825
+ exit(-1)
826
+ end
827
+
828
+ # check if pool or engine... add
829
+ is_pool = true
830
+ uf_scanners = @nsc.engines
831
+ if uf_scanners.collect{|scanner| scanner.id}.include?(site_engine_id)
832
+ is_pool = false
833
+ end
834
+
835
+ # Obtain the Scan Engine Pool definition for the provided scan_site_id
836
+ pool_name = ""
837
+ pool_id = 0
838
+ if is_pool
839
+ pool_listing = @nsc.list_engine_pools
840
+ if pool_listing.length > 0
841
+ upp pool_listing
842
+ pool_listing.each do |pool|
843
+ if pool.id.to_s.include?(site_engine_id.to_s)
844
+ pool_id = pool.id
845
+ pool_name = pool.name
846
+ end
847
+ end
848
+ else
849
+ STDERR.puts "No Scan Engine Pools Defined, the id maybe a Scan Engine"
850
+ exit(-1)
851
+ end
852
+ end
853
+
854
+ if is_pool
855
+ pool = Nexpose::EnginePool.load(@nsc, pool_name)
856
+ pool_size = 0
857
+ pool_size = pool.engines.length
858
+ else
859
+ pool_size = 1
860
+ end
861
+ upp pool_size
862
+
863
+ ## BEGIN Single Scan task
864
+ scan_task = true
865
+ scan_task_retries = 1
866
+ new_scan = ""
867
+
868
+ # In preparation for an iterative scan task attempt, close the nsc session
869
+ logout_success = @nsc.logout
870
+
871
+ while scan_task && ( scan_task_retries <= max_scan_task_attempts ) do
872
+ puts "- entering while scan_task loop"
873
+
874
+ # Make a connection and login to the NSC server
875
+ uputs("CONNECT", "Attempting connection to NSC: #{@nsc_server.to_s} with user: #{@nsc_user.to_s}")
876
+ begin
877
+ @nsc = Nexpose::Connection.new( @nsc_server.to_s, @nsc_user.to_s, @nsc_passwd.to_s)
878
+ @nsc.login
879
+ rescue SystemCallError => e
880
+ # EJG add conditional api error handling and consider wrapping coonect/login into a method...
881
+ uputs("LOGIN", "An error occurred while attempting to connect to the specified server: #{e.to_s}")
882
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
883
+ exit(-1)
884
+ end
885
+
886
+ if @nsc.session_id
887
+ uputs("SCAN CONNECT2", 'NSC Login Successful')
888
+ else
889
+ uputs("SCAN CONNECT2", 'NSC Login Failed')
890
+ exit
891
+ end
892
+
893
+ # debug
894
+ puts Time.now.strftime("%Y%m%d_%H%M%S") + " Attempt: #{scan_task_retries} for target range: #{range_ctr} of: #{num_ranges}\n"
895
+ # Now check the current scan activity to determine if availability is good for the provided site
896
+ scan_activity = @nsc.scan_activity
897
+ upp scan_activity
898
+ scans_running = scan_activity.length
899
+ site_scans_running = 0
900
+ site_scan_ids = []
901
+ scan_activity.each do |scan_summary|
902
+ act_str = "Scan Id: #{scan_summary.scan_id.to_s.ljust(5)} site: #{scan_summary.site_id.to_s.ljust(4)} status: [#{scan_summary.status.to_s.ljust(10)}]" +
903
+ " engine: #{scan_summary.engine_id.to_s.ljust(3)} start time: #{scan_summary.start_time}"
904
+ uputs("SCAN_ACT:#{range_ctr}", act_str)
905
+ if scan_summary.site_id == scan_site_id && scan_summary.status.include?("running")
906
+ site_scan_ids.push(scan_summary.scan_id)
907
+ site_scans_running += 1
908
+ end
909
+ end
910
+
911
+ uputs("SCAN:#{range_ctr}", "Current Activity- Site:#{scan_site_id} isPool:#{is_pool.to_s} Pool:#{pool_id},#{pool_name} Pool Size:#{pool_size} Running Scans:#{scans_running} Site Scans:#{site_scans_running}")
912
+ nsc_err_flag = false
913
+ if site_scans_running < pool_size
914
+ if valid_ipv4(target_range)
915
+ valid_range = ip2rangev4(target_range)
916
+ elsif valid_cidrv4(target_range)
917
+ valid_range = cidr2rangev4(target_range)
918
+ elsif valid_ipv4_dottedcsv(target_range)
919
+ # add dotted csv validation for low,high order
920
+ valid_range = target_range
921
+ elsif valid_ipv4_dotteddashed(target_range)
922
+ valid_range = dotteddashed2rangev4(target_range)
923
+ elsif valid_ipv4_dotteddashedshort(target_range)
924
+ valid_range = dotteddashedshort2rangev4(target_range)
925
+ else
926
+ uputs("SCAN:#{range_ctr}", "API ERROR: An invalid target range was provided: #{target_range}")
927
+ next
928
+ # ubail(-1, "An invalid target range was provided: #{target_range}")
929
+ end
930
+
931
+ uputs("SCAN:#{range_ctr}", "Scan requested type/subnet id:#{targets[range_ctr - 1]['type']}/#{targets[range_ctr - 1]['subnetid']} target range: #{target_range}")
932
+ uputs("SCAN:#{range_ctr}", "Scan requested type/subnet id:#{targets[range_ctr - 1]['type']}/#{targets[range_ctr - 1]['subnetid']} validated target range: #{valid_range}")
933
+
934
+ # scan_hosts.push( { :range => valid_range.to_s } )
935
+ scan_hosts.push( Nexpose::IPRange.new(valid_range.split(',').first.to_s, valid_range.split(',').last.to_s) )
936
+ begin
937
+ # using nexpose gem 0.5.4 - EJG, and new changes
938
+ new_scan = @nsc.scan_assets(scan_site_id, scan_hosts)
939
+ rescue Nexpose::APIError => e
940
+ STDERR.puts e
941
+ uputs("SCAN:#{range_ctr}", "API ERROR: [#{e}]")
942
+ nsc_err_flag = true
943
+ scan_task_retries += 1
944
+ uputs("SCAN:#{range_ctr}", "...sleeping due to a recoverable error... for scan task: #{range_ctr} [#{target_range}] next attempt: #{scan_task_retries} in #{scan_task_sleep} secs")
945
+ sleep(scan_task_sleep)
946
+ end
947
+
948
+ if !nsc_err_flag
949
+ uputs("SCAN:#{range_ctr}", "Scanning target range: #{range_ctr} of: #{num_ranges}")
950
+ scan_task = false
951
+ target_complete = true
952
+ nsc_err_flag = false
953
+ end
954
+ else
955
+ puts "- entering else condition for no scanners"
956
+ logout_success = @nsc.logout
957
+ # no scan resources available, sleep and wait until retires/timeout exhausted
958
+ if scan_task_retries >= max_scan_task_attempts
959
+ puts "No scan resources available\n"
960
+ exit(-1)
961
+ end
962
+ scan_task_retries += 1
963
+ uputs("SCAN:#{range_ctr}", "...sleeping... for scan task: #{range_ctr} [#{target_range}] next attempt: #{scan_task_retries} in #{scan_task_sleep} secs")
964
+ sleep(scan_task_sleep)
965
+ end
966
+ end
967
+ # END of Single Range Scan task
968
+
969
+ if target_complete
970
+ if targets_sha2
971
+ File.open("." + targets_sha2, 'w') {|f| f.write(range_ctr) }
972
+ end
973
+
974
+ # Make a connection and login to the NSC server
975
+ uputs("CONNECT", "Attempting connection to NSC: #{@nsc_server.to_s} with user: #{@nsc_user.to_s}")
976
+ begin
977
+ @nsc = Nexpose::Connection.new( @nsc_server.to_s, @nsc_user.to_s, @nsc_passwd.to_s)
978
+ @nsc.login
979
+ rescue SystemCallError => e
980
+ # EJG add conditional api error handling and consider wrapping coonect/login into a method...
981
+ uputs("LOGIN", "An error occurred while attempting to connect to the specified server: #{e.to_s}")
982
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
983
+ exit(-1)
984
+ end
985
+
986
+ if @nsc.session_id
987
+ uputs("ACT CONNECT", 'NSC Login Successful')
988
+ else
989
+ uputs("ACT CONNECT", 'NSC Login Failed')
990
+ exit
991
+ end
992
+
993
+ if new_scan.kind_of? Nexpose::Scan
994
+ uputs("SCAN:#{range_ctr}", "The scan id: #{new_scan.id.to_s} for Subnet Id:#{targets[range_ctr - 1]['subnetid']} and the target range: #{target_range.to_s} launched after #{scan_task_retries} attempt(s) on engine id: #{new_scan.engine.to_s}")
995
+ else
996
+ uputs("SCAN:#{range_ctr}", "The new scan for Subnet Id:#{targets[range_ctr - 1]['subnetid']} and the target range: #{target_range.to_s} encountered an error.")
997
+ puts "The new scan encountered an error. Unable to report on the scan id.\n"
998
+ end
999
+ end
1000
+ puts "End of a single scan task\n"
1001
+
1002
+ end
1003
+ logout_success = @nsc.logout
1004
+ # END of Target Ranges loop
1005
+ if targets_sha2
1006
+ File.open("." + targets_sha2, 'w') {|f| f.write("COMPLETED") }
1007
+ end
1008
+ when 2 # list
1009
+ uputs("SCAN", 'list SCAN action requested')
1010
+ scan_activity = scan_activity()
1011
+ upp scan_activity
1012
+ if scan_activity.length > 0
1013
+ # EJG adding timeDiff conditional action
1014
+ scan_activity.each do |scan_ids, scans|
1015
+ case args.action
1016
+ when "exceeds", "max"
1017
+ if args.filter && args.filter.include?("max_secs")
1018
+ max_secs = args.filter.split(':').last.to_i
1019
+ if scans[:timeDiff].to_i >= max_secs
1020
+ # puts scans[:timeDiff].to_s + "\n"
1021
+ puts scan_ids.to_s + "\n"
1022
+ upp(scans)
1023
+ end
1024
+ end
1025
+ else
1026
+ puts scans[:act_str].to_s + "\n"
1027
+ end
1028
+ end
1029
+ else
1030
+ puts "There are no scans in queue.\n"
1031
+ end
1032
+ when 4 # show
1033
+ uputs("SCAN", 'show SCAN action requested')
1034
+ if is_numeric(args.id)
1035
+ #scanSummary = Nexpose::DataTable._get_dyn_table(@nsc, "/ajax/scan_statistics.txml?scanid=#{args.id}")
1036
+ #scanDets = Hash.new
1037
+ #scanRuntime = DateTime.now.strftime('%s').to_i - (scanSummary[0]['Started'].to_i / 1000).to_i
1038
+ #scanDets = { "Scan Id" => args.id, "Pending Scans" => scanSummary[0]['Pending Scans'], "Active Scans" => scanSummary[0]['Currently Scanning'], "Scan Run Secs" => scanRuntime }
1039
+ #puts scanDets.to_s + "\n"
1040
+
1041
+ scanSummary = @nsc.scan_statistics(args.id)
1042
+ upp scanSummary
1043
+ else
1044
+ puts 'Not yet implemented'
1045
+ end
1046
+ when 8 # update
1047
+ # EJG
1048
+ uputs("SCAN", 'update SCAN action requested')
1049
+ if args.action
1050
+ case args.action
1051
+ when "stop", "halt"
1052
+ if is_numeric(args.id)
1053
+ uputs("SCAN", "stop SCAN action requested for scan id: #{args.id}")
1054
+ @nsc.stop_scan(args.id, 60)
1055
+ end
1056
+ when "export"
1057
+ if is_numeric(args.id)
1058
+ uputs("SCAN", "export SCAN action requested for scan id: #{args.id}")
1059
+ @nsc.export_scan(args.id, "#{@scanpath.to_s}/scan-#{args.id.to_s}.zip")
1060
+ else
1061
+ uputs("SCAN", "export SCAN action failed for scan id: #{args.id}, expecting scan id")
1062
+ end
1063
+ when "import"
1064
+ if is_numeric(args.id)
1065
+ uputs("SCAN", "import SCAN action requested for site id: #{args.id} and file: #{args.filterv}")
1066
+ @nsc.import_scan(args.id.to_i, @scanpath.to_s + '/' + args.filterv.to_s)
1067
+ # Poll until scan is complete before attempting to import the next scan.
1068
+ upp @nsc.site_scan_history(args.id.to_i)
1069
+ last_scan = @nsc.site_scan_history(args.id.to_i).max_by { |s| s.start_time }.scan_id
1070
+ while (@nsc.scan_status(last_scan) == 'running')
1071
+ sleep 10
1072
+ end
1073
+ uputs("SCAN", "import SCAN integration completed for site id: #{args.id} and file: #{args.filterv}")
1074
+ else
1075
+ uputs("SCAN", "import SCAN action failed for site id: #{args.id}, expecting site id")
1076
+ end
1077
+ else
1078
+ puts 'The action requested is not implemented for target'
1079
+ end
1080
+ else
1081
+ puts 'Not yet implemented'
1082
+ end
1083
+ when 16 # delete
1084
+ uputs("ACTION", 'delete SCAN action requested')
1085
+ puts 'Not yet implemented, and not likely to be implemented'
1086
+ else
1087
+ uputs("ACTION", 'The action requested is not implemented for target')
1088
+ puts 'The action requested is not implemented for target'
1089
+ end
1090
+ when 16 # TARGET SITE
1091
+ case @action
1092
+ when 1 # create
1093
+ uputs("ACTION", 'create SITE action requested')
1094
+ puts 'Not yet implemented, and not likely to be implemented'
1095
+ when 2 # list
1096
+ uputs("ACTION", 'list SITE action requested')
1097
+ site_listing = @nsc.list_sites
1098
+ site_listing.each do |site|
1099
+ puts("Site: id:#{site.id}\t name:'#{site.name}'\t--description:#{site.description}\n")
1100
+ end
1101
+ upp site_listing
1102
+ when 4 # show
1103
+ uputs("ACTION", 'show SITE action requested')
1104
+ site = ""
1105
+ if (args.id != nil)
1106
+ scan_site_id = args.id.to_i
1107
+ end
1108
+ begin
1109
+ site = Nexpose::Site.load(@nsc, scan_site_id)
1110
+ rescue Nexpose::APIError => e
1111
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
1112
+ exit(-1)
1113
+ end
1114
+ puts("Site: id:#{site.id}\t name:'#{site.name}'\tconfig version:#{site.config_version}\n--description:#{site.description}\n")
1115
+ puts("--scan engine: #{site.engine}\tscan_template: '#{site.scan_template_name}'\n")
1116
+ puts("--assets:")
1117
+ site.assets.each do |iprange|
1118
+ puts(" IPRange from: #{iprange.from} - #{iprange.to}\n")
1119
+ end
1120
+ puts("--exclude:")
1121
+ site.exclude.each do |iprange|
1122
+ puts(" IPRange from: #{iprange.from} - #{iprange.to}\n")
1123
+ end
1124
+ upp site
1125
+ when 8 # update
1126
+ uputs("ACTION", 'update SITE action requested')
1127
+ site = ""
1128
+ if (args.id != nil)
1129
+ scan_site_id = args.id.to_i
1130
+ end
1131
+ begin
1132
+ site = Nexpose::Site.load(@nsc, scan_site_id)
1133
+ rescue Nexpose::APIError => e
1134
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
1135
+ exit(-1)
1136
+ end
1137
+
1138
+ case args.filter
1139
+ when "exclude", "EXCLUDE"
1140
+ if args.action == "replace"
1141
+ site.exclude = []
1142
+ end
1143
+ # parse --filterv for IPRange to replace/add
1144
+ if valid_ipv4_dottedcsv(args.filterv)
1145
+ valid_xrange = args.filterv
1146
+ else
1147
+ puts("ERROR Expecting IPRange in X.X.X.X,Y.Y.Y.Y format: from,to\n")
1148
+ exit(-1)
1149
+ end
1150
+ site.exclude.push(Nexpose::IPRange.new(valid_xrange.split(',').first.to_s, valid_xrange.split(',').last.to_s))
1151
+ site.save(@nsc)
1152
+ else
1153
+ puts 'Not yet implemented'
1154
+ end
1155
+ when 16 # delete
1156
+ uputs("ACTION", 'delete SITE action requested')
1157
+ puts 'Not yet implemented'
1158
+ else
1159
+ uputs("ACTION", 'The action requested is not implemented for target')
1160
+ puts 'The action requested is not implemented for target'
1161
+ end
1162
+ when 32 # TARGET ASSET
1163
+ case @action
1164
+ when 1 # create
1165
+ uputs("ACTION", 'create ASSET action requested')
1166
+ puts 'Not yet implemented'
1167
+ when 2 # list
1168
+ uputs("ACTION", 'list ASSET action requested')
1169
+ assetgroups = @nsc.asset_groups
1170
+ uputs("ACTION", "There are #{assetgroups.size} asset groups.")
1171
+ assetgroups.each do |ags|
1172
+ puts "id: #{ags.id.to_s.ljust(10)} name: #{ags.name}"
1173
+ end
1174
+ upp assetgroups
1175
+ when 4 # show
1176
+ uputs("ACTION", 'show ASSET action requested')
1177
+ if is_numeric(args.id)
1178
+ ag = Nexpose::AssetGroup.load(@nsc, args.id)
1179
+ puts "There are #{ag.assets.size} assets in asset group id: #{args.id} name: \"#{ag.name}\".\n"
1180
+ ag.assets.each do |asset|
1181
+ puts "id: #{asset.id.to_s.ljust(10)} address: #{asset.address}"
1182
+ end
1183
+ else
1184
+ puts 'The Asset Group Id must be numeric.'
1185
+ end
1186
+ upp(ag)
1187
+ when 8 # update
1188
+ uputs("ACTION", 'update ASSET action requested')
1189
+ puts 'Not yet implemented'
1190
+ when 16 # delete
1191
+ uputs("ACTION", 'delete ASSET action requested')
1192
+ puts 'Not yet implemented'
1193
+ else
1194
+ uputs("ACTION", 'The action requested is not implemented for target')
1195
+ puts 'The action requested is not implemented for target'
1196
+ end
1197
+ when 64 # TARGET REPORT
1198
+ case @action
1199
+ when 1 # create
1200
+ uputs("ACTION", 'create REPORT action requested')
1201
+
1202
+ if (args.id != nil)
1203
+ report_config = Nexpose::ReportConfig.load(@nsc, args.id)
1204
+ report_config.filters.delete_if do |filter|
1205
+ if filter.type == "device"
1206
+ upp filter
1207
+ true
1208
+ end
1209
+ end
1210
+ uputs("DEBUG", 'report_config.filters post device filter deletion')
1211
+ upp(report_config.filters)
1212
+
1213
+ if (args.host != nil)
1214
+ # EJG pass site id through...
1215
+ device = @nsc.find_device_by_address( args.host, args.site)
1216
+ if (device != nil)
1217
+ report_config.id = -1
1218
+ scan_asset_device_id = device.id.to_i
1219
+ report_config.name = "Asset: " + args.host.to_s + " (" + Time.now.strftime("%Y%m%d%H%M%S") + ")"
1220
+ report_config.add_filter('device', scan_asset_device_id)
1221
+ report_config.save(@nsc)
1222
+ else
1223
+ puts "Device Id is nil. No match found for ip: " + args.host.to_s + "\n"
1224
+ end
1225
+ else
1226
+ report_config.name = "Default Report Name (" + Time.now.strftime("%Y%m%d%H%M%S") + ")"
1227
+ end
1228
+ puts "- Running the report now...\n"
1229
+ report_run = report_config.generate(@nsc)
1230
+
1231
+ # check for report run status
1232
+ report_summary = @nsc.last_report(report_config.id)
1233
+ while report_summary.status != "Generated"
1234
+ puts "- Sleeping... " + report_summary.status.to_s + "\n"
1235
+ sleep(2)
1236
+ report_summary = @nsc.last_report(report_config.id)
1237
+ end
1238
+ # pp report_summary
1239
+
1240
+ puts "---\n- Report Id: " + report_config.id.to_s + " \n"
1241
+ puts "---\n- The report can be found via:\n https://#{@nsc_server}:3780" + report_summary.uri.to_s + "\n"
1242
+
1243
+ # download("https://#{@nsc_server}:3780" + report_summary.uri.to_s, "./tmp/RemediationPlan-" + Time.now.strftime("%Y%m%d_%H%M%S") + ".pdf", nsc)
1244
+ download("https://#{@nsc_server}:3780" + report_summary.uri.to_s, "./tmp/doc.pdf", @nsc)
1245
+ end
1246
+
1247
+ when 2 # list
1248
+ uputs("ACTION", 'list REPORT action requested')
1249
+ report_listing = @nsc.list_reports
1250
+ report_listing.each do |report|
1251
+ puts("Report id:#{report.config_id}\ttemplate:#{report.template_id}\tname:#{report.name}\n")
1252
+ upp report
1253
+ end
1254
+ when 4 # show
1255
+ uputs("ACTION", 'show REPORT action requested')
1256
+ report = @nsc.last_report(args.id)
1257
+ upp report
1258
+ when 8 # update
1259
+ uputs("ACTION", 'update REPORT action requested')
1260
+ puts 'Not yet implemented'
1261
+ when 16 # delete
1262
+ uputs("ACTION", 'delete REPORT action requested')
1263
+ puts 'Not yet implemented'
1264
+ else
1265
+ uputs("ACTION", 'The action requested is not implemented for target')
1266
+ puts 'The action requested is not implemented for target'
1267
+ end
1268
+ when 128 # TARGET VULN
1269
+ case @action
1270
+ when 1 # create
1271
+ uputs("ACTION", 'create VULN action requested')
1272
+ puts 'Not yet implemented, and not likely to be implemented'
1273
+ when 2 # list
1274
+ uputs("ACTION", 'list VULN action requested')
1275
+ vuln_list = @nsc.all_vulns
1276
+ upp vuln_list
1277
+ when 4 # show
1278
+ uputs("ACTION", 'show VULN action requested')
1279
+ puts 'Not yet implemented'
1280
+ when 8 # update
1281
+ uputs("ACTION", 'update VULN action requested')
1282
+ puts 'Not yet implemented, and not likely to be implemented'
1283
+ when 16 # delete
1284
+ uputs("ACTION", 'delete VULN action requested')
1285
+ puts 'Not yet implemented'
1286
+ else
1287
+ uputs("ACTION", 'The action requested is not implemented for target')
1288
+ puts 'The action requested is not implemented for target'
1289
+ end
1290
+ when 256 # TARGET COMMAND
1291
+ case @action
1292
+ when 32 # run
1293
+ uputs("ACTION", 'run COMMAND action requested')
1294
+ puts "\nRunning [#{args.COMMAND}]\n\n"
1295
+ postd = @nsc.console_command(args.COMMAND)
1296
+ puts postd.to_s
1297
+ puts "\n"
1298
+ else
1299
+ uputs("ACTION", 'The action requested is not implemented for target: COMMAND')
1300
+ puts 'The action requested is not implemented for target: COMMAND'
1301
+ end
1302
+ when 512 # TARGET DASSET
1303
+ case @action
1304
+ when 1, 8 # create and update
1305
+ valid_filterv = nil
1306
+ base_field_str = "Nexpose::Search::Field::"
1307
+ base_op_str = "Nexpose::Search::Operator::"
1308
+
1309
+ # For now, only support default IP_RANGE defined dynamic asset groups
1310
+ field_str = "IP_RANGE"
1311
+ op_str = "IN"
1312
+ asset_crit = nil
1313
+
1314
+ # Insert loop for multi/single dyanmic asset group creation
1315
+ # EJG
1316
+ dags_sha2 = false
1317
+ dags_cursor = 0
1318
+ dags = []
1319
+ dag_filtervs = []
1320
+
1321
+ # read all dags into an array, from cli arg or file
1322
+ if args.targets
1323
+ # expecting fields: action,filter,filterv,name,description
1324
+ uputs("DASSET", "Dynamic Asset Groups provided within: #{args.targets} file")
1325
+ end
1326
+
1327
+ # if batch processing, grab @action value from field 1 in the targets file record
1328
+ valid_str = ""
1329
+ if args.filter
1330
+ valid_str = validate_searchstring(args.filter)
1331
+ end
1332
+ isValid = false
1333
+ if valid_str != "ERROR"
1334
+ isValid = true
1335
+ field_str = valid_str.split(',').first.to_s
1336
+ op_str = valid_str.split(',').last.to_s
1337
+ else
1338
+ end
1339
+
1340
+ if args.filterv
1341
+ # now test filterv based on filter_str and op_str
1342
+ if field_str == "IP_RANGE"
1343
+ valid_range = '127.0.0.1:127.0.0.1'
1344
+ if valid_ipv4(args.filterv)
1345
+ valid_range = ip2rangev4(args.filterv)
1346
+ elsif valid_cidrv4(args.filterv)
1347
+ valid_range = cidr2rangev4(args.filterv)
1348
+ elsif valid_ipv4_dottedcsv(args.filterv)
1349
+ # add dotted csv validation for low,high order
1350
+ valid_range = args.filterv
1351
+ elsif valid_ipv4_dottedcolon(args.filterv)
1352
+ # add dotted colon validation for low,high order
1353
+ valid_range = args.filterv
1354
+ elsif valid_ipv4_dotteddashed(args.filterv)
1355
+ valid_range = dotteddashed2rangev4(args.filterv)
1356
+ elsif valid_ipv4_dotteddashedshort(args.filterv)
1357
+ valid_range = dotteddashedshort2rangev4(args.filterv)
1358
+ else
1359
+ uputs("DASSET", "API ERROR: An invalid asset group range was provided: #{args.filterv.to_s}")
1360
+ end
1361
+ # to allow target file csv format, expect : delimited range or replace "," with ":" here.
1362
+ vrange = valid_range.dup
1363
+ vrange.tr!(",",":")
1364
+
1365
+ valid_filterv = [vrange.split(':').first.to_s, vrange.split(':').last.to_s]
1366
+ elsif field_str == "CVSS_SCORE"
1367
+ # test for float, and possibly IN_RANGE op
1368
+ valid_filterv = [7.0]
1369
+ elsif field_str == "RISK_SCORE" || field_str == "SITE_ID"
1370
+ # test for fixnum, and possibly IN_RANGE op
1371
+ valid_filterv = [args.filterv.to_i]
1372
+ elsif field_str == "OS"
1373
+ # test for str
1374
+ valid_filterv = [args.filterv.to_s]
1375
+ else
1376
+ # assume fixnum, with exception of IN_RANGE op
1377
+ valid_filterv = ["ERROR"]
1378
+ end
1379
+ uputs("DASSET", "The filterv value passed: #{args.filterv} vs the validated value: #{valid_filterv.to_s}")
1380
+ else
1381
+ end
1382
+
1383
+ if isValid
1384
+ asset_crit = Nexpose::Criterion.new(Object.const_get(base_field_str + field_str), Object.const_get(base_op_str + op_str), valid_filterv)
1385
+
1386
+ # DynamicAsset Group Name and Description, consider adding type validation and MAX length
1387
+ dag_name = 'api.test'
1388
+ if args.name
1389
+ dag_name = args.name.to_s
1390
+ end
1391
+ dag_descr = 'This DynamicAssetGroup has been defined programmatically via API and automation script.'
1392
+ if args.description
1393
+ dag_descr = args.description.to_s
1394
+ end
1395
+
1396
+ # new or load, based on create vs update
1397
+ if @action == 1
1398
+ uputs("ACTION", 'create DASSET action requested')
1399
+ criteria = Nexpose::Criteria.new(asset_crit)
1400
+ dag = Nexpose::DynamicAssetGroup.new(dag_name, criteria, dag_descr)
1401
+ else
1402
+ uputs("ACTION", 'update DASSET action requested')
1403
+ dag = Nexpose::DynamicAssetGroup.load(@nsc, args.id.to_i)
1404
+ dag.criteria.criteria << asset_crit
1405
+ end
1406
+ uputs("DASSET", "Attempting save the DynamicAssetGroup: #{@dag_name.to_s}")
1407
+ begin
1408
+ if dag.save(@nsc)
1409
+ uputs("DASSET", "Testing")
1410
+ upp dag
1411
+ uputs("DASSET", "Testing - End")
1412
+ puts "Dynamic Asset Group was successfully created/updated - id: #{dag.id.to_s.ljust(10)} name: \"#{dag.name.to_s}\""
1413
+ else
1414
+ puts "Dynamic Asset Group create/update failed - name: \"#{dag.name.to_s}\""
1415
+ end
1416
+ rescue Nexpose::APIError => e
1417
+ uputs("DASSET", "An error occurred while attempting to save the DynamicAssetGroup: #{e.to_s}")
1418
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
1419
+ end
1420
+ upp dag
1421
+
1422
+ # End of DAG list loop
1423
+ else
1424
+ puts "ERROR: DynamicAssetGroup filter: #{args.filter} is invalid or unsupported."
1425
+ end
1426
+ when 2 # list
1427
+ uputs("ACTION", 'list DASSET action requested')
1428
+ assetgroups = @nsc.asset_groups
1429
+ uputs("ACTION", "There are #{assetgroups.size} asset groups.")
1430
+ assetgroups.each do |ags|
1431
+ if ags.dynamic
1432
+ agsnamepattern = ".*"
1433
+ if args.filter && args.filterv
1434
+ agsnamepattern = args.filterv
1435
+ end
1436
+ if /#{agsnamepattern}/.match(ags.name)
1437
+ puts "id: #{ags.id.to_s.ljust(10)} name: #{ags.name}"
1438
+ end
1439
+ end
1440
+ end
1441
+ upp assetgroups
1442
+ when 4 # show
1443
+ uputs("ACTION", 'show DASSET action requested')
1444
+ if is_numeric(args.id)
1445
+ dag = Nexpose::DynamicAssetGroup.load(@nsc, args.id)
1446
+ puts "Dynamic Asset Group id: #{dag.id.to_s.ljust(10)} name: \"#{dag.name.to_s}\""
1447
+ puts "- description: #{dag.description.to_s}"
1448
+ puts "- is defined by the following criteria:"
1449
+ dag.criteria.criteria.each do |criterion|
1450
+ puts "\t field: #{criterion.field.to_s.ljust(20)} operator: #{criterion.operator.to_s.ljust(15)} value: #{criterion.value}"
1451
+ end
1452
+ upp dag
1453
+ else
1454
+ puts 'The Dynamic Asset Group Id must be numeric.'
1455
+ end
1456
+ upp(dag)
1457
+ when 32 # original update
1458
+ uputs("ACTION", 'update DASSET action requested')
1459
+ if is_numeric(args.id)
1460
+ if args.action
1461
+ dag = Nexpose::DynamicAssetGroup.load(@nsc, args.id.to_i)
1462
+
1463
+ # actions = name, add criteria, match, flush and replace criteria, consider help to display valid search criteria
1464
+ # filter = Field, Operator, Value
1465
+ case args.action
1466
+ when "name"
1467
+ dag.name = args.filter.to_s
1468
+ when "append" # append new criteria
1469
+ field_str = "Nexpose::Search::Field::IP_RANGE"
1470
+ asset_crit = Nexpose::Criterion.new(Object.const_get(field_str), Nexpose::Search::Operator::IN, ["10.249.20.0", "10.249.20.255"])
1471
+ dag.criteria.criteria << asset_crit
1472
+ when "replace" # replace criteria
1473
+ dag.criteria.criteria.clear
1474
+ field_str = "Nexpose::Search::Field::OS"
1475
+ asset_crit = Nexpose::Criterion.new(Object.const_get(field_str), Nexpose::Search::Operator::CONTAINS, 'windows')
1476
+ dag.criteria.criteria << asset_crit
1477
+ else
1478
+ puts 'The action requested is not implemented for target'
1479
+ end
1480
+ else
1481
+ puts "No update action was specified."
1482
+ end
1483
+
1484
+ if dag.save(@nsc)
1485
+ puts "Dynamic Asset Group was successfully updated - id: #{dag.id.to_s.ljust(10)} name: \"#{dag.name.to_s}\""
1486
+ else
1487
+ puts "Dynamic Asset Group update failed - id: #{dag.id.to_s.ljust(10)} name: \"#{dag.name.to_s}\""
1488
+ end
1489
+ else
1490
+ puts 'The Dynamic Asset Group Id must be numeric.'
1491
+ end
1492
+ when 16 # delete
1493
+ # EJG
1494
+ uputs("ACTION", 'delete DASSET action requested')
1495
+ @nsc.delete_asset_group(args.id.to_i)
1496
+ puts "DASSET id #{args.id.to_s} has been deleted."
1497
+ else
1498
+ uputs("ACTION", 'The action requested is not implemented for target')
1499
+ puts 'The action requested is not implemented for target'
1500
+ end
1501
+ when 1024 # TARGET TAG
1502
+ case @action
1503
+ when 1, 8 # create and update
1504
+ valid_filterv = nil
1505
+ base_field_str = "Nexpose::Search::Field::"
1506
+ base_op_str = "Nexpose::Search::Operator::"
1507
+
1508
+ # For now, only support default IP_RANGE defined tags
1509
+ field_str = "IP_RANGE"
1510
+ op_str = "IN"
1511
+ asset_crit = nil
1512
+
1513
+ # Insert loop for multi/single tag creation
1514
+ # EJG
1515
+ tags_sha2 = false
1516
+ tags_cursor = 0
1517
+ tags = []
1518
+ tag_filtervs = []
1519
+
1520
+ # read all tags into an array, from cli arg or file
1521
+ if args.targets
1522
+ # expecting fields: action,filter,filterv,name,description
1523
+ uputs("TAG", "Tags provided within: #{args.targets} file")
1524
+ end
1525
+
1526
+ # if batch processing, grab @action value from field 1 in the targets file record
1527
+ valid_str = ""
1528
+ if args.filter
1529
+ valid_str = validate_searchstring(args.filter)
1530
+ end
1531
+ isValid = false
1532
+ if valid_str != "ERROR"
1533
+ isValid = true
1534
+ field_str = valid_str.split(',').first.to_s
1535
+ op_str = valid_str.split(',').last.to_s
1536
+ else
1537
+ end
1538
+
1539
+ if args.filterv
1540
+ # now test filterv based on filter_str and op_str
1541
+ if field_str == "IP_RANGE"
1542
+ valid_range = '127.0.0.1:127.0.0.1'
1543
+ if valid_ipv4(args.filterv)
1544
+ valid_range = ip2rangev4(args.filterv)
1545
+ elsif valid_cidrv4(args.filterv)
1546
+ valid_range = cidr2rangev4(args.filterv)
1547
+ elsif valid_ipv4_dottedcsv(args.filterv)
1548
+ # add dotted csv validation for low,high order
1549
+ valid_range = args.filterv
1550
+ elsif valid_ipv4_dottedcolon(args.filterv)
1551
+ # add dotted colon validation for low,high order
1552
+ valid_range = args.filterv
1553
+ elsif valid_ipv4_dotteddashed(args.filterv)
1554
+ valid_range = dotteddashed2rangev4(args.filterv)
1555
+ elsif valid_ipv4_dotteddashedshort(args.filterv)
1556
+ valid_range = dotteddashedshort2rangev4(args.filterv)
1557
+ else
1558
+ uputs("TAG", "API ERROR: An invalid tag range was provided: #{args.filterv.to_s}")
1559
+ end
1560
+ # to allow target file csv format, expect : delimited range or replace "," with ":" here.
1561
+ vrange = valid_range.dup
1562
+ vrange.tr!(",",":")
1563
+
1564
+ valid_filterv = [vrange.split(':').first.to_s, vrange.split(':').last.to_s]
1565
+ elsif field_str == "CVSS_SCORE"
1566
+ # test for float, and possibly IN_RANGE op
1567
+ valid_filterv = [7.0]
1568
+ elsif field_str == "RISK_SCORE" || field_str == "SITE_ID"
1569
+ # test for fixnum, and possibly IN_RANGE op
1570
+ valid_filterv = [args.filterv.to_i]
1571
+ elsif field_str == "OS"
1572
+ # test for str
1573
+ valid_filterv = [args.filterv.to_s]
1574
+ else
1575
+ # assume fixnum, with exception of IN_RANGE op
1576
+ valid_filterv = ["ERROR"]
1577
+ end
1578
+ uputs("TAG", "The filterv value passed: #{args.filterv} vs the validated value: #{valid_filterv.to_s}")
1579
+ else
1580
+ end
1581
+
1582
+ if isValid
1583
+ asset_crit = Nexpose::Tag::Criterion.new(Object.const_get(base_field_str + field_str), Object.const_get(base_op_str + op_str), valid_filterv)
1584
+
1585
+ # Tag Name and Description, consider adding type validation and MAX length
1586
+ tag_name = 'api.test'
1587
+ if args.name
1588
+ tag_name = args.name.to_s
1589
+ end
1590
+
1591
+ # new or load, based on create vs update
1592
+ if @action == 1
1593
+ uputs("ACTION", 'create TAG action requested')
1594
+ criteria = Nexpose::Tag::Criteria.new(asset_crit)
1595
+ tag = Nexpose::Tag.new(tag_name, Nexpose::Tag::Type::Generic::CUSTOM)
1596
+ tag.color = "#de7200"
1597
+ tag.search_criteria = criteria
1598
+ else
1599
+ uputs("ACTION", 'update TAG action requested')
1600
+ tag = Nexpose::Tag.load(@nsc, args.id.to_i)
1601
+ tag.search_criteria << asset_crit
1602
+ end
1603
+ uputs("TAG", "Attempting save the Tag: #{@tag_name.to_s}")
1604
+ begin
1605
+ if tag.save(@nsc)
1606
+ puts "Tag was successfully created/updated - id: #{tag.id.to_s.ljust(10)} name: \"#{tag.name.to_s}\""
1607
+ else
1608
+ puts "Tag create/update failed - name: \"#{tag.name.to_s}\""
1609
+ end
1610
+ rescue Nexpose::APIError => e
1611
+ uputs("TAG", "An error occurred while attempting to save the Tag: #{e.to_s}")
1612
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
1613
+ end
1614
+ upp tag
1615
+
1616
+ # End of TAG list loop
1617
+ else
1618
+ puts "ERROR: Tag filter: #{args.filter} is invalid or unsupported."
1619
+ end
1620
+ when 2 # list
1621
+ uputs("ACTION", 'list TAG action requested')
1622
+ tags = @nsc.tags
1623
+ uputs("ACTION", "There are #{tags.size} asset tags.")
1624
+ tagsnamepattern = ".*"
1625
+ if args.filter && args.filterv
1626
+ tagsnamepattern = args.filterv
1627
+ end
1628
+ tags.each do |tag|
1629
+ if /#{tagsnamepattern}/.match(tag.name)
1630
+ puts "id: #{tag.id.to_s.ljust(10)} name: #{tag.name}"
1631
+ end
1632
+ end
1633
+ upp tags
1634
+ when 4 # show
1635
+ if args.id
1636
+ tag = Nexpose::Tag.load(@nsc, args.id)
1637
+ upp tag
1638
+ end
1639
+ else
1640
+ uputs("ACTION", 'The action requested is not implemented for target')
1641
+ puts 'The action requested is not implemented for target'
1642
+ end
1643
+ when 2048 # TARGET CONSOLE
1644
+ case @action
1645
+ when 32 # run
1646
+ if args.action
1647
+ case args.action
1648
+ when "backup"
1649
+ backupdescr = 'Nexpose Console Backup via automation.'
1650
+ if args.description
1651
+ backupdescr = args.description.to_s
1652
+ end
1653
+ active_scans = @nsc.scan_activity
1654
+
1655
+ # attempt to start the backup
1656
+ if active_scans.empty?
1657
+ platform_independent = true
1658
+ @nsc.backup(platform_independent, backupdescr)
1659
+ puts 'The backup job requested has been submitted and will run shortly.'
1660
+ uputs("BACKUP", 'The backup requested is running')
1661
+ else
1662
+ puts 'There are active scans, please run the backup once the scans have completed.'
1663
+ uputs("BACKUP", 'The backup requested can not be run at this time, there are active scans')
1664
+ end
1665
+ else
1666
+ puts 'The action requested is not implemented for target'
1667
+ end
1668
+ else
1669
+ puts 'No CONSOLE action was passed'
1670
+ end
1671
+ else
1672
+ uputs("ACTION", 'The action requested is not implemented for target')
1673
+ puts 'The action requested is not implemented for target'
1674
+ end
1675
+ when 8192 # TARGET ROLE
1676
+ case @action
1677
+ when 1 # create, via copy for now
1678
+ rolename = args.name
1679
+ newrole = Nexpose::Role.copy(@nsc, rolename, 'global' )
1680
+ newrole.name = args.newname.to_str
1681
+ newrole.full_name = "#{args.description.to_str}"
1682
+ newrole.save(@nsc)
1683
+ upp newrole
1684
+ puts "\n"
1685
+ when 2 # list
1686
+ uputs("ACTION", 'list ROLE action requested')
1687
+ postd = @nsc.roles
1688
+ upp postd
1689
+ puts "\n"
1690
+ when 4 # show
1691
+ rolename = args.name
1692
+ role = Nexpose::Role.load(@nsc, rolename, 'global' )
1693
+ upp role
1694
+ puts "\n"
1695
+ when 16 # delete
1696
+ rolename = args.name
1697
+ role = Nexpose::Role.load(@nsc, rolename, 'global' )
1698
+ role.delete(@nsc)
1699
+ tryrole = Nexpose::Role.load(@nsc, rolename, 'global' )
1700
+ upp tryrole
1701
+ puts "\n"
1702
+ else
1703
+ uputs("ACTION", 'The action requested is not implemented for target: ROLE')
1704
+ puts 'The action requested is not implemented for target: ROLE'
1705
+ end
1706
+ else
1707
+ # there is no default target
1708
+ uputs("ACTION", 'No default action requested')
1709
+ raise 'No action target was provided, see --help'
1710
+ end
1711
+
1712
+ # logout and close the connection
1713
+ uputs("COMMAND", 'The task requested has been completed, closing the NSC connection')
1714
+ begin
1715
+ logout_success = @nsc.logout
1716
+ rescue Nexpose::APIError => e
1717
+ STDERR.puts "ERROR [ " + e.to_s + " ]"
1718
+ uputs("CONNECT", "The NSC connection has already been closed, goodbye")
1719
+ exit(0)
1720
+ end
1721
+ uputs("CONNECT", "The NSC connection is closed, goodbye")
1722
+ ##############################################################################
1723
+ # end