&& Int->check($minutes)
&& Int->check($seconds);
-my $html;
-my $template = Bugzilla->template();
+my $html_crit_high;
+my $html_moderate_low;
+my $template_crit_high = Bugzilla->template();
+my $template_moderate_low = Bugzilla->template();
my $end_date = DateTime->new(
year => $year,
month => $month,
);
$end_date->set_time_zone('UTC');
-my $start_date = $end_date->clone()->subtract(months => 12);
-my $report_week = $end_date->ymd('-');
-my $teams = decode_json(Bugzilla->params->{report_secbugs_teams});
-my $sec_keywords = ['sec-critical', 'sec-high'];
-my $report = Bugzilla::Report::SecurityRisk->new(
+my $start_date = $end_date->clone()->subtract(months => 12);
+my $start_date_no_graphs = $end_date->clone()->subtract(months => 2);
+my $report_week = $end_date->ymd('-');
+my $teams = decode_json(Bugzilla->params->{report_secbugs_teams});
+
+# Sec Critical and Sec High report
+my $sec_keywords_crit_high = ['sec-critical', 'sec-high'];
+my $report_crit_high = Bugzilla::Report::SecurityRisk->new(
start_date => $start_date,
end_date => $end_date,
teams => $teams,
- sec_keywords => $sec_keywords,
+ sec_keywords => $sec_keywords_crit_high,
very_old_days => 45
);
-
-my $bugs_by_team = $report->results->[-1]->{bugs_by_team};
-my @sorted_team_names = sort { ## no critic qw(BuiltinFunctions::ProhibitReverseSortBlock
- @{$bugs_by_team->{$b}->{open}} <=> @{$bugs_by_team->{$a}->{open}} ## no critic qw(Freenode::DollarAB)
- || $a cmp $b
-} keys %$teams;
-
-my $vars = {
+my $sorted_teams_crit_high = sorted_team_names_by_open_bugs($report_crit_high);
+my $vars_crit_high = {
urlbase => Bugzilla->localconfig->urlbase,
report_week => $report_week,
- teams => \@sorted_team_names,
- sec_keywords => $sec_keywords,
- results => $report->results,
- deltas => $report->deltas,
- missing_products => $report->missing_products,
- missing_components => $report->missing_components,
- very_old_days => $report->very_old_days,
+ teams => $sorted_teams_crit_high,
+ sec_keywords => $sec_keywords_crit_high,
+ results => $report_crit_high->results,
+ deltas => $report_crit_high->deltas,
+ missing_products => $report_crit_high->missing_products,
+ missing_components => $report_crit_high->missing_components,
+ very_old_days => $report_crit_high->very_old_days,
build_bugs_link => \&build_bugs_link,
};
-
-$template->process('reports/email/security-risk.html.tmpl', $vars, \$html)
- or ThrowTemplateError($template->error());
-
-# For now, only send HTML email.
-my @parts = (
+$template_crit_high->process(
+ 'reports/email/security-risk.html.tmpl',
+ $vars_crit_high,
+ \$html_crit_high
+) or ThrowTemplateError($template_crit_high->error());
+
+# Sec Moderate and Sec Low report
+# These have to be done separately since they do not want the by
+# teams results combined. This template is specific is to this request,
+# for a generic template use security-risk.html.tmpl as above.
+my $report_moderate = Bugzilla::Report::SecurityRisk->new(
+ start_date => $start_date_no_graphs,
+ end_date => $end_date,
+ teams => $teams,
+ sec_keywords => ['sec-moderate'],
+ very_old_days => 45
+);
+my $report_low = Bugzilla::Report::SecurityRisk->new(
+ start_date => $start_date_no_graphs,
+ end_date => $end_date,
+ teams => $teams,
+ sec_keywords => ['sec-low'],
+ very_old_days => 45
+);
+my $sorted_teams_moderate = sorted_team_names_by_open_bugs($report_moderate);
+my $sorted_teams_low = sorted_team_names_by_open_bugs($report_low);
+my $vars_moderate_low = {
+ urlbase => Bugzilla->localconfig->urlbase,
+ report_week => $report_week,
+ sec_keywords => ['sec-moderate', 'sec-low'],
+ teams_moderate => $sorted_teams_moderate,
+ results_moderate => $report_moderate->results,
+ deltas_moderate => $report_moderate->deltas,
+ teams_low => $sorted_teams_low,
+ results_low => $report_low->results,
+ deltas_low => $report_low->deltas,
+ very_old_days => $report_low->very_old_days,
+ build_bugs_link => \&build_bugs_link,
+};
+$template_moderate_low->process(
+ 'reports/email/security-risk-moderate-low.html.tmpl',
+ $vars_moderate_low,
+ \$html_moderate_low
+) or ThrowTemplateError($template_moderate_low->error());
+
+# Crit + High Report. For now, only send HTML email.
+my @parts_crit_high = (
Email::MIME->create(
attributes => {
content_type => 'text/html',
charset => 'UTF-8',
encoding => 'quoted-printable',
},
- body_str => $html,
+ body_str => $html_crit_high,
),
map {
Email::MIME->create(
name => "$_.png",
encoding => 'base64',
},
- body => $report->graphs->{$_}->slurp,
+ body => $report_crit_high->graphs->{$_}->slurp,
)
- } sort { $a cmp $b } keys %{$report->graphs}
+ } sort { $a cmp $b } keys %{$report_crit_high->graphs}
);
-my $email = Email::MIME->create(
+my $email_crit_high = Email::MIME->create(
header_str => [
From => Bugzilla->params->{'mailfrom'},
To => Bugzilla->params->{report_secbugs_emails},
Subject => "Security Bugs Report for $report_week",
'X-Bugzilla-Type' => 'admin',
],
- parts => [@parts],
+ parts => [@parts_crit_high],
);
-MessageToMTA($email);
+MessageToMTA($email_crit_high);
+
+# Moderate + Low Report. For now, only send HTML email.
+my @parts_moderate_low = (
+ Email::MIME->create(
+ attributes => {
+ content_type => 'text/html',
+ charset => 'UTF-8',
+ encoding => 'quoted-printable',
+ },
+ body_str => $html_moderate_low,
+ )
+);
+
+my $email_moderate_low = Email::MIME->create(
+ header_str => [
+ From => Bugzilla->params->{'mailfrom'},
+ To => Bugzilla->params->{report_secbugs_emails},
+ Subject => "Security Bugs Report (moderate & low) for $report_week",
+ 'X-Bugzilla-Type' => 'admin',
+ ],
+ parts => [@parts_moderate_low],
+);
+
+MessageToMTA($email_moderate_low);
my $report_dump_file = path(bz_locations->{datadir}, "$year-$month-$day.dump");
-$report_dump_file->spurt(Dumper($report));
+$report_dump_file->spurt(Dumper($report_crit_high));
+# Don't dump moderate low
sub build_bugs_link {
my ($arr, $product) = @_;
$uri->query_param(product => $product) if $product;
return $uri->as_string;
}
+
+sub sorted_team_names_by_open_bugs {
+ my ($report) = @_;
+ my $bugs_by_team = $report->results->[-1]->{bugs_by_team};
+ my @sorted_team_names = sort { ## no critic qw(BuiltinFunctions::ProhibitReverseSortBlock
+ @{$bugs_by_team->{$b}->{open}} <=> @{$bugs_by_team->{$a}->{open}} ## no critic qw(Freenode::DollarAB)
+ || $a cmp $b
+ } keys %$teams;
+ return \@sorted_team_names;
+}
--- /dev/null
+[%# 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.
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+
+<!doctype html>
+<html>
+<head>
+ <title>Security [% terms.Bugs %] Report for the week of [% report_week FILTER html %]</title>
+ <base href="[% urlbase FILTER txt %]">
+</head>
+<body>
+
+<h3>Sec-Moderate by Team</h3>
+<table style="border-spacing: 0">
+ <tr>
+ <th style="padding: 0px 15px 10px 0px;">Team</th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right;">
+ Open<br>[% results_moderate.reverse.0.date.strftime('%m/%d') FILTER html %]
+ </th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right;">Closed<br />Last Week</th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey;">Added<br />Last Week</th>
+ [% FOREACH result IN results_moderate.reverse %]
+ [% NEXT IF loop.count < 2 %]
+ [% LAST IF loop.count > 4 %]
+ <th style="padding: 0px 15px 10px [% IF loop.count == 2 %] 10px [% ELSE %] 0px [% END %]; text-align: right; [% IF loop.count == 1 %] border-right: 1px solid grey; [% END %]">
+ Open<br>[% result.date.strftime('%m/%d') FILTER html %]
+ </th>
+ [% END %]
+ </tr>
+ [% FOREACH team IN teams_moderate %]
+ <tr>
+ <td style="padding: 0px 15px 10px 0px;">[% team FILTER html %]</td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right;">
+ [% IF results_moderate.reverse.0.bugs_by_team.$team.open.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(results_moderate.reverse.0.bugs_by_team.$team.open) FILTER html %]">
+ [% results_moderate.reverse.0.bugs_by_team.$team.open.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right;
+ [% IF deltas_moderate.by_team.$team.closed.size %] background-color: #e6ffe6 [% END %]">
+ [% IF deltas_moderate.by_team.$team.closed.size %]
+ <a style="text-decoration: none;"
+ href="[% build_bugs_link(deltas_moderate.by_team.$team.closed) FILTER html %]">
+ -[% deltas_moderate.by_team.$team.closed.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey;
+ [% IF deltas_moderate.by_team.$team.added.size %] background-color: #ffe6e6 [% END %]">
+ [% IF deltas_moderate.by_team.$team.added.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(deltas_moderate.by_team.$team.added) FILTER html %]">
+ +[% deltas_moderate.by_team.$team.added.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ [% FOREACH result IN results_moderate.reverse %]
+ [% NEXT IF loop.count < 2 %]
+ [% LAST IF loop.count > 4 %]
+ <td style="padding: 0px 15px 10px [% IF loop.count == 2 %] 10px [% ELSE %] 0px [% END %]; text-align: right; [% IF loop.count == 1 %] border-right: 1px solid grey; [% END %]">
+ [% IF result.bugs_by_team.$team.open.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(result.bugs_by_team.$team.open) FILTER html %]">
+ [% result.bugs_by_team.$team.open.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ [% END %]
+ </tr>
+ [% END %]
+</table>
+<br/>
+
+<h3>Sec-Low by Team</h3>
+<table style="border-spacing: 0">
+ <tr>
+ <th style="padding: 0px 15px 10px 0px;">Team</th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right;">
+ Open<br>[% results_low.reverse.0.date.strftime('%m/%d') FILTER html %]
+ </th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right;">Closed<br />Last Week</th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey;">Added<br />Last Week</th>
+ [% FOREACH result IN results_low.reverse %]
+ [% NEXT IF loop.count < 2 %]
+ [% LAST IF loop.count > 4 %]
+ <th style="padding: 0px 15px 10px [% IF loop.count == 2 %] 10px [% ELSE %] 0px [% END %]; text-align: right; [% IF loop.count == 1 %] border-right: 1px solid grey; [% END %]">
+ Open<br>[% result.date.strftime('%m/%d') FILTER html %]
+ </th>
+ [% END %]
+ </tr>
+ [% FOREACH team IN teams_low %]
+ <tr>
+ <td style="padding: 0px 15px 10px 0px;">[% team FILTER html %]</td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right;">
+ [% IF results_low.reverse.0.bugs_by_team.$team.open.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(results_low.reverse.0.bugs_by_team.$team.open) FILTER html %]">
+ [% results_low.reverse.0.bugs_by_team.$team.open.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right;
+ [% IF deltas_low.by_team.$team.closed.size %] background-color: #e6ffe6 [% END %]">
+ [% IF deltas_low.by_team.$team.closed.size %]
+ <a style="text-decoration: none;"
+ href="[% build_bugs_link(deltas_low.by_team.$team.closed) FILTER html %]">
+ -[% deltas_low.by_team.$team.closed.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey;
+ [% IF deltas_low.by_team.$team.added.size %] background-color: #ffe6e6 [% END %]">
+ [% IF deltas_low.by_team.$team.added.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(deltas_low.by_team.$team.added) FILTER html %]">
+ +[% deltas_low.by_team.$team.added.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ [% FOREACH result IN results_low.reverse %]
+ [% NEXT IF loop.count < 2 %]
+ [% LAST IF loop.count > 4 %]
+ <td style="padding: 0px 15px 10px [% IF loop.count == 2 %] 10px [% ELSE %] 0px [% END %]; text-align: right; [% IF loop.count == 1 %] border-right: 1px solid grey; [% END %]">
+ [% IF result.bugs_by_team.$team.open.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(result.bugs_by_team.$team.open) FILTER html %]">
+ [% result.bugs_by_team.$team.open.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ [% END %]
+ </tr>
+ [% END %]
+</table>
+<br/>
+
+<h3>[% terms.Bugs %] By Severity</h3>
+<table style="border-spacing: 0">
+ <tr>
+ <th style="padding: 0px 15px 10px 0px;">Category</th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right;">
+ Open<br>[% results_moderate.reverse.0.date.strftime('%m/%d') FILTER html %]
+ </th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right;">Closed<br />Last Week</th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right;">Added<br />Last Week</th>
+ <th style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey;">
+ Older than <br> [% very_old_days FILTER html %] Days<br>
+ </th>
+ [% FOREACH result IN results_moderate.reverse %]
+ [% NEXT IF loop.count < 2 %]
+ [% LAST IF loop.count > 4 %]
+ <th style="padding: 0px 15px 10px [% IF loop.count == 2 %] 10px [% ELSE %] 0px [% END %]; text-align: right; [% IF loop.count == 1 %] border-right: 1px solid grey; [% END %]">
+ Open<br>[% result.date.strftime('%m/%d') FILTER html %]
+ </th>
+ [% END %]
+ </tr>
+ [% FOREACH keyword IN sec_keywords %]
+ [% IF keyword == 'sec-moderate' %]
+ [% results = results_moderate %]
+ [% ELSIF keyword == 'sec-low' %]
+ [% results = results_low %]
+ [% END %]
+ <tr>
+ <td style="padding: 0px 15px 10px 0px;">[% keyword FILTER html %]</td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right;">
+ [% IF results.reverse.0.bugs_by_sec_keyword.$keyword.open.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(results.reverse.0.bugs_by_sec_keyword.$keyword.open) FILTER html %]">
+ [% results.reverse.0.bugs_by_sec_keyword.$keyword.open.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right;
+ [% IF deltas.by_sec_keyword.$keyword.closed.size %] background-color: #e6ffe6 [% END %]">
+ [% IF deltas.by_sec_keyword.$keyword.closed.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(deltas.by_sec_keyword.$keyword.closed) FILTER html %]">
+ -[% deltas.by_sec_keyword.$keyword.closed.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right;
+ [% IF deltas.by_sec_keyword.$keyword.added.size %] background-color: #ffe6e6 [% END %]">
+ [% IF deltas.by_sec_keyword.$keyword.added.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(deltas.by_sec_keyword.$keyword.added) FILTER html %]">
+ +[% deltas.by_sec_keyword.$keyword.added.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ <td style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey; ">
+ [% IF results.reverse.0.bugs_by_sec_keyword.$keyword.very_old_bugs.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(results.reverse.0.bugs_by_sec_keyword.$keyword.very_old_bugs) FILTER html %]">
+ [% results.reverse.0.bugs_by_sec_keyword.$keyword.very_old_bugs.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ [% FOREACH result IN results.reverse %]
+ [% NEXT IF loop.count < 2 %]
+ [% LAST IF loop.count > 4 %]
+ <td style="padding: 0px 15px 10px [% IF loop.count == 2 %] 10px [% ELSE %] 0px [% END %]; text-align: right; [% IF loop.count == 1 %] border-right: 1px solid grey; [% END %]">
+ [% IF result.bugs_by_sec_keyword.$keyword.open.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(result.bugs_by_sec_keyword.$keyword.open) FILTER html %]">
+ [% result.bugs_by_sec_keyword.$keyword.open.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
+ </td>
+ [% END %]
+ </tr>
+ [% END %]
+</table>
+
+<p>
+To narrow down open [% terms.bugs %] click on the link and at the bottom of the search results use the
+'Edit Search' functionality to filter by component and so on. This will filter only the open [% terms.bugs %]
+counted in the report (as long as you do not modify the '[% terms.Bugs %] numbered' section of the search).
+Keep in mind that you will only be able to see [% terms.bugs %] that you are allowed to see. Also keep in mind that
+this report treats marking a [% terms.bug %] as 'stalled' the same as closing it.
+</p>
+
+<p>This report was generated from the live Bugzilla instance. In rare cases, historical statistics may vary from prior reports if [% terms.bugs %] were reclassified after those reports were generated.</p>
+
+</body>
+</html>