1#!/bin/sh
2
3: << =cut
4
5=head1 NAME
6
7debsecan - Plugin to monitor the number of CVE vulnerabilities present on a Debian-ish
8system (using debsecan). This plugin can either report the sum of vulnerabilities present in each packages ('pkg' mode, default), or the number of unique CVEs affecting the system ('cve' mode).
9
10The 'cve' mode is a better indication of the risk level of the system (as
11multiple packages with the same vulnerable source get counted repeatedly), but
12the 'pkg' provides valuable information to identify packages with high number
13of vulnerabilities that should be considered for deletion.
14
15Simply symlink this plugin into your Munin plugins directory as
16- debsecan_pkg (the extra_info will list the number of CVE affecting each package)
17- debsecan_cve (the extra_info will list the number of packages affected by each CVE)
18
19For backward compatibility, a symlink without a mode will default to 'pkg'.
20
21=head1 CONFIGURATION
22
23The default configuration is as follows.
24
25    [debsecan]
26    env.suite jessie
27    env.fixed_warning 1
28    env.fixed_critical 1000
29    env.remote_warning 1
30    env.remote_critical 10
31
32The name of the group needs to match the name of the symlink to be applied.
33Shell globbing patterns are allowed.
34
35=head1 AUTHORS
36
37* Nicolas BOUTHORS <nbouthors@nbi.fr> http://nbi.fr/, Inspiration of the moment 10/10/2007
38* Olivier Mehani <shtrom+munin@ssji.net>, 2016
39
40=head1 LICENSE
41
42Public Domain
43
44=head1 MAGIC MARKERS
45
46%# family=auto
47%# capabilities=autoconf
48
49=cut
50
51# Auto enable if we have debsecan only
52if [ "$1" = "autoconf" ] ; then
53  if [ -x /usr/bin/debsecan ]; then
54    echo yes
55  else
56    echo 'no (/usr/bin/debsecan not found)'
57  fi
58  exit 0
59fi
60
61# Fail if we don't have debsecan
62if [ ! -x /usr/bin/debsecan ]; then
63  echo 'error: /usr/bin/debsecan not found' >&2
64  exit 1
65fi
66
67SUITE=${suite:-sid}
68FIXEDWARN=${fixed_warning:-1}
69FIXEDCRIT=${fixed_critical:-1000}
70REMOTEWARN=${remote_warning:-1}
71REMOTECRIT=${remote_critical:-10}
72
73MODE=$(echo "$0" | sed 's/.*_//')
74case "${MODE}" in
75	'cve')
76		TITLE_ADD="unique "
77		FIELD=1
78		;;
79	'pkg' | *)
80		TITLE_ADD="package "
81		FIELD=2
82		;;
83esac
84
85if [ "$1" = "config" ] ; then
86  cat <<EOF_
87graph_title DebSecan: ${TITLE_ADD}vulnerabilities
88graph_info ${TITLE_ADD}vulnerabilities for ${SUITE}
89graph_args -l 0 --base 1000
90graph_vlabel number of CVE
91graph_category system
92graph_period second
93graph_info This graph show the number of known ${TITLE_ADD}vulnerabilities present on your system. Use debsecan to see details.
94remote.label remote
95remote.colour FF0000
96remote.type GAUGE
97remote.draw AREASTACK
98remote.min 0
99remote.info The number of ${TITLE_ADD}remotely exploitable CVEs with any priority
100remote.warning ${REMOTEWARN}
101remote.critical ${REMOTECRIT}
102high.label high
103high.colour DD2200
104high.type GAUGE
105high.draw AREASTACK
106high.min 0
107high.info The number of ${TITLE_ADD}CVEs marked high priority
108medium.label medium
109medium.colour FFAA00
110medium.type GAUGE
111medium.draw AREASTACK
112medium.min 0
113medium.info The number of ${TITLE_ADD}CVEs marked medium priority
114low.label low
115low.colour 0000FF
116low.type GAUGE
117low.draw AREASTACK
118low.min 0
119low.info The number of ${TITLE_ADD}CVEs marked low priority
120other.label other
121other.colour 00AAFF
122other.type GAUGE
123other.draw AREASTACK
124other.min 0
125other.info The number of ${TITLE_ADD}CVEs with unspecified priority
126fixed.label fixed
127fixed.type GAUGE
128fixed.draw LINE2
129fixed.min 0
130fixed.info The number of ${TITLE_ADD}CVEs fixed by available updates
131fixed.warning ${FIXEDWARN}
132fixed.critical ${FIXEDCRIT}
133EOF_
134  exit 0
135fi
136
137ALL=$(debsecan --suite "${SUITE}" 2> /dev/null)
138REMOTE=$(echo "$ALL" | grep -w 'remotely')
139NONREMOTE=$(echo "$ALL" | grep -wv 'remotely')
140
141HIGH=$(echo "${NONREMOTE}" | grep -w 'high urgency')
142MEDIUM=$(echo "${NONREMOTE}" | grep -w 'medium urgency')
143LOW=$(echo "${NONREMOTE}" | grep -w 'low urgency')
144OTHER=$(echo "${NONREMOTE}" | grep -wv 'urgency')
145FIXED=$(echo "${ALL}" | grep -w '(fixed')
146
147# Arguments: Field offset to aggregate by
148count_entries() {
149	CUT_FIELD="${1}"
150	cut -f "${CUT_FIELD}" -d " "| sort | uniq -c
151}
152
153case "${MODE}" in
154	'cve')
155		remote_count=$(echo "${REMOTE}" | count_entries "${FIELD}" | wc -l)
156		high_count=$(echo "${HIGH}" | count_entries "${FIELD}" | wc -l)
157		medium_count=$(echo "${MEDIUM}" | count_entries "${FIELD}" | wc -l)
158		low_count=$(echo "${LOW}" | count_entries "${FIELD}" | wc -l)
159		other_count=$(echo "${OTHER}" | count_entries "${FIELD}" | wc -l)
160		fixed_count=$(echo "${FIXED}" | count_entries "${FIELD}" | wc -l)
161		;;
162	'pkg' | *)
163		remote_count=$(echo "${REMOTE}" | wc -l)
164		high_count=$(echo "${HIGH}" | wc -l)
165		medium_count=$(echo "${MEDIUM}" | wc -l)
166		low_count=$(echo "${LOW}" | wc -l)
167		other_count=$(echo "${OTHER}" | wc -l)
168		fixed_count=$(echo "${FIXED}" | wc -l)
169		;;
170esac
171
172# Reformat the output of the cut|sort|uniq... to a more human-friendly "item (count)" format
173CVECOUNTRE='s/^ *\([0-9]\+\) \+\([^ ]\+\)/\2 (\1)/'
174
175# shellcheck disable=SC2005 disable=SC2046
176# The nested $(echo ...)s are needed to yet the newlines
177cat <<EOF
178remote.value $remote_count
179remote.extinfo $(echo $(echo "${REMOTE}" | count_entries "${FIELD}" | sort -nr | sed "${CVECOUNTRE}"))
180high.value $high_count
181high.extinfo $(echo $(echo "${HIGH}" | count_entries "${FIELD}" | sort -nr | sed "${CVECOUNTRE}"))
182medium.value $medium_count
183medium.extinfo $(echo $(echo "${MEDIUM}" | count_entries "${FIELD}" | sort -nr | sed "${CVECOUNTRE}"))
184low.value $low_count
185low.extinfo $(echo $(echo "${LOW}" | count_entries "${FIELD}" | sort -nr | sed "${CVECOUNTRE}"))
186other.value $other_count
187other.extinfo $(echo $(echo "${OTHER}" | count_entries "${FIELD}" | sort -nr | sed "${CVECOUNTRE}"))
188fixed.value $fixed_count
189fixed.extinfo $(echo $(echo "${FIXED}" | count_entries "${FIELD}" | sort -nr | sed "${CVECOUNTRE}"))
190EOF
191