package Iodef::Pb::Format::Html; use base 'Iodef::Pb::Format'; use strict; use warnings; use HTML::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); my $config = $args->{'config'}; my @config_search_path = ('claoverride', $args->{'query'}, 'client' ); # fields class evenrowclass oddrowclass display my $cfg_fields = $args->{'html_fields'} || $self->SUPER::confor($config, \@config_search_path, 'html_fields', undef); my $cfg_compress_address = $args->{'compress_address'} || $self->SUPER::confor($config, \@config_search_path, 'compress_address', undef); my $cfg_class = $args->{'html_class'} || $self->SUPER::confor($config, \@config_search_path, 'html_class', undef); my $cfg_evenrowclass = $args->{'html_evenrowclass'} || $self->SUPER::confor($config, \@config_search_path, 'html_evenrowclass', undef); my $cfg_oddrowclass = $args->{'html_oddrowclass'} || $self->SUPER::confor($config, \@config_search_path, 'html_oddrowclass', undef); my $cfg_uuid = $args->{'html_uuid'} || $self->SUPER::confor($config, \@config_search_path, 'html_uuid', undef); my $cfg_relatedid = $args->{'html_relatedid'} || $self->SUPER::confor($config, \@config_search_path, 'html_relatedid', undef); my $cfg_html_showmeta = $args->{'html_showmeta'} || $self->SUPER::confor($config, \@config_search_path, 'html_showmeta', undef); my @cols; push(@cols,'id') if($cfg_uuid); push(@cols,'relatedid') if($cfg_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_html_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'}); } } } 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_html_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 $table = HTML::Table->new( -head => \@cols, -class => $cfg_class || '', -evenrowclass => $cfg_evenrowclass || '', -oddrowclass => $cfg_oddrowclass || '', ); 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'} .= '...'; } if($e->{'alternativeid'} && $e->{'alternativeid'} =~ /[a-zA-Z0-9.-]+\.[a-z]{2,5}/){ my $addr = ($e->{'alternativeid'} =~ /^http/) ? $e->{'alternativeid'} : 'httmp://'.$e->{'alternativeid'}; $e->{'alternativeid'} = "$addr"; } $table->addRow(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'}); return $table->getTable(); } 1;