[Rt-commit] rt branch, 4.0-trunk, updated. rt-4.0.8-184-g09db186

Kevin Falcone falcone at bestpractical.com
Mon Nov 26 21:38:17 EST 2012


The branch, 4.0-trunk has been updated
       via  09db186a0fbfa70eded2054e215e120ebc6eae7c (commit)
       via  155e65849b0940b7db5dfbb7e9be4a60b5bccda1 (commit)
      from  7451d9998c30e569ecc300c0751fe8034f173931 (commit)

Summary of changes:
 t/security/CVE-2011-2083-cf-urls.t                |  48 ++++++++
 t/security/CVE-2011-2083-clickable-xss.t          |  52 +++++++++
 t/security/CVE-2011-2083-scrub.t                  |  18 +++
 t/security/CVE-2011-2084-attach-tickets.t         |  64 +++++++++++
 t/security/CVE-2011-2084-cf-values.t              | 132 ++++++++++++++++++++++
 t/security/CVE-2011-2084-modifyscrips-templates.t | 126 +++++++++++++++++++++
 t/security/CVE-2011-2084-transactions.t           |  59 ++++++++++
 t/security/CVE-2011-4458-verp.t                   |  48 ++++++++
 t/security/CVE-2011-4460-rows-per-page.t          |  32 ++++++
 t/security/CVE-2011-5092-datetimeformat.t         |  48 ++++++++
 t/security/CVE-2011-5092-graph-links.t            |  27 +++++
 t/security/CVE-2011-5092-installmode.t            |  24 ++++
 t/security/CVE-2011-5092-localizeddatetime.t      |  30 +++++
 t/security/CVE-2011-5092-prefs.t                  |  77 +++++++++++++
 t/security/CVE-2011-5093-execute-code.t           |  53 +++++++++
 t/{mail => security}/fake-sendmail                |   5 +-
 16 files changed, 839 insertions(+), 4 deletions(-)
 create mode 100644 t/security/CVE-2011-2083-cf-urls.t
 create mode 100644 t/security/CVE-2011-2083-clickable-xss.t
 create mode 100644 t/security/CVE-2011-2083-scrub.t
 create mode 100644 t/security/CVE-2011-2084-attach-tickets.t
 create mode 100644 t/security/CVE-2011-2084-cf-values.t
 create mode 100644 t/security/CVE-2011-2084-modifyscrips-templates.t
 create mode 100644 t/security/CVE-2011-2084-transactions.t
 create mode 100644 t/security/CVE-2011-4458-verp.t
 create mode 100644 t/security/CVE-2011-4460-rows-per-page.t
 create mode 100644 t/security/CVE-2011-5092-datetimeformat.t
 create mode 100644 t/security/CVE-2011-5092-graph-links.t
 create mode 100644 t/security/CVE-2011-5092-installmode.t
 create mode 100644 t/security/CVE-2011-5092-localizeddatetime.t
 create mode 100644 t/security/CVE-2011-5092-prefs.t
 create mode 100644 t/security/CVE-2011-5093-execute-code.t
 copy t/{mail => security}/fake-sendmail (77%)

- Log -----------------------------------------------------------------
commit 155e65849b0940b7db5dfbb7e9be4a60b5bccda1
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Wed Nov 21 17:36:34 2012 -0500

    Add security tests for vulnerabilities released 2012-05-22
    
    This includes tests for:
    
      CVE-2011-2083  XSS vulnerabilities
      CVE-2011-2084  Information leakage of private data
      CVE-2011-4458  Arbitrary execution of code via VERP
      CVE-2011-5092  Arbitrary execution of code
      CVE-2011-5093  Arbitrary execution of code via DisallowExecuteCode
      CVE-2011-4460  SQL injection for read (but not write) access
    
    Note that CVE-2011-4458 previously encompassed CVE-2011-5092 and
    CVE-2011-5093 as well, but has been split into three separate CVEs.

