Commit 89b15c8f authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 491467: Make Search.pm and buglist.cgi consistently take column ids for the…

Bug 491467: Make Search.pm and buglist.cgi consistently take column ids for the "fields" and "order" arguments, to prevent problems with using SQL fragments in the order and columnlist. Patch by Max Kanat-Alexander <mkanat@bugzilla.org> r=wicked, a=mkanat
parent 6a51c4c3
...@@ -52,7 +52,6 @@ use Storable qw(dclone); ...@@ -52,7 +52,6 @@ use Storable qw(dclone);
use constant BLOB_TYPE => DBI::SQL_BLOB; use constant BLOB_TYPE => DBI::SQL_BLOB;
use constant ISOLATION_LEVEL => 'REPEATABLE READ'; use constant ISOLATION_LEVEL => 'REPEATABLE READ';
use constant GROUPBY_REGEXP => '(?:.*\s+AS\s+|SUBSTRING\()?(\w+(\.\w+)?)(?:\s+(ASC|DESC|FROM\s+.+))?$';
# Set default values for what used to be the enum types. These values # Set default values for what used to be the enum types. These values
# are no longer stored in localconfig. If we are upgrading from a # are no longer stored in localconfig. If we are upgrading from a
......
...@@ -52,7 +52,6 @@ use base qw(Bugzilla::DB); ...@@ -52,7 +52,6 @@ use base qw(Bugzilla::DB);
use constant EMPTY_STRING => '__BZ_EMPTY_STR__'; use constant EMPTY_STRING => '__BZ_EMPTY_STR__';
use constant ISOLATION_LEVEL => 'READ COMMITTED'; use constant ISOLATION_LEVEL => 'READ COMMITTED';
use constant BLOB_TYPE => { ora_type => ORA_BLOB }; use constant BLOB_TYPE => { ora_type => ORA_BLOB };
use constant GROUPBY_REGEXP => '((CASE\s+WHEN.+END)|(SUBSTR.+\))|(TO_CHAR\(.+\))|(\(SCORE.+\))|(\(MATCH.+\))|(\w+(\.\w+)?))(\s+AS\s+)?(.*)?$';
sub new { sub new {
my ($class, $user, $pass, $host, $dbname, $port) = @_; my ($class, $user, $pass, $host, $dbname, $port) = @_;
......
...@@ -561,7 +561,7 @@ sub CollectSeriesData { ...@@ -561,7 +561,7 @@ sub CollectSeriesData {
# login name or a renamed product or component, etc. # login name or a renamed product or component, etc.
eval { eval {
my $search = new Bugzilla::Search('params' => $cgi, my $search = new Bugzilla::Search('params' => $cgi,
'fields' => ["bugs.bug_id"], 'fields' => ["bug_id"],
'user' => $user); 'user' => $user);
my $sql = $search->getSQL(); my $sql = $search->getSQL();
$data = $shadow_dbh->selectall_arrayref($sql); $data = $shadow_dbh->selectall_arrayref($sql);
......
...@@ -199,14 +199,14 @@ if (scalar(%count)) { ...@@ -199,14 +199,14 @@ if (scalar(%count)) {
$params->param('product', join(',', @query_products)); $params->param('product', join(',', @query_products));
} }
my $query = new Bugzilla::Search('fields' => [qw(bugs.bug_id my $query = new Bugzilla::Search('fields' => [qw(bug_id
map_components.name component
bugs.bug_severity bug_severity
bugs.op_sys op_sys
bugs.target_milestone target_milestone
bugs.short_desc short_desc
bugs.bug_status bug_status
bugs.resolution resolution
) )
], ],
'params' => $params, 'params' => $params,
......
...@@ -102,53 +102,42 @@ else { ...@@ -102,53 +102,42 @@ else {
} }
} }
my %columns; # Valid bug fields that can be reported on.
$columns{'bug_severity'} = "bugs.bug_severity"; my @columns = qw(
$columns{'priority'} = "bugs.priority"; assigned_to
$columns{'rep_platform'} = "bugs.rep_platform"; reporter
$columns{'assigned_to'} = "map_assigned_to.login_name"; qa_contact
$columns{'reporter'} = "map_reporter.login_name"; component
$columns{'qa_contact'} = "map_qa_contact.login_name"; classification
$columns{'bug_status'} = "bugs.bug_status"; version
$columns{'resolution'} = "bugs.resolution"; votes
$columns{'component'} = "map_components.name"; keywords
$columns{'product'} = "map_products.name"; target_milestone
$columns{'classification'} = "map_classifications.name"; );
$columns{'version'} = "bugs.version"; # Single-select fields (custom or not) are also accepted as valid.
$columns{'op_sys'} = "bugs.op_sys"; my @single_selects = Bugzilla->get_fields({ type => FIELD_TYPE_SINGLE_SELECT,
$columns{'votes'} = "bugs.votes"; obsolete => 0 });
$columns{'keywords'} = "bugs.keywords"; push(@columns, map { $_->name } @single_selects);
$columns{'target_milestone'} = "bugs.target_milestone"; my %valid_columns = map { $_ => 1 } @columns;
# Single-select fields are also accepted as valid column names.
my @single_select_fields =
grep { $_->type == FIELD_TYPE_SINGLE_SELECT } Bugzilla->active_custom_fields;
foreach my $custom_field (@single_select_fields) {
my $field_name = $custom_field->name;
$columns{$field_name} = "bugs.$field_name";
}
# One which means "nothing". Any number would do, really. It just gets SELECTed
# so that we always select 3 items in the query.
$columns{''} = "42217354";
# Validate the values in the axis fields or throw an error. # Validate the values in the axis fields or throw an error.
!$row_field !$row_field
|| ($columns{$row_field} && trick_taint($row_field)) || ($valid_columns{$row_field} && trick_taint($row_field))
|| ThrowCodeError("report_axis_invalid", {fld => "x", val => $row_field}); || ThrowCodeError("report_axis_invalid", {fld => "x", val => $row_field});
!$col_field !$col_field
|| ($columns{$col_field} && trick_taint($col_field)) || ($valid_columns{$col_field} && trick_taint($col_field))
|| ThrowCodeError("report_axis_invalid", {fld => "y", val => $col_field}); || ThrowCodeError("report_axis_invalid", {fld => "y", val => $col_field});
!$tbl_field !$tbl_field
|| ($columns{$tbl_field} && trick_taint($tbl_field)) || ($valid_columns{$tbl_field} && trick_taint($tbl_field))
|| ThrowCodeError("report_axis_invalid", {fld => "z", val => $tbl_field}); || ThrowCodeError("report_axis_invalid", {fld => "z", val => $tbl_field});
my @axis_fields = ($row_field, $col_field, $tbl_field); my @axis_fields = ($row_field || EMPTY_COLUMN,
my @selectnames = map($columns{$_}, @axis_fields); $col_field || EMPTY_COLUMN,
$tbl_field || EMPTY_COLUMN);
# Clone the params, so that Bugzilla::Search can modify them # Clone the params, so that Bugzilla::Search can modify them
my $params = new Bugzilla::CGI($cgi); my $params = new Bugzilla::CGI($cgi);
my $search = new Bugzilla::Search('fields' => \@selectnames, my $search = new Bugzilla::Search('fields' => \@axis_fields,
'params' => $params); 'params' => $params);
my $query = $search->getSQL(); my $query = $search->getSQL();
...@@ -179,9 +168,9 @@ foreach my $result (@$results) { ...@@ -179,9 +168,9 @@ foreach my $result (@$results) {
$col = ' ' if ($col eq ''); $col = ' ' if ($col eq '');
$tbl = ' ' if ($tbl eq ''); $tbl = ' ' if ($tbl eq '');
$row = "" if ($row eq $columns{''}); $row = "" if ($row eq EMPTY_COLUMN);
$col = "" if ($col eq $columns{''}); $col = "" if ($col eq EMPTY_COLUMN);
$tbl = "" if ($tbl eq $columns{''}); $tbl = "" if ($tbl eq EMPTY_COLUMN);
# account for the fact that names may start with '_' or '.'. Change this # account for the fact that names may start with '_' or '.'. Change this
# so the template doesn't hide hash elements with those keys # so the template doesn't hide hash elements with those keys
......
...@@ -87,11 +87,11 @@ ...@@ -87,11 +87,11 @@
[% END %] [% END %]
<th colspan="[% splitheader ? 2 : 1 %]" class="first-child"> <th colspan="[% splitheader ? 2 : 1 %]" class="first-child">
[% desc = '' %] [% desc = '' %]
[% IF (om = order.match("^bugs\.bug_id( desc)?")) %] [% IF (om = order.match("^bug_id( DESC)?")) %]
[% desc = ' desc' IF NOT om.0 %] [% desc = ' DESC' IF NOT om.0 %]
[% END %] [% END %]
<a href="buglist.cgi? <a href="buglist.cgi?
[% urlquerypart FILTER html %]&amp;order=bugs.bug_id[% desc FILTER url_quote %] [% urlquerypart FILTER html %]&amp;order=bug_id[% desc FILTER url_quote %]
[%-#%]&amp;query_based_on= [%-#%]&amp;query_based_on=
[% defaultsavename OR searchname FILTER url_quote %]">ID</a> [% defaultsavename OR searchname FILTER url_quote %]">ID</a>
</th> </th>
...@@ -130,20 +130,13 @@ ...@@ -130,20 +130,13 @@
[% BLOCK columnheader %] [% BLOCK columnheader %]
<th colspan="[% splitheader ? 2 : 1 %]"> <th colspan="[% splitheader ? 2 : 1 %]">
[% IF column.name.match('\s+AS\s+') %]
[%# For aliased columns, use their ID for sorting. %]
[% column.sortalias = id %]
[% ELSE %]
[%# Other columns may sort on their name directly. %]
[% column.sortalias = column.name %]
[% END %]
[% desc = '' %] [% desc = '' %]
[% IF (om = order.match("$column.sortalias( desc)?")) %] [% IF (om = order.match("$id( DESC)?")) %]
[% desc = ' desc' IF NOT om.0 %] [% desc = ' DESC' IF NOT om.0 %]
[% END %] [% END %]
[% order = order.remove("$column.sortalias( desc)?,?") %] [% order = order.remove("$id( DESC)?,?") %]
<a href="buglist.cgi?[% urlquerypart FILTER html %]&amp;order= <a href="buglist.cgi?[% urlquerypart FILTER html %]&amp;order=
[% column.sortalias FILTER url_quote %][% desc FILTER url_quote %] [% id FILTER url_quote %][% desc FILTER url_quote %]
[% ",$order" FILTER url_quote IF order %] [% ",$order" FILTER url_quote IF order %]
[%-#%]&amp;query_based_on= [%-#%]&amp;query_based_on=
[% defaultsavename OR searchname FILTER url_quote %]"> [% defaultsavename OR searchname FILTER url_quote %]">
...@@ -204,6 +197,13 @@ ...@@ -204,6 +197,13 @@
[%- get_status(bug.$column).truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html %] [%- get_status(bug.$column).truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html %]
[% ELSIF column == 'resolution' %] [% ELSIF column == 'resolution' %]
[%- get_resolution(bug.$column).truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html %] [%- get_resolution(bug.$column).truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html %]
[%# Display the login name of the user if their real name is empty. %]
[% ELSIF column.match('_realname$') && bug.$column == '' %]
[% SET login_column = column.remove('_realname$') %]
[% bug.${login_column}.truncate(abbrev.$column.maxlength,
abbrev.$column.ellipsis) FILTER html %]
[% ELSE %] [% ELSE %]
[%- bug.$column.truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html -%] [%- bug.$column.truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html -%]
[% END %] [% END %]
......
...@@ -431,16 +431,16 @@ sub run_queries { ...@@ -431,16 +431,16 @@ sub run_queries {
next unless $savedquery; # silently ignore missing queries next unless $savedquery; # silently ignore missing queries
# Execute the saved query # Execute the saved query
my @searchfields = ( my @searchfields = qw(
'bugs.bug_id', bug_id
'bugs.bug_severity', bug_severity
'bugs.priority', priority
'bugs.rep_platform', rep_platform
'bugs.assigned_to', assigned_to
'bugs.bug_status', bug_status
'bugs.resolution', resolution
'bugs.short_desc', short_desc
'map_assigned_to.login_name', assigned_to
); );
# A new Bugzilla::CGI object needs to be created to allow # A new Bugzilla::CGI object needs to be created to allow
# Bugzilla::Search to execute a saved query. It's exceedingly weird, # Bugzilla::Search to execute a saved query. It's exceedingly weird,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment