diff --git a/.bzrignore b/.bzrignore index f30e8f9ae..4b27e5744 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1,4 +1,3 @@ -.htaccess /lib/* /template/en/custom /docs/en/html diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..f0e4ee1f4 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ +#### Details + +This PR fixes/adds a feature... + +#### Additional info +* [bmo#](https://bugzilla.mozilla.org/show_bug.cgi?id=) + +#### Test Plan + +1. Open the show_bug view +2. Edit the bug +3. ... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..c9198c50e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,74 @@ +# This is a basic workflow to help you get started with Actions + +name: Release Tests + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the main branch +on: + push: + branches: [ 5.0.4 ] + pull_request: + branches: [ 5.0.4 ] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + ubuntu: + name: Release Tests on Ubuntu 20.04 + runs-on: ubuntu-20.04 + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: apt install + run: | + sudo apt-get update + sudo apt-get -y dist-upgrade + sudo apt-get install --ignore-hold --allow-downgrades -y \ + apache2 \ + mariadb-client-10.3 \ + netcat \ + libappconfig-perl \ + libdate-calc-perl \ + libtemplate-perl \ + build-essential \ + libdatetime-timezone-perl \ + libdatetime-perl \ + libemail-address-perl \ + libemail-sender-perl \ + libemail-mime-perl \ + libemail-mime-modifier-perl \ + libdbi-perl \ + libdbix-connector-perl \ + libdbd-mysql-perl \ + libcgi-pm-perl \ + libmath-random-isaac-perl \ + libmath-random-isaac-xs-perl \ + libapache2-mod-perl2 \ + libapache2-mod-perl2-dev \ + libchart-perl \ + libxml-perl \ + libxml-twig-perl \ + perlmagick \ + libgd-graph-perl \ + libtemplate-plugin-gd-perl \ + libsoap-lite-perl \ + libhtml-scrubber-perl \ + libjson-rpc-perl \ + libdaemon-generic-perl \ + libtheschwartz-perl \ + libtest-taint-perl \ + libauthen-radius-perl \ + libfile-slurp-perl \ + libencode-detect-perl \ + libmodule-build-perl \ + libnet-ldap-perl \ + libauthen-sasl-perl \ + libfile-mimeinfo-perl \ + libhtml-formattext-withlinks-perl \ + libpod-coverage-perl \ + graphviz + - name: Get Perl Version and debug info + run: '/usr/bin/perl -V' + - name: Run tests + run: '/usr/bin/perl runtests.pl' diff --git a/.gitignore b/.gitignore index f30e8f9ae..ba98f70c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.htaccess +/**/.htaccess /lib/* /template/en/custom /docs/en/html diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..7eada0dd6 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,12 @@ +version: 2 +build: + os: "ubuntu-20.04" + tools: + python: "3.10" + apt_packages: + - libfile-copy-recursive-perl + jobs: + post_build: + - perl docs/makedocs.pl --pod-only + - cp -rp docs/en/html/integrating/api/ "$READTHEDOCS_OUTPUT/html/integrating/api" + - cp -p docs/en/html/style.css "$READTHEDOCS_OUTPUT/html/" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 01ecfcfb4..000000000 --- a/.travis.yml +++ /dev/null @@ -1,78 +0,0 @@ -language: perl - -addons: - postgresql: "9.1" - -perl: - - 5.10 - - 5.12 - - 5.14 - - 5.16 - -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.10 - env: TEST_SUITE=docs - - perl: 5.12 - env: TEST_SUITE=docs - - perl: 5.14 - env: TEST_SUITE=docs - - perl: 5.10 - env: TEST_SUITE=webservices DB=mysql - - perl: 5.10 - env: TEST_SUITE=webservices DB=pg - - perl: 5.10 - env: TEST_SUITE=selenium DB=mysql - - perl: 5.10 - env: TEST_SUITE=selenium DB=pg - - perl: 5.12 - env: TEST_SUITE=webservices DB=mysql - - perl: 5.12 - env: TEST_SUITE=webservices DB=pg - - perl: 5.12 - env: TEST_SUITE=selenium DB=mysql - - perl: 5.12 - env: TEST_SUITE=selenium DB=pg - - perl: 5.14 - env: TEST_SUITE=webservices DB=mysql - - perl: 5.14 - env: TEST_SUITE=webservices DB=pg - - perl: 5.14 - env: TEST_SUITE=selenium DB=mysql - - perl: 5.14 - env: TEST_SUITE=selenium DB=pg - -before_install: - - git clone https://github.com/bugzilla/qa.git -b 5.0 qa - -install: true - -before_script: - - mysql -u root mysql -e "GRANT ALL PRIVILEGES ON *.* TO bugs@localhost IDENTIFIED BY 'bugs'; FLUSH PRIVILEGES;" - - psql -c "CREATE USER bugs WITH PASSWORD 'bugs' CREATEDB;" -U postgres - -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.pm b/Bugzilla.pm index aa78f1483..e8c078cf8 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -11,6 +11,8 @@ use 5.10.1; use strict; use warnings; +BEGIN { eval { utf8->import; require 'utf8_heavy.pl' }; } + # We want any compile errors to get to the browser, if possible. BEGIN { # This makes sure we're in a CGI. @@ -87,7 +89,7 @@ sub init_page { # On Windows, these paths are tainted, preventing # File::Spec::Win32->tmpdir from using them. But we need # a place to temporary store attachments which are uploaded. - foreach my $temp (qw(TMPDIR TMP TEMP)) { + foreach my $temp (qw(TMPDIR TMP TEMP WINDIR)) { trick_taint($ENV{$temp}) if $ENV{$temp}; } # Some DLLs used by Strawberry Perl are also in c\bin, @@ -96,6 +98,7 @@ sub init_page { my $c_path = $path = dirname($^X); $c_path =~ s/\bperl\b(?=\\bin)/c/; $path .= ";$c_path"; + trick_taint($path); } } # Some environment variables are not taint safe diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 932fb6b17..33183797b 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -865,6 +865,8 @@ sub create { sub run_create_validators { my ($class, $params) = @_; + $params->{submitter_id} = Bugzilla->user->id || ThrowUserError('invalid_user'); + # Let's validate the attachment content first as it may # alter some other attachment attributes. $params->{data} = $class->_check_data($params); @@ -872,7 +874,6 @@ sub run_create_validators { $params->{creation_ts} ||= Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); $params->{modification_time} = $params->{creation_ts}; - $params->{submitter_id} = Bugzilla->user->id || ThrowUserError('invalid_user'); return $params; } diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 34bf95ff7..8b4493f85 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -305,15 +305,15 @@ sub new { my $param = shift; # Remove leading "#" mark if we've just been passed an id. - if (!ref $param && $param =~ /^#(\d+)$/) { + if (!ref $param && $param =~ /^#([0-9]+)$/) { $param = $1; } # If we get something that looks like a word (not a number), # make it the "name" param. if (!defined $param - || (!ref($param) && $param !~ /^\d+$/) - || (ref($param) && $param->{id} !~ /^\d+$/)) + || (!ref($param) && $param !~ /^[0-9]+$/) + || (ref($param) && $param->{id} !~ /^[0-9]+$/)) { if ($param) { my $alias = ref($param) ? $param->{id} : $param; @@ -556,15 +556,15 @@ sub _extract_bug_ids { my $s = $comment->already_wrapped ? qr/\s/ : qr/\h/; my $text = $comment->body; # Full bug links - push @bug_ids, $text =~ /\b$urlbase_re\Qshow_bug.cgi?id=\E(\d+)(?:\#c\d+)?/g; + push @bug_ids, $text =~ /\b$urlbase_re\Qshow_bug.cgi?id=\E([0-9]+)(?:\#c[0-9]+)?/g; # bug X - my $bug_re = qr/\Q$bug_word\E$s*\#?$s*(\d+)/i; + my $bug_re = qr/\Q$bug_word\E$s*\#?$s*([0-9]+)/i; push @bug_ids, $text =~ /\b$bug_re/g; # bugs X, Y, Z - my $bugs_re = qr/\Q$bugs_word\E$s*\#?$s*(\d+)(?:$s*,$s*\#?$s*(\d+))+/i; + my $bugs_re = qr/\Q$bugs_word\E$s*\#?$s*([0-9]+)(?:$s*,$s*\#?$s*([0-9]+))+/i; push @bug_ids, $text =~ /\b$bugs_re/g; # Old duplicate markers - push @bug_ids, $text =~ /(?<=^\*\*\*\ This\ bug\ has\ been\ marked\ as\ a\ duplicate\ of\ )(\d+)(?=\ \*\*\*\Z)/; + push @bug_ids, $text =~ /(?<=^\*\*\*\ This\ bug\ has\ been\ marked\ as\ a\ duplicate\ of\ )([0-9]+)(?=\ \*\*\*\Z)/; } # Make sure to filter invalid bug IDs. @bug_ids = grep { $_ < MAX_INT_32 } @bug_ids; diff --git a/Bugzilla/BugMail.pm b/Bugzilla/BugMail.pm index d4a1597ab..1aff405d8 100644 --- a/Bugzilla/BugMail.pm +++ b/Bugzilla/BugMail.pm @@ -21,6 +21,7 @@ use Bugzilla::Mailer; use Bugzilla::Hook; use Bugzilla::MIME; +use Encode qw(); use Date::Parse; use Date::Format; use Scalar::Util qw(blessed); @@ -169,8 +170,8 @@ sub Send { } if ($change->{field_name} eq 'dependson' || $change->{field_name} eq 'blocked') { - push @referenced_bug_ids, split(/[\s,]+/, $change->{old}); - push @referenced_bug_ids, split(/[\s,]+/, $change->{new}); + push @referenced_bug_ids, split(/[\s,]+/, $change->{old} // ''); + push @referenced_bug_ids, split(/[\s,]+/, $change->{new} // ''); } } @@ -451,6 +452,7 @@ sub _generate_bugmail { encoding => 'quoted-printable', }, body_str => $msg_text, + encode_check => Encode::FB_DEFAULT ) ); if ($user->setting('email_format') eq 'html') { @@ -463,13 +465,15 @@ sub _generate_bugmail { encoding => 'quoted-printable', }, body_str => $msg_html, + encode_check => Encode::FB_DEFAULT ); } my $email = Bugzilla::MIME->new($msg_header); - if (scalar(@parts) == 1) { - $email->content_type_set($parts[0]->content_type); - } else { + + # If there's only one part, we don't need to set the overall content type + # because Email::MIME will automatically take it from that part (bug 1657496) + if (scalar(@parts) > 1) { $email->content_type_set('multipart/alternative'); # Some mail clients need same encoding for each part, even empty ones. $email->charset_set('UTF-8') if $use_utf8; diff --git a/Bugzilla/BugUrl.pm b/Bugzilla/BugUrl.pm index 1e836ca1e..1d75fe8f1 100644 --- a/Bugzilla/BugUrl.pm +++ b/Bugzilla/BugUrl.pm @@ -108,8 +108,9 @@ sub _do_list_select { my $objects = $class->SUPER::_do_list_select(@_); foreach my $object (@$objects) { - eval "use " . $object->class; die $@ if $@; - bless $object, $object->class; + eval "use " . $object->class; + # If the class cannot be loaded, then we build a generic object. + bless $object, ($@ ? 'Bugzilla::BugUrl' : $object->class); } return $objects diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 0b8a48697..9b1ff9235 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -66,7 +66,7 @@ sub new { # else we will be redirected outside Bugzilla. my $script_name = $self->script_name; $path_info =~ s/^\Q$script_name\E//; - if ($path_info) { + if ($script_name && $path_info) { print $self->redirect($self->url(-path => 0, -query => 1)); } } @@ -283,11 +283,74 @@ sub close_standby_message { print $self->multipart_end(); print $self->multipart_start(-type => $contenttype); } - else { + elsif (!$self->{_header_done}) { print $self->header($contenttype); } } +our $ALLOW_UNSAFE_RESPONSE = 0; +# responding to text/plain or text/html is safe +# responding to any request with a referer header is safe +# some things need to have unsafe responses (attachment.cgi) +# everything else should get a 403. +sub _prevent_unsafe_response { + my ($self, $headers) = @_; + my $safe_content_type_re = qr{ + ^ (*COMMIT) # COMMIT makes the regex faster + # by preventing back-tracking. see also perldoc pelre. + # application/x-javascript, xml, atom+xml, rdf+xml, xml-dtd, and json + (?: application/ (?: x(?: -javascript | ml (?: -dtd )? ) + | (?: atom | rdf) \+ xml + | json ) + # text/csv, text/calendar, text/plain, and text/html + | text/ (?: c (?: alendar | sv ) + | plain + | html ) + # used for HTTP push responses + | multipart/x-mixed-replace) + }sx; + my $safe_referer_re = do { + # Note that urlbase must end with a /. + # It almost certainly does, but let's be extra careful. + my $urlbase = correct_urlbase(); + $urlbase =~ s{/$}{}; + qr{ + # Begins with literal urlbase + ^ (*COMMIT) + \Q$urlbase\E + # followed by a slash or end of string + (?: / + | $ ) + }sx + }; + + return if $ALLOW_UNSAFE_RESPONSE; + + if (Bugzilla->usage_mode == USAGE_MODE_BROWSER) { + # Safe content types are ones that arn't images. + # For now let's assume plain text and html are not valid images. + my $content_type = $headers->{'-type'} // $headers->{'-content_type'} // 'text/html'; + my $is_safe_content_type = $content_type =~ $safe_content_type_re; + + # Safe referers are ones that begin with the urlbase. + my $referer = $self->referer; + my $is_safe_referer = $referer && $referer =~ $safe_referer_re; + + if (!$is_safe_referer && !$is_safe_content_type) { + print $self->SUPER::header(-type => 'text/html', -status => '403 Forbidden'); + if ($content_type ne 'text/html') { + print "Untrusted Referer Header\n"; + if ($ENV{MOD_PERL}) { + my $r = $self->r; + $r->rflush; + $r->status(200); + } + } + exit; + } + } +} + # Override header so we can add the cookies in sub header { my $self = shift; @@ -302,6 +365,7 @@ sub header { else { %headers = @_; } + $self->_prevent_unsafe_response(\%headers); if ($self->{'_content_disp'}) { $headers{'-content_disposition'} = $self->{'_content_disp'}; @@ -356,6 +420,7 @@ sub header { Bugzilla::Hook::process('cgi_headers', { cgi => $self, headers => \%headers } ); + $self->{_header_done} = 1; return $self->SUPER::header(%headers) || ""; } diff --git a/Bugzilla/Chart.pm b/Bugzilla/Chart.pm index 3c69006aa..faf7a4785 100644 --- a/Bugzilla/Chart.pm +++ b/Bugzilla/Chart.pm @@ -420,11 +420,9 @@ sub dump { # Make sure we've read in our data my $data = $self->data; - + require Data::Dumper; - say "
Bugzilla::Chart object:"; - print html_quote(Data::Dumper::Dumper($self)); - print ""; + return Data::Dumper::Dumper($self); } 1; diff --git a/Bugzilla/Component.pm b/Bugzilla/Component.pm index 9bc0a4493..d5a6ece5d 100644 --- a/Bugzilla/Component.pm +++ b/Bugzilla/Component.pm @@ -148,7 +148,8 @@ sub remove_from_db { $dbh->bz_start_transaction(); # Products must have at least one component. - if (scalar(@{$self->product->components}) == 1) { + my @components = @{ $self->product->components }; + if (scalar(@components) == 1) { ThrowUserError('component_is_last', { comp => $self }); } @@ -165,6 +166,8 @@ sub remove_from_db { ThrowUserError('component_has_bugs', {nb => $self->bug_count}); } } + # Update the list of components in the product object. + $self->product->{components} = [grep { $_->id != $self->id } @components]; $self->SUPER::remove_from_db(); $dbh->bz_commit_transaction(); diff --git a/Bugzilla/Config.pm b/Bugzilla/Config.pm index 1c02d9dda..458616701 100644 --- a/Bugzilla/Config.pm +++ b/Bugzilla/Config.pm @@ -16,10 +16,9 @@ use autodie qw(:default); use Bugzilla::Constants; use Bugzilla::Hook; -use Bugzilla::Util qw(trick_taint); +use Bugzilla::Util qw(trick_taint read_text write_text); use JSON::XS; -use File::Slurp; use File::Temp; use File::Basename; @@ -284,7 +283,7 @@ sub write_params { my $param_file = bz_locations()->{'datadir'} . '/params.json'; my $json_data = JSON::XS->new->canonical->pretty->encode($param_data); - write_file($param_file, { binmode => ':utf8', atomic => 1 }, \$json_data); + write_text($param_file, $json_data); # It's not common to edit parameters and loading # Bugzilla::Install::Filesystem is slow. @@ -301,8 +300,8 @@ sub read_param_file { my $file = bz_locations()->{'datadir'} . '/params.json'; if (-e $file) { - my $data; - read_file($file, binmode => ':utf8', buf_ref => \$data); + my $data = read_text($file); + trick_taint($data); # If params.json has been manually edited and e.g. some quotes are # missing, we don't want JSON::XS to leak the content of the file diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index cd4419b66..0e50f82fc 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -200,11 +200,11 @@ use Memoize; # CONSTANTS # # Bugzilla version -use constant BUGZILLA_VERSION => "5.0.2"; +use constant BUGZILLA_VERSION => "5.0.4.1"; # A base link to the current REST Documentation. We place it here # as it will need to be updated to whatever the current release is. -use constant REST_DOC => "http://www.bugzilla.org/docs/tip/en/html/api/"; +use constant REST_DOC => 'https://bugzilla.readthedocs.org/en/5.0/api/'; # Location of the remote and local XML files to track new releases. use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml'; @@ -508,16 +508,42 @@ use constant INSTALLATION_MODE_NON_INTERACTIVE => 1; use constant DB_MODULE => { # MySQL 5.0.15 was the first production 5.0.x release. 'mysql' => {db => 'Bugzilla::DB::Mysql', db_version => '5.0.15', - dbd => { + db_blocklist => ['^[89]\.'], + # the following is a "human-readable" version to show in the + # release notes + db_blklst_str => '>= 8.0', + dbd => { package => 'DBD-mysql', module => 'DBD::mysql', # Disallow development versions - blacklist => ['_'], + blocklist => ['_'], # For UTF-8 support. 4.001 makes sure that blobs aren't # marked as UTF-8. version => '4.001', }, name => 'MySQL'}, + + # MariaDB is a drop-in replacement for MySQL and works with Bugzilla + 'mariadb' => {db => 'Bugzilla::DB::Mysql', db_version => '5.1', + # MariaDB is indistinguishable from MySQL, but skipped 8 and + # 9 so blocklist it anyway in case someone has the driver set + # to mariadb but actually has MySQL. + db_blocklist => ['^[89]\.'], + # no string to show the user on the release notes though. + dbd => { + package => 'DBD-mysql', + module => 'DBD::mysql', + + # Disallow development versions + blocklist => ['_'], + + # For UTF-8 support. 4.001 makes sure that blobs aren't + # marked as UTF-8. + version => '4.001', + }, + name => 'MariaDB' + }, + # Also see Bugzilla::DB::Pg::bz_check_server_version, which has special # code to require DBD::Pg 2.17.2 for PostgreSQL 9 and above. 'pg' => {db => 'Bugzilla::DB::Pg', db_version => '8.03.0000', diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 5bc83f9d6..f59a32feb 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -204,17 +204,33 @@ sub bz_check_server_version { $self->disconnect; my $sql_want = $db->{db_version}; + my $sql_dontwant = exists $db->{db_blocklist} ? $db->{db_blocklist} : []; my $version_ok = vers_cmp($sql_vers, $sql_want) > -1 ? 1 : 0; + my $blocklisted; + if ($version_ok) { + $blocklisted = grep($sql_vers =~ /$_/, @$sql_dontwant); + $version_ok = 0 if $blocklisted; + } my $sql_server = $db->{name}; if ($output) { Bugzilla::Install::Requirements::_checking_for({ package => $sql_server, wanted => $sql_want, - found => $sql_vers, ok => $version_ok }); + found => $sql_vers, ok => $version_ok, + blocklisted => $blocklisted }); } # Check what version of the database server is installed and let # the user know if the version is too old to be used with Bugzilla. + if ($blocklisted) { + die <
data hash:"; - say html_quote(Data::Dumper::Dumper(%data)); - say "\ndata array:"; - say html_quote(Data::Dumper::Dumper(@image_data)) . "\n\n"; + $vars->{'debug_hash'} = Data::Dumper::Dumper(%data); + $vars->{'debug_array'} = Data::Dumper::Dumper(@image_data); } # All formats point to the same section of the documentation. diff --git a/request.cgi b/request.cgi index 566781a0c..347cb7a44 100755 --- a/request.cgi +++ b/request.cgi @@ -305,13 +305,22 @@ sub queue { $vars->{'requests'} = \@requests; $vars->{'types'} = \@types; - my %components; - foreach my $prod (@{$user->get_selectable_products}) { - foreach my $comp (@{$prod->components}) { - $components{$comp->name} = 1; + # This code is needed to populate the Product and Component select fields. + my ($products, %components); + if (Bugzilla->params->{useclassification}) { + foreach my $class (@{$user->get_selectable_classifications}) { + push @$products, @{$user->get_selectable_products($class->id)}; } } - $vars->{'components'} = [ sort { $a cmp $b } keys %components ]; + else { + $products = $user->get_selectable_products; + } + + foreach my $product (@$products) { + $components{$_->name} = 1 foreach @{$product->components}; + } + $vars->{'products'} = $products; + $vars->{'components'} = [ sort keys %components ]; $vars->{'urlquerypart'} = $cgi->canonicalise_query('ctype'); diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi index 476df1e8e..7b2d2f55d 100755 --- a/showdependencygraph.cgi +++ b/showdependencygraph.cgi @@ -55,13 +55,19 @@ sub CreateImagemap { $default = qq{\n}; } - if ($line =~ /^rectangle \((.*),(.*)\) \((.*),(.*)\) (http[^ ]*) (\d+)(\\n.*)?$/) { + if ($line =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) (http[^ ]*) (\d+)(?:\\n.*)?$/) { my ($leftx, $rightx, $topy, $bottomy, $url, $bugid) = ($1, $3, $2, $4, $5, $6); # Pick up bugid from the mapdata label field. Getting the title from # bugtitle hash instead of mapdata allows us to get the summary even # when showsummary is off, and also gives us status and resolution. + # This text is safe; it has already been escaped. my $bugtitle = $bugtitles{$bugid}; + + # The URL is supposed to be safe, because it's built manually. + # But in case someone manages to inject code, it's safer to escape it. + $url = html_quote($url); + $map .= qq{\n}; @@ -198,6 +204,9 @@ foreach my $k (@bug_ids) { utf8::encode($summary) if utf8::is_utf8($summary); } $summary =~ s/([\\\"])/\\$1/g; + # Newlines must be escaped too, to not break the .map file + # and to prevent code injection. + $summary =~ s/\n/\\n/g; push(@params, qq{label="$k\\n$summary"}); } diff --git a/t/002goodperl.t b/t/002goodperl.t index d1858361f..8fcd1a343 100644 --- a/t/002goodperl.t +++ b/t/002goodperl.t @@ -14,7 +14,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. lib t); use Support::Files; diff --git a/t/003safesys.t b/t/003safesys.t index 443f96415..e1d04bed8 100644 --- a/t/003safesys.t +++ b/t/003safesys.t @@ -14,7 +14,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. lib t); use Support::Files; diff --git a/t/004template.t b/t/004template.t index 0a6f0e0aa..f336441ac 100644 --- a/t/004template.t +++ b/t/004template.t @@ -13,7 +13,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. lib t); use Support::Templates; @@ -114,10 +114,10 @@ foreach my $include_path (@include_paths) { # Forbid single quotes to delimit URLs, see bug 926085. if ($data =~ /href=\\?'/) { - ok(0, "$path contains blacklisted constructs: href='...'"); + ok(0, "$path contains blocklisted constructs: href='...'"); } else { - ok(1, "$path contains no blacklisted constructs"); + ok(1, "$path contains no blocklisted constructs"); } } } diff --git a/t/005whitespace.t b/t/005whitespace.t index b6de8cee3..b7f10cae2 100644 --- a/t/005whitespace.t +++ b/t/005whitespace.t @@ -13,7 +13,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. lib t); use Support::Files; use Support::Templates; diff --git a/t/006spellcheck.t b/t/006spellcheck.t index 24e00242d..ea298c5f6 100644 --- a/t/006spellcheck.t +++ b/t/006spellcheck.t @@ -14,7 +14,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. lib t); use Support::Files; # -1 because 006spellcheck.t must not be checked. diff --git a/t/007util.t b/t/007util.t index 66c2df032..6f5ea8b72 100644 --- a/t/007util.t +++ b/t/007util.t @@ -13,7 +13,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. lib t); use Support::Files; use Test::More tests => 17; use DateTime; diff --git a/t/009bugwords.t b/t/009bugwords.t index e36651edb..5b0bf1f94 100644 --- a/t/009bugwords.t +++ b/t/009bugwords.t @@ -19,7 +19,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. t lib); use Support::Files; use Support::Templates; diff --git a/t/010dependencies.t b/t/010dependencies.t index afd29a652..e0e990172 100644 --- a/t/010dependencies.t +++ b/t/010dependencies.t @@ -69,7 +69,7 @@ foreach my $module (keys %mods) { $used =~ s#/#::#g; $used =~ s#\.pm$##; $used =~ s#\$module#[^:]+#; - $used =~ s#\${[^}]+}#[^:]+#; + $used =~ s#\$\{[^}]+\}#[^:]+#; $used =~ s#[" ]##g; push(@use, grep(/^\Q$used\E$/, keys %mods)); } diff --git a/t/011pod.t b/t/011pod.t index 8a7f374ce..fd37faa7c 100644 --- a/t/011pod.t +++ b/t/011pod.t @@ -14,7 +14,7 @@ use 5.10.1; use strict; use warnings; -use lib 't'; +use lib qw(. lib t); use Support::Files; use Pod::Checker; diff --git a/taskgraph.json b/taskgraph.json index f292653be..ba1d1f3e0 100644 --- a/taskgraph.json +++ b/taskgraph.json @@ -10,14 +10,15 @@ "reruns": 3, "maxRunTime": 3000, "task": { + "expires": "2018-02-18T17:33:38.806Z", "metadata": { "name": "Basic Sanity Tests" }, "provisionerId": "aws-provisioner-v1", "workerType": "b2gtest", "payload": { - "image": "dklawren/docker-bugzilla", - "command": ["/runtests.sh"], + "image": "bugzilla/bugzilla-ci", + "command": ["runtests.sh"], "env": { "TEST_SUITE": "sanity" }, @@ -25,13 +26,19 @@ "public/runtests_log": { "type": "file", "path": "/runtests.log", - "expires": "2016-02-17T17:33:38.806Z" + "expires": "2018-02-17T17:33:38.806Z" } } }, "extra": { "treeherder": { - "symbol": "San" + "symbol": "San", + "machine": { + "platform": "linux64" + }, + "build": { + "platform": "linux64" + } } } } @@ -40,14 +47,15 @@ "reruns": 3, "maxRunTime": 3000, "task": { + "expires": "2018-02-18T17:33:38.806Z", "metadata": { "name": "Documentation Build Test" }, "provisionerId": "aws-provisioner-v1", "workerType": "b2gtest", "payload": { - "image": "dklawren/docker-bugzilla", - "command": ["/runtests.sh"], + "image": "bugzilla/bugzilla-ci", + "command": ["runtests.sh"], "env": { "TEST_SUITE": "docs" }, @@ -55,13 +63,19 @@ "public/runtests_log": { "type": "file", "path": "/runtests.log", - "expires": "2016-02-17T17:33:38.806Z" + "expires": "2018-02-17T17:33:38.806Z" } } }, "extra": { "treeherder": { - "symbol": "Doc" + "symbol": "Doc", + "machine": { + "platform": "linux64" + }, + "build": { + "platform": "linux64" + } } } } @@ -70,14 +84,15 @@ "reruns": 3, "maxRunTime": 7200, "task": { + "expires": "2018-02-18T17:33:38.806Z", "metadata": { "name": "WebService API Tests (MySQL)" }, "provisionerId": "aws-provisioner-v1", "workerType": "b2gtest", "payload": { - "image": "dklawren/docker-bugzilla", - "command": ["/runtests.sh"], + "image": "bugzilla/bugzilla-ci", + "command": ["runtests.sh"], "env": { "TEST_SUITE": "webservices" }, @@ -85,18 +100,24 @@ "public/runtests_log": { "type": "file", "path": "/runtests.log", - "expires": "2016-02-17T17:33:38.806Z" + "expires": "2018-02-17T17:33:38.806Z" }, "public/httpd_error_log": { "type": "file", "path": "/var/log/httpd/error_log", - "expires": "2016-02-17T17:33:38.806Z" + "expires": "2018-02-17T17:33:38.806Z" } } }, "extra": { "treeherder": { - "symbol": "API" + "symbol": "API", + "machine": { + "platform": "linux64" + }, + "build": { + "platform": "linux64" + } } } } @@ -105,38 +126,45 @@ "reruns": 3, "maxRunTime": 7200, "task": { + "expires": "2018-02-18T17:33:38.806Z", "metadata": { "name": "Selenium Tests (MySQL)" }, "provisionerId": "aws-provisioner-v1", "workerType": "b2gtest", "payload": { - "image": "dklawren/docker-bugzilla", - "command": ["/runtests.sh"], + "image": "bugzilla/bugzilla-ci", + "command": ["runtests.sh"], "env": { "TEST_SUITE": "selenium" }, "artifacts": { "public/runtests_log": { "type": "file", - "path": "/runtests.log", - "expires": "2016-02-17T17:33:38.806Z" + "path": "/tmp/runtests.log", + "expires": "2018-02-17T17:33:38.806Z" }, "public/httpd_error_log": { "type": "file", "path": "/var/log/httpd/error_log", - "expires": "2016-02-17T17:33:38.806Z" + "expires": "2018-02-17T17:33:38.806Z" }, "public/selenium_log": { "type": "file", - "path": "/selenium.log", - "expires": "2016-02-17T17:33:38.806Z" + "path": "/tmp/selenium.log", + "expires": "2018-02-17T17:33:38.806Z" } } }, "extra": { "treeherder": { - "symbol": "Sel" + "symbol": "Sel", + "machine": { + "platform": "linux64" + }, + "build": { + "platform": "linux64" + } } } } @@ -145,33 +173,41 @@ "reruns": 3, "maxRunTime": 7200, "task": { + "expires": "2018-02-18T17:33:38.806Z", "metadata": { "name": "WebService API Tests (Pg)" }, "provisionerId": "aws-provisioner-v1", "workerType": "b2gtest", "payload": { - "image": "dklawren/docker-bugzilla:pgsql", - "command": ["/runtests.sh"], + "image": "bugzilla/bugzilla-ci", + "command": ["runtests.sh"], "env": { + "BUGS_DB_DRIVER": "pg", "TEST_SUITE": "webservices" }, "artifacts": { "public/runtests_log": { "type": "file", - "path": "/runtests.log", - "expires": "2016-02-17T17:33:38.806Z" + "path": "/tmp/runtests.log", + "expires": "2018-02-17T17:33:38.806Z" }, "public/httpd_error_log": { "type": "file", "path": "/var/log/httpd/error_log", - "expires": "2016-02-17T17:33:38.806Z" + "expires": "2018-02-17T17:33:38.806Z" } } }, "extra": { "treeherder": { - "symbol": "API-Pg" + "symbol": "API-Pg", + "machine": { + "platform": "linux64" + }, + "build": { + "platform": "linux64" + } } } } @@ -180,38 +216,46 @@ "reruns": 3, "maxRunTime": 7200, "task": { + "expires": "2018-02-18T17:33:38.806Z", "metadata": { "name": "Selenium Tests (Pg)" }, "provisionerId": "aws-provisioner-v1", "workerType": "b2gtest", "payload": { - "image": "dklawren/docker-bugzilla:pgsql", - "command": ["/runtests.sh"], + "image": "bugzilla/bugzilla-ci", + "command": ["runtests.sh"], "env": { + "BUGS_DB_DRIVER": "pg", "TEST_SUITE": "selenium" }, "artifacts": { "public/runtests_log": { "type": "file", - "path": "/runtests.log", - "expires": "2016-02-17T17:33:38.806Z" + "path": "/tmp/runtests.log", + "expires": "2018-02-17T17:33:38.806Z" }, "public/httpd_error_log": { "type": "file", "path": "/var/log/httpd/error_log", - "expires": "2016-02-17T17:33:38.806Z" + "expires": "2018-02-17T17:33:38.806Z" }, "public/selenium_log": { "type": "file", - "path": "/selenium.log", - "expires": "2016-02-17T17:33:38.806Z" + "path": "/tmp/selenium.log", + "expires": "2018-02-17T17:33:38.806Z" } } }, "extra": { "treeherder": { - "symbol": "Sel-Pg" + "symbol": "Sel-Pg", + "machine": { + "platform": "linux64" + }, + "build": { + "platform": "linux64" + } } } } diff --git a/template/en/default/account/auth/login-small.html.tmpl b/template/en/default/account/auth/login-small.html.tmpl index 65aa861a1..a1a074372 100644 --- a/template/en/default/account/auth/login-small.html.tmpl +++ b/template/en/default/account/auth/login-small.html.tmpl @@ -27,8 +27,6 @@ Log In - [% Hook.process('additional_methods') %] - -