summaryrefslogtreecommitdiffstats
path: root/t
diff options
context:
space:
mode:
authorDylan William Hardison <dylan@hardison.net>2018-10-14 18:19:50 +0200
committerDylan William Hardison <dylan@hardison.net>2018-10-14 18:19:50 +0200
commitce00a61057535d49aa0e83181a1d317d7842571b (patch)
tree280243de9ff791449fb2c82f3f0f2b9bd931d5b2 /t
parent6367a26da4093a8379e370ef328e9507c98b2e7e (diff)
parent6657fa9f5210d5b5a9b14c0cba6882bd56232054 (diff)
downloadbugzilla-ce00a61057535d49aa0e83181a1d317d7842571b.tar.gz
bugzilla-ce00a61057535d49aa0e83181a1d317d7842571b.tar.xz
Merge remote-tracking branch 'bmo/master'
Diffstat (limited to 't')
-rw-r--r--t/.perlcritic-history2
-rw-r--r--t/006spellcheck.t6
-rw-r--r--t/007util.t6
-rw-r--r--t/008filter.t40
-rw-r--r--t/009bugwords.t18
-rw-r--r--t/012throwables.t22
-rw-r--r--t/json-boxes.t36
-rw-r--r--t/markdown.t6
-rw-r--r--t/mojo-example.t76
-rw-r--r--t/security-risk.t156
10 files changed, 319 insertions, 49 deletions
diff --git a/t/.perlcritic-history b/t/.perlcritic-history
index fc5e3d6eb..0119922a7 100644
--- a/t/.perlcritic-history
+++ b/t/.perlcritic-history
@@ -77,7 +77,7 @@ $VAR1 = [
'Perl::Critic::Policy::Modules::RequireEndWithOne' => 0,
'Perl::Critic::Policy::InputOutput::ProhibitTwoArgOpen' => 2,
'Perl::Critic::Policy::ValuesAndExpressions::RequireConstantVersion' => 0,
- 'Perl::Critic::Policy::InputOutput::RequireCheckedSyscalls' => 3,
+ 'Perl::Critic::Policy::InputOutput::RequireCheckedSyscalls' => 4,
'Perl::Critic::Policy::ValuesAndExpressions::RequireInterpolationOfMetachars' => 0,
'Perl::Critic::Policy::TestingAndDebugging::RequireUseStrict' => 0,
'Perl::Critic::Policy::ControlStructures::ProhibitUntilBlocks' => 0,
diff --git a/t/006spellcheck.t b/t/006spellcheck.t
index 70181ef85..a7e7c6d72 100644
--- a/t/006spellcheck.t
+++ b/t/006spellcheck.t
@@ -75,9 +75,9 @@ foreach my $file (@testitems) {
last;
}
}
-
+
close (FILE);
-
+
if ($found_word) {
ok(0,"$file: found SPELLING ERROR $found_word --WARNING");
} else {
@@ -86,6 +86,6 @@ foreach my $file (@testitems) {
} else {
ok(0,"could not open $file for spellcheck --WARNING");
}
-}
+}
exit 0;
diff --git a/t/007util.t b/t/007util.t
index b8e9505d8..b6d23bc32 100644
--- a/t/007util.t
+++ b/t/007util.t
@@ -18,7 +18,7 @@ use Support::Files;
use Test::More tests => 17;
use DateTime;
-BEGIN {
+BEGIN {
use_ok('Bugzilla');
use_ok('Bugzilla::Util');
}
@@ -53,13 +53,13 @@ is(format_time("2002.11.24 00:05:56", "%Y-%m-%d %R %Z"), "2002-11-24 00:05 $tz",
my %email_strings = (
'somebody@somewhere.com' => 'somebody',
'Somebody <somebody@somewhere.com>' => 'Somebody <somebody>',
- 'One Person <one@person.com>, Two Person <two@person.com>'
+ 'One Person <one@person.com>, Two Person <two@person.com>'
=> 'One Person <one>, Two Person <two>',
'This string contains somebody@somewhere.com and also this@that.com'
=> 'This string contains somebody and also this',
);
foreach my $input (keys %email_strings) {
- is(Bugzilla::Util::email_filter($input), $email_strings{$input},
+ is(Bugzilla::Util::email_filter($input), $email_strings{$input},
"email_filter('$input')");
}
diff --git a/t/008filter.t b/t/008filter.t
index 443fb2b4f..d86e6c7a6 100644
--- a/t/008filter.t
+++ b/t/008filter.t
@@ -11,7 +11,7 @@
# This test scans all our templates for every directive. Having eliminated
# those which cannot possibly cause XSS problems, it then checks the rest
-# against the safe list stored in the filterexceptions.pl file.
+# against the safe list stored in the filterexceptions.pl file.
# Sample exploit code: '>"><script>alert('Oh dear...')</script>
@@ -42,17 +42,17 @@ foreach my $path (@Support::Templates::include_paths) {
chdir $topdir; # absolute path
my @testitems = Support::Templates::find_actual_files($path);
chdir $topdir; # absolute path
-
+
next unless @testitems;
-
+
# Some people require this, others don't. No-one knows why.
chdir $path; # relative path
-
+
# We load a %safe list of acceptable exceptions.
if (-r "filterexceptions.pl") {
do "filterexceptions.pl";
if (ON_WINDOWS) {
- # filterexceptions.pl uses / separated paths, while
+ # filterexceptions.pl uses / separated paths, while
# find_actual_files returns \ separated ones on Windows.
# Here, we convert the filter exception hash to use \.
foreach my $file (keys %safe) {
@@ -65,16 +65,16 @@ foreach my $path (@Support::Templates::include_paths) {
}
}
}
-
+
# We preprocess the %safe hash of lists into a hash of hashes. This allows
- # us to flag which members were not found, and report that as a warning,
+ # us to flag which members were not found, and report that as a warning,
# thereby keeping the lists clean.
foreach my $file (keys %safe) {
if (ref $safe{$file} eq 'ARRAY') {
my $list = $safe{$file};
$safe{$file} = {};
foreach my $directive (@$list) {
- $safe{$file}{$directive} = 0;
+ $safe{$file}{$directive} = 0;
}
}
}
@@ -105,14 +105,14 @@ foreach my $path (@Support::Templates::include_paths) {
if (!directive_ok($file, $directive)) {
# This intentionally makes no effort to eliminate duplicates; to do
- # so would merely make it more likely that the user would not
+ # so would merely make it more likely that the user would not
# escape all instances when attempting to correct an error.
push(@unfiltered, "$lineno:$directive");
}
- }
+ }
my $fullpath = File::Spec->catfile($path, $file);
-
+
if (@unfiltered) {
my $uflist = join("\n ", @unfiltered);
ok(0, "($lang/$flavor) $fullpath has unfiltered directives:\n $uflist\n--ERROR");
@@ -121,12 +121,12 @@ foreach my $path (@Support::Templates::include_paths) {
# Find any members of the exclusion list which were not found
my @notfound;
foreach my $directive (keys %{$safe{$file}}) {
- push(@notfound, $directive) if ($safe{$file}{$directive} == 0);
+ push(@notfound, $directive) if ($safe{$file}{$directive} == 0);
}
if (@notfound) {
my $nflist = join("\n ", @notfound);
- ok(0, "($lang/$flavor) $fullpath - filterexceptions.pl has extra members:\n $nflist\n" .
+ ok(0, "($lang/$flavor) $fullpath - filterexceptions.pl has extra members:\n $nflist\n" .
"--WARNING");
}
else {
@@ -141,7 +141,7 @@ sub directive_ok {
my ($file, $directive) = @_;
# Comments
- return 1 if $directive =~ /^#/;
+ return 1 if $directive =~ /^#/;
# Remove any leading/trailing whitespace.
$directive =~ s/^\s*//;
@@ -183,7 +183,7 @@ sub directive_ok {
# Simple assignments
return 1 if $directive =~ /^[\w\.\$\{\}]+\s+=\s+/;
- # Conditional literals with either sort of quotes
+ # Conditional literals with either sort of quotes
# There must be no $ in the string for it to be a literal
return 1 if $directive =~ /^(["'])[^\$]*[^\\]\1/;
return 1 if $directive =~ /^(["'])\1/;
@@ -191,10 +191,10 @@ sub directive_ok {
# Special values always used for numbers
return 1 if $directive =~ /^[ijkn]$/;
return 1 if $directive =~ /^count$/;
-
+
# Params
return 1 if $directive =~ /^Param\(/;
-
+
# Hooks
return 1 if $directive =~ /^Hook.process\(/;
@@ -206,12 +206,12 @@ sub directive_ok {
# Special Template Toolkit loop variable
return 1 if $directive =~ /^loop\.(index|count)$/;
-
+
# Branding terms
return 1 if $directive =~ /^terms\./;
-
+
# Things which are already filtered
- # Note: If a single directive prints two things, and only one is
+ # Note: If a single directive prints two things, and only one is
# filtered, we may not catch that case.
return 1 if $directive =~ /FILTER\ (html|csv|js|base64|css_class_quote|ics|
quoteUrls|time|uri|xml|lower|html_light|
diff --git a/t/009bugwords.t b/t/009bugwords.t
index 740cbf6aa..ebfd99507 100644
--- a/t/009bugwords.t
+++ b/t/009bugwords.t
@@ -9,9 +9,9 @@
#Bugzilla Test 9#
####bugwords#####
-# Bugzilla has a mechanism for taking various words, including "bug", "bugs",
+# Bugzilla has a mechanism for taking various words, including "bug", "bugs",
# and "a bug" and automatically replacing them in the templates with the local
-# terminology. It does this by using the 'terms' hash, so "bug" becomes
+# terminology. It does this by using the 'terms' hash, so "bug" becomes
# "[% terms.bug %]". This test makes sure the relevant words aren't used
# bare.
@@ -27,7 +27,7 @@ use Bugzilla::Util;
use File::Spec;
-use Test::More tests => ($Support::Templates::num_actual_files);
+use Test::More tests => ($Support::Templates::num_actual_files);
# Find all the templates
my @testitems;
@@ -38,10 +38,10 @@ for my $path (@Support::Templates::include_paths) {
foreach my $file (@testitems) {
my @errors;
-
+
# Read the entire file into a string
local $/;
- open (FILE, "<$file") || die "Can't open $file: $!\n";
+ open (FILE, "<$file") || die "Can't open $file: $!\n";
my $slurp = <FILE>;
close (FILE);
@@ -53,7 +53,7 @@ foreach my $file (@testitems) {
my @lineno = ($` =~ m/\n/gs);
my $lineno = scalar(@lineno) + 1;
-
+
# "a bug", "bug", "bugs"
if (grep /(a?[\s>]bugs?[\s.:;,<])/i, $text) {
# Exclude variable assignment.
@@ -63,14 +63,14 @@ foreach my $file (@testitems) {
}
}
}
-
+
if (scalar(@errors)) {
ok(0, "$file contains invalid bare words (e.g. 'bug') --WARNING");
-
+
foreach my $error (@errors) {
print "$error->[0]: $error->[1]\n";
}
- }
+ }
else {
ok(1, "$file has no invalid barewords");
}
diff --git a/t/012throwables.t b/t/012throwables.t
index 4317c0e89..af7a27eb2 100644
--- a/t/012throwables.t
+++ b/t/012throwables.t
@@ -48,7 +48,7 @@ foreach my $include_path (@include_paths) {
my $file = File::Spec->catfile($include_path, $path);
$file =~ s/\s.*$//; # nuke everything after the first space
$file =~ s|\\|/|g if ON_WINDOWS; # convert \ to / in path if on windows
- $test_templates{$file} = ()
+ $test_templates{$file} = ()
if $file =~ m#global/(code|user)-error(?:-errors)?\.html\.tmpl#;
# Make sure the extension is not disabled
@@ -77,14 +77,14 @@ foreach my $file (keys %test_templates) {
Register(\%test_templates, $file, "could not open file --WARNING");
next;
}
-
+
my $lineno=0;
while (my $line = <TMPL>) {
$lineno++;
if ($line =~ /\[%\s[A-Z]+\s*error\s*==\s*"(.+)"\s*%\]/) {
my $errtag = $1;
if ($errtag =~ /\s/) {
- Register(\%test_templates, $file,
+ Register(\%test_templates, $file,
"has an error definition \"$errtag\" at line $lineno with "
. "space(s) embedded --ERROR");
}
@@ -125,7 +125,7 @@ foreach my $file (keys %test_modules) {
push @{$Errors{$errtype}{$errtag}{used_in}{$file}}, $lineno;
}
}
-
+
close(TMPL);
}
@@ -148,7 +148,7 @@ foreach my $errtype (keys %Errors) {
if (scalar @langs) {
UsedIn($errtype, $errtag, join(', ',@langs));
}
-
+
# Now check for tag usage in all DEFINED languages
foreach my $lang (keys %{$Errors{$errtype}{$errtag}{defined_in}}) {
if (!defined $Errors{$errtype}{$errtag}{used_in}) {
@@ -162,7 +162,7 @@ foreach my $errtype (keys %Errors) {
# And make sure that everything defined in WS_ERROR_CODE
# is actually a valid error.
foreach my $err_name (keys %{WS_ERROR_CODE()}) {
- if (!defined $Errors{'code'}{$err_name}
+ if (!defined $Errors{'code'}{$err_name}
&& !defined $Errors{'user'}{$err_name})
{
Register(\%test_modules, 'WS_ERROR_CODE',
@@ -218,18 +218,18 @@ sub UsedIn {
my ($errtype, $errtag, $lang) = @_;
$lang = $lang || "any";
foreach my $file (keys %{$Errors{$errtype}{$errtag}{used_in}}) {
- Register(\%test_modules, $file,
- "$errtype error tag '$errtag' is used at line(s) ("
- . join (',', @{$Errors{$errtype}{$errtag}{used_in}{$file}})
+ Register(\%test_modules, $file,
+ "$errtype error tag '$errtag' is used at line(s) ("
+ . join (',', @{$Errors{$errtype}{$errtag}{used_in}{$file}})
. ") but not defined for language(s): $lang");
}
}
sub DefinedIn {
my ($errtype, $errtag, $lang) = @_;
foreach my $file (keys %{$Errors{$errtype}{$errtag}{defined_in}{$lang}}) {
- Register(\%test_templates, $file,
+ Register(\%test_templates, $file,
"$errtype error tag '$errtag' is defined at line(s) ("
- . join (',', @{$Errors{$errtype}{$errtag}{defined_in}{$lang}{$file}})
+ . join (',', @{$Errors{$errtype}{$errtag}{defined_in}{$lang}{$file}})
. ") but is not used anywhere", 1);
}
}
diff --git a/t/json-boxes.t b/t/json-boxes.t
new file mode 100644
index 000000000..4d9816e83
--- /dev/null
+++ b/t/json-boxes.t
@@ -0,0 +1,36 @@
+#!/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 strict;
+use warnings;
+use 5.10.1;
+use lib qw( . lib local/lib/perl5 );
+
+use Scalar::Util qw(weaken);
+use Mojo::JSON qw(encode_json);
+use Scalar::Util qw(refaddr);
+use Test2::V0;
+
+use ok 'Bugzilla::WebService::JSON';
+
+my $json = Bugzilla::WebService::JSON->new;
+my $ref = {foo => 1};
+is(refaddr $json->decode($json->encode($ref)), refaddr $ref);
+
+my $box = $json->encode($ref);
+
+is($json->decode(q[{"foo":1}]), {foo => 1});
+is($json->decode($box), {foo => 1});
+
+is "$box", $box->label;
+
+$box->encode;
+
+is encode_json([ $box ]), encode_json([ encode_json($box->value) ]);
+is "$box", q[{"foo":1}];
+
+done_testing;
diff --git a/t/markdown.t b/t/markdown.t
index 0344706c9..83e0dc051 100644
--- a/t/markdown.t
+++ b/t/markdown.t
@@ -9,7 +9,7 @@ use strict;
use warnings;
use lib qw( . lib local/lib/perl5 );
use Bugzilla;
-use Test::More;
+use Test2::V0;
my $parser = Bugzilla->markdown_parser;
@@ -61,7 +61,9 @@ my $table_html = <<'HTML';
<tr>
<td>val1</td>
<td align="center">val2</td>
-</tr></tbody></table>
+</tr>
+</tbody>
+</table>
HTML
is(
diff --git a/t/mojo-example.t b/t/mojo-example.t
new file mode 100644
index 000000000..8ed4835da
--- /dev/null
+++ b/t/mojo-example.t
@@ -0,0 +1,76 @@
+#!/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 strict;
+use warnings;
+use 5.10.1;
+use lib qw( . lib local/lib/perl5 );
+
+BEGIN {
+ $ENV{LOG4PERL_CONFIG_FILE} = 'log4perl-t.conf';
+ # There's a plugin called Hostage that makes the application require specific Host: headers.
+ # we disable that for these tests.
+ $ENV{BUGZILLA_DISABLE_HOSTAGE} = 1;
+}
+
+# this provides a default urlbase.
+# Most localconfig options the other Bugzilla::Test::Mock* modules take care for us.
+use Bugzilla::Test::MockLocalconfig ( urlbase => 'http://bmo-web.vm' );
+
+# This configures an in-memory sqlite database.
+use Bugzilla::Test::MockDB;
+
+# This redirects reads and writes from the config file (data/params)
+use Bugzilla::Test::MockParams (
+ phabricator_enabled => 1,
+ announcehtml => '<div id="announcement">Mojo::Test is awesome</div>',
+);
+
+# Util provides a few functions more making mock data in the DB.
+use Bugzilla::Test::Util qw(create_user issue_api_key);
+
+use Test2::V0;
+use Test2::Tools::Mock;
+use Test::Mojo;
+
+my $api_user = create_user('api@mozilla.org', '*');
+my $api_key = issue_api_key('api@mozilla.org')->api_key;
+
+# Mojo::Test loads the application and provides methods for
+# testing requests without having to run a server.
+my $t = Test::Mojo->new('Bugzilla::Quantum');
+
+# we ensure this file exists so the /__lbhearbeat__ test passes.
+$t->app->home->child('__lbheartbeat__')->spurt('httpd OK');
+
+# Method chaining is used extensively.
+$t->get_ok('/__lbheartbeat__')->status_is(200)->content_is('httpd OK');
+
+# this won't work until we can mock memcached.
+# $t->get_ok('/__heartbeat__')->status_is(200);
+
+# we can use json_is or json_like to check APIs.
+# The first pair to json_like is a JSON pointer (RFC 6901)
+$t->get_ok('/bzapi/configuration')->status_is(200)->json_like( '/announcement' => qr/Mojo::Test is awesome/ );
+
+# for web requests, you use text_like (or text_is) with CSS selectors.
+$t->get_ok('/')->status_is(200)->text_like( '#announcement' => qr/Mojo::Test is awesome/ );
+
+# Chaining is not magical, you can break up longer lines
+# by calling methods on $t, as below.
+$t->get_ok('/rest/whoami' => { 'X-Bugzilla-API-Key' => $api_key });
+$t->status_is(200);
+$t->json_is('/name' => $api_user->login);
+$t->json_is('/id' => $api_user->id);
+
+# Each time you call $t->get_ok, post_ok, etc the previous request is cleared.
+$t->get_ok('/rest/whoami');
+$t->status_is(200);
+$t->json_is('/name' => '');
+$t->json_is('/id' => 0);
+
+done_testing;
diff --git a/t/security-risk.t b/t/security-risk.t
new file mode 100644
index 000000000..520953bc0
--- /dev/null
+++ b/t/security-risk.t
@@ -0,0 +1,156 @@
+#!/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 strict;
+use warnings;
+use 5.10.1;
+use lib qw( . lib local/lib/perl5 );
+use Bugzilla;
+
+BEGIN { Bugzilla->extensions };
+
+use Test::More;
+use Test2::Tools::Mock;
+use Try::Tiny;
+
+use ok 'Bugzilla::Report::SecurityRisk';
+can_ok('Bugzilla::Report::SecurityRisk', qw(new results));
+
+sub check_open_state_mock {
+ my ($state) = @_;
+ return grep { /^$state$/ } qw(UNCOMFIRMED NEW ASSIGNED REOPENED);
+}
+
+try {
+ use Bugzilla::Report::SecurityRisk;
+ my $report = Bugzilla::Report::SecurityRisk->new(
+ start_date => DateTime->new( year => 2000, month => 1, day => 9 ),
+ end_date => DateTime->new( year => 2000, month => 1, day => 16 ),
+ products => [ 'Firefox', 'Core' ],
+ sec_keywords => [ 'sec-critical', 'sec-high' ],
+ check_open_state => \&check_open_state_mock,
+ initial_bug_ids => [ 1, 2, 3, 4 ],
+ initial_bugs => {
+ 1 => {
+ id => 1,
+ product => 'Firefox',
+ sec_level => 'sec-high',
+ is_open => 0,
+ created_at => DateTime->new( year => 2000, month => 1, day => 1 ),
+ },
+ 2 => {
+ id => 2,
+ product => 'Core',
+ sec_level => 'sec-critical',
+ is_open => 0,
+ created_at => DateTime->new( year => 2000, month => 1, day => 1 ),
+ },
+ 3 => {
+ id => 3,
+ product => 'Core',
+ sec_level => 'sec-high',
+ is_open => 1,
+ created_at => DateTime->new( year => 2000, month => 1, day => 5 ),
+ },
+ 4 => {
+ id => 4,
+ product => 'Firefox',
+ sec_level => 'sec-critical',
+ is_open => 1,
+ created_at => DateTime->new( year => 2000, month => 1, day => 10 ),
+ },
+ },
+ events => [
+ # Canned event's should be in reverse chronological order.
+ {
+ bug_id => 2,
+ bug_when => DateTime->new( year => 2000, month => 1, day => 14 ),
+ field_name => 'keywords',
+ removed => '',
+ added => 'sec-critical',
+
+ },
+ {
+ bug_id => 1,
+ bug_when => DateTime->new( year => 2000, month => 1, day => 12 ),
+ field_name => 'bug_status',
+ removed => 'ASSIGNED',
+ added => 'RESOLVED',
+ },
+ ],
+ );
+ my $actual_results = $report->results;
+ my $expected_results = [
+ {
+ date => DateTime->new( year => 2000, month => 1, day => 9 ),
+ bugs_by_product => {
+ 'Firefox' => {
+ # Rewind the event that caused 1 to close.
+ open => [1],
+ closed => [],
+ median_age_open => 8
+ },
+ 'Core' => {
+ # 2 wasn't a sec-critical bug on the report date.
+ open => [3],
+ closed => [],
+ median_age_open => 4
+ }
+ },
+ bugs_by_sec_keyword => {
+ 'sec-critical' => {
+ # 2 wasn't a sec-crtical bug and 4 wasn't created yet on the report date.
+ open => [],
+ closed => [],
+ median_age_open => 0
+ },
+ 'sec-high' => {
+ # Rewind the event that caused 1 to close.
+ open => [ 1, 3 ],
+ closed => [],
+ median_age_open => 6
+ }
+ },
+ },
+ { # The report on 2000-01-16 matches the state of initial_bugs.
+ date => DateTime->new( year => 2000, month => 1, day => 16 ),
+ bugs_by_product => {
+ 'Firefox' => {
+ open => [4],
+ closed => [1],
+ median_age_open => 6
+ },
+ 'Core' => {
+ open => [3],
+ closed => [2],
+ median_age_open => 11
+ }
+ },
+ bugs_by_sec_keyword => {
+ 'sec-critical' => {
+ open => [4],
+ closed => [2],
+ median_age_open => 6
+ },
+ 'sec-high' => {
+ open => [3],
+ closed => [1],
+ median_age_open => 11
+ }
+ },
+ },
+ ];
+
+ is_deeply($actual_results, $expected_results, 'Report results are accurate');
+
+}
+catch {
+ fail('got an exception during main part of test');
+ diag($_);
+};
+
+done_testing;