| File: | bin/abuse_check |
| Coverage: | 51.9% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | #!/usr/bin/env perl | |||||
| 2 | # ----------------------------------------------------------------------- | |||||
| 3 | # example_abuse_check.pl -- demonstrate Email::Abuse::Investigator | |||||
| 4 | # | |||||
| 5 | # Usage: | |||||
| 6 | # perl example_abuse_check.pl spam.eml | |||||
| 7 | # perl example_abuse_check.pl < spam.eml | |||||
| 8 | # ----------------------------------------------------------------------- | |||||
| 9 | 3 3 | 5846 5 | use 5.010; | |||
| 10 | ||||||
| 11 | 3 3 3 | 6 2 25 | use strict; | |||
| 12 | 3 3 3 | 4 2 55 | use warnings; | |||
| 13 | 3 3 3 | 539 24456 6 | use autodie qw(:all); | |||
| 14 | ||||||
| 15 | 3 3 3 | 29462 285 6 | use utf8; | |||
| 16 | 3 3 3 | 595 1542 8 | use open qw(:std :encoding(UTF-8)); | |||
| 17 | 3 3 3 | 24858 796 7 | use lib '.'; # find Email/Abuse/Investigator.pm nearby | |||
| 18 | 3 3 3 | 1472 7 620 | use Email::Abuse::Investigator; | |||
| 19 | ||||||
| 20 | # ---- Read the raw email ------------------------------------------------ | |||||
| 21 | 3 | 293281 | my $raw; | |||
| 22 | 3 | 6 | if (@ARGV) { | |||
| 23 | # Validate path: reject NUL bytes, bare newlines, and directory-traversal | |||||
| 24 | # sequences before opening. This also makes the path taint-safe under -T. | |||||
| 25 | 3 | 10 | my ($safe_path) = $ARGV[0] =~ /\A([^\x00\r\n]+)\z/ | |||
| 26 | or die "Invalid characters in path '$ARGV[0]'\n"; | |||||
| 27 | 3 | 54 | die "Path traversal attempt in '$safe_path'\n" | |||
| 28 | if $safe_path =~ m{(?:^|/)\.\.(?:/|$)}; | |||||
| 29 | 2 | 5 | open my $fh, '<:raw', $safe_path or die "Cannot open $safe_path: $!"; | |||
| 30 | 0 | 0 | local $/; | |||
| 31 | 0 | 0 | $raw = <$fh>; | |||
| 32 | 0 | 0 | close $fh; | |||
| 33 | } else { | |||||
| 34 | 0 | 0 | binmode STDIN, ':raw'; | |||
| 35 | 0 | 0 | local $/; | |||
| 36 | 0 | 0 | $raw = <STDIN>; | |||
| 37 | } | |||||
| 38 | 0 | 0 | die 'No email data supplied' unless $raw && length $raw; | |||
| 39 | ||||||
| 40 | # ---- Analyse ----------------------------------------------------------- | |||||
| 41 | 0 | 0 | my $analyser = Email::Abuse::Investigator->new( | |||
| 42 | verbose => 1, | |||||
| 43 | timeout => 15, | |||||
| 44 | # List your own outbound relay IPs/CIDRs here so they are skipped | |||||
| 45 | # when walking the Received: chain for the true origin: | |||||
| 46 | # trusted_relays => ['192.0.2.1', '198.51.100.0/24'], | |||||
| 47 | ); | |||||
| 48 | 0 | 0 | $analyser->parse_email($raw); | |||
| 49 | ||||||
| 50 | # ---- Full analyst report ----------------------------------------------- | |||||
| 51 | 0 | 0 | print $analyser->report(); | |||
| 52 | ||||||
| 53 | # ---- Send-ready abuse report text and contacts ------------------------- | |||||
| 54 | 0 | 0 | my @contacts = $analyser->abuse_contacts(); | |||
| 55 | 0 | 0 | if (@contacts) { | |||
| 56 | 0 | 0 | print "\n"; | |||
| 57 | 0 | 0 | print '=' x 72, "\n"; | |||
| 58 | 0 | 0 | print " ABUSE REPORT TEXT (send this to each contact below)\n"; | |||
| 59 | 0 | 0 | print '=' x 72, "\n\n"; | |||
| 60 | 0 | 0 | print $analyser->abuse_report_text(); | |||
| 61 | ||||||
| 62 | 0 | 0 | print "\n"; | |||
| 63 | 0 | 0 | print '=' x 72, "\n"; | |||
| 64 | 0 | 0 | print " SEND TO:\n"; | |||
| 65 | 0 | 0 | print '=' x 72, "\n"; | |||
| 66 | 0 | 0 | for my $c (@contacts) { | |||
| 67 | 0 | 0 | printf " %-45s %s\n", $c->{address}, $c->{role}; | |||
| 68 | } | |||||
| 69 | 0 | 0 | print "\n"; | |||
| 70 | } else { | |||||
| 71 | 0 | 0 | print "\n(No abuse contacts could be determined.)\n"; | |||
| 72 | } | |||||
| 73 | ||||||
| 74 | 0 | 0 | exit 0; | |||