use strict;
use warnings;

use RT::Test tests => undef;
use Email::Abstract;

# Multiple email addresses for a single user are supported in 3.8 and 4.0 via
# commas, so we really only care if you can inject other headers.  It doesn't
# much matter if you can inject other recipients via your user email address or
# other values since you can do that in many visible and not-so-visible supported (!)
# ways.
# 
#                        HEADER INJECTION VIA USER EMAIL ADDRESS
# 
#                                                   4.0-trunk   security/4.0/email-header-injection
#                                                  -------------------------------------------------
# Via recipient headers w/ RecordOutgoingEmail ON  | No       | No                                 |
# Via recipient headers w/ RecordOutgoingEmail OFF | Yes      | No                                 |
# Via RT-Originator (X-RT-Originator in 4.2)       | Yes      | No                                 |
# Via other default headers                        | No       | No                                 |
#                                                  -------------------------------------------------
# 
# With RecordOutgoingEmail ON, your recipient headers are filtered through
# Email::Address, potentially mangling addresses if you've inserted newlines or
# other funky chars that run together.

my ($ok, $msg);

# Create evil user
my $user    = RT::User->new( RT->SystemUser );
($ok, $msg) = $user->Create(
    Name            => "eviluser",
    EmailAddress    => "foo\@example.com\nEvil: yes\n\nMalicious",
);
ok $user->id, "Created user with evil email address: $msg";
like $user->EmailAddress, qr/\nEvil: yes/, "Email address still evil";
like $user->EmailAddress, qr/\n\nMalicious/, "Email address still malicious";

($ok, $msg) = $user->PrincipalObj->GrantRight(
    Right   => 'CreateTicket',
    Object  => RT->System,
);
ok $ok, "Granted CreateTicket to evil user: $msg";

note "To: header (any recipient header)";
{
    for my $record_outgoing (1, 0) {
        RT->Config->Set( RecordOutgoingEmail => $record_outgoing );
        note "RecordOutgoingEmail is " . ($record_outgoing ? "ON" : "OFF");

        # Create ticket which will...
        my $ticket  = RT::Ticket->new( RT->SystemUser );
        ($ok, $msg) = $ticket->Create(
            Queue       => 1,
            Subject     => "test recipient ticket",
            Requestor   => $user->PrincipalId,
        );
        ok $ticket->id, "Created ticket: $msg";

        # ... send an autoreply to said user, putting their email address in the To: header
        my @email = RT::Test->fetch_caught_mails;
        is @email, 1, "Caught one email";

        my $entity = Email::Abstract->new($email[0])->cast('MIME::Entity');
        my $head = $entity->head;
        like $head->get("Subject"), qr/autoreply/i, "Looks like autoreply";
        like $head->get("To"), qr/foo\@example\.com/, "To: contains foo\@example.com";
        ok !$head->get("Evil"), "No Evil header";
        unlike $entity->stringify_body, qr/Malicious/, "No Malicious body";
    }
}

note "X-RT-Originator (previously RT-Originator) header";
{
    for my $originator (1, 0) {
        RT->Config->Set( UseOriginatorHeader => $originator );
        note "UseOriginatorHeader is " . ($originator ? "ON" : "OFF");

        # Create ticket as evil user
        my $ticket  = RT::Ticket->new( RT::CurrentUser->new($user) );
        ($ok, $msg) = $ticket->Create(
            Queue       => 1,
            Subject     => "test originator ticket",
            Requestor   => 'unsuspecting@example.com',  # provide any recipient
        );
        ok $ticket->id, "Created ticket: $msg";

        # ... sends an email
        my @email = RT::Test->fetch_caught_mails;
        is @email, 1, "Caught one email";

        my $entity = Email::Abstract->new($email[0])->cast('MIME::Entity');
        my $head = $entity->head;
        if ($originator) {
            like $head->get("X-RT-Originator"), qr/foo\@example\.com/, "X-RT-Originator contains email";
            like $head->get("X-RT-Originator"), qr/Evil: yes/, "Evil didn't leak out of X-RT-Originator";
        }
        ok !$head->get("Evil"), "No Evil header";
        unlike $entity->stringify_body, qr/Malicious/, "No Malicious body";
    }
}

done_testing;
