|
@ -1,7 +1,7 @@ |
|
|
#!/usr/bin/env bash |
|
|
#!/usr/bin/env bash |
|
|
# |
|
|
# |
|
|
# archrisks - Get security risk severity & count of installed packages on Arch Linux |
|
|
# archrisks - Get security risk severity & count of installed packages on Arch Linux |
|
|
# Copyright (C) 2021 Pekka Helenius |
|
|
|
|
|
|
|
|
# Copyright (C) 2021,2024 Pekka Helenius |
|
|
# |
|
|
# |
|
|
# This program is free software: you can redistribute it and/or modify |
|
|
# This program is free software: you can redistribute it and/or modify |
|
|
# it under the terms of the GNU General Public License as published by |
|
|
# it under the terms of the GNU General Public License as published by |
|
@ -53,319 +53,430 @@ ARCH_MANAGERS=( |
|
|
# [13,spinach]="-Syy|-Qi|-Q|-Si|no_root" |
|
|
# [13,spinach]="-Syy|-Qi|-Q|-Si|no_root" |
|
|
# [14,trizen]="-Syy|-Qi|-Q|-Si|no_root" |
|
|
# [14,trizen]="-Syy|-Qi|-Q|-Si|no_root" |
|
|
) |
|
|
) |
|
|
|
|
|
SELECTED_MANAGER= |
|
|
|
|
|
MANAGER_PRIORITY_LOWLIMIT=-1 |
|
|
|
|
|
|
|
|
priority_lowlimit=-1 |
|
|
|
|
|
default_order="level" |
|
|
|
|
|
default_reverse=0 |
|
|
|
|
|
|
|
|
|
|
|
provider="security.archlinux.org" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SORT_ORDER="level" |
|
|
|
|
|
SORT_REVERSE=0 |
|
|
|
|
|
|
|
|
|
|
|
NETWORK_HOST_ENDPOINT="security.archlinux.org" |
|
|
|
|
|
|
|
|
input_count=${#@} |
|
|
input_count=${#@} |
|
|
[[ $input_count -eq 1 ]] && input_1=${1} |
|
|
|
|
|
[[ $input_count -eq 2 ]] && input_1=${1}; input_2=${2} |
|
|
|
|
|
|
|
|
[[ "${input_count}" -eq 1 ]] && input_1="${1}" |
|
|
|
|
|
[[ "${input_count}" -eq 2 ]] && input_1="${1}"; input_2="${2}" |
|
|
|
|
|
|
|
|
function helpCaller() { |
|
|
|
|
|
echo -e " |
|
|
|
|
|
|
|
|
usage() { |
|
|
|
|
|
echo -e " |
|
|
Usage: $0 |
|
|
Usage: $0 |
|
|
-h|--help |
|
|
|
|
|
1st arg: --sort=<name,issues,level,status,desc> (optional) |
|
|
|
|
|
2nd arg: --reverse (optional) |
|
|
|
|
|
|
|
|
-h|--help |
|
|
|
|
|
1st arg: --sort=<name,issues,level,status,desc> (optional) |
|
|
|
|
|
2nd arg: --reverse (optional) |
|
|
" |
|
|
" |
|
|
exit 0 |
|
|
|
|
|
|
|
|
exit 0 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function inputParser() { |
|
|
|
|
|
|
|
|
|
|
|
if [[ ${input_count} -gt 2 ]] || [[ ${input_1} == "-h" ]] || [[ ${input_1} == "--help" ]]; then |
|
|
|
|
|
helpCaller |
|
|
|
|
|
elif [[ ${input_count} -eq 0 ]]; then |
|
|
|
|
|
sort_order=${default_order} |
|
|
|
|
|
sort_reverse=${default_reverse} |
|
|
|
|
|
else |
|
|
|
|
|
sort_order=$(echo ${input_1} | sed -r 's/^\-\-sort=(.*)/\1/') |
|
|
|
|
|
|
|
|
|
|
|
case ${sort_order} in |
|
|
|
|
|
name|issues|level|version|desc) |
|
|
|
|
|
echo "Custom sort order selected: ${sort_order}" |
|
|
|
|
|
;; |
|
|
|
|
|
*) |
|
|
|
|
|
echo "Unknown sorting order selected (${sort_order})." |
|
|
|
|
|
helpCaller |
|
|
|
|
|
esac |
|
|
|
|
|
|
|
|
|
|
|
if [[ ${input_count} -eq 2 ]]; then |
|
|
|
|
|
case ${input_2} in |
|
|
|
|
|
"--reverse") |
|
|
|
|
|
echo "Reverse ordering" |
|
|
|
|
|
sort_reverse=1 |
|
|
|
|
|
;; |
|
|
|
|
|
*) |
|
|
|
|
|
echo "Unknown option '${input_2}'" |
|
|
|
|
|
sort_reverse=${default_reverse} |
|
|
|
|
|
esac |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
input_parser() { |
|
|
|
|
|
|
|
|
|
|
|
if \ |
|
|
|
|
|
[[ "${input_count}" -gt 2 ]] || \ |
|
|
|
|
|
[[ "${input_1}" == "-h" ]] || \ |
|
|
|
|
|
[[ "${input_1}" == "--help" ]] |
|
|
|
|
|
then |
|
|
|
|
|
usage |
|
|
|
|
|
|
|
|
|
|
|
elif [[ "${input_count}" -ne 0 ]] |
|
|
|
|
|
then |
|
|
|
|
|
SORT_ORDER=$(echo "${input_1}" | sed -r 's/^\-\-sort=(.*)/\1/') |
|
|
|
|
|
|
|
|
|
|
|
case "${SORT_ORDER}" in |
|
|
|
|
|
name|issues|level|version|desc) |
|
|
|
|
|
echo "Custom sort order selected: ${SORT_ORDER}" |
|
|
|
|
|
;; |
|
|
|
|
|
*) |
|
|
|
|
|
echo "Unknown sorting order selected (${SORT_ORDER})." |
|
|
|
|
|
usage |
|
|
|
|
|
esac |
|
|
|
|
|
|
|
|
|
|
|
if [[ "${input_count}" -eq 2 ]] |
|
|
|
|
|
then |
|
|
|
|
|
case "${input_2}" in |
|
|
|
|
|
"--reverse") |
|
|
|
|
|
echo "Reverse ordering" |
|
|
|
|
|
SORT_REVERSE=1 |
|
|
|
|
|
;; |
|
|
|
|
|
*) |
|
|
|
|
|
echo "Unknown option '${input_2}'" |
|
|
|
|
|
SORT_REVERSE=0 |
|
|
|
|
|
esac |
|
|
fi |
|
|
fi |
|
|
|
|
|
fi |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function internetTest() { |
|
|
|
|
|
if [[ $(ping -c 1 $provider 2>&1 | grep -c "Name or service not known") -ne 0 ]]; then |
|
|
|
|
|
echo -e "\nCan't connect to $provider. Please check your internet connection and try again.\n" |
|
|
|
|
|
exit 0 |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
connection_test() { |
|
|
|
|
|
|
|
|
|
|
|
local host_endpoint |
|
|
|
|
|
|
|
|
|
|
|
host_endpoint="${1}" |
|
|
|
|
|
|
|
|
|
|
|
if [[ $(ping -c 1 "${host_endpoint}" 2>&1 | grep -c "Name or service not known") -ne 0 ]] |
|
|
|
|
|
then |
|
|
|
|
|
echo -e "\nCan't connect to $host_endpoint. Please check your internet connection and try again.\n" |
|
|
|
|
|
exit 0 |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function findMyPackageManager() { |
|
|
|
|
|
|
|
|
function find_my_package_manager() { |
|
|
|
|
|
|
|
|
i=0 |
|
|
|
|
|
for managerStr in ${!ARCH_MANAGERS[@]}; do |
|
|
|
|
|
|
|
|
local i |
|
|
|
|
|
local managers_list |
|
|
|
|
|
local managers_priority_list |
|
|
|
|
|
local manager_priority |
|
|
|
|
|
local manager |
|
|
|
|
|
|
|
|
manager_priority=$(echo ${managerStr} | awk -F ',' '{print $1}') |
|
|
|
|
|
manager=$(echo ${managerStr} | awk -F ',' '{print $2}') |
|
|
|
|
|
|
|
|
i=0 |
|
|
|
|
|
managers_list=() |
|
|
|
|
|
managers_priority_list=() |
|
|
|
|
|
|
|
|
if [[ ${manager_priority} -lt ${priority_lowlimit} ]]; then |
|
|
|
|
|
echo "Minimum priority is $((${priority_lowlimit} + 1)). You have a package which has lower priority value. Exiting." |
|
|
|
|
|
exit 1 |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
for manager_str in ${!ARCH_MANAGERS[@]}; do |
|
|
|
|
|
|
|
|
if [[ $(echo $(which ${manager} &>/dev/null)$?) -eq 0 ]]; then |
|
|
|
|
|
managers_list[$i]=${manager} |
|
|
|
|
|
managers_priority_list[$i]=${manager_priority} |
|
|
|
|
|
let i++ |
|
|
|
|
|
fi |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
OLDIFS=${IFS} |
|
|
|
|
|
IFS="," |
|
|
|
|
|
manager_array=(${manager_str}) |
|
|
|
|
|
IFS=${OLDIFS} |
|
|
|
|
|
|
|
|
|
|
|
manager_priority="${manager_array[0]}" |
|
|
|
|
|
manager="${manager_array[1]}" |
|
|
|
|
|
|
|
|
if [[ ${#managers_list[@]} -eq 0 ]]; then |
|
|
|
|
|
echo "Not any valid package manager found. Exiting." |
|
|
|
|
|
exit 1 |
|
|
|
|
|
|
|
|
if [[ "${manager_priority}" -lt "${MANAGER_PRIORITY_LOWLIMIT}" ]] |
|
|
|
|
|
then |
|
|
|
|
|
echo "Minimum priority is $((${MANAGER_PRIORITY_LOWLIMIT} + 1)). You have a package which has lower priority value. Exiting." |
|
|
|
|
|
exit 1 |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if [[ $(echo ${managers_priority_list[@]} | tr ' ' '\n' | uniq -d | wc -l) -ne 0 ]]; then |
|
|
|
|
|
echo "Package managers with same priority found. Check internal manager list for duplicates. Exiting." |
|
|
|
|
|
exit 1 |
|
|
|
|
|
|
|
|
if [[ $(type -P ${manager}) ]] |
|
|
|
|
|
then |
|
|
|
|
|
managers_list[$i]="${manager}" |
|
|
|
|
|
managers_priority_list[$i]=${manager_priority} |
|
|
|
|
|
let i++ |
|
|
fi |
|
|
fi |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
if [[ ${#managers_list[@]} -eq 0 ]] |
|
|
|
|
|
then |
|
|
|
|
|
echo "Not any valid package manager found. Exiting." |
|
|
|
|
|
exit 1 |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
if [[ $(echo ${managers_priority_list[@]} | tr ' ' '\n' | uniq -d | wc -l) -ne 0 ]] |
|
|
|
|
|
then |
|
|
|
|
|
echo "Package managers with same priority found. Check internal manager list for duplicates. Exiting." |
|
|
|
|
|
exit 1 |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# Select package manager by priority. Highest is selected. |
|
|
|
|
|
i=0 |
|
|
|
|
|
while [[ "${i}" -le $((${#managers_list[@]} - 1)) ]] |
|
|
|
|
|
do |
|
|
|
|
|
if [[ ${managers_priority_list[i]} -gt ${priority_lowlimit} ]] |
|
|
|
|
|
then |
|
|
|
|
|
priority_lowlimit=${managers_priority_list[i]} |
|
|
|
|
|
SELECTED_MANAGER=${managers_list[i]} |
|
|
|
|
|
fi |
|
|
|
|
|
let i++ |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
OLDIFS=${IFS} |
|
|
|
|
|
IFS="|" |
|
|
|
|
|
pkg_command=(${ARCH_MANAGERS["$priority_lowlimit,$SELECTED_MANAGER"]}) |
|
|
|
|
|
IFS=${OLDIFS} |
|
|
|
|
|
|
|
|
|
|
|
command_refresh="${pkg_command[0]}" |
|
|
|
|
|
command_pkginfo_local="${pkg_command[1]}" |
|
|
|
|
|
command_pkginfo_local_short="${pkg_command[2]}" |
|
|
|
|
|
command_pkginfo_remote="${pkg_command[3]}" |
|
|
|
|
|
command_require_root="${pkg_command[4]}" |
|
|
|
|
|
|
|
|
|
|
|
if [[ "${command_require_root}" == "root" ]] |
|
|
|
|
|
then |
|
|
|
|
|
if [[ ! $(id -u) -eq 0 ]] |
|
|
|
|
|
then |
|
|
|
|
|
echo -e "\nThis command requires root privileges.\n" |
|
|
|
|
|
exit 0 |
|
|
|
|
|
fi |
|
|
|
|
|
fi |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
# Select package manager by priority. Highest is selected. |
|
|
|
|
|
i=0 |
|
|
|
|
|
while [[ ${i} -le $((${#managers_list[@]} - 1)) ]]; do |
|
|
|
|
|
if [[ ${managers_priority_list[i]} -gt ${priority_lowlimit} ]]; then |
|
|
|
|
|
priority_lowlimit=${managers_priority_list[i]} |
|
|
|
|
|
selected_manager=${managers_list[i]} |
|
|
|
|
|
fi |
|
|
|
|
|
let i++ |
|
|
|
|
|
|
|
|
# TODO: We can't really depend on parsing output strings since they vary between Arch package managers |
|
|
|
|
|
package_version_parsed() { |
|
|
|
|
|
echo "${1}" | awk -F ' ' '{print $2}' | sed -r 's/[a-z]+.*//; s/[:_+-]/\./g; s/[^0-9]$//;' |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
package_version_check() { |
|
|
|
|
|
|
|
|
|
|
|
local system_version |
|
|
|
|
|
local repo_version |
|
|
|
|
|
local version_array_1 |
|
|
|
|
|
local version_array_2 |
|
|
|
|
|
local first_version_numbers |
|
|
|
|
|
local last_version_numbers |
|
|
|
|
|
local comparables |
|
|
|
|
|
local version_status_msg |
|
|
|
|
|
local check1 |
|
|
|
|
|
local check2 |
|
|
|
|
|
local s |
|
|
|
|
|
|
|
|
|
|
|
# Expected output syntax: "^<string> <version number>$" |
|
|
|
|
|
# TODO: We can't really depend on parsing output strings since they vary between Arch package managers |
|
|
|
|
|
system_version=$(${SELECTED_MANAGER} ${command_pkginfo_local_short} "${1}") |
|
|
|
|
|
repo_version=$(${SELECTED_MANAGER} ${command_pkginfo_remote} $1 | grep -E "^Version\s*:" | sed -r 's/.*(:\s*.*$)/\1/') |
|
|
|
|
|
|
|
|
|
|
|
version_array_1=$(package_version_parsed "${system_version}") |
|
|
|
|
|
version_array_2=$(package_version_parsed "${repo_version}") |
|
|
|
|
|
|
|
|
|
|
|
#Count of version elements (0 18 2 1 contains 4 numbers, for example) |
|
|
|
|
|
first_version_numbers=$(echo "${version_array_1}" | awk -F '.' '{print split($0, a)}') |
|
|
|
|
|
last_version_numbers=$(echo "${version_array_2}" | awk -F '.' '{print split($0, a)}') |
|
|
|
|
|
|
|
|
|
|
|
# Count of comparable version elements (maximum) |
|
|
|
|
|
# We compare this much of elements, not more |
|
|
|
|
|
if [[ "${last_version_numbers}" -lt "${first_version_numbers}" ]] |
|
|
|
|
|
then |
|
|
|
|
|
comparables="${last_version_numbers}" |
|
|
|
|
|
else |
|
|
|
|
|
comparables="${first_version_numbers}" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# If all numbers are same, we don't analyze them more deeply. |
|
|
|
|
|
if [[ "${version_array_1}" == "${version_array_2}" ]] |
|
|
|
|
|
then |
|
|
|
|
|
version_status_msg="${green}Package is updated" |
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
|
|
|
s=1 |
|
|
|
|
|
while [ ${s} -le ${comparables} ] |
|
|
|
|
|
do |
|
|
|
|
|
check1=$(echo -e "${version_array_1}" | awk -v var=$s -F '.' '{print $var}') |
|
|
|
|
|
check2=$(echo -e "${version_array_2}" | awk -v var=$s -F '.' '{print $var}') |
|
|
|
|
|
|
|
|
|
|
|
if [[ ${check2} -gt ${check1} ]] |
|
|
|
|
|
then |
|
|
|
|
|
# Repo number is greater |
|
|
|
|
|
version_status_msg="${yellow}Update available" |
|
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
elif [[ ${check2} -lt ${check1} ]] |
|
|
|
|
|
then |
|
|
|
|
|
# System number is greater |
|
|
|
|
|
version_status_msg="${reset}Newer package installed" |
|
|
|
|
|
break |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
let s++ |
|
|
done |
|
|
done |
|
|
|
|
|
fi |
|
|
|
|
|
if [[ -z "${version_status_msg}" ]] |
|
|
|
|
|
then |
|
|
|
|
|
version_status_msg="${reset}Unknown" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
pkg_command=${ARCH_MANAGERS["$priority_lowlimit,$selected_manager"]} |
|
|
|
|
|
|
|
|
echo "${version_status_msg}" |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
command_refresh=$(echo $pkg_command | awk -F '|' '{print $1}') |
|
|
|
|
|
command_pkginfo_local=$(echo $pkg_command | awk -F '|' '{print $2}') |
|
|
|
|
|
command_pkginfo_local_short=$(echo $pkg_command | awk -F '|' '{print $3}') |
|
|
|
|
|
command_pkginfo_remote=$(echo $pkg_command | awk -F '|' '{print $4}') |
|
|
|
|
|
command_require_root=$(echo $pkg_command | awk -F '|' '{print $5}') |
|
|
|
|
|
|
|
|
exec_tool() { |
|
|
|
|
|
|
|
|
if [[ ${command_require_root} == "root" ]]; then |
|
|
|
|
|
if [[ ! $(id -u) -eq 0 ]]; then |
|
|
|
|
|
echo -e "\nThis command requires root privileges.\n" |
|
|
|
|
|
exit 0 |
|
|
|
|
|
fi |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
local i |
|
|
|
|
|
local package_count |
|
|
|
|
|
local risks_parsed_count |
|
|
|
|
|
local risks |
|
|
|
|
|
local description_column_max_chars |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
local r_package_name |
|
|
|
|
|
local r_package_security_issues_count |
|
|
|
|
|
local r_package_security_issues_level |
|
|
|
|
|
local r_package_security_issues_level |
|
|
|
|
|
local r_package_description |
|
|
|
|
|
|
|
|
|
|
|
local risk_entries |
|
|
|
|
|
|
|
|
|
|
|
local sort_params |
|
|
|
|
|
local sort_column |
|
|
|
|
|
|
|
|
|
|
|
local package_alert_importance_status |
|
|
|
|
|
local package_alert_msg_color |
|
|
|
|
|
local package_alert_importance_output_status |
|
|
|
|
|
|
|
|
|
|
|
local package_security_issues_count |
|
|
|
|
|
local security_issues_package_summary |
|
|
|
|
|
local security_issues_total_count |
|
|
|
|
|
|
|
|
|
|
|
local security_msg_color |
|
|
|
|
|
|
|
|
|
|
|
i=1 |
|
|
|
|
|
description_column_max_chars=35 |
|
|
|
|
|
sort_params=() |
|
|
|
|
|
|
|
|
function runTool() { |
|
|
|
|
|
echo "Security report date: $(date '+%d-%m-%Y, %X') (TZ: $(timedatectl status | grep "Time zone:" | awk '{print $3}'))" |
|
|
|
|
|
|
|
|
input_parser |
|
|
|
|
|
connection_test "${NETWORK_HOST_ENDPOINT}" |
|
|
|
|
|
find_my_package_manager |
|
|
|
|
|
|
|
|
echo -e "\nSynchronizing package databases with ${selected_manager}\n" |
|
|
|
|
|
${selected_manager} ${command_refresh} || exit |
|
|
|
|
|
|
|
|
echo "Security report date: $(date '+%d-%m-%Y, %X') (TZ: $(timedatectl status | grep "Time zone:" | awk '{print $3}'))" |
|
|
|
|
|
echo -e "\nSynchronizing package databases with ${SELECTED_MANAGER}\n" |
|
|
|
|
|
|
|
|
if [[ ! $(which arch-audit | wc -l) -eq 1 ]]; then |
|
|
|
|
|
echo -e "\nCouldn't find Arch Linux security utility (arch-audit) in \$PATH. Please make sure it's installed.\n" |
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
${SELECTED_MANAGER} ${command_refresh} || exit |
|
|
|
|
|
|
|
|
count=0 |
|
|
|
|
|
prs_count=0 |
|
|
|
|
|
IFS=$'\n' |
|
|
|
|
|
|
|
|
if [[ ! $(type -P arch-audit) ]] |
|
|
|
|
|
then |
|
|
|
|
|
echo -e "\nCouldn't find Arch Linux security utility (arch-audit) in \$PATH. Please make sure it's installed.\n" |
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
for i in $(arch-audit); do |
|
|
|
|
|
package_name=$(echo "$i" | awk -F ' ' '{print $1}') |
|
|
|
|
|
risk_level=$(echo "$i" | grep -oE "Low|Medium|High|Critical") |
|
|
|
|
|
risks_count=$(echo "$i" | grep -oP "(?<=by ).+(?=\. )" | sed 's/, /\n/g' | wc -l) |
|
|
|
|
|
#risks_count=$(echo "$i" | awk -F 'CVE' '{print NF-1}') |
|
|
|
|
|
|
|
|
packages= |
|
|
|
|
|
package_count=0 |
|
|
|
|
|
risks_parsed_count=0 |
|
|
|
|
|
IFS=$'\n' |
|
|
|
|
|
for au in $(arch-audit); do |
|
|
|
|
|
package_name=$(echo "${au}" | awk -F ' ' '{print $1}') |
|
|
|
|
|
risk_level=$(echo "${au}" | grep -oE "Low|Medium|High|Critical") |
|
|
|
|
|
risks_count=$(echo "${au}" | grep -oP "(?<=by ).+(?=\. )" | sed 's/, /\n/g' | wc -l) |
|
|
|
|
|
risks[$package_count]="$package_name;$risk_level;$risks_count" |
|
|
|
|
|
packages="${packages}, ${package_name}" |
|
|
|
|
|
let package_count++ |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
echo -e "Analyzing ${#risks[*]} vulnerable packages. This takes a while...\n" |
|
|
|
|
|
|
|
|
|
|
|
echo -e "Vulnerable packages are:\n\n$(echo ${packages} | sed 's/^, //')\n" |
|
|
|
|
|
|
|
|
risks[$count]="$package_name $risk_level $risks_count" |
|
|
|
|
|
|
|
|
for risk_parsed in ${risks[@]}; do |
|
|
|
|
|
|
|
|
let count++ |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
OLDIFS=${IFS} |
|
|
|
|
|
IFS=";" |
|
|
|
|
|
risk_parsed=(${risk_parsed}) |
|
|
|
|
|
IFS=${OLDIFS} |
|
|
|
|
|
|
|
|
echo -e "\nAnalyzing ${#risks[*]} vulnerable packages. This takes a while...\n" |
|
|
|
|
|
|
|
|
# Package name |
|
|
|
|
|
r_package_name="${risk_parsed[0]}" |
|
|
|
|
|
|
|
|
i=1 |
|
|
|
|
|
for risk_parsed in $(echo "${risks[*]}"); do |
|
|
|
|
|
|
|
|
echo -en "Analysing package ${i}/${#risks[*]} (${r_package_name})... \r" |
|
|
|
|
|
|
|
|
echo -en "Analysing package ${i}/${#risks[*]}... \r" |
|
|
|
|
|
|
|
|
# Package security issues detected |
|
|
|
|
|
r_package_security_issues_count="${risk_parsed[2]}" |
|
|
|
|
|
|
|
|
# Package in question |
|
|
|
|
|
col1=$(echo "$risk_parsed" | awk -F ' ' '{print $1}') |
|
|
|
|
|
|
|
|
|
|
|
# Security issues detected |
|
|
|
|
|
col2=$(echo "$risk_parsed" | awk -F ' ' '{print $3}') |
|
|
|
|
|
|
|
|
# Package security issues overall level: Critical, High, Medium or Low |
|
|
|
|
|
r_package_security_issues_level=$(echo "${risk_parsed[1]}" | sed 's/Critical/0/g; s/High/1/g; s/Medium/2/g; s/Low/3/g') |
|
|
|
|
|
|
|
|
#Critical, High, Medium or Low risk |
|
|
|
|
|
col3=$(echo "$risk_parsed" | awk -F ' ' '{print $2}' | sed 's/Critical/0/g; s/High/1/g; s/Medium/2/g; s/Low/3/g') |
|
|
|
|
|
|
|
|
|
|
|
col5=$(${selected_manager} ${command_pkginfo_local} $col1 | grep -i description | awk -F ": " '{print $2}') |
|
|
|
|
|
maxchars=35 |
|
|
|
|
|
|
|
|
|
|
|
if [[ $(echo $col5 | wc -m) -gt $maxchars ]]; then |
|
|
|
|
|
col5=$(echo "$(echo $col5 | cut -c 1-$maxchars)...") |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
versioncheck() { |
|
|
|
|
|
|
|
|
r_package_description=$(${SELECTED_MANAGER} "${command_pkginfo_local}" "${r_package_name}" | grep -i description | awk -F ": " '{print $2}') |
|
|
|
|
|
|
|
|
# TODO: We can't really depend on parsing output strings since they vary between Arch package managers |
|
|
|
|
|
parsedver() { |
|
|
|
|
|
echo $1 | awk -F ' ' '{print $2}' | sed -r 's/[a-z]+.*//; s/[:_+-]/\./g; s/[^0-9]$//;' |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# Expected output syntax: "^<string> <version number>$" |
|
|
|
|
|
# TODO: We can't really depend on parsing output strings since they vary between Arch package managers |
|
|
|
|
|
system_version=$(${selected_manager} ${command_pkginfo_local_short} $1) |
|
|
|
|
|
repo_version=$(${selected_manager} ${command_pkginfo_remote} $1 | grep -E "^Version\s*:" | sed -r 's/.*(:\s*.*$)/\1/') |
|
|
|
|
|
|
|
|
|
|
|
version_array_1=$(parsedver $system_version) |
|
|
|
|
|
version_array_2=$(parsedver $repo_version) |
|
|
|
|
|
|
|
|
|
|
|
#Count of version elements (0 18 2 1 contains 4 numbers, for example) |
|
|
|
|
|
firstvernums=$(echo $version_array_1 | awk -F '.' '{print split($0, a)}') |
|
|
|
|
|
lastvernums=$(echo $version_array_2 | awk -F '.' '{print split($0, a)}') |
|
|
|
|
|
|
|
|
|
|
|
# Count of comparable version elements (maximum) |
|
|
|
|
|
# We compare this much of elements, not more |
|
|
|
|
|
if [[ $lastvernums -lt $firstvernums ]]; then |
|
|
|
|
|
comparables=$lastvernums |
|
|
|
|
|
else |
|
|
|
|
|
comparables=$firstvernums |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# If all numbers are same, we don't analyze them more deeply. |
|
|
|
|
|
if [[ $version_array_1 == $version_array_2 ]]; then |
|
|
|
|
|
col4="${green}Package is updated" |
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
|
|
|
s=1 |
|
|
|
|
|
while [ $s -le $comparables ]; do |
|
|
|
|
|
|
|
|
if [[ $(echo "${r_package_description}" | wc -m) -gt ${description_column_max_chars} ]] |
|
|
|
|
|
then |
|
|
|
|
|
r_package_description=$(printf "%s..." $(echo "${r_package_description}" | cut -c 1-${description_column_max_chars})) |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
check1=$(echo -e $version_array_1 | awk -v var=$s -F '.' '{print $var}') |
|
|
|
|
|
check2=$(echo -e $version_array_2 | awk -v var=$s -F '.' '{print $var}') |
|
|
|
|
|
|
|
|
r_package_version_status=$(package_version_check "${r_package_name}") |
|
|
|
|
|
|
|
|
if [[ $check2 -gt $check1 ]]; then |
|
|
|
|
|
# Repo number is greater |
|
|
|
|
|
col4="${yellow}Update available" |
|
|
|
|
|
break |
|
|
|
|
|
|
|
|
risk_entries[$risks_parsed_count]=$(printf "%s|%s|%s|%s|%s\n" \ |
|
|
|
|
|
"${r_package_name}" \ |
|
|
|
|
|
"${r_package_security_issues_count}" \ |
|
|
|
|
|
"${r_package_security_issues_level}" \ |
|
|
|
|
|
"${r_package_version_status}" \ |
|
|
|
|
|
"${r_package_description}" \ |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
elif [[ $check2 -lt $check1 ]]; then |
|
|
|
|
|
# System number is greater |
|
|
|
|
|
col4="${reset}Newer package installed" |
|
|
|
|
|
break |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
let risks_parsed_count++ |
|
|
|
|
|
let i++ |
|
|
|
|
|
|
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
let s++ |
|
|
|
|
|
done |
|
|
|
|
|
fi |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
versioncheck $col1 |
|
|
|
|
|
|
|
|
|
|
|
risk_entries[$prs_count]=$(printf "%s|%s|%s|%s|%s\n" "$col1" "$col2" "$col3" "$col4" "$col5") |
|
|
|
|
|
|
|
|
|
|
|
let prs_count++ |
|
|
|
|
|
let i++ |
|
|
|
|
|
|
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
echo -e "\e[1m" |
|
|
|
|
|
printf "\n%-25s%-20s%-15s%-25s%s\n" "Package" "Security issues" "Risk level" "Version status" "Description" |
|
|
|
|
|
echo -e "\e[0m" |
|
|
|
|
|
|
|
|
|
|
|
sort_params=() |
|
|
|
|
|
case ${sort_order} in |
|
|
|
|
|
name) |
|
|
|
|
|
sort_column="-k1" |
|
|
|
|
|
;; |
|
|
|
|
|
issues) |
|
|
|
|
|
sort_column="-k2" |
|
|
|
|
|
sort_params+=("-n") |
|
|
|
|
|
;; |
|
|
|
|
|
level) |
|
|
|
|
|
sort_column="-k3" |
|
|
|
|
|
;; |
|
|
|
|
|
version) |
|
|
|
|
|
sort_column="-k4" |
|
|
|
|
|
;; |
|
|
|
|
|
desc) |
|
|
|
|
|
sort_column="-k5" |
|
|
|
|
|
;; |
|
|
|
|
|
#*) |
|
|
|
|
|
# echo "Unknown sorting order selected. Exiting." |
|
|
|
|
|
# exit 1 |
|
|
|
|
|
esac |
|
|
|
|
|
|
|
|
|
|
|
if [[ ${sort_reverse} == 1 ]]; then |
|
|
|
|
|
sort_params+=("-r") |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
i=0 |
|
|
|
|
|
for line in $(echo "${risk_entries[*]}" | sort ${sort_params[*]} -t'|' ${sort_column}); do |
|
|
|
|
|
|
|
|
|
|
|
if [[ $(echo "$line" | awk -F '|' '{print $3}') -eq 0 ]]; then |
|
|
|
|
|
alert_color="${red}" |
|
|
|
|
|
importance="Critical" |
|
|
|
|
|
|
|
|
|
|
|
elif [[ $(echo "$line" | awk -F '|' '{print $3}') -eq 1 ]]; then |
|
|
|
|
|
alert_color="${orange}" |
|
|
|
|
|
importance="High" |
|
|
|
|
|
|
|
|
|
|
|
elif [[ $(echo "$line" | awk -F '|' '{print $3}') -eq 2 ]]; then |
|
|
|
|
|
alert_color="${yellow}" |
|
|
|
|
|
importance="Medium" |
|
|
|
|
|
|
|
|
|
|
|
elif [[ $(echo "$line" | awk -F '|' '{print $3}') -eq 3 ]]; then |
|
|
|
|
|
alert_color="${green}" |
|
|
|
|
|
importance="Low" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
sec_count=$(echo "$line" | awk -F '|' '{print $2}') |
|
|
|
|
|
|
|
|
|
|
|
if [[ $sec_count -lt 5 ]]; then |
|
|
|
|
|
secclr="${green}" |
|
|
|
|
|
elif [[ $sec_count -ge 5 ]] && [[ $sec_count -lt 10 ]]; then |
|
|
|
|
|
secclr="${yellow}" |
|
|
|
|
|
elif [[ $sec_count -ge 10 ]] && [[ $sec_count -lt 20 ]]; then |
|
|
|
|
|
secclr="${orange}" |
|
|
|
|
|
elif [[ $sec_count -ge 20 ]]; then |
|
|
|
|
|
secclr="${red}" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
secsum[$i]=$sec_count |
|
|
|
|
|
|
|
|
|
|
|
echo "$line" | awk -F '|' -v clr1="${alert_color}" -v clr2="${secclr}" -v rs="${reset}" -v var="${importance}" '{printf "%-25s%s%-20s%s%-15s%-30s%s%s\n",$1,clr2,$2,clr1,var,$4,rs,$5}' |
|
|
|
|
|
|
|
|
|
|
|
let i++ |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
secsums_total=$(echo $(printf "%d+" ${secsum[@]})0 | bc) |
|
|
|
|
|
|
|
|
|
|
|
echo -e "\nTotal:" |
|
|
|
|
|
printf "%-25s%s\n\n" "$count" "$secsums_total" |
|
|
|
|
|
printf "Check %s for more information.\n\n" "${provider}" |
|
|
|
|
|
|
|
|
echo -e "\e[1m" |
|
|
|
|
|
printf "\n%-25s%-20s%-15s%-25s%s\n" "Package" "Security issues" "Risk level" "Version status" "Description" |
|
|
|
|
|
echo -e "\e[0m" |
|
|
|
|
|
|
|
|
|
|
|
case "${SORT_ORDER}" in |
|
|
|
|
|
name) |
|
|
|
|
|
sort_column="-k1" |
|
|
|
|
|
;; |
|
|
|
|
|
issues) |
|
|
|
|
|
sort_column="-k2" |
|
|
|
|
|
sort_params+=("-n") |
|
|
|
|
|
;; |
|
|
|
|
|
level) |
|
|
|
|
|
sort_column="-k3" |
|
|
|
|
|
;; |
|
|
|
|
|
version) |
|
|
|
|
|
sort_column="-k4" |
|
|
|
|
|
;; |
|
|
|
|
|
desc) |
|
|
|
|
|
sort_column="-k5" |
|
|
|
|
|
;; |
|
|
|
|
|
#*) |
|
|
|
|
|
# echo "Unknown sorting order selected. Exiting." |
|
|
|
|
|
# exit 1 |
|
|
|
|
|
esac |
|
|
|
|
|
|
|
|
|
|
|
if [[ "${SORT_REVERSE}" -eq 1 ]] |
|
|
|
|
|
then |
|
|
|
|
|
sort_params+=("-r") |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
i=0 |
|
|
|
|
|
IFS=$'\n' |
|
|
|
|
|
for line in $(echo "${risk_entries[*]}" | sort ${sort_params[*]} -t'|' ${sort_column}) |
|
|
|
|
|
do |
|
|
|
|
|
|
|
|
|
|
|
package_alert_importance_status=$(echo "${line}" | awk -F '|' '{print $3}') |
|
|
|
|
|
|
|
|
|
|
|
case "${package_alert_importance_status}" in |
|
|
|
|
|
0) |
|
|
|
|
|
package_alert_msg_color="${red}" |
|
|
|
|
|
package_alert_importance_output_status="Critical" |
|
|
|
|
|
;; |
|
|
|
|
|
1) |
|
|
|
|
|
package_alert_msg_color="${orange}" |
|
|
|
|
|
package_alert_importance_output_status="High" |
|
|
|
|
|
;; |
|
|
|
|
|
2) |
|
|
|
|
|
package_alert_msg_color="${yellow}" |
|
|
|
|
|
package_alert_importance_output_status="Medium" |
|
|
|
|
|
;; |
|
|
|
|
|
3) |
|
|
|
|
|
package_alert_msg_color="${green}" |
|
|
|
|
|
package_alert_importance_output_status="Low" |
|
|
|
|
|
;; |
|
|
|
|
|
esac |
|
|
|
|
|
|
|
|
|
|
|
package_security_issues_count=$(echo "${line}" | awk -F '|' '{print $2}') |
|
|
|
|
|
|
|
|
|
|
|
if [[ ${package_security_issues_count} -lt 5 ]] |
|
|
|
|
|
then |
|
|
|
|
|
security_msg_color="${green}" |
|
|
|
|
|
elif [[ ${package_security_issues_count} -ge 5 ]] && [[ ${package_security_issues_count} -lt 10 ]] |
|
|
|
|
|
then |
|
|
|
|
|
security_msg_color="${yellow}" |
|
|
|
|
|
elif [[ ${package_security_issues_count} -ge 10 ]] && [[ ${package_security_issues_count} -lt 20 ]] |
|
|
|
|
|
then |
|
|
|
|
|
security_msg_color="${orange}" |
|
|
|
|
|
else |
|
|
|
|
|
security_msg_color="${red}" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
security_issues_package_summary[$i]=${package_security_issues_count} |
|
|
|
|
|
|
|
|
|
|
|
echo "${line}" | awk -F '|' \ |
|
|
|
|
|
-v clr1="${package_alert_msg_color}" \ |
|
|
|
|
|
-v clr2="${security_msg_color}" \ |
|
|
|
|
|
-v rs="${reset}" \ |
|
|
|
|
|
-v var="${package_alert_importance_output_status}" \ |
|
|
|
|
|
'{printf "%-25s%s%-20s%s%-15s%-30s%s%s\n",$1,clr2,$2,clr1,var,$4,rs,$5}' |
|
|
|
|
|
|
|
|
|
|
|
let i++ |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
security_issues_total_count=$(echo $(printf "%d+" "${security_issues_package_summary[@]}")0 | bc) |
|
|
|
|
|
|
|
|
|
|
|
echo -e "\nTotal:" |
|
|
|
|
|
printf "%-25s%s\n\n" "${package_count}" "${security_issues_total_count}" |
|
|
|
|
|
printf "Check %s for more information.\n\n" "${NETWORK_HOST_ENDPOINT}" |
|
|
|
|
|
fi |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
inputParser |
|
|
|
|
|
findMyPackageManager |
|
|
|
|
|
internetTest |
|
|
|
|
|
runTool |
|
|
|
|
|
|
|
|
exec_tool |