nexposecli 0.1.7

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/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