]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1597800 - Send a sec bug report for moderate and low levels
authorIsrael Madueme <purelogiq@gmail.com>
Wed, 11 Dec 2019 22:16:49 +0000 (17:16 -0500)
committerGitHub <noreply@github.com>
Wed, 11 Dec 2019 22:16:49 +0000 (17:16 -0500)
This augments the existing secbugsreport.pl script to also send a report
on sec moderate and sec low bugs in a separate email according to the
sec teams requests. Will run in the same script so no need for an extra
cron job.

scripts/secbugsreport.pl
template/en/default/reports/email/security-risk-moderate-low.html.tmpl [new file with mode: 0644]

index 29276c774220c8a6dcc40af00d0ac74005ca0231..fbe840f7e4274d275bdd1f02af0f09d0257e8147 100644 (file)
@@ -44,8 +44,10 @@ exit 0
   && 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,
@@ -57,49 +59,87 @@ my $end_date = DateTime->new(
 );
 $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(
@@ -112,25 +152,50 @@ my @parts = (
         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) = @_;
@@ -139,3 +204,13 @@ sub build_bugs_link {
   $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;
+}
diff --git a/template/en/default/reports/email/security-risk-moderate-low.html.tmpl b/template/en/default/reports/email/security-risk-moderate-low.html.tmpl
new file mode 100644 (file)
index 0000000..d93df32
--- /dev/null
@@ -0,0 +1,249 @@
+[%# 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>