@@ -220,6 +220,11 @@ def cmd_hosts_help
220
220
end
221
221
222
222
def change_host_info ( rws , data )
223
+ if rws == [ nil ]
224
+ print_error ( "In order to change the host info, you must provide a range of hosts" )
225
+ return
226
+ end
227
+
223
228
rws . each do |rw |
224
229
rw . each do |ip |
225
230
id = framework . db . get_host ( :address => ip ) . id
@@ -230,6 +235,11 @@ def change_host_info(rws, data)
230
235
end
231
236
232
237
def change_host_name ( rws , data )
238
+ if rws == [ nil ]
239
+ print_error ( "In order to change the host name, you must provide a range of hosts" )
240
+ return
241
+ end
242
+
233
243
rws . each do |rw |
234
244
rw . each do |ip |
235
245
id = framework . db . get_host ( :address => ip ) . id
@@ -240,6 +250,11 @@ def change_host_name(rws, data)
240
250
end
241
251
242
252
def change_host_comment ( rws , data )
253
+ if rws == [ nil ]
254
+ print_error ( "In order to change the comment, you must provide a range of hosts" )
255
+ return
256
+ end
257
+
243
258
rws . each do |rw |
244
259
rw . each do |ip |
245
260
id = framework . db . get_host ( :address => ip ) . id
@@ -249,12 +264,59 @@ def change_host_comment(rws, data)
249
264
end
250
265
end
251
266
267
+ def add_host_tag ( rws , tag_name )
268
+ if rws == [ nil ]
269
+ print_error ( "In order to add a tag, you must provide a range of hosts" )
270
+ return
271
+ end
272
+
273
+ rws . each do |rw |
274
+ rw . each do |ip |
275
+ wspace = framework . db . workspace
276
+ host = framework . db . get_host ( :workspace => wspace , :address => ip )
277
+ if host
278
+ possible_tags = Mdm ::Tag . includes ( :hosts ) . where ( "hosts.workspace_id = ? and hosts.address = ? and tags.name = ?" , wspace . id , ip , tag_name ) . order ( "tags.id DESC" ) . limit ( 1 )
279
+ tag = ( possible_tags . blank? ? Mdm ::Tag . new : possible_tags . first )
280
+ tag . name = tag_name
281
+ tag . hosts = [ host ]
282
+ tag . save! if tag . changed?
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ def delete_host_tag ( rws , tag_name )
289
+ wspace = framework . db . workspace
290
+ tag_ids = [ ]
291
+ if rws == [ nil ]
292
+ found_tags = Mdm ::Tag . includes ( :hosts ) . where ( "hosts.workspace_id = ? and tags.name = ?" , wspace . id , tag_name )
293
+ found_tags . each do |t |
294
+ tag_ids << t . id
295
+ end
296
+ else
297
+ rws . each do |rw |
298
+ rw . each do |ip |
299
+ found_tags = Mdm ::Tag . includes ( :hosts ) . where ( "hosts.workspace_id = ? and hosts.address = ? and tags.name = ?" , wspace . id , ip , tag_name )
300
+ found_tags . each do |t |
301
+ tag_ids << t . id
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ tag_ids . each do |id |
308
+ tag = Mdm ::Tag . find_by_id ( id )
309
+ tag . hosts . delete
310
+ tag . destroy
311
+ end
312
+ end
313
+
252
314
def cmd_hosts ( *args )
253
315
return unless active?
254
316
::ActiveRecord ::Base . connection_pool . with_connection {
255
317
onlyup = false
256
318
set_rhosts = false
257
- mode = :search
319
+ mode = [ ]
258
320
delete_count = 0
259
321
260
322
rhosts = [ ]
@@ -263,17 +325,18 @@ def cmd_hosts(*args)
263
325
264
326
output = nil
265
327
default_columns = ::Mdm ::Host . column_names . sort
266
- virtual_columns = [ 'svcs' , 'vulns' , 'workspace' ]
328
+ default_columns << 'tags' # Special case
329
+ virtual_columns = [ 'svcs' , 'vulns' , 'workspace' , 'tags' ]
267
330
268
331
col_search = [ 'address' , 'mac' , 'name' , 'os_name' , 'os_flavor' , 'os_sp' , 'purpose' , 'info' , 'comments' ]
269
332
270
333
default_columns . delete_if { |v | ( v [ -2 , 2 ] == "id" ) }
271
334
while ( arg = args . shift )
272
335
case arg
273
336
when '-a' , '--add'
274
- mode = :add
337
+ mode << :add
275
338
when '-d' , '--delete'
276
- mode = :delete
339
+ mode << :delete
277
340
when '-c'
278
341
list = args . shift
279
342
if ( !list )
@@ -297,14 +360,17 @@ def cmd_hosts(*args)
297
360
when '-S' , '--search'
298
361
search_term = /#{ args . shift } /nmi
299
362
when '-i' , '--info'
300
- mode = :new_info
363
+ mode << :new_info
301
364
info_data = args . shift
302
365
when '-n' , '--name'
303
- mode = :new_name
366
+ mode << :new_name
304
367
name_data = args . shift
305
368
when '-m' , '--comment'
306
- mode = :new_comment
369
+ mode << :new_comment
307
370
comment_data = args . shift
371
+ when '-t' , '--tag'
372
+ mode << :tag
373
+ tag_name = args . shift
308
374
when '-h' , '--help'
309
375
print_line "Usage: hosts [ options ] [addr1 addr2 ...]"
310
376
print_line
@@ -320,6 +386,7 @@ def cmd_hosts(*args)
320
386
print_line " -i,--info Change the info of a host"
321
387
print_line " -n,--name Change the name of a host"
322
388
print_line " -m,--comment Change the comment of a host"
389
+ print_line " -t,--tag Add or specify a tag to a range of hosts"
323
390
print_line
324
391
print_line "Available columns: #{ default_columns . join ( ", " ) } "
325
392
print_line
@@ -338,7 +405,9 @@ def cmd_hosts(*args)
338
405
col_names = default_columns + virtual_columns
339
406
end
340
407
341
- if mode == :add
408
+ mode << :search if mode . empty?
409
+
410
+ if mode == [ :add ]
342
411
host_ranges . each do |range |
343
412
range . each do |address |
344
413
host = framework . db . find_or_create_host ( :host => address )
@@ -358,30 +427,53 @@ def cmd_hosts(*args)
358
427
# Sentinal value meaning all
359
428
host_ranges . push ( nil ) if host_ranges . empty?
360
429
361
- case mode
362
- when :new_info
430
+ case
431
+ when mode == [ :new_info ]
363
432
change_host_info ( host_ranges , info_data )
364
433
return
365
- when :new_name
434
+ when mode == [ :new_name ]
366
435
change_host_name ( host_ranges , name_data )
367
436
return
368
- when :new_comment
437
+ when mode == [ :new_comment ]
369
438
change_host_comment ( host_ranges , comment_data )
370
439
return
440
+ when mode == [ :tag ]
441
+ begin
442
+ add_host_tag ( host_ranges , tag_name )
443
+ rescue ::Exception => e
444
+ if e . message . include? ( 'Validation failed' )
445
+ print_error ( e . message )
446
+ else
447
+ raise e
448
+ end
449
+ end
450
+ return
451
+ when mode . include? ( :tag ) && mode . include? ( :delete )
452
+ delete_host_tag ( host_ranges , tag_name )
453
+ return
371
454
end
372
455
373
456
each_host_range_chunk ( host_ranges ) do |host_search |
374
457
framework . db . hosts ( framework . db . workspace , onlyup , host_search ) . each do |host |
375
458
if search_term
376
- next unless host . attribute_names . any? { |a | host [ a . intern ] . to_s . match ( search_term ) }
459
+ next unless (
460
+ host . attribute_names . any? { |a | host [ a . intern ] . to_s . match ( search_term ) } ||
461
+ !Mdm ::Tag . includes ( :hosts ) . where ( "hosts.workspace_id = ? and hosts.address = ? and tags.name = ?" , framework . db . workspace . id , host . address , search_term . source ) . order ( "tags.id DESC" ) . empty?
462
+ )
377
463
end
464
+
378
465
columns = col_names . map do |n |
379
466
# Deal with the special cases
380
467
if virtual_columns . include? ( n )
381
468
case n
382
469
when "svcs" ; host . services . length
383
470
when "vulns" ; host . vulns . length
384
471
when "workspace" ; host . workspace . name
472
+ when "tags"
473
+ found_tags = Mdm ::Tag . includes ( :hosts ) . where ( "hosts.workspace_id = ? and hosts.address = ?" , framework . db . workspace . id , host . address ) . order ( "tags.id DESC" )
474
+ tag_names = [ ]
475
+ found_tags . each { |t | tag_names << t . name }
476
+ found_tags * ", "
385
477
end
386
478
# Otherwise, it's just an attribute
387
479
else
@@ -394,7 +486,7 @@ def cmd_hosts(*args)
394
486
addr = ( host . scope ? host . address + '%' + host . scope : host . address )
395
487
rhosts << addr
396
488
end
397
- if mode == :delete
489
+ if mode == [ :delete ]
398
490
host . destroy
399
491
delete_count += 1
400
492
end
0 commit comments