#!/bin/bash
# Copyright (C) 2008-2018 Canonical, Ltd.
# Author: Kees Cook <kees@ubuntu.com>
# Author: Jamie Strandboge <jamie@ubuntu.com>
# Author: Steve Beattie <sbeattie@ubuntu.com>
# License: GPLv3
#
# This script attempts to build up statistics for a monthly report on
# security update activity on USNs, triage, and outstanding tasks.
# It is designed to be run on the month _following_ the month one wants
# a report for. e.g. run this script in April if you want the March report.
#
# By default, report in a more prose-style. E.g.:
#  The security team had a busy month. We published 29 Ubuntu Security
#  Notices which fixed 63 security issues (CVEs) across 30 supported
#  packages. Additionally, we triaged 487 public security vulnerability
#  reports, retaining only those that applied to Ubuntu.
#
#  For all the supported packages in Ubuntu, there are 67 medium-priority
#  issues and 206 low issues that need to be fixed in 142 packages.
#
#  For all partner packages in Ubuntu, there is 1 medium-priority issue
#  that needs to be fixed in 1 package.
#
#  For all community-supported packages in Ubuntu, there are 7 high-priority
#  issues, 721 medium-priority, and 1005 low-priority issues that need to be
#  fixed in 686 packages.
set -e
export LANG=C

help() {
    cat <<EOM
This script attempts to build up statistics for a monthly report on
security update activity on USNs, triage, and outstanding tasks.
It is designed to be run on the month _following_ the month one wants
a report for. e.g. run this script in April if you want the March report.

Typical usage:
$ $UCT/scripts/`basename $0`

Can also specify different months with:
$ $UCT/scripts/`basename $0` <first> <second>

Eg:
$ $UCT/scripts/`basename $0` March April
EOM
}

use_cached_usndb=
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
    help
    exit 0
elif [ "$1" = "--cache" ]; then
    use_cached_usndb="yes"
    shift
fi

# Make sure we won't destroy any pending commits
if [ -n "$(git status --porcelain)" ]; then
    echo "Aborting: git tree is unclean. Please commit changes before contining" >&2
    git status -s 1>&2
    exit 1
fi

REP_MON=$(date +%B -d 'last month')
REP_MONN=$(date +%m -d 'last month')
REP_YEAR=$(date +%Y -d 'last month')

THIS_MONN=$(date +%m)
THIS_YEAR=$(date +%Y)

if [ -n "$1" ] && [ -n "$2" ]; then
    REP_MON=$(date +%B -d "$1 01")
    REP_MONN=$(date +%m -d "$1 01")
    REP_YEAR=$(date +%Y -d "$1 01")

    THIS_MONN=$(date +%m -d "$2 01")
    THIS_YEAR=$(date +%Y -d "$2 01")
    if [ "$THIS_MONN" -lt "$REP_MONN" ]; then
        THIS_YEAR=$(($THIS_YEAR + 1))
    fi
fi

echo "Fetching USN publication list..." >&2
USNS=$(echo $(curl -s https://lists.ubuntu.com/archives/ubuntu-security-announce/$REP_YEAR-$REP_MON/date.html | fgrep '">[USN-'  | cut -d- -f2,3 | cut -d\] -f1))

echo "Fetching USN database..." >&2
if [ "$use_cached_usndb" = "yes" ] && [ -f "./database.pickle" ]; then
    echo "(skipped-- using cached ./database.pickle)"
else
    if grep -q people ~/.ssh/config; then
        rsync -q -e ssh people.canonical.com:~ubuntu-security/public_html/usn/database.pickle ./database.pickle
    else
        ./scripts/fetch-db database.pickle.bz2
    fi
fi
PUBLISHED=$(./scripts/report-usn-numbers.py --prose database.pickle $USNS)

# We depend on a script to perform output while the tree is reverted, so
# copy the script out of the tree for later use.
SCRIPTS=$(mktemp -d -t monthly-report-XXXXXX)
for i in report-todo-numbers check-cves cache_urllib.py cve_lib.py source_map.py usn_lib.py
do
    cp -a scripts/$i "$SCRIPTS"/$i
done

TMP1=$(mktemp -t work1-XXXXXX)
TMP2=$(mktemp -t work2-XXXXXX)
TMP1_UBUNTU=$(mktemp -t work1u-XXXXXX)
TMP2_UBUNTU=$(mktemp -t work2u-XXXXXX)
if [ ! -x "scripts/monthly-report" ]; then
    echo "Please run this from the top-level directory of Ubuntu CVE Tracker"
    exit 1
fi

git_rewind ()
{
    local changed count

    count=0
    changed="$1"
    while :; do
        git checkout -q $(git rev-list  -1  --before="$changed" master) && break
        changed=$(date +%Y-%m-%d -d "$changed - 1 day")
        count=$(( count + 1 ))
        if [ "$count" -gt 32 ]; then
            echo "Eeek, rewound from $1 past $changed.  Something is wrong."
            exit 1
        fi
    done
    #echo "rewound to $changed"
}


echo "Rewinding git tree for stats..." >&2
# Locate the last day something changed
git_rewind $(date +%Y-%m-%d -d "$REP_YEAR-$REP_MONN-01 - 1 day")
# Gather stats at the time
$SCRIPTS/check-cves --known > "$TMP1"
$SCRIPTS/check-cves --known --skip-nfu > "$TMP1_UBUNTU"

echo "Fast-forwarding git tree for stats..." >&2
# Fast-forward to end of month
git checkout -q master
last_month_end=`date +%Y-%m-%d -d "$THIS_YEAR-$THIS_MONN-01 - 1 day"`
last_month_fn=`date +%B -d "$THIS_YEAR-$THIS_MONN-01 - 1 day"`
git_rewind "$last_month_end"
$SCRIPTS/check-cves --known > "$TMP2"
$SCRIPTS/check-cves --known --skip-nfu > "$TMP2_UBUNTU"
WORK=$($SCRIPTS/report-todo-numbers --prose --show-unique-sources --skip-low -E -- -S)

echo "Returning git tree to present-day..." >&2
# Get back to normal
git checkout -q master

# Calculate difference
TRIAGED=$(diff -u "$TMP1" "$TMP2" | grep '^+CVE' | wc -l)
rm -f "$TMP1" "$TMP2"
FOR_US=$(diff -u "$TMP1_UBUNTU" "$TMP2_UBUNTU" | grep '^+CVE' | wc -l)
rm -f "$TMP1_UBUNTU" "$TMP2_UBUNTU"
rm -rf "$SCRIPTS"

# Report!
echo ""
echo "Add the following template text to https://wiki.canonical.com/UbuntuEngineering/MonthlyReport:"
echo ""
echo "=== Development ==="
echo " * TO BE FILLED IN (eg, look at statuses from team)"
echo ""
echo "== Security =="
echo "=== Reactive ==="
echo "In the month of $last_month_fn, the Ubuntu Security team:"
echo " * $PUBLISHED"
echo " * Triaged $TRIAGED public security vulnerability reports, retaining the $FOR_US that applied to Ubuntu."

echo ""
echo "As of the end of $last_month_fn (${last_month_end}):"
echo "$WORK"


