diff options
37 files changed, 415 insertions, 112 deletions
diff --git a/.bzrignore b/.bzrignore new file mode 100644 index 000000000..7ab83e7ad --- /dev/null +++ b/.bzrignore @@ -0,0 +1,32 @@ +.htaccess +/lib/* +/template/en/custom +/docs/bugzilla.ent +/docs/en/xml/bugzilla.ent +/docs/en/txt +/docs/en/html +/docs/en/pdf +/skins/custom +/graphs +/data +/localconfig +/index.html + +/skins/contrib/Dusk/IE-fixes.css +/skins/contrib/Dusk/admin.css +/skins/contrib/Dusk/attachment.css +/skins/contrib/Dusk/create_attachment.css +/skins/contrib/Dusk/dependency-tree.css +/skins/contrib/Dusk/duplicates.css +/skins/contrib/Dusk/editusers.css +/skins/contrib/Dusk/enter_bug.css +/skins/contrib/Dusk/help.css +/skins/contrib/Dusk/panel.css +/skins/contrib/Dusk/page.css +/skins/contrib/Dusk/params.css +/skins/contrib/Dusk/reports.css +/skins/contrib/Dusk/show_bug.css +/skins/contrib/Dusk/search_form.css +/skins/contrib/Dusk/show_multiple.css +/skins/contrib/Dusk/summarize-time.css +.DS_Store diff --git a/.gitignore b/.gitignore index 519f6c3b6..7ab83e7ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,32 @@ -localconfig +.htaccess +/lib/* +/template/en/custom +/docs/bugzilla.ent +/docs/en/xml/bugzilla.ent +/docs/en/txt +/docs/en/html +/docs/en/pdf +/skins/custom +/graphs +/data +/localconfig +/index.html + +/skins/contrib/Dusk/IE-fixes.css +/skins/contrib/Dusk/admin.css +/skins/contrib/Dusk/attachment.css +/skins/contrib/Dusk/create_attachment.css +/skins/contrib/Dusk/dependency-tree.css +/skins/contrib/Dusk/duplicates.css +/skins/contrib/Dusk/editusers.css +/skins/contrib/Dusk/enter_bug.css +/skins/contrib/Dusk/help.css +/skins/contrib/Dusk/panel.css +/skins/contrib/Dusk/page.css +/skins/contrib/Dusk/params.css +/skins/contrib/Dusk/reports.css +/skins/contrib/Dusk/show_bug.css +/skins/contrib/Dusk/search_form.css +/skins/contrib/Dusk/show_multiple.css +/skins/contrib/Dusk/summarize-time.css +.DS_Store diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..94c9ce1d2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,48 @@ +language: perl +perl: + - 5.10 + - 5.12 + +env: + - TEST_SUITE=sanity + - TEST_SUITE=docs + - TEST_SUITE=webservices DB=mysql + - TEST_SUITE=selenium DB=mysql + - TEST_SUITE=webservices DB=pg + - TEST_SUITE=selenium DB=pg + +matrix: + exclude: + - perl: 5.12 + env: TEST_SUITE=docs + - perl: 5.10 + env: TEST_SUITE=webservices DB=mysql + - perl: 5.12 + env: TEST_SUITE=selenium DB=mysql + - perl: 5.10 + env: TEST_SUITE=webservices DB=pg + - perl: 5.12 + env: TEST_SUITE=selenium DB=pg + +before_install: + - git clone https://github.com/bugzilla/qa.git -b 4.4 qa + +install: true + +script: ./qa/travis.sh + +after_failure: + - sudo cat /var/log/apache2/error.log + +notifications: + irc: + channels: + - "irc.mozilla.org#qa-bugzilla" + - "irc.mozilla.org#bugzilla" + template: + - "Bugzilla %{branch} : %{author} : %{message}" + - "Commit Message : %{commit_message}" + - "Commit Link : %{compare_url}" + - "Build Link : %{build_url}" + on_success: change + on_failure: always diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index d4d94b23f..d0e8f462b 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -911,12 +911,6 @@ sub update { join(', ', @added_names)]; } - # Flags - my ($removed, $added) = Bugzilla::Flag->update_flags($self, $old_bug, $delta_ts); - if ($removed || $added) { - $changes->{'flagtypes.name'} = [$removed, $added]; - } - # Comments foreach my $comment (@{$self->{added_comments} || []}) { # Override the Comment's timestamp to be identical to the update @@ -939,6 +933,9 @@ sub update { Bugzilla->user->id, $delta_ts, $comment->id); } + # Clear the cache of comments + delete $self->{comments}; + # Insert the values into the multiselect value tables my @multi_selects = grep {$_->type == FIELD_TYPE_MULTI_SELECT} Bugzilla->active_custom_fields; @@ -971,6 +968,12 @@ sub update { join(', ', map { $_->name } @$added_see)]; } + # Flags + my ($removed, $added) = Bugzilla::Flag->update_flags($self, $old_bug, $delta_ts); + if ($removed || $added) { + $changes->{'flagtypes.name'} = [$removed, $added]; + } + $_->update foreach @{ $self->{_update_ref_bugs} || [] }; delete $self->{_update_ref_bugs}; diff --git a/Bugzilla/Chart.pm b/Bugzilla/Chart.pm index 0a655769f..e343a0535 100644 --- a/Bugzilla/Chart.pm +++ b/Bugzilla/Chart.pm @@ -94,10 +94,9 @@ sub init { if ($self->{'datefrom'} && $self->{'dateto'} && $self->{'datefrom'} > $self->{'dateto'}) { - ThrowUserError("misarranged_dates", - {'datefrom' => $cgi->param('datefrom'), - 'dateto' => $cgi->param('dateto')}); - } + ThrowUserError('misarranged_dates', { 'datefrom' => scalar $cgi->param('datefrom'), + 'dateto' => scalar $cgi->param('dateto') }); + } } # Alter Chart so that the selected series are added to it. diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 27887ae0d..314767251 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -182,8 +182,7 @@ use Memoize; # CONSTANTS # # Bugzilla version -use constant BUGZILLA_VERSION => "4.4.5"; - +use constant BUGZILLA_VERSION => "4.4.6"; # Location of the remote and local XML files to track new releases. use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml'; diff --git a/Bugzilla/DB/Mysql.pm b/Bugzilla/DB/Mysql.pm index c7ce1927a..dc93b7406 100644 --- a/Bugzilla/DB/Mysql.pm +++ b/Bugzilla/DB/Mysql.pm @@ -70,17 +70,18 @@ sub new { $self->{private_bz_dsn} = $dsn; bless ($self, $class); - - # Bug 321645 - disable MySQL strict mode, if set + + # Check for MySQL modes. my ($var, $sql_mode) = $self->selectrow_array( "SHOW VARIABLES LIKE 'sql\\_mode'"); + # Disable ANSI and strict modes, else Bugzilla will crash. if ($sql_mode) { # STRICT_TRANS_TABLE or STRICT_ALL_TABLES enable MySQL strict mode, # causing bug 321645. TRADITIONAL sets these modes (among others) as # well, so it has to be stipped as well my $new_sql_mode = - join(",", grep {$_ !~ /^STRICT_(?:TRANS|ALL)_TABLES|TRADITIONAL$/} + join(",", grep {$_ !~ /^(?:ANSI|STRICT_(?:TRANS|ALL)_TABLES|TRADITIONAL)$/} split(/,/, $sql_mode)); if ($sql_mode ne $new_sql_mode) { diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 3cba94c88..affeaee68 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -455,14 +455,15 @@ sub create { sub update { my $self = shift; my $dbh = Bugzilla->dbh; - my $timestamp = shift || $dbh->selectrow_array('SELECT NOW()'); + my $timestamp = shift || $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); my $changes = $self->SUPER::update(@_); if (scalar(keys %$changes)) { $dbh->do('UPDATE flags SET modification_date = ? WHERE id = ?', undef, ($timestamp, $self->id)); - $self->{'modification_date'} = format_time($timestamp, '%Y.%m.%d %T'); + $self->{'modification_date'} = + format_time($timestamp, '%Y.%m.%d %T', Bugzilla->local_timezone); } return $changes; } @@ -1006,18 +1007,32 @@ sub notify { $default_lang = Bugzilla::User->new()->setting('lang'); } + # Get comments on the bug + my $all_comments = $bug->comments({ after => $bug->lastdiffed }); + @$all_comments = grep { $_->type || $_->body =~ /\S/ } @$all_comments; + + # Get public only comments + my $public_comments = [ grep { !$_->is_private } @$all_comments ]; + foreach my $to (keys %recipients) { # Add threadingmarker to allow flag notification emails to be the # threaded similar to normal bug change emails. my $thread_user_id = $recipients{$to} ? $recipients{$to}->id : 0; - my $vars = { 'flag' => $flag, - 'old_flag' => $old_flag, - 'to' => $to, - 'date' => $timestamp, - 'bug' => $bug, - 'attachment' => $attachment, - 'threadingmarker' => build_thread_marker($bug->id, $thread_user_id) }; + # We only want to show private comments to users in the is_insider group + my $comments = $recipients{$to} && $recipients{$to}->is_insider + ? $all_comments : $public_comments; + + my $vars = { + flag => $flag, + old_flag => $old_flag, + to => $to, + date => $timestamp, + bug => $bug, + attachment => $attachment, + threadingmarker => build_thread_marker($bug->id, $thread_user_id), + new_comments => $comments, + }; my $lang = $recipients{$to} ? $recipients{$to}->setting('lang') : $default_lang; diff --git a/Bugzilla/Group.pm b/Bugzilla/Group.pm index 04c36f694..5404dec7e 100644 --- a/Bugzilla/Group.pm +++ b/Bugzilla/Group.pm @@ -53,7 +53,7 @@ use constant UPDATE_COLUMNS => qw( # Parameters that are lists of groups. use constant GROUP_PARAMS => qw(chartgroup insidergroup timetrackinggroup - querysharegroup); + querysharegroup debug_group); ############################### #### Accessors ###### diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index fab13b7e3..019809c60 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -720,10 +720,12 @@ sub create { }, # In CSV, quotes are doubled, and any value containing a quote or a - # comma is enclosed in quotes. + # comma is enclosed in quotes. If a field starts with an equals + # sign, it is proceed by a space. csv => sub { my ($var) = @_; + $var = ' ' . $var if substr($var, 0, 1) eq '='; $var =~ s/\"/\"\"/g; if ($var !~ /^-?(\d+\.)?\d*$/) { $var = "\"$var\""; diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index 164ff40bf..4bd10e16c 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -569,10 +569,14 @@ sub datetime_from { return undef if !@time; - # strptime() counts years from 1900, and months from 0 (January). - # We have to fix both values. + # strptime() counts years from 1900, except if they are older than 1901 + # in which case it returns the full year (so 1890 -> 1890, but 1984 -> 84, + # and 3790 -> 1890). We make a guess and assume that 1100 <= year < 3000. + $time[5] += 1900 if $time[5] < 1100; + my %args = ( - year => $time[5] + 1900, + year => $time[5], + # Months start from 0 (January). month => $time[4] + 1, day => $time[3], hour => $time[2], diff --git a/Bugzilla/WebService.pm b/Bugzilla/WebService.pm index 2a0e8890f..ac4cd25ce 100644 --- a/Bugzilla/WebService.pm +++ b/Bugzilla/WebService.pm @@ -269,7 +269,7 @@ hashes. Some RPC calls support specifying sub fields. If an RPC call states that it support sub field restrictions, you can restrict what information is -returned within the first field. For example, if you call Products.get +returned within the first field. For example, if you call Product.get with an include_fields of components.name, then only the component name would be returned (and nothing else). You can include the main field, and exclude a sub field. diff --git a/Build.PL b/Build.PL new file mode 100644 index 000000000..024a56024 --- /dev/null +++ b/Build.PL @@ -0,0 +1,61 @@ +#!/usr/bin/perl +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +use 5.10.1; +use strict; +use warnings; + +use FindBin qw($RealBin); +use lib ($RealBin, "$RealBin/lib"); + +use Module::Build 0.36_14; + +use Bugzilla::Install::Requirements qw(REQUIRED_MODULES OPTIONAL_MODULES); +use Bugzilla::Constants qw(BUGZILLA_VERSION); + +sub requires { + my $requirements = REQUIRED_MODULES(); + my $hrequires = {}; + foreach my $module (@$requirements) { + $hrequires->{$module->{module}} = $module->{version}; + } + return $hrequires; +}; + +sub build_requires { + return requires(); +} + +sub recommends { + my $recommends = OPTIONAL_MODULES(); + my @blacklist = ('Apache-SizeLimit', 'mod_perl'); # Does not compile properly on Travis + my $hrecommends = {}; + foreach my $module (@$recommends) { + next if grep($_ eq $module->{package}, @blacklist); + $hrecommends->{$module->{module}} = $module->{version}; + } + return $hrecommends; +} + +my $build = Module::Build->new( + module_name => 'Bugzilla', + dist_abstract => <<END, +Bugzilla is a free bug-tracking system that is developed by an active +community of volunteers. You can install and use it without having to +pay any license fee. +END + dist_version_from => 'Bugzilla/Constants.pm', + dist_version => BUGZILLA_VERSION, + requires => requires(), + recommends => recommends(), + license => 'Mozilla_2_0', + create_readme => 0, + create_makefile_pl => 0 +); + +$build->create_build_script; diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP new file mode 100644 index 000000000..69204e63f --- /dev/null +++ b/MANIFEST.SKIP @@ -0,0 +1,53 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +#!start included /usr/share/perl5/ExtUtils/MANIFEST.SKIP +# Avoid version control files. +\B\.git\b +\B\.bzr\b +\B\.bzrignore\b +\B\.gitignore\b +\B\.gitrev\b +\B\.patch\b + +# Avoid Makemaker generated and utility files. +\bMANIFEST\.bak +\bMakefile$ +\bblib/ +\bMakeMaker-\d +\bpm_to_blib\.ts$ +\bpm_to_blib$ +\bblibdirs\.ts$ # 6.18 through 6.25 generated this + +# Avoid Module::Build generated and utility files. +\bBuild$ +\b_build/ + +# Avoid temp and backup files. +~$ +\.old$ +\#$ +\b\.# +\.bak$ +\.swp$ + +#!end included /usr/share/perl5/ExtUtils/MANIFEST.SKIP + +# Avoid Module::Build generated and utility files. +\bBuild$ +\bBuild.bat$ +\b_build +\bBuild.COM$ +\bBUILD.COM$ +\bbuild.com$ + +# Avoid archives of this distribution +\bBugzilla-[\d\.\_]+ + +# Bugzilla specific avoids +\bdata\/\b +\blocalconfig$ diff --git a/attachment.cgi b/attachment.cgi index 18d783bf4..9b5d66122 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -205,8 +205,9 @@ sub validateContext { my $context = $cgi->param('context') || "patch"; if ($context ne "file" && $context ne "patch") { - detaint_natural($context) - || ThrowUserError("invalid_context", { context => $cgi->param('context') }); + my $orig_context = $context; + detaint_natural($context) + || ThrowUserError("invalid_context", { context => $orig_context }); } return $context; @@ -524,13 +525,14 @@ sub insert { # Get the filehandle of the attachment. my $data_fh = $cgi->upload('data'); + my $attach_text = $cgi->param('attach_text'); my $attachment = Bugzilla::Attachment->create( {bug => $bug, creation_ts => $timestamp, - data => scalar $cgi->param('attach_text') || $data_fh, + data => $attach_text || $data_fh, description => scalar $cgi->param('description'), - filename => $cgi->param('attach_text') ? "file_$bugid.txt" : scalar $cgi->upload('data'), + filename => $attach_text ? "file_$bugid.txt" : $data_fh, ispatch => scalar $cgi->param('ispatch'), isprivate => scalar $cgi->param('isprivate'), mimetype => $content_type, diff --git a/buglist.cgi b/buglist.cgi index 81350dc81..e3d8fe711 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -916,7 +916,7 @@ if (scalar(@products) == 1) { # This is used in the "Zarroo Boogs" case. elsif (my @product_input = $cgi->param('product')) { if (scalar(@product_input) == 1 and $product_input[0] ne '') { - $one_product = new Bugzilla::Product({ name => $cgi->param('product') }); + $one_product = new Bugzilla::Product({ name => $product_input[0] }); } } # We only want the template to use it if the user can actually diff --git a/docs/bugzilla.ent.tmpl b/docs/bugzilla.ent.tmpl index 98ec0a7ce..1ac67b7ed 100644 --- a/docs/bugzilla.ent.tmpl +++ b/docs/bugzilla.ent.tmpl @@ -1,5 +1,5 @@ -<!ENTITY bz-ver "4.4.5"> -<!ENTITY bz-date "2014-07-24"> +<!ENTITY bz-ver "4.4.6"> +<!ENTITY bz-date "2014-10-06"> <!ENTITY current-year "2014"> diff --git a/editflagtypes.cgi b/editflagtypes.cgi index e9c430d7d..aa789fc74 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -44,23 +44,24 @@ my @products = @{$vars->{products}}; my $action = $cgi->param('action') || 'list'; my $token = $cgi->param('token'); -my $product = $cgi->param('product'); -my $component = $cgi->param('component'); +my $prod_name = $cgi->param('product'); +my $comp_name = $cgi->param('component'); my $flag_id = $cgi->param('id'); -if ($product) { +my ($product, $component); + +if ($prod_name) { # Make sure the user is allowed to view this product name. # Users with global editcomponents privs can see all product names. - ($product) = grep { lc($_->name) eq lc($product) } @products; - $product || ThrowUserError('product_access_denied', { name => $cgi->param('product') }); + ($product) = grep { lc($_->name) eq lc($prod_name) } @products; + $product || ThrowUserError('product_access_denied', { name => $prod_name }); } -if ($component) { - ($product && $product->id) - || ThrowUserError('flag_type_component_without_product'); - ($component) = grep { lc($_->name) eq lc($component) } @{$product->components}; +if ($comp_name) { + $product || ThrowUserError('flag_type_component_without_product'); + ($component) = grep { lc($_->name) eq lc($comp_name) } @{$product->components}; $component || ThrowUserError('product_unknown_component', { product => $product->name, - comp => $cgi->param('component') }); + comp => $comp_name }); } # If 'categoryAction' is set, it has priority over 'action'. diff --git a/editgroups.cgi b/editgroups.cgi index d603ab183..e3b9f60d1 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -19,9 +19,6 @@ use Bugzilla::Product; use Bugzilla::User; use Bugzilla::Token; -use constant SPECIAL_GROUPS => ('chartgroup', 'insidergroup', - 'timetrackinggroup', 'querysharegroup'); - my $cgi = Bugzilla->cgi; my $dbh = Bugzilla->dbh; my $template = Bugzilla->template; @@ -224,7 +221,7 @@ if ($action eq 'new') { if ($action eq 'del') { # Check that an existing group ID is given - my $group = Bugzilla::Group->check({ id => $cgi->param('group') }); + my $group = Bugzilla::Group->check({ id => scalar $cgi->param('group') }); $group->check_remove({ test_only => 1 }); $vars->{'shared_queries'} = $dbh->selectrow_array('SELECT COUNT(*) @@ -248,7 +245,7 @@ if ($action eq 'del') { if ($action eq 'delete') { check_token_data($token, 'delete_group'); # Check that an existing group ID is given - my $group = Bugzilla::Group->check({ id => $cgi->param('group') }); + my $group = Bugzilla::Group->check({ id => scalar $cgi->param('group') }); $vars->{'name'} = $group->name; $group->remove_from_db({ remove_from_users => scalar $cgi->param('removeusers'), diff --git a/editusers.cgi b/editusers.cgi index a5ba6d1e3..9778aa808 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -704,7 +704,9 @@ sub check_user { sub mirrorListSelectionValues { my $cgi = Bugzilla->cgi; if (defined($cgi->param('matchtype'))) { - foreach ('matchvalue', 'matchstr', 'matchtype', 'grouprestrict', 'groupid') { + foreach ('matchvalue', 'matchstr', 'matchtype', + 'grouprestrict', 'groupid', 'enabled_only') + { $vars->{'listselectionvalues'}{$_} = $cgi->param($_); } } diff --git a/js/field.js b/js/field.js index c0d0aaa6e..356c0cd5a 100644 --- a/js/field.js +++ b/js/field.js @@ -46,10 +46,14 @@ function validateEnterBug(theform) { _errorFor(attach_desc, 'attach_desc'); focus_me = attach_desc; } - var check_description = status_comment_required[bug_status.value]; - if (check_description && YAHOO.lang.trim(description.value) == '') { - _errorFor(description, 'description'); - focus_me = description; + // bug_status can be undefined if the bug_status field is not editable by + // the currently logged in user. + if (bug_status) { + var check_description = status_comment_required[bug_status.value]; + if (check_description && YAHOO.lang.trim(description.value) == '') { + _errorFor(description, 'description'); + focus_me = description; + } } if (YAHOO.lang.trim(short_desc.value) == '') { _errorFor(short_desc); diff --git a/lib/README b/lib/README new file mode 100644 index 000000000..5778a9a3f --- /dev/null +++ b/lib/README @@ -0,0 +1,4 @@ +This directory contains the Perl modules that Bugzilla requires to run. + +If you would rather have Bugzilla use the Perl modules installed on your +system, you can delete everything in this directory. diff --git a/post_bug.cgi b/post_bug.cgi index 33f5652a5..0a0f8562c 100755 --- a/post_bug.cgi +++ b/post_bug.cgi @@ -150,7 +150,10 @@ if (defined $cgi->param('version')) { # after the bug is filed. # Add an attachment if requested. -if (defined($cgi->upload('data')) || $cgi->param('attach_text')) { +my $data_fh = $cgi->upload('data'); +my $attach_text = $cgi->param('attach_text'); + +if ($data_fh || $attach_text) { $cgi->param('isprivate', $cgi->param('comment_is_private')); # Must be called before create() as it may alter $cgi->param('ispatch'). @@ -165,9 +168,9 @@ if (defined($cgi->upload('data')) || $cgi->param('attach_text')) { $attachment = Bugzilla::Attachment->create( {bug => $bug, creation_ts => $timestamp, - data => scalar $cgi->param('attach_text') || $cgi->upload('data'), + data => $attach_text || $data_fh, description => scalar $cgi->param('description'), - filename => $cgi->param('attach_text') ? "file_$id.txt" : scalar $cgi->upload('data'), + filename => $attach_text ? "file_$id.txt" : $data_fh, ispatch => scalar $cgi->param('ispatch'), isprivate => scalar $cgi->param('isprivate'), mimetype => $content_type, diff --git a/relogin.cgi b/relogin.cgi index 4338c8ee0..337d1b208 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -84,19 +84,21 @@ elsif ($action eq 'begin-sudo') { { $credentials_provided = 1; } - + # Next, log in the user my $user = Bugzilla->login(LOGIN_REQUIRED); - + + my $target_login = $cgi->param('target_login'); + my $reason = $cgi->param('reason') || ''; + # At this point, the user is logged in. However, if they used a method # where they could have provided a username/password (i.e. CGI), but they # did not provide a username/password, then throw an error. if ($user->authorizer->can_login && !$credentials_provided) { ThrowUserError('sudo_password_required', - { target_login => $cgi->param('target_login'), - reason => $cgi->param('reason')}); + { target_login => $target_login, reason => $reason }); } - + # The user must be in the 'bz_sudoers' group unless ($user->in_group('bz_sudoers')) { ThrowUserError('auth_failure', { group => 'bz_sudoers', @@ -120,30 +122,22 @@ elsif ($action eq 'begin-sudo') { && ($token_data eq 'sudo_prepared')) { ThrowUserError('sudo_preparation_required', - { target_login => scalar $cgi->param('target_login'), - reason => scalar $cgi->param('reason')}); + { target_login => $target_login, reason => $reason }); } delete_token($cgi->param('token')); # Get & verify the target user (the user who we will be impersonating) - my $target_user = - new Bugzilla::User({ name => $cgi->param('target_login') }); + my $target_user = new Bugzilla::User({ name => $target_login }); unless (defined($target_user) && $target_user->id && $user->can_see_user($target_user)) { - ThrowUserError('user_match_failed', - { 'name' => $cgi->param('target_login') } - ); + ThrowUserError('user_match_failed', { name => $target_login }); } if ($target_user->in_group('bz_sudo_protect')) { ThrowUserError('sudo_protected', { login => $target_user->login }); } - # If we have a reason passed in, keep it under 200 characters - my $reason = $cgi->param('reason') || ''; - $reason = substr($reason, 0, 200); - # Calculate the session expiry time (T + 6 hours) my $time_string = time2str('%a, %d-%b-%Y %T %Z', time + MAX_SUDO_TOKEN_AGE, 'GMT'); @@ -163,9 +157,12 @@ elsif ($action eq 'begin-sudo') { # For the present, change the values of Bugzilla::user & Bugzilla::sudoer Bugzilla->sudo_request($target_user, $user); - + # NOTE: If you want to log the start of an sudo session, do it here. + # If we have a reason passed in, keep it under 200 characters + $reason = substr($reason, 0, 200); + # Go ahead and send out the message now my $message; my $mail_template = Bugzilla->template_inner($target_user->setting('lang')); diff --git a/t/002goodperl.t b/t/002goodperl.t index e691b39dd..2cbee8ef5 100644 --- a/t/002goodperl.t +++ b/t/002goodperl.t @@ -16,7 +16,7 @@ use lib 't'; use Support::Files; -use Test::More tests => (scalar(@Support::Files::testitems) * 3); +use Test::More tests => (scalar(@Support::Files::testitems) * 4); my @testitems = @Support::Files::testitems; # get the files to test. @@ -110,4 +110,35 @@ foreach my $file (@testitems) { close(FILE); } + +# Forbird the { foo => $cgi->param() } syntax, for security reasons. +foreach my $file (@testitems) { + $file =~ s/\s.*$//; # nuke everything after the first space (#comment) + next unless $file; # skip null entries + if (!open(FILE, $file)) { + ok(0, "could not open $file --WARNING"); + next; + } + my $lineno = 0; + my @unsafe_args; + + while (my $file_line = <FILE>) { + $lineno++; + $file_line =~ s/^\s*(.+)\s*$/$1/; # Remove leading and trailing whitespaces. + if ($file_line =~ /^[^#]+=> \$cgi\->param/) { + push(@unsafe_args, "$file_line on line $lineno"); + } + } + + if (@unsafe_args) { + ok(0, "$file incorrectly passes a CGI argument to a hash --ERROR\n" . + join("\n", @unsafe_args)); + } + else { + ok(1, "$file has no vulnerable hash syntax"); + } + + close(FILE); +} + exit 0; diff --git a/template/en/default/admin/groups/list.html.tmpl b/template/en/default/admin/groups/list.html.tmpl index af7da33a6..859f26205 100644 --- a/template/en/default/admin/groups/list.html.tmpl +++ b/template/en/default/admin/groups/list.html.tmpl @@ -74,7 +74,8 @@ } %] -[% FOREACH group IN ["chartgroup", "insidergroup", "timetrackinggroup", "querysharegroup"] %] +[% FOREACH group IN ["chartgroup", "insidergroup", "timetrackinggroup", + "querysharegroup", "debug_group"] %] [% special_group = Param(group) %] [% IF special_group %] diff --git a/template/en/default/admin/products/edit.html.tmpl b/template/en/default/admin/products/edit.html.tmpl index c38530125..a4fcd188f 100644 --- a/template/en/default/admin/products/edit.html.tmpl +++ b/template/en/default/admin/products/edit.html.tmpl @@ -42,12 +42,12 @@ </th> <td> [% IF product.components.size -%] - [% FOREACH component = product.components %] + [% FOREACH comp = product.components %] <a href="editcomponents.cgi?action=edit&product= [%- product.name FILTER uri %]&component= - [%- component.name FILTER uri %]">[% component.name FILTER html %]</a>: - [% IF component.description %] - [% component.description FILTER html_light %] + [%- comp.name FILTER uri %]">[% comp.name FILTER html %]</a>: + [% IF comp.description %] + [% comp.description FILTER html_light %] [% ELSE %] <font color="red">description missing</font> [% END %] diff --git a/template/en/default/admin/users/responsibilities.html.tmpl b/template/en/default/admin/users/responsibilities.html.tmpl index 9e6e48c6a..67ea7d294 100644 --- a/template/en/default/admin/users/responsibilities.html.tmpl +++ b/template/en/default/admin/users/responsibilities.html.tmpl @@ -23,26 +23,26 @@ <th>Default QA Contact</th> <th>Default CC</th> </tr> - [% FOREACH component = item.components %] + [% FOREACH comp = item.components %] <tr> <td> - [% IF user.in_group("editcomponents", component.product_id) %] + [% IF user.in_group("editcomponents", comp.product_id) %] <a href="editcomponents.cgi?action=edit&product= [% item.product.name FILTER uri %]&component= - [% component.name FILTER uri %]"> + [% comp.name FILTER uri %]"> [% END %] - [% component.name FILTER html %] - [% IF user.in_group("editcomponents", component.product_id) %] + [% comp.name FILTER html %] + [% IF user.in_group("editcomponents", comp.product_id) %] </a> [% END %] </td> [% FOREACH responsibility = ['default_assignee', 'default_qa_contact'] %] <td class="center"> - [% component.$responsibility.id == otheruser.id ? "X" : " " %] + [% comp.$responsibility.id == otheruser.id ? "X" : " " %] </td> [% END %] <td class="center"> - [% component.initial_cc.contains(otheruser) ? "X" : " " %] + [% comp.initial_cc.contains(otheruser) ? "X" : " " %] </td> </tr> [% END %] diff --git a/template/en/default/config.rdf.tmpl b/template/en/default/config.rdf.tmpl index b14d0d056..0d183cf56 100644 --- a/template/en/default/config.rdf.tmpl +++ b/template/en/default/config.rdf.tmpl @@ -139,8 +139,8 @@ [% END %] <bz:components> <Seq> - [% FOREACH component = product.components %] - <li resource="[% escaped_urlbase %]component.cgi?name=[% component.name FILTER uri + [% FOREACH comp = product.components %] + <li resource="[% escaped_urlbase %]component.cgi?name=[% comp.name FILTER uri %]&product=[% product.name FILTER uri %]"/> [% END %] </Seq> @@ -176,16 +176,16 @@ <bz:components> <Seq> [% FOREACH product = products %] - [% FOREACH component = product.components %] + [% FOREACH comp = product.components %] <li> - <bz:component rdf:about="[% escaped_urlbase %]component.cgi?name=[% component.name FILTER uri + <bz:component rdf:about="[% escaped_urlbase %]component.cgi?name=[% comp.name FILTER uri %]&product=[% product.name FILTER uri %]"> - <bz:name>[% component.name FILTER html %]</bz:name> - <bz:is_active>[% component.is_active FILTER html %]</bz:is_active> + <bz:name>[% comp.name FILTER html %]</bz:name> + <bz:is_active>[% comp.is_active FILTER html %]</bz:is_active> [% IF show_flags %] <bz:flag_types> <Seq> - [% flag_types = component.flag_types.bug.merge(component.flag_types.attachment) %] + [% flag_types = comp.flag_types.bug.merge(comp.flag_types.attachment) %] [% FOREACH flag_type = flag_types %] [% NEXT UNLESS flag_type.is_active %] [% all_visible_flag_types.${flag_type.id} = flag_type %] diff --git a/template/en/default/email/flagmail.txt.tmpl b/template/en/default/email/flagmail.txt.tmpl index 169dfa892..037673dfc 100644 --- a/template/en/default/email/flagmail.txt.tmpl +++ b/template/en/default/email/flagmail.txt.tmpl @@ -64,11 +64,14 @@ Attachment [% attidsummary %] [%- FILTER bullet = wrap(80) %] -[% USE Bugzilla %] -[%-# .defined is necessary to avoid a taint issue in Perl < 5.10.1, see bug 509794. %] -[% IF Bugzilla.cgi.param("comment").defined && Bugzilla.cgi.param("comment").length > 0 %] -------- Additional Comments from [% user.identity %] -[%+ Bugzilla.cgi.param("comment") FILTER strip_control_chars %] +[% FOREACH comment = new_comments %] + +[%- IF comment.count %] +--- Comment #[% comment.count %] from [% comment.author.identity %] --- +[% ELSE %] +--- Description --- +[% END %] +[%+ comment.body_full({ is_bugmail => 1, wrap => 1 }) FILTER strip_control_chars %] [% END %] [%- END %] diff --git a/template/en/default/filterexceptions.pl b/template/en/default/filterexceptions.pl index 189862527..e37fec1a7 100644 --- a/template/en/default/filterexceptions.pl +++ b/template/en/default/filterexceptions.pl @@ -170,7 +170,6 @@ ], 'global/messages.html.tmpl' => [ - 'message_tag', 'series.frequency * 2', ], diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl index f55ab92a4..ba961c392 100644 --- a/template/en/default/global/messages.html.tmpl +++ b/template/en/default/global/messages.html.tmpl @@ -918,7 +918,7 @@ [% IF !message %] [% message = BLOCK %] You are using [% terms.Bugzilla %]'s messaging functions incorrectly. You - passed in the string '[% message_tag %]'. The correct use is to pass + passed in the string '[% message_tag FILTER html %]'. The correct use is to pass in a tag, and define that tag in the file <kbd>messages.html.tmpl</kbd>.<br> <br> If you are a [% terms.Bugzilla %] end-user seeing this message, please diff --git a/template/en/default/pages/release-notes.html.tmpl b/template/en/default/pages/release-notes.html.tmpl index 16ddffbe4..5c11360f3 100644 --- a/template/en/default/pages/release-notes.html.tmpl +++ b/template/en/default/pages/release-notes.html.tmpl @@ -45,6 +45,12 @@ <h2 id="v44_point">Updates in this 4.4.x Release</h2> +<h3>4.4.6</h3> + +<p>This release fixes several security issues. See the + <a href="http://www.bugzilla.org/security/4.0.14/">Security Advisory</a> + for details.</p> + <h3>4.4.5</h3> <p>This release fixes a security issue. See the diff --git a/template/en/default/reports/report-table.csv.tmpl b/template/en/default/reports/report-table.csv.tmpl index e2a92b51d..e94014b92 100644 --- a/template/en/default/reports/report-table.csv.tmpl +++ b/template/en/default/reports/report-table.csv.tmpl @@ -23,11 +23,13 @@ [% END %] [% tbl_field_disp FILTER csv %]: [% tbl_disp FILTER csv %] [% END %] -[% IF row_field %] +[% IF row_field && col_field %] + [% row_field_disp _ ' / ' _ col_field_disp FILTER csv %] +[% ELSIF row_field %] [% row_field_disp FILTER csv %] +[% ELSE %] + [% col_field_disp FILTER csv %] [% END %] -[% " / " IF col_field AND row_field %] -[% col_field_disp FILTER csv %] [% IF col_field -%] [% FOREACH col = col_names -%] [% colsepchar %] diff --git a/template/en/default/search/search-specific.html.tmpl b/template/en/default/search/search-specific.html.tmpl index 1093f70bc..f09d4bdc0 100644 --- a/template/en/default/search/search-specific.html.tmpl +++ b/template/en/default/search/search-specific.html.tmpl @@ -27,7 +27,7 @@ for "crash secure SSL flash". <form name="queryform" method="get" action="buglist.cgi"> <input type="hidden" name="query_format" value="specific"> -<input type="hidden" name="order" value="relevance desc"> +<input type="hidden" name="order" value="Importance"> <input type="hidden" id="no_redirect" name="no_redirect" value="0"> <script type="text/javascript"> if (history && history.replaceState) { diff --git a/testserver.pl b/testserver.pl index d296b730f..77489d252 100755 --- a/testserver.pl +++ b/testserver.pl @@ -39,7 +39,7 @@ if (!ON_WINDOWS) { foreach my $pscmd (@pscmds) { open PH, "$pscmd 2>/dev/null |"; while (my $line = <PH>) { - if ($line =~ /^(?:\S*\/)?(?:httpd|apache)2?\s+(\d+)$/) { + if ($line =~ /^(?:\S*\/)?(?:httpd|apache?)2?\s+(\d+)$/) { $sgid = $1 if $1 > $sgid; } } @@ -163,6 +163,7 @@ sub cancelChangePassword { # password and that the new password is valid. sub changePassword { my ($user_id, $token) = @_; + my $dbh = Bugzilla->dbh; my $password = $cgi->param('password'); (defined $password && defined $cgi->param('matchpassword')) @@ -176,6 +177,8 @@ sub changePassword { $user->set_password($password); $user->update(); delete_token($token); + $dbh->do(q{DELETE FROM tokens WHERE userid = ? + AND tokentype = 'password'}, undef, $user_id); Bugzilla->logout_user_by_id($user_id); @@ -254,7 +257,7 @@ sub cancelChangeEmail { # check to see if it has been altered if ($user->login ne $old_email) { $user->set_login($old_email); - $user->update({ keep_session => 1 }); + $user->update({ keep_tokens => 1 }); $vars->{'message'} = "email_change_canceled_reinstated"; } @@ -306,7 +309,7 @@ sub confirm_create_account { my $otheruser = Bugzilla::User->create({ login_name => $login_name, - realname => $cgi->param('realname'), + realname => scalar $cgi->param('realname'), cryptpassword => $password}); # Now delete this token. |