Email-Abuse-Investigator

 view release on metacpan or  search on metacpan

lib/Email/Abuse/Investigator.pm  view on Meta::CPAN

		push @out, sprintf('  %-14s : %s', $label,
			_sanitise_output($decoded ne $v ? "$decoded  [encoded: $v]" : $v));
	}
	push @out, '';

	# Risk assessment section
	my $risk = $self->risk_assessment();
	push @out, "[ RISK ASSESSMENT: $risk->{level} (score: $risk->{score}) ]";
	if (@{ $risk->{flags} }) {
		for my $f (@{ $risk->{flags} }) {
			push @out, "  [$f->{severity}] " . _sanitise_output($f->{detail});
		}
	} else {
		push @out, '  (no specific red flags detected)';
	}
	push @out, '';

	# Originating host section
	push @out, '[ ORIGINATING HOST ]';
	my $orig = $self->originating_ip();
	if ($orig) {
		push @out, '  IP           : ' . _sanitise_output($orig->{ip});
		push @out, '  Reverse DNS  : ' . _sanitise_output($orig->{rdns})    if $orig->{rdns};
		push @out, '  Country      : ' . _sanitise_output($orig->{country}) if $orig->{country};
		push @out, '  Organisation : ' . _sanitise_output($orig->{org})     if $orig->{org};
		push @out, '  Abuse addr   : ' . _sanitise_output($orig->{abuse})   if $orig->{abuse};
		push @out, "  Confidence   : $orig->{confidence}";
		push @out, '  Note         : ' . _sanitise_output($orig->{note})    if $orig->{note};
	} else {
		push @out, '  (could not determine originating IP)';
	}
	push @out, '';

	# Sending software section (omitted if none found)
	my @sw = $self->sending_software();
	if (@sw) {
		push @out, '[ SENDING SOFTWARE / INFRASTRUCTURE CLUES ]';
		for my $s (@sw) {
			push @out, sprintf('  %-14s : %s', $s->{header}, _sanitise_output($s->{value}));
			push @out, "  Note           : $s->{note}";
			push @out, '';
		}
	}

	# Received chain tracking IDs (only hops with id or for are shown)
	my @trail = grep { defined $_->{id} || defined $_->{for} }
	            $self->received_trail();
	if (@trail) {
		push @out, '[ RECEIVED CHAIN TRACKING IDs ]';
		push @out, '  (Supply these to the relevant ISP abuse team to trace the session)';
		push @out, '';
		for my $hop (@trail) {
			push @out, '  IP           : ' . (_sanitise_output($hop->{ip}) // '(unknown)');
			push @out, '  Envelope for : ' . _sanitise_output($hop->{for}) if $hop->{for};
			push @out, '  Server ID    : ' . _sanitise_output($hop->{id})  if $hop->{id};
			push @out, '';
		}
	}

	# Embedded URLs section -- grouped by hostname
	push @out, '[ EMBEDDED HTTP/HTTPS URLs ]';
	my @urls = $self->embedded_urls();
	if (@urls) {
		my (%host_order, %host_meta, %host_paths);
		my $seq = 0;
		for my $u (@urls) {
			my $h = $u->{host};
			unless (exists $host_order{$h}) {
				$host_order{$h} = $seq++;
				$host_meta{$h}  = {
					ip      => $u->{ip},
					org     => $u->{org},
					abuse   => $u->{abuse},
					country => $u->{country},
				};
			}
			push @{ $host_paths{$h} }, $u->{url};
		}

		# Output each host group in first-seen order
		for my $h (sort { $host_order{$a} <=> $host_order{$b} } keys %host_order) {
			my $m    = $host_meta{$h};
			my $bare = lc $h; $bare =~ s/^www\.//;
			push @out, '  Host         : ' . _sanitise_output($h) .
			           (($URL_SHORTENERS{$bare} || $self->{url_shorteners}->{$bare})
			            ? '  *** URL SHORTENER -- real destination hidden ***' : '');
			push @out, '  IP           : ' . _sanitise_output($m->{ip})      if $m->{ip};
			push @out, '  Country      : ' . _sanitise_output($m->{country}) if $m->{country};
			push @out, '  Organisation : ' . _sanitise_output($m->{org})     if $m->{org};
			push @out, '  Abuse addr   : ' . _sanitise_output($m->{abuse})   if $m->{abuse};
			my @paths = @{ $host_paths{$h} };
			if (@paths == 1) {
				push @out, '  URL          : ' . _sanitise_output($paths[0]);
			} else {
				push @out, '  URLs (' . scalar(@paths) . ')     :';
				push @out, '    ' . _sanitise_output($_) for @paths;
			}
			push @out, '';
		}
	} else {
		push @out, '  (none found)';
		push @out, '';
	}

	# Contact / reply-to domains section
	push @out, '[ CONTACT / REPLY-TO DOMAINS ]';
	my @mdoms = $self->mailto_domains();
	if (@mdoms) {
		for my $d (@mdoms) {
			push @out, '  Domain       : ' . _sanitise_output($d->{domain});
			push @out, '  Found in     : ' . _sanitise_output($d->{source});
			if ($d->{recently_registered}) {
				push @out, '  *** WARNING: RECENTLY REGISTERED - possible phishing domain ***';
			}
			push @out, '  Registered   : ' . $d->{registered}       if $d->{registered};
			push @out, '  Expires      : ' . $d->{expires}           if $d->{expires};
			push @out, '  Registrar    : ' . _sanitise_output($d->{registrar})       if $d->{registrar};
			push @out, '  Reg. abuse   : ' . _sanitise_output($d->{registrar_abuse}) if $d->{registrar_abuse};
			if ($d->{web_ip}) {
				push @out, '  Web host IP  : ' . _sanitise_output($d->{web_ip});
				push @out, '  Web host org : ' . _sanitise_output($d->{web_org})   if $d->{web_org};



( run in 0.503 second using v1.01-cache-2.11-cpan-71847e10f99 )