X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Flib%2FRT%2FSearchBuilder.pm;h=e4a17f464bdb7ec63fe2bcee00ccb38459d50e5d;hp=178b66b4395008ca20eb3e757ff72770d652414b;hb=75162bb14b3e38d66617077843f4dfdcaf09d5c4;hpb=ef20b2b6b1feb47ad02b5ff7525f1a0fd11d0fa4 diff --git a/rt/lib/RT/SearchBuilder.pm b/rt/lib/RT/SearchBuilder.pm index 178b66b43..e4a17f464 100644 --- a/rt/lib/RT/SearchBuilder.pm +++ b/rt/lib/RT/SearchBuilder.pm @@ -1,40 +1,40 @@ # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC -# -# +# +# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC +# +# # (Except where explicitly superseded by other copyright notices) -# -# +# +# # LICENSE: -# +# # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. -# +# # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at -# http://www.gnu.org/copyleft/gpl.html. -# -# +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. +# +# # CONTRIBUTION SUBMISSION POLICY: -# +# # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) -# +# # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that @@ -43,8 +43,9 @@ # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. -# +# # END BPS TAGGED BLOCK }}} + =head1 NAME RT::SearchBuilder - a baseclass for RT collection objects @@ -57,11 +58,6 @@ =head1 METHODS -=begin testing - -ok (require RT::SearchBuilder); - -=end testing =cut @@ -72,10 +68,10 @@ use RT::Base; use DBIx::SearchBuilder "1.40"; use strict; -use vars qw(@ISA); -@ISA = qw(DBIx::SearchBuilder RT::Base); +use warnings; + +use base qw(DBIx::SearchBuilder RT::Base); -# {{{ sub _Init sub _Init { my $self = shift; @@ -88,26 +84,30 @@ sub _Init { } $self->SUPER::_Init( 'Handle' => $RT::Handle); } -# }}} -# {{{ sub LimitToEnabled +sub OrderByCols { + my $self = shift; + my @sort; + for my $s (@_) { + next if defined $s->{FIELD} and $s->{FIELD} =~ /\W/; + $s->{FIELD} = $s->{FUNCTION} if $s->{FUNCTION}; + push @sort, $s; + } + return $self->SUPER::OrderByCols( @sort ); +} =head2 LimitToEnabled -Only find items that haven\'t been disabled +Only find items that haven't been disabled =cut sub LimitToEnabled { my $self = shift; - - $self->Limit( FIELD => 'Disabled', - VALUE => '0', - OPERATOR => '=' ); -} -# }}} -# {{{ sub LimitToDisabled + $self->{'handled_disabled_column'} = 1; + $self->Limit( FIELD => 'Disabled', VALUE => '0' ); +} =head2 LimitToDeleted @@ -117,16 +117,20 @@ Only find items that have been deleted. sub LimitToDeleted { my $self = shift; - - $self->{'find_disabled_rows'} = 1; - $self->Limit( FIELD => 'Disabled', - OPERATOR => '=', - VALUE => '1' - ); + + $self->{'handled_disabled_column'} = $self->{'find_disabled_rows'} = 1; + $self->Limit( FIELD => 'Disabled', VALUE => '1' ); } -# }}} -# {{{ sub LimitAttribute +=head2 FindAllRows + +Find all matching rows, regardless of whether they are disabled or not + +=cut + +sub FindAllRows { + shift->{'find_disabled_rows'} = 1; +} =head2 LimitAttribute PARAMHASH @@ -140,17 +144,17 @@ If NULL is set, also select rows without the named Attribute. =cut -my %Negate = qw( - = != - != = - > <= - < >= - >= < - <= > - LIKE NOT LIKE - NOT LIKE LIKE - IS IS NOT - IS NOT IS +my %Negate = ( + '=' => '!=', + '!=' => '=', + '>' => '<=', + '<' => '>=', + '>=' => '<', + '<=' => '>', + 'LIKE' => 'NOT LIKE', + 'NOT LIKE' => 'LIKE', + 'IS' => 'IS NOT', + 'IS NOT' => 'IS', ); sub LimitAttribute { @@ -217,9 +221,6 @@ sub LimitAttribute { VALUE => 'NULL', ) if $args{NULL}; } -# }}} - -# {{{ sub LimitCustomField =head2 LimitCustomField @@ -278,39 +279,54 @@ sub LimitCustomField { ); } -# {{{ sub FindAllRows - -=head2 FindAllRows - -Find all matching rows, regardless of whether they are disabled or not - -=cut - -sub FindAllRows { - shift->{'find_disabled_rows'} = 1; -} - -# {{{ sub Limit - =head2 Limit PARAMHASH This Limit sub calls SUPER::Limit, but defaults "CASESENSITIVE" to 1, thus making sure that by default lots of things don't do extra work trying to match lower(colname) agaist lc($val); +We also force VALUE to C when the OPERATOR is C or C. +This ensures that we don't pass invalid SQL to the database or allow SQL +injection attacks when we pass through user specified values. + =cut sub Limit { my $self = shift; - my %args = ( CASESENSITIVE => 1, - @_ ); - - return $self->SUPER::Limit(%args); -} + my %ARGS = ( + CASESENSITIVE => 1, + OPERATOR => '=', + @_, + ); -# }}} + # We use the same regex here that DBIx::SearchBuilder uses to exclude + # values from quoting + if ( $ARGS{'OPERATOR'} =~ /IS/i ) { + # Don't pass anything but NULL for IS and IS NOT + $ARGS{'VALUE'} = 'NULL'; + } -# {{{ sub ItemsOrderBy + if ($ARGS{FUNCTION}) { + ($ARGS{ALIAS}, $ARGS{FIELD}) = split /\./, delete $ARGS{FUNCTION}, 2; + $self->SUPER::Limit(%ARGS); + } elsif ($ARGS{FIELD} =~ /\W/ + or $ARGS{OPERATOR} !~ /^(=|<|>|!=|<>|<=|>= + |(NOT\s*)?LIKE + |(NOT\s*)?(STARTS|ENDS)WITH + |(NOT\s*)?MATCHES + |IS(\s*NOT)? + |IN)$/ix) { + $RT::Logger->crit("Possible SQL injection attack: $ARGS{FIELD} $ARGS{OPERATOR}"); + $self->SUPER::Limit( + %ARGS, + FIELD => 'id', + OPERATOR => '<', + VALUE => '0', + ); + } else { + $self->SUPER::Limit(%ARGS); + } +} =head2 ItemsOrderBy @@ -335,55 +351,44 @@ sub ItemsOrderBy { return $items; } -# }}} - -# {{{ sub ItemsArrayRef - =head2 ItemsArrayRef Return this object's ItemsArray, in the order that ItemsOrderBy sorts it. -=begin testing - -use_ok(RT::Queues); -ok(my $queues = RT::Queues->new($RT::SystemUser), 'Created a queues object'); -ok( $queues->UnLimit(),'Unlimited the result set of the queues object'); -my $items = $queues->ItemsArrayRef(); -my @items = @{$items}; - -ok($queues->NewItem->_Accessible('Name','read')); -my @sorted = sort {lc($a->Name) cmp lc($b->Name)} @items; -ok (@sorted, "We have an array of queues, sorted". join(',',map {$_->Name} @sorted)); - -ok (@items, "We have an array of queues, raw". join(',',map {$_->Name} @items)); -my @sorted_ids = map {$_->id } @sorted; -my @items_ids = map {$_->id } @items; - -is ($#sorted, $#items); -is ($sorted[0]->Name, $items[0]->Name); -is ($sorted[-1]->Name, $items[-1]->Name); -is_deeply(\@items_ids, \@sorted_ids, "ItemsArrayRef sorts alphabetically by name");; - - -=end testing - =cut sub ItemsArrayRef { my $self = shift; - my @items; - return $self->ItemsOrderBy($self->SUPER::ItemsArrayRef()); } -# }}} +# make sure that Disabled rows never get seen unless +# we're explicitly trying to see them. -eval "require RT::SearchBuilder_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Vendor.pm}); -eval "require RT::SearchBuilder_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Local.pm}); +sub _DoSearch { + my $self = shift; -1; + if ( $self->{'with_disabled_column'} + && !$self->{'handled_disabled_column'} + && !$self->{'find_disabled_rows'} + ) { + $self->LimitToEnabled; + } + return $self->SUPER::_DoSearch(@_); +} +sub _DoCount { + my $self = shift; + if ( $self->{'with_disabled_column'} + && !$self->{'handled_disabled_column'} + && !$self->{'find_disabled_rows'} + ) { + $self->LimitToEnabled; + } + return $self->SUPER::_DoCount(@_); +} + +RT::Base->_ImportOverlays(); +1;