summaryrefslogtreecommitdiff
path: root/rt/t/security
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2014-09-15 20:44:48 -0700
committerIvan Kohler <ivan@freeside.biz>2014-09-15 20:44:48 -0700
commited1f84b4e8f626245995ecda5afcf83092c153b2 (patch)
tree3f58bbef5fbf2502e65d29b37b5dbe537519e89d /rt/t/security
parentfe9ea9183e8a16616d6d04a7b5c7498d28e78248 (diff)
RT 4.0.22
Diffstat (limited to 'rt/t/security')
-rw-r--r--rt/t/security/CVE-2011-2083-cf-urls.t48
-rw-r--r--rt/t/security/CVE-2011-2083-clickable-xss.t52
-rw-r--r--rt/t/security/CVE-2011-2083-scrub.t18
-rw-r--r--rt/t/security/CVE-2011-2084-attach-tickets.t64
-rw-r--r--rt/t/security/CVE-2011-2084-cf-values.t132
-rw-r--r--rt/t/security/CVE-2011-2084-modifyscrips-templates.t126
-rw-r--r--rt/t/security/CVE-2011-2084-transactions.t59
-rw-r--r--rt/t/security/CVE-2011-4458-verp.t48
-rw-r--r--rt/t/security/CVE-2011-4460-rows-per-page.t32
-rw-r--r--rt/t/security/CVE-2011-5092-datetimeformat.t48
-rw-r--r--rt/t/security/CVE-2011-5092-graph-links.t27
-rw-r--r--rt/t/security/CVE-2011-5092-installmode.t24
-rw-r--r--rt/t/security/CVE-2011-5092-localizeddatetime.t30
-rw-r--r--rt/t/security/CVE-2011-5092-prefs.t77
-rw-r--r--rt/t/security/CVE-2011-5093-execute-code.t53
-rw-r--r--rt/t/security/fake-sendmail24
16 files changed, 862 insertions, 0 deletions
diff --git a/rt/t/security/CVE-2011-2083-cf-urls.t b/rt/t/security/CVE-2011-2083-cf-urls.t
new file mode 100644
index 000000000..b1e1f3b0f
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-2083-clickable-xss.t b/rt/t/security/CVE-2011-2083-clickable-xss.t
new file mode 100644
index 000000000..008c80378
--- /dev/null
+++ b/rt/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&#39;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/rt/t/security/CVE-2011-2083-scrub.t b/rt/t/security/CVE-2011-2083-scrub.t
new file mode 100644
index 000000000..f05378398
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-2084-attach-tickets.t b/rt/t/security/CVE-2011-2084-attach-tickets.t
new file mode 100644
index 000000000..d7352cb85
--- /dev/null
+++ b/rt/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@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@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/rt/t/security/CVE-2011-2084-cf-values.t b/rt/t/security/CVE-2011-2084-cf-values.t
new file mode 100644
index 000000000..1178b15af
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-2084-modifyscrips-templates.t b/rt/t/security/CVE-2011-2084-modifyscrips-templates.t
new file mode 100644
index 000000000..f68706e52
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-2084-transactions.t b/rt/t/security/CVE-2011-2084-transactions.t
new file mode 100644
index 000000000..817288ded
--- /dev/null
+++ b/rt/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@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/rt/t/security/CVE-2011-4458-verp.t b/rt/t/security/CVE-2011-4458-verp.t
new file mode 100644
index 000000000..f84b79403
--- /dev/null
+++ b/rt/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@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/rt/t/security/CVE-2011-4460-rows-per-page.t b/rt/t/security/CVE-2011-4460-rows-per-page.t
new file mode 100644
index 000000000..92d6853e5
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-5092-datetimeformat.t b/rt/t/security/CVE-2011-5092-datetimeformat.t
new file mode 100644
index 000000000..470f4f4f6
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-5092-graph-links.t b/rt/t/security/CVE-2011-5092-graph-links.t
new file mode 100644
index 000000000..5e98dd3b5
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-5092-installmode.t b/rt/t/security/CVE-2011-5092-installmode.t
new file mode 100644
index 000000000..ce88a4fec
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-5092-localizeddatetime.t b/rt/t/security/CVE-2011-5092-localizeddatetime.t
new file mode 100644
index 000000000..733afc08a
--- /dev/null
+++ b/rt/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/rt/t/security/CVE-2011-5092-prefs.t b/rt/t/security/CVE-2011-5092-prefs.t
new file mode 100644
index 000000000..b8e15aae0
--- /dev/null
+++ b/rt/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@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@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/rt/t/security/CVE-2011-5093-execute-code.t b/rt/t/security/CVE-2011-5093-execute-code.t
new file mode 100644
index 000000000..5124ab88b
--- /dev/null
+++ b/rt/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/rt/t/security/fake-sendmail b/rt/t/security/fake-sendmail
new file mode 100644
index 000000000..43259b603
--- /dev/null
+++ b/rt/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;