blob: 5a9d26184c588430707f4645b8105e15554fd2cb [file] [log] [blame]
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (c) 2024 - Greg Kroah-Hartman <gregkh@linuxfoundation.org>
#
# strak - "fixed/tight" in Dutch.
# Given a specific git id, list all of the CVE ids that are NOT fixed
# in that release.
#
# Usage:
# strak [options] GIT_SHA
# For full options, see the help text below.
#
# Requires:
# A kernel git tree with the SHA to be used in it
# set to 1 to get some debugging logging messages (or use -v/--verbose option)
DEBUG=0
# Initialize our color variables if we are a normal terminal
if [[ -t 1 ]]; then
txtred=$(tput setaf 1) # Red
txtgrn=$(tput setaf 2) # Green
txtblu=$(tput setaf 4) # Blue
txtcyn=$(tput setaf 6) # Cyan
txtrst=$(tput sgr0) # Text reset
else
txtred=""
txtgrn=""
txtblu=""
txtcyn=""
txtrst=""
fi
# set where the tool was run from,
# the name of our script,
# and the git version of it
DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
SCRIPT=${0##*/}
SCRIPT_VERSION=$(cd "${DIR}" && git ls-tree --abbrev=12 HEAD | grep "${SCRIPT}" | awk '{print $3}')
#############################
# help()
# Print out help options and exit
#############################
help()
{
echo "Usage: $0 [OPTIONS] GIT_SHA"
echo " List all CVE ids that are NOT fixed for this release."
echo " In other words, all of the public vulerabilities that this commit id has in it."
echo ""
echo "Arguments:"
echo " -h, --help This information"
echo " --fixed=FIXED_VERSION Kernel version to show what was fixed in it"
echo " -v, --verbose Show debugging information to stdout"
echo ""
exit 1
}
#############################
# dbg()
# if DEBUG is enabled, print out a message.
# Can also be set with -v or --verbose command line option
# Does so with an initial "#" so it can be easily filtered out
# arguments:
# "message to print"
# assumes:
# DEBUG is defined to something
#############################
dbg()
{
if [[ ${DEBUG} -ge 1 ]] ; then
echo "${txtcyn}# ${1}${txtrst}"
fi
}
#############################
# info()
# Print out a string as "information"
# Does so with an initial "#" so it can be easily filtered out
# arguments:
# "message to print"
#############################
info()
{
echo "${txtgrn}# ${1}${txtrst}"
}
check_id()
{
local git_id=$1
local cve_id=$2
local sha
local cve
local root
local vuln_file
local vulnerable_option
local vulnerable_sha
local dyad_out
local dyad_entries
local dyad_entry
local found
local must_look
local is_in_vuln
sha=$(cat "${cve_id}")
cve=$(echo "${cve_id}" | cut -f 1 -d '.' | cut -f 4 -d '/')
root=$(echo "${cve_id}" | cut -f 1 -d '.')
dbg "checking ${cve}:"
# Look to see if we have a "og_vuln" that is provided to us in a
# published CVE. This is used for when we can't determine it on our
# own, but we have manually looked it up later on and added it to a
# CVE-*.vulnerable file
vuln_file="${root}.vulnerable"
#echo "vuln_file=${vuln_file}"
vulnerable_option=""
vulnerable_sha=""
if [[ -f "${vuln_file}" ]]; then
vulnerable_sha=$(cat "${vuln_file}")
vulnerable_option="--vulnerable=${vulnerable_sha}"
fi
sha_id=$(cat ${cve_id})
# We want to call dyad without quotes for the arguments as we "know" these
# arguments are ok, we just set them above explicitly.
# shellcheck disable=SC2086
dbg "${root}.dyad"
dyad_out=$(cat "${root}.dyad" | grep -v "^#")
#dyad_out=$("${dyad}" ${sha_id} | grep -v "^#")
#dbg "dyad_out=${dyad_out}"
dyad_entries=()
for dyad_entry in ${dyad_out}; do
dyad_entries+=("${dyad_entry}")
done
dbg " dyad_entries: ${#dyad_entries[@]}"
found=0
must_look=0
for entry in "${dyad_entries[@]}"; do
x=(${entry//:/ })
vuln=${x[0]}
vuln_git=${x[1]}
fix=${x[2]}
fix_git=${x[3]}
dbg " dyad: git_id=${git_id} vuln_git=${vuln_git} fix_git=${fix_git}"
if [[ "${vuln_git}" == "0" ]]; then
vuln_git="1da177e4c3f41524e886b7f1b8a0c1fc7321cac2"
fi
is_in_vuln=$(cd "${KERNEL_TREE}" && git merge-base --is-ancestor ${vuln_git} ${git_id})
if [[ $? -eq 0 ]]; then
# This id is a root of our id, so let's dig further!
must_look=1
# first, if this is NOT fixed, then of course this is vulnerable
if [[ "${fix_git}" == "0" ]]; then
# not fixed, let's just move on
continue
fi
# the "vulnerable id is a root of our id, see if it's been fixed!"
is_in_vuln=$(cd "${KERNEL_TREE}" && git merge-base --is-ancestor ${fix_git} ${git_id})
if [[ $? -eq 0 ]]; then
# our id has this fixed!
found=1
fi
fi
done
dbg " must_look=${must_look} found=${found}"
if [[ "${must_look}" == "1" ]]; then
if [[ "${found}" == "0" ]]; then
echo "${txtgrn}${git_id}${txtrst} is vulnerable to ${txtred}${cve}${txtrst}"
fi
fi
}
search_year()
{
local git_id=$1
local year=$2
local threads=$(nproc)
# get a count of ids for this year
count=$(ls cve/published/${year}/*.sha1 | wc -l)
dbg "Searching ${txtcyn}${count}${txtrst} CVE ids for ${txtgrn}${year}${txtrst} with ${txtcyn}${threads}${txtrst} processes..."
for id in cve/published/${year}/*.sha1 ; do
while :
do
if [[ $(jobs -p | wc -l) -lt ${threads} ]]; then
dbg "git_id=${git_id} id=${id}"
check_id "${git_id}" "${id}" &
break
else
sleep 1
fi
done
done
wait
}
fixed_version()
{
local fixed_version=$1
local out
local id
local year
local commit
cd "cve/published/"
out=$(git grep -i -l "fixed in ${fixed_version}" | cut -f 2 -d '/' | cut -f 1 -d '.')
if [[ "${out}" != "" ]]; then
while IFS= read -r id; do
year=$(echo "${id}" | cut -f 2 -d '-')
commit=$(cat ${year}/${id}.sha1)
echo "${id} is fixed in ${1} with commit ${commit}"
done <<< "${out}"
else
echo "${1} does not have any CVE ids assigned yet."
fi
}
#############################
#############################
# "main" logic starts here
#############################
#############################
# Verify that some basic environment variables are set up
KERNEL_TREE=${CVEKERNELTREE}
if [[ ! -d "${KERNEL_TREE}" ]] ; then
echo "${txtred}ERROR:${txtrst}"
echo " ${txtblu}CVEKERNELTREE${txtrst} needs setting to the stable repo directory"
echo -e "\nEither manually export them or add them to your .bashrc/.zshrc et al."
echo -e "\nSee HOWTO in the root of this repo"
exit 1
fi
# Parse the command line
short_opts="hv"
long_opts="help,verbose,fixed:"
FIXED_VERSION=""
TMP=$(getopt -o "${short_opts}" --long "${long_opts}" --name="${SCRIPT}" -- "$@")
eval set -- "${TMP}"
while :; do
dbg "arg=${1}"
case "${1}" in
-h | --help ) help;;
--fixed ) FIXED_VERSION="${2}"; shift 2 ;;
-v | --verbose ) DEBUG=1; shift ;;
-- ) shift; break ;;
esac
done
# Rest of the command line argument is the git sha we are looking at
GIT_SHA=$1
if [[ "${GIT_SHA}" == "" && "${FIXED_VERSION}" == "" ]]; then
help
exit 1
fi
# Now we can test for unset variables, before we would have failed if someone
# forgot the git sha on the command line.
set -o nounset
cd "${DIR}"/../ || exit 1
if [[ "${FIXED_VERSION}" != "" ]]; then
fixed_version "${FIXED_VERSION}"
exit 0
fi
dbg "git id to look up = ${GIT_SHA}"
for y in cve/published/* ; do
year=$(echo "${y}" | cut -f 3 -d '/')
search_year ${GIT_SHA} ${year}
done
#search_year "${GIT_SHA}" 2020
exit 0