From: Eric Wong Date: Fri, 10 Jan 2014 23:50:16 +0000 (+0000) Subject: reject messages if ORIGINAL_RECIPIENT is not specified X-Git-Tag: v1.0.0~1334 X-Git-Url: http://www.git.stargrave.org/?p=public-inbox.git;a=commitdiff_plain;h=bf4630c098ad7159ba36dea6cfe77c4cf6806ffe reject messages if ORIGINAL_RECIPIENT is not specified SpamAssassin doesn't seem to have this heuristic, but the lack of the intended email address in To:/Cc: headers cannot be a good sign (especially when this is a _public_ inbox). --- diff --git a/Makefile.PL b/Makefile.PL index b2fbb18f..5d3ee75c 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -15,6 +15,7 @@ WriteMakefile( 'Email::MIME' => 0, 'Email::MIME::ContentType' => 0, 'Email::Filter' => 0, + 'Email::Address' => 0, }, ); diff --git a/lib/PublicInbox.pm b/lib/PublicInbox.pm new file mode 100644 index 00000000..b51c000f --- /dev/null +++ b/lib/PublicInbox.pm @@ -0,0 +1,33 @@ +# Copyright (C) 2013, Eric Wong and all contributors +# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt) +package PublicInbox; +use strict; +use warnings; +use Email::Address; + +# drop plus addressing for matching +sub __drop_plus { + my ($str_addr) = @_; + $str_addr =~ s/\+.*\@/\@/; + $str_addr; +} + +# do not allow Bcc, only Cc and To if ORIGINAL_RECIPIENT (postfix) env is set +sub recipient_specified { + my ($klass, $filter) = @_; + my $or = $ENV{ORIGINAL_RECIPIENT}; + defined($or) or return 1; # for imports + my @or = Email::Address->parse($or); + my $oaddr = __drop_plus($or[0]->address); + $oaddr = qr/\b\Q$oaddr\E\b/i; + my @to = Email::Address->parse($filter->to); + my @cc = Email::Address->parse($filter->cc); + foreach my $addr (@to, @cc) { + if (__drop_plus($addr->address) =~ $oaddr) { + return 1; + } + } + return 0; +} + +1; diff --git a/public-inbox-mda b/public-inbox-mda index 177c891e..72f1eac4 100755 --- a/public-inbox-mda +++ b/public-inbox-mda @@ -4,6 +4,7 @@ use strict; use warnings; use Email::Filter; +use Email::Address; use PublicInbox::Filter; use IPC::Run qw(run); my $usage = "public-inbox-mda main_repo fail_repo < rfc2822_message"; @@ -14,6 +15,7 @@ my $max = 1024 * 500; # same as spamc my $filtered; if (length($filter->simple->as_string) <= $max + && PublicInbox->recipient_specified($filter) && do_spamc($filter->simple, \$filtered)) { # update our message with SA headers (in case our filter rejects it) my $simple = Email::Simple->new($filtered); diff --git a/t/recipient.t b/t/recipient.t new file mode 100644 index 00000000..9cb1969e --- /dev/null +++ b/t/recipient.t @@ -0,0 +1,59 @@ +# Copyright (C) 2014, Eric Wong and all contributors +# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt) +use strict; +use warnings; +use Test::More; +use Email::Simple; +use Email::Filter; +use PublicInbox; + +sub do_checks { + my ($s) = @_; + + my $f = Email::Filter->new(data => $s->as_string); + local %ENV; + delete $ENV{ORIGINAL_RECIPIENT}; + + ok(PublicInbox->recipient_specified($f), + "ORIGINAL_RECIPIENT unset is OK"); + + $ENV{ORIGINAL_RECIPIENT} = 'foo@example.com'; + ok(!PublicInbox->recipient_specified($f), + "wrong ORIGINAL_RECIPIENT rejected"); + + $ENV{ORIGINAL_RECIPIENT} = 'b@example.com'; + ok(PublicInbox->recipient_specified($f), + "ORIGINAL_RECIPIENT in To: is OK"); + + $ENV{ORIGINAL_RECIPIENT} = 'c@example.com'; + ok(PublicInbox->recipient_specified($f), + "ORIGINAL_RECIPIENT in Cc: is OK"); +} + +{ + do_checks(Email::Simple->create( + header => [ + From => 'a@example.com', + To => 'b@example.com', + Cc => 'c@example.com', + 'Content-Type' => 'text/plain', + Subject => 'this is a subject', + ], + body => "hello world\n", + )); +} + +{ + do_checks(Email::Simple->create( + header => [ + From => 'a@example.com', + To => 'b+plus@example.com', + Cc => 'John Doe ', + 'Content-Type' => 'text/plain', + Subject => 'this is a subject', + ], + body => "hello world\n", + )); +} + +done_testing();