package Iodef::Pb::Format::Table; use base 'Iodef::Pb::Format'; use strict; use warnings; use Text::Table; use Regexp::Common qw/net/; my $addr_regex = qr/$RE{'net'}{'IPv4'}|https?|ftp|[a-z0-9._]+\.[a-z]{2,6}/; sub write_out { my $self = shift; my $args = shift; my $array = $self->to_keypair($args); # we will look for each variable in the [query] section # if it isnt there, we will check the [client] section. # if it's not there either, we'll use the default my $config = $args->{'config'}; my @config_search_path = ( 'claoverride', $args->{'query'}, 'client' ); my $cfg_fields = $args->{'fields'} || $self->SUPER::confor($config, \@config_search_path, 'fields', undef); my $cfg_display = $args->{'display'} || $self->SUPER::confor($config, \@config_search_path, 'display', undef); my $cfg_compress_address = $args->{'compress_address'} || $self->SUPER::confor($config, \@config_search_path, 'compress_address', undef); my $cfg_description = $args->{'description'} || $self->SUPER::confor($config, \@config_search_path, 'description', undef); my $cfg_table_nowarning = $args->{'table_nowarning'} || $self->SUPER::confor($config, \@config_search_path, 'table_nowarning', undef); my $cfg_table_showmeta = $args->{'table_showmeta'} || $self->SUPER::confor($config, \@config_search_path, 'table_showmeta', undef); my @cols; push(@cols,'id') if($args->{'table_uuid'}); push(@cols,'relatedid') if($args->{'table_relatedid'}); push(@cols,( 'restriction', 'guid', 'assessment', 'description', 'confidence', 'detecttime', 'reporttime', )); # override if($cfg_fields){ @cols = @$cfg_fields; } my %c; unless($cfg_fields){ foreach my $e (@$array){ $c{'address'} = 1 if($e->{'address'}); $c{'hash'} = 1 if($e->{'hash'}); $c{'protocol'} = 1 if($e->{'protocol'}); $c{'portlist'} = 1 if($e->{'portlist'}); $c{'malware_hash'} = 1 if($e->{'malware_hash'}); $c{'rdata'} = 1 if($e->{'rdata'}); if($cfg_table_showmeta){ $c{'asn'} = 1 if($e->{'asn'}); $c{'asn_desc'} = 1 if($e->{'asn_desc'}); $c{'prefix'} = 1 if($e->{'prefix'}); $c{'cc'} = 1 if($e->{'cc'}); $c{'rir'} = 1 if($e->{'rir'}); $c{'malware_detection_rate'} = 1 if($e->{'malware_detection_rate'}); } # this could be a performance killer # work-around for searches that don't have # an address associated with it and would confuse # output $c{'address'} = 1 if($e->{'description'} =~ /search $addr_regex$/); } } push(@cols,'address') if($c{'address'}); push(@cols,'rdata') if($c{'rdata'}); push(@cols,'malware_hash') if($c{'malware_hash'}); push(@cols,'prefix') if($c{'prefix'}); push(@cols,'hash') if($c{'hash'} && !$c{'address'}); push(@cols,'protocol') if($c{'protocol'}); push(@cols,'portlist') if($c{'portlist'}); ## TODO -- make these optional flags passed through ## table_showmeta or via the -f option if($cfg_table_showmeta && !$cfg_fields){ push(@cols,'asn') if($c{'asn'}); push(@cols,'asn_desc') if($c{'asn_desc'}); push(@cols,'cc') if($c{'cc'}); push(@cols,'rir') if($c{'rir'}); push(@cols,'malware_detection_rate'), if($c{'malware_detection_rate'}); } ## TODO -- malware hash lookups? push(@cols,( 'alternativeid_restriction', 'alternativeid', )) unless($cfg_fields); my @header = map { $_, { is_sep => 1, title => '|' } } @cols; pop(@header); my $table = Text::Table->new(@header); foreach my $e (@$array){ # work-around for hash searches that don't show the address # at some point we'll move this back up the stack to Format.pm unless($e->{'address'}){ for($e->{'description'}){ if(/search ($addr_regex)$/){ $e->{'address'} = $1; last; } } } else { if($cfg_compress_address && length($e->{'address'}) > 32){ $e->{'address'} = substr($e->{'address'},0,31); $e->{'address'} .= '...'; } } if($cfg_compress_address && length($e->{'description'}) > 32){ $e->{'description'} = substr($e->{'description'},0,31); $e->{'description'} .= '...'; } if($cfg_compress_address && $e->{'alternativeid'} && length($e->{'alternativeid'}) > 75){ $e->{'alternativeid'} = substr($e->{'alternativeid'},0,74); $e->{'alternativeid'} = $e->{'alternativeid'} .= '...'; } $table->load([ map { $e->{$_} } @cols]); } ## TODO -- what if RestrictionType in Iodef::Pb and FeedType get out of sync? my $restriction = $self->convert_restriction($args->{'restriction'}) || 'private'; if($self->get_group_map && $self->get_group_map->{$args->{'guid'}}){ $args->{'guid'} = $self->get_group_map->{$args->{'guid'}}; } ## TODO - guid should be responded to by the router $args->{'uuid'} = '' unless($args->{'uuid'}); $args->{'guid'} = '' unless($args->{'guid'}); my $meta = "feed description: $args->{'description'} feed reporttime: $args->{'reporttime'} feed uuid: $args->{'uuid'} feed guid: $args->{'guid'} feed restriction: $restriction feed confidence: $args->{'confidence'}\n\n"; unless($cfg_table_nowarning){ $meta = 'WARNING: Turn off this warning by adding: \'table_nowarning = 1\' to your ~/.cif config'."\n\n".$meta; $meta = 'WARNING: This table output not to be used for parsing, see "-p plugins" (via cif -h)'."\n".$meta; } $table = $meta . $table; return $table; } 1;