diff --git a/t/security/CVE-2011-2083-cf-urls.t b/t/security/CVE-2011-2083-cf-urls.t
new file mode 100644
index 0000000..b1e1f3b
--- /dev/null
+++ b/t/security/CVE-2011-2083-cf-urls.t
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my ($base, $m) = RT::Test->started_ok;
+
+my $link = RT::Test->load_or_create_custom_field(
+    Name            => 'link',
+    Type            => 'Freeform',
+    MaxValues       => 1,
+    Queue           => 0,
+    LinkValueTo     => '__CustomField__',
+);
+
+my $include = RT::Test->load_or_create_custom_field(
+    Name                    => 'include',
+    Type                    => 'Freeform',
+    MaxValues               => 1,
+    Queue                   => 0,
+    IncludeContentForValue  => '__CustomField__',
+);
+
+my $data_uri = 'data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ+';
+my $xss      = q{')-eval(decodeURI('alert("xss")'))-('};
+
+my $ticket = RT::Ticket->new(RT->SystemUser);
+$ticket->Create(
+    Queue                       => 'General',
+    Subject                     => 'ticket A',
+    'CustomField-'.$link->id    => $data_uri,
+    'CustomField-'.$include->id => $xss,
+);
+ok $ticket->Id, 'created ticket';
+
+ok $m->login('root', 'password'), "logged in";
+$m->get_ok($base . "/Ticket/Display.html?id=" . $ticket->id);
+
+# look for lack of link to data:text/html;base64,...
+ok !$m->find_link(text => $data_uri), "no data: link";
+ok !$m->find_link(url  => $data_uri), "no data: link";
+
+# look for unescaped JS
+$m->content_lacks($xss, 'escaped js');
+
+$m->warning_like(qr/Potentially dangerous URL type/, "found warning about dangerous link");
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-2083-clickable-xss.t b/t/security/CVE-2011-2083-clickable-xss.t
new file mode 100644
index 0000000..008c803
--- /dev/null
+++ b/t/security/CVE-2011-2083-clickable-xss.t
@@ -0,0 +1,52 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+use Test::Warn;
+
+my ($base, $m) = RT::Test->started_ok;
+
+my $ticket = RT::Test->create_ticket(
+    Queue   => 'General',
+    Subject => 'test ticket A',
+);
+my $id = $ticket->id;
+ok $id, "created ticket";
+
+my @links = (
+    'javascript:alert("xss")',
+    'data:text/html,<script>alert("xss")</script>',
+);
+
+for my $link ( map { ($_, ucfirst $_) } @links ) {
+    my ($ok, $msg);
+    warnings_like {
+        ($ok, $msg) = $ticket->AddLink(
+            Type    => 'RefersTo',
+            Target  => $link,
+        );
+    } [qr/Could not determine a URI scheme/, qr/Couldn't resolve/];
+    ok !$ok, $msg;
+
+    ok $m->login, "logged in";
+    $m->get_ok($base);
+    $m->follow_link_ok({ text => 'test ticket A' }, 'ticket page');
+    $m->follow_link_ok({ text => 'Links' }, 'links page');
+    $m->submit_form_ok({
+        with_fields => {
+            "$id-RefersTo" => $link,
+        },
+        button  => 'SubmitTicket',
+    }, 'submitted links page');
+    $m->content_contains("Couldn't resolve ");
+    $m->next_warning_like(qr/Could not determine a URI scheme/, 'expected warning');
+    $m->next_warning_like(qr/Couldn't resolve/, 'expected warning');
+
+    my $element = $m->find_link( url => $link );
+    ok !$element, "no <a> link";
+}
+
+$m->no_leftover_warnings_ok;
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-2083-scrub.t b/t/security/CVE-2011-2083-scrub.t
new file mode 100644
index 0000000..f053783
--- /dev/null
+++ b/t/security/CVE-2011-2083-scrub.t
@@ -0,0 +1,18 @@
+use strict;
+use warnings;
+
+use RT::Test nodb => 1, tests => undef;
+use RT::Interface::Web; # This gets us HTML::Mason::Commands
+use Test::LongString;
+
+{
+    my $html = '<div id="metadata"><span class="actions"><a>OH HAI</a></span></div><p>Moose</p>';
+    my $expected = '<div><span><a>OH HAI</a></span></div><p>Moose</p>';
+    is_string(scrub_html($html), $expected, "class and id are stripped");
+}
+
+sub scrub_html {
+    return HTML::Mason::Commands::ScrubHTML(shift);
+}
+
+done_testing;
diff --git a/t/security/CVE-2011-2084-attach-tickets.t b/t/security/CVE-2011-2084-attach-tickets.t
new file mode 100644
index 0000000..d7352cb
--- /dev/null
+++ b/t/security/CVE-2011-2084-attach-tickets.t
@@ -0,0 +1,64 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $user = RT::Test->load_or_create_user(
+    Name            => 'user',
+    EmailAddress    => 'user at example.com',
+    Privileged      => 1,
+    Password        => 'password',
+);
+
+ok(
+    RT::Test->set_rights(
+        { Principal => 'Everyone',  Right => [qw/CreateTicket/] },
+        { Principal => 'Requestor', Right => [qw/ShowTicket/] },
+    ),
+    'set rights'
+);
+
+my $secret = "sekrit message";
+
+RT::Test->create_tickets(
+    {},
+    {
+        Subject     => 'ticket A',
+        Requestor   => $user->EmailAddress,
+        Content     => "user's ticket",
+    },
+    {
+        Subject     => 'ticket B',
+        Requestor   => 'root at localhost',
+        Content     => $secret,
+    },
+);
+
+my $ticket_b = RT::Test->last_ticket;
+
+my ($baseurl, $m) = RT::Test->started_ok;
+ok $m->login( 'user', 'password' ), 'logged in as user';
+
+$m->get_ok("$baseurl/Ticket/Display.html?id=" . $ticket_b->id);
+$m->content_contains('No permission');
+$m->warning_like(qr/no permission/i, 'no permission warning');
+
+RT::Test->clean_caught_mails;
+
+# Ticket Create is just one example of where this is vulnerable
+$m->get_ok('/Ticket/Create.html?Queue=1');
+$m->submit_form_ok({
+    form_name   => 'TicketCreate',
+    fields      => {
+        Subject         => 'ticket C',
+        AttachTickets   => $ticket_b->id,
+    },
+}, 'create a ticket');
+
+my @mail = RT::Test->fetch_caught_mails;
+ok @mail, "got some outgoing emails";
+unlike $mail[0], qr/\Q$secret\E/, "doesn't contain ticket user can't see";
+
+undef $m;
+done_testing;
+
diff --git a/t/security/CVE-2011-2084-cf-values.t b/t/security/CVE-2011-2084-cf-values.t
new file mode 100644
index 0000000..1178b15
--- /dev/null
+++ b/t/security/CVE-2011-2084-cf-values.t
@@ -0,0 +1,132 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+use JSON qw(decode_json);
+
+my ($base, $m) = RT::Test->started_ok;
+
+my $cf1 = RT::Test->load_or_create_custom_field(
+    Name            => 'cf1',
+    Type            => 'Select',
+    MaxValues       => 1,
+    Queue           => 0,
+);
+ok $cf1->id, "created cf1";
+
+my $cf2 = RT::Test->load_or_create_custom_field(
+    Name            => 'cf2',
+    Type            => 'Select',
+    MaxValues       => 1,
+    Queue           => 0,
+);
+ok $cf2->id, "created cf2";
+
+ok( $cf1->AddValue( Name => "cf1 value $_" ) ) for qw(a b c);
+ok( $cf2->AddValue( Name => "cf2 value $_" ) ) for qw(x y z);
+
+sub ac {
+    my (%args) = (
+        CF          => $cf1->id,
+        Term        => "%",
+        Context     => undef,
+        ContextId   => undef,
+        ContextType => undef,
+        @_
+    );
+    $args{term} = delete $args{Term};
+
+    if (my $obj = delete $args{Context}) {
+        $args{ContextId}   = $obj->Id  unless defined $args{ContextId};
+        $args{ContextType} = ref($obj) unless defined $args{ContextType};
+    }
+
+    $args{"Object---CustomField-$args{CF}-Values"} = "";
+    delete $args{CF};
+
+    delete $args{$_} for grep {not defined $args{$_}} keys %args;
+
+    my $URI = URI->new("$base/Helpers/Autocomplete/CustomFieldValues");
+    $URI->query_form( %args );
+    $m->get_ok($URI, "GET to autocompleter");
+    return decode_json($m->content);
+}
+
+$m->login;
+is_deeply ac(CF => 12345, ContextId => 1, ContextType => "RT::Queue"),
+    [], 'nothing for invalid CF';
+
+is_deeply ac(),
+    [], "Nothing without a context id";
+is_deeply ac( ContextId => 12345, ContextType => "RT::Queue"),
+    [], "Nothing with invalid contextid id";
+is_deeply ac( ContextId => 12, ContextType => "RT::User"),
+    [], "Nothing with invalid contextid type";
+
+
+
+my $user = RT::Test->load_or_create_user(
+    Name        => 'user',
+    Password    => 'password',
+    Privileged  => 1,
+);
+my $queue = RT::Test->load_or_create_queue( Name => 'CF Test' );
+ok $queue->id, 'found or created queue';
+my $ticket = RT::Test->create_ticket(
+    Queue => $queue->id,
+    Subject => "CF application",
+);
+ok $queue->id, 'created ticket';
+
+$m->logout;
+$m->login('user','password');
+
+is_deeply ac( Context => $queue ), [], 'queue context, no permissions, no result';
+is_deeply ac( Context => $ticket ), [], 'ticket context, no permissions, no result';
+
+ok( RT::Test->set_rights(
+    { Principal => $user, Right => [qw(SeeCustomField)], Object => $queue },
+), 'add queue level CF viewing rights');
+
+my $cfvalues = [ ( map { { value => "cf1 value $_" , label => "cf1 value $_" } } qw(a b c) ) ];
+is_deeply ac( Context => $queue ), $cfvalues, 'queue context, with permissions get result';
+is_deeply ac( Context => $ticket ), $cfvalues, 'ticket context, with permissions get result';
+
+{
+    diag "Switching to non-global CFs";
+    my $globalq = RT::Queue->new( RT->SystemUser );
+    my ($status, $msg) = $cf1->RemoveFromObject( $globalq );
+    ok($status, "Removed CF1 globally: $msg");
+    ($status, $msg) = $cf1->AddToObject( $queue );
+    ok($status, "Added CF1 to queue @{[$queue->id]}: $msg");
+    ($status, $msg) = $cf2->RemoveFromObject( $globalq );
+    ok($status, "Removed CF2 globally: $msg");
+}
+
+is_deeply ac( CF => $cf2->id, Context => $queue ), [], 'queue context, but not applied, get no result';
+is_deeply ac( CF => $cf2->id, Context => $ticket ), [], 'ticket context, but not applied, get no result';
+
+is_deeply ac( Context => $queue ), $cfvalues, 'queue context, applied correctly, get result';
+is_deeply ac( Context => $ticket ), $cfvalues, 'ticket context, applied correctly, get result';
+
+
+
+diag "Ticket-level rights";
+
+ok( RT::Test->set_rights(
+    { Principal => "Owner", Right => [qw(SeeCustomField)], Object => $queue },
+    { Principal => $user,   Right => [qw(OwnTicket SeeTicket)], Object => RT->System },
+), 'add owner level CF viewing rights');
+
+is_deeply ac( Context => $queue ), [], 'queue context, but not owner';
+is_deeply ac( Context => $ticket ), [], 'ticket context, but not owner';
+
+my ($status, $msg) = $ticket->SetOwner( $user->id );
+ok( $status, "Set owner to user: $msg" );
+
+is_deeply ac( Context => $queue ), [], 'queue context is not enough';
+is_deeply ac( Context => $ticket ), $cfvalues, 'ticket context, get values';
+
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-2084-modifyscrips-templates.t b/t/security/CVE-2011-2084-modifyscrips-templates.t
new file mode 100644
index 0000000..f68706e
--- /dev/null
+++ b/t/security/CVE-2011-2084-modifyscrips-templates.t
@@ -0,0 +1,126 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+sub set_fails {
+    my $col  = shift;
+    my $obj  = shift;
+    my $to   = ref $_[0] ? +shift->Id : shift;
+    my $from = $obj->$col;
+    my $meth = "Set$col";
+
+    my ($ok, $msg) = $obj->$meth($to);
+    ok !$ok, "$meth denied: $msg";
+    is $obj->$col, $from, "$col left alone";
+}
+
+sub set_ok {
+    my $col  = shift;
+    my $obj  = shift;
+    my $to   = ref $_[0] ? +shift->Id : shift;
+    my $from = $obj->$col;
+    my $meth = "Set$col";
+
+    my ($ok, $msg) = $obj->$meth($to);
+    ok $ok, "$meth allowed: $msg";
+    is $obj->$col, $to, "$col updated";
+}
+
+my $qa = RT::Test->load_or_create_queue( Name => 'Queue A' );
+my $qb = RT::Test->load_or_create_queue( Name => 'Queue B' );
+ok $qa->id, "created Queue A";
+ok $qb->id, "created Queue B";
+
+my $user = RT::Test->load_or_create_user( Name => 'testuser' );
+my $cu   = RT::CurrentUser->new( $user );
+ok $user->id, "created testuser";
+
+diag "ModifyScrips";
+{
+    my $scrip = RT::Scrip->new( RT->SystemUser );
+    my ($scrip_id, $msg) = $scrip->Create(
+        Description     => 'Testing',
+        Queue           => $qa->Id,
+        ScripCondition  => 'User Defined',
+        ScripAction     => 'User Defined',
+        Template        => 'Blank',
+        CustomIsApplicableCode  => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}',
+        CustomPrepareCode       => '1;',
+        CustomCommitCode        => 'warn "scrip fired!";',
+    );
+    ok $scrip_id, $msg;
+
+    RT::Test->set_rights(
+        { Principal => $user, Right => 'ShowScrips' },
+        { Principal => $user, Right => 'ModifyScrips', Object => $qa },
+    );
+
+    $scrip = RT::Scrip->new( $cu );
+    $scrip->Load( $scrip_id );
+    ok $scrip->id, "loaded scrip as test user";
+    is $scrip->Queue, $qa->Id, 'queue is A';
+
+    ok +($scrip->SetName('Testing ModifyScrips'));
+
+    set_fails( Queue => $scrip => $qb );
+    set_fails( Queue => $scrip => 0 );
+    set_fails( Queue => $scrip => undef );
+    set_fails( Queue => $scrip => '' );
+
+    RT::Test->add_rights( Principal => $user, Right => 'ModifyScrips', Object => $qb );
+
+    set_ok( Queue => $scrip => $qb );
+    set_fails( Queue => $scrip => 0 );
+    set_fails( Queue => $scrip => undef );
+    set_fails( Queue => $scrip => '' );
+
+    RT::Test->add_rights( Principal => $user, Right => 'ModifyScrips' );
+
+    set_ok( Queue => $scrip => 0 );
+
+    set_fails( Template => $scrip => 2 );
+
+    RT::Test->add_rights( Principal => $user, Right => 'ShowTemplate' );
+
+    set_ok( Template => $scrip => 2 );
+    is $scrip->TemplateObj->Name, 'Autoreply', 'template name is right';
+}
+
+diag "ModifyTemplate";
+{
+    RT::Test->set_rights(
+        { Principal => $user, Right => 'ShowTemplate' },
+        { Principal => $user, Right => 'ModifyTemplate', Object => $qa },
+    );
+
+    my $template = RT::Template->new( RT->SystemUser );
+    my ($id, $msg) = $template->Create(
+        Queue   => $qa->Id,
+        Name    => 'Testing',
+        Type    => 'Perl',
+        Content => "\n\nThis is a test template.\n",
+    );
+    ok $id, $msg;
+
+    $template = RT::Template->new( $cu );
+    $template->Load( $id );
+    ok $template->id, "loaded template as test user";
+    is $template->Queue, $qa->Id, 'queue is A';
+
+    ok +($template->SetName('Testing ModifyTemplate'));
+
+    set_fails( Queue => $template => $qb );
+    set_fails( Queue => $template => 0 );
+
+    RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate', Object => $qb );
+
+    set_ok( Queue => $template => $qb );
+    set_fails( Queue => $template => 0 );
+
+    RT::Test->add_rights( Principal => $user, Right => 'ModifyTemplate' );
+
+    set_ok( Queue => $template => 0 );
+}
+
+done_testing;
diff --git a/t/security/CVE-2011-2084-transactions.t b/t/security/CVE-2011-2084-transactions.t
new file mode 100644
index 0000000..817288d
--- /dev/null
+++ b/t/security/CVE-2011-2084-transactions.t
@@ -0,0 +1,59 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+# A privileged user, but with no privs
+my $bad = RT::Test->load_or_create_user(
+    Name => 'testing',
+    EmailAddress => 'test at example.com',
+    Password => 'password',
+);
+ok( $bad, "Got a user object back" );
+ok( $bad->id, "Successfully created a user" );
+
+
+# A ticket CF
+my $obj = RT::Test->load_or_create_custom_field(
+    Name  => "Private CF",
+    Type  => "Freeform",
+    Queue => 0,
+);
+
+my ($t) = RT::Test->create_tickets( {},
+    { Subject => 'Testing' }
+);
+ok($t->id, "Created a ticket");
+
+# Add a txn on it
+my ($cfid) = $t->AddCustomFieldValue(
+    Field => $obj->Id,
+    Value => "hidden-value"
+);
+ok($cfid, "Got CF id $cfid");
+my $update_id = $t->Transactions->Last->Id;
+
+# Somebody else shouldn't be able to see the old and new values
+my ($base, $m) = RT::Test->started_ok;
+$m->post_ok("$base/REST/1.0/transaction/$update_id", [
+    user    => 'testing',
+    pass    => 'password',
+    format  => 'l',
+]);
+$m->content_lacks("hidden-value");
+
+# Make a transaction on a user
+my $root = RT::Test->load_or_create_user( Name => "root" );
+$root->SetHomePhone("hidden-value");
+$update_id = $root->Transactions->Last->Id;
+
+# Which should also be hidden from random privileged users
+$m->post_ok("$base/REST/1.0/transaction/$update_id", [
+    user    => 'testing',
+    pass    => 'password',
+    format  => 'l',
+]);
+$m->content_lacks("hidden-value");
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-4458-verp.t b/t/security/CVE-2011-4458-verp.t
new file mode 100644
index 0000000..f84b794
--- /dev/null
+++ b/t/security/CVE-2011-4458-verp.t
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+RT->Config->Set( MailCommand  => 'sendmailpipe' );
+RT->Config->Set( VERPPrefix   => "verp-" );
+RT->Config->Set( VERPDomain   => "example.com" );
+
+# Ensure that the fake sendmail knows where to write to
+$ENV{RT_MAILLOGFILE} = RT::Test->temp_directory . "/sendmailpipe.log";
+my $fake = File::Spec->rel2abs( File::Spec->catfile(
+        't', 'security', 'fake-sendmail' ) );
+RT->Config->Set( SendmailPath => $fake);
+
+ok(
+    RT::Test->set_rights(
+        { Principal => 'Everyone',  Right => [qw/CreateTicket/] },
+    ),
+    'set rights'
+);
+
+my $bad = RT::Test->load_or_create_user(
+    EmailAddress => 'danger-$USER at example.com',
+);
+ok( $bad, "Got a user object back" );
+ok( $bad->id, "Successfully created a user" );
+
+my $current_user = RT::CurrentUser->new(RT->SystemUser);
+my ($id, $msg) = $current_user->Load($bad->Id);
+ok( $id, "Loaded the user successfully" );
+
+my $ticket = RT::Ticket->new( $current_user );
+($id, $msg) = $ticket->Create(
+    Requestor => $bad->Id,
+    Subject   => "Danger, Will Robinson!",
+    Queue     => "General"
+);
+ok( $id, "Created a ticket: $msg" );
+
+open(LOG, "<", $ENV{RT_MAILLOGFILE}) or die "Can't open log file: $!";
+while (my $line = <LOG>) {
+    next unless $line =~ /^-f/;
+    like($line, qr/\$USER/, "Contains uninterpolated \$USER");
+}
+close(LOG);
+
+done_testing;
diff --git a/t/security/CVE-2011-4460-rows-per-page.t b/t/security/CVE-2011-4460-rows-per-page.t
new file mode 100644
index 0000000..92d6853
--- /dev/null
+++ b/t/security/CVE-2011-4460-rows-per-page.t
@@ -0,0 +1,32 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+plan skip_all => 'valid SQL only on mysql'
+    unless RT->Config->Get('DatabaseType') eq 'mysql';
+
+my ($base, $m) = RT::Test->started_ok;
+ok $m->login, "logged in";
+
+my $t = RT::Ticket->new( RT->SystemUser );
+$t->Create(
+    Queue   => 1,
+    Subject => 'seed',
+);
+ok $t->id, 'created seed ticket';
+
+my $root = RT::User->new( RT->SystemUser );
+$root->Load('root');
+my $password = $root->__Value('Password');
+ok $password, 'pulled hashed password from db';
+
+my $sql = q[1 union select 1+id as id, 1+id as EffectiveId, 1 as Queue, 'ticket' as Type, 0 as IssueStatement, 0 as Resolution, 12 as Owner, Password as Subject, 0 as InitialPriority, 0 as FinalPriority, 0 as Priority, 0 as TimeEstimated, 0 as TimeWorked, Name as Status, 0 as TimeLeft, null as Told, null as Starts, null as Started, null as Due, null as Resolved, 0 as LastUpdatedBy, null as LastUpdated, 6 as Creator, null as Created, 0 as Disabled from Users];
+RT::Interface::Web::EscapeURI(\$sql);
+
+$m->get_ok("$base/Search/Results.html?Format=id,Subject,Status;Query=id%3E0;OrderBy=|;Rows=$sql");
+$m->content_lacks($password, "our password hash doesn't show up!");
+$m->warning_like(qr/isn't numeric/);
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-5092-datetimeformat.t b/t/security/CVE-2011-5092-datetimeformat.t
new file mode 100644
index 0000000..470f4f4
--- /dev/null
+++ b/t/security/CVE-2011-5092-datetimeformat.t
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my ($base, $m) = RT::Test->started_ok;
+
+my $user = RT::Test->load_or_create_user(
+    Name        => 'user',
+    Password    => 'password',
+    Privileged  => 1,
+);
+
+ok $user->id, 'created user';
+
+ok(
+    RT::Test->set_rights(
+        { Principal => 'privileged', Right => [qw(ModifySelf ShowTicket)] },
+    ),
+    "granted ModifySelf to privileged"
+);
+
+my $ticket = RT::Test->create_ticket(
+    Queue   => 'General',
+    Subject => 'testing',
+);
+
+ok $ticket->id, 'created ticket';
+
+$m->login('user');
+$m->get_ok("$base/Prefs/Other.html");
+my $format = 'Formatters';
+$m->submit_form_ok({
+    form_name   => 'ModifyPreferences',
+    fields      => {
+        DateTimeFormat  => $format,
+    },
+    button      => 'Update',
+}, 'update prefs');
+is $user->Preferences(RT->System, {})->{DateTimeFormat}, $format, 'set preference';
+
+$m->no_warnings_ok;
+$m->get_ok("$base/Ticket/Display.html?id=" . $ticket->id);
+$m->next_warning_like(qr/Invalid date formatter.+?\Q$format\E/, 'invalid formatter warning');
+$m->content_lacks($_, "lacks formatter in page") for @RT::Date::FORMATTERS;
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-5092-graph-links.t b/t/security/CVE-2011-5092-graph-links.t
new file mode 100644
index 0000000..5e98dd3
--- /dev/null
+++ b/t/security/CVE-2011-5092-graph-links.t
@@ -0,0 +1,27 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my ($base, $m) = RT::Test->started_ok;
+$m->login;
+
+for my $arg (qw(LeadingLink ShowLinks)) {
+    my $ticket = RT::Test->create_ticket(
+        Queue   => 'General',
+        Subject => 'testing',
+    );
+    ok $ticket->id, 'created ticket';
+
+    ok !$ticket->ToldObj->Unix, 'no Told';
+    $m->get_ok("$base/Ticket/Graphs/index.html?$arg=SetTold;id=" . $ticket->id);
+
+    $ticket->Load($ticket->id); # cache busting
+
+    ok !$ticket->ToldObj->Unix, 'still no Told';
+    $m->content_lacks('GotoFirstItem', 'no GotoFirstItem error');
+    $m->content_like(qr|<img[^>]+?src=['"]/Ticket/Graphs/@{[$ticket->id]}|, 'found image element');
+}
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-5092-installmode.t b/t/security/CVE-2011-5092-installmode.t
new file mode 100644
index 0000000..ce88a4f
--- /dev/null
+++ b/t/security/CVE-2011-5092-installmode.t
@@ -0,0 +1,24 @@
+use strict;
+use warnings;
+
+BEGIN {
+    $ENV{RT_TEST_WEB_HANDLER} = 'inline';
+}
+
+use RT::Test tests => undef;
+use Test::Warn;
+
+my ($base, $m) = RT::Test->started_ok;
+
+$m->login;
+$m->content_like(qr/RT at a glance/i, 'homepage');
+
+warning_like {
+    ok !RT->InstallMode(1), 'install mode failed to turn on';
+} qr/tried to turn on InstallMode/;
+
+$m->reload;
+$m->content_like(qr/RT at a glance/i, 'still homepage');
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-5092-localizeddatetime.t b/t/security/CVE-2011-5092-localizeddatetime.t
new file mode 100644
index 0000000..733afc0
--- /dev/null
+++ b/t/security/CVE-2011-5092-localizeddatetime.t
@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $root = RT::CurrentUser->new('root');
+my ($ok, $msg) = $root->UserObj->SetLang('en-us');
+ok $ok, $msg;
+
+my $year = (localtime time)[5] + 1900;
+my $date = RT::Date->new( $root );
+$date->SetToNow;
+
+like $date->AsString( Format => 'LocalizedDateTime' ),
+     qr/\Q$year\E/, 'contains full year';
+
+unlike $date->AsString( Format => 'LocalizedDateTime', DateFormat => 'date_format_short' ),
+     qr/\Q$year\E/, 'lacks full year';
+
+eval {
+    $date->AsString( Format => 'LocalizedDateTime', DateFormat => 'bogus::format' );
+};
+ok !$@, "didn't die with bogus DateFormat";
+
+eval {
+    $date->AsString( Format => 'LocalizedDateTime', TimeFormat => 'bogus::format' );
+};
+ok !$@, "didn't die with bogus TimeFormat";
+
+done_testing;
diff --git a/t/security/CVE-2011-5092-prefs.t b/t/security/CVE-2011-5092-prefs.t
new file mode 100644
index 0000000..b8e15aa
--- /dev/null
+++ b/t/security/CVE-2011-5092-prefs.t
@@ -0,0 +1,77 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my ($base, $m) = RT::Test->started_ok;
+
+my $user = RT::Test->load_or_create_user(
+    Name            => 'ausername',
+    EmailAddress    => 'user at example.com',
+    Password        => 'password',
+    Privileged      => 1,
+);
+
+ok $user->id, 'created user';
+
+ok(
+    RT::Test->set_rights(
+        { Principal => 'privileged', Right => [qw(ModifySelf ShowTicket)] },
+    ),
+    "granted ModifySelf to privileged"
+);
+
+$m->login('ausername');
+
+{
+    $m->get_ok("$base/Prefs/Other.html");
+    my $style = '../css/base';
+    $m->submit_form_ok({
+        with_fields => {
+            WebDefaultStylesheet => $style,
+        },
+        button      => 'Update',
+    }, 'update prefs');
+    is(RT->Config->Get('WebDefaultStylesheet', $user), $style, 'set preference');
+
+    SKIP: {
+        skip "RT::User->Stylesheet wasn't backported", 1 unless $user->can("Stylesheet");
+        is $user->Stylesheet, RT->Config->Get('WebDefaultStylesheet'), '$user->Stylesheet is the default';
+    }
+
+    $m->get_ok($base);
+    $m->content_unlike(qr/<link.+?\Q$style\E/, "lack .. path in page <link>");
+    $m->content_contains( RT->Config->Get('WebDefaultStylesheet') );
+}
+
+{
+    $m->get_ok("$base/Prefs/Other.html");
+    my $format = '/../../m/_elements/full_site_link';
+    $m->submit_form_ok({
+        form_name   => 'ModifyPreferences',
+        fields      => {
+            UsernameFormat => $format,
+        },
+        button      => 'Update',
+    }, 'update prefs');
+    $m->content_contains('saved');
+
+    my $ticket = RT::Test->create_ticket(
+        Queue       => 'General',
+        Subject     => 'test ticket',
+        Requestor   => 'user at example.com',
+    );
+    ok $ticket->id, 'created ticket';
+    $m->get_ok($base . "/Ticket/Display.html?id=" . $ticket->id);
+    $m->content_lacks('NotMobile', "lacks NotMobile");
+    $m->next_warning_like(qr/UsernameFormat/, 'caught UsernameFormat warning');
+}
+
+{
+    $m->get_ok("$base/Helpers/Toggle/ShowRequestor?Status=/../../../Elements/Logo;Requestor=root");
+    $m->content_lacks('logo', "didn't display /Elements/Logo");
+    $m->content_contains('Results.html', "found link to search results");
+}
+
+undef $m;
+done_testing;
diff --git a/t/security/CVE-2011-5093-execute-code.t b/t/security/CVE-2011-5093-execute-code.t
new file mode 100644
index 0000000..5124ab8
--- /dev/null
+++ b/t/security/CVE-2011-5093-execute-code.t
@@ -0,0 +1,53 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $template = RT::Template->new( RT->SystemUser );
+my ($ok, $msg) = $template->Create(
+    Queue   => 0,
+    Name    => 'test',
+    Type    => 'Simple',
+    Content => <<'.',
+===Create-Ticket: testing
+Queue: General
+Subject: duplicate: { $Tickets{TOP}->Subject }
+.
+);
+ok $ok, $msg;
+
+my $ticket = RT::Test->create_ticket(
+    Queue   => 'General',
+    Subject => 'a ticket',
+);
+ok $ticket->id, "created ticket";
+
+for my $type (qw(Simple Perl)) {
+    if ($template->Type ne $type) {
+        my ($ok, $msg) = $template->SetType($type);
+        ok $ok, $msg;
+    }
+
+    require RT::Action::CreateTickets;
+    my $action = RT::Action::CreateTickets->new(
+        CurrentUser     => RT->SystemUser,
+        TemplateObj     => $template,
+        TicketObj       => $ticket,
+    );
+    $action->{TransactionObj} = $ticket->Transactions->First;
+    ok $action->Prepare, 'prepares';
+    ok $action->Commit, 'commits';
+
+    my $new_ticket = RT::Test->last_ticket;
+    ok $new_ticket->id > $ticket->id, 'new ticket';
+
+    if ($type eq 'Perl') {
+        is $new_ticket->Subject, 'duplicate: a ticket', 'interpolated';
+        isnt $new_ticket->Subject, 'duplicate: { $Tickets{TOP}->Subject }', 'interpolated';
+    } else {
+        isnt $new_ticket->Subject, 'duplicate: a ticket', 'not interpolated';
+        is $new_ticket->Subject, 'duplicate: { $Tickets{TOP}->Subject }', 'not interpolated';
+    }
+}
+
+done_testing;
diff --git a/t/security/fake-sendmail b/t/security/fake-sendmail
new file mode 100755
index 0000000..43259b6
--- /dev/null
+++ b/t/security/fake-sendmail
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+die "No \$RT_MAILLOGFILE set in environment"
+    unless $ENV{RT_MAILLOGFILE};
+open LOG, ">", $ENV{RT_MAILLOGFILE}
+    or die "Can't write to $ENV{RT_MAILLOGFILE}: $!";
+
+my $needs_newline;
+for (@ARGV) {
+    if (/^-/) {
+        print LOG "\n" if $needs_newline++;
+        print LOG $_;
+    } else {
+        print LOG " $_";
+    }
+}
+print LOG "\n";
+
+1 while $_ = <STDIN>;
+
+exit 0;

commit 09db186a0fbfa70eded2054e215e120ebc6eae7c
Merge: 7451d99 155e658
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Mon Nov 26 14:00:19 2012 -0500

    Merge branch 'security/4.0/tests' into 4.0-trunk


-----------------------------------------------------------------------


More information about the Rt-commit mailing list