diff --git a/security/vuxml/files/euvd_provider.sh b/security/vuxml/files/euvd_provider.sh index 821d2fcc06a2..d47f510a4e25 100644 --- a/security/vuxml/files/euvd_provider.sh +++ b/security/vuxml/files/euvd_provider.sh @@ -1,69 +1,69 @@ # Provider for the European Union Vulnerability Database # https://euvd.enisa.europa.eu/ tmp_euvd="" init_euvd() { tmp_euvd=$(mktemp "${TMPDIR:-/tmp}"/euvd_json_data.XXXXXXXXXX) || exit 1 - fetch -q -o "${tmp_euvd}" "https://euvdservices.enisa.europa.eu/api/enisaid?id=${CVE_ID}" || exit 1 + fetch -q -o "${tmp_euvd}" "https://euvdservices.enisa.europa.eu/api/enisaid?id=${CVE_ID}" } cleanup_euvd() { rm -f "${tmp_euvd}" 2>/dev/null } get_cvename_from_euvd() { # EUVD response includes "aliases" (CVE ID if available) jq -r '.aliases // .id' "${tmp_euvd}" } get_cveurl_from_euvd() { echo "https://euvd.enisa.europa.eu/ui/vuln/${CVE_ID}" } get_details_from_euvd() { jq -r '.description // empty | @html' "${tmp_euvd}" | fmt -p -s | sed '1!s/^/\t/' } get_discovery_date_from_euvd() { raw=$(jq -r '.datePublished // empty' "${tmp_euvd}") if [ -n "$raw" ]; then trimmed=$(echo "$raw" | cut -d, -f1-2) if date -d "$trimmed" "+%Y-%m-%d" >/dev/null 2>&1; then date -d "$trimmed" "+%Y-%m-%d" else date -j -f "%b %d, %Y" "$trimmed" "+%Y-%m-%d" fi fi } get_entry_date_from_euvd() { echo "${entry_date}" } get_product_name_from_euvd() { jq -r ' .enisaIdProduct[]?.product?.name ' "${tmp_euvd}" } get_product_range_from_euvd() { jq -r '.enisaIdProduct[]? | "\(.product_version? | gsub("<";"<") | gsub(">";">") | gsub("&";"&"))"' "${tmp_euvd}" } get_package_name_from_euvd() { jq -r '.enisaIdProduct[0]?.product?.name // empty' "${tmp_euvd}" } get_references_from_euvd() { jq -r '.references // empty | @html' "${tmp_euvd}" | tr " " "\n" } get_source_from_euvd() { jq -r '.assigner // empty | @html' "${tmp_euvd}" } get_topic_from_euvd() { # Use first sentence of description jq -r '.description // empty' "${tmp_euvd}" | cut -f1 -d. } diff --git a/security/vuxml/files/newentry.sh b/security/vuxml/files/newentry.sh index 0fcffae816ad..a4c4befb76f4 100644 --- a/security/vuxml/files/newentry.sh +++ b/security/vuxml/files/newentry.sh @@ -1,271 +1,274 @@ #! /bin/sh set -eu vuxml_file="$1" CVE_ID="" SA_ID="" show_usage() { exec >&2 echo "Usage: newentry.sh /path/to/vuxml/document [CVE_ID|SA_ID]" exit 1 } if [ -z "${vuxml_file}" ]; then show_usage fi # ----------------- # Process arguments # ----------------- shift while [ $# -gt 0 ]; do case "$1" in CVE_ID=*) CVE_ID="${1#CVE_ID=}" shift ;; SA_ID=*) SA_ID="${1#SA_ID=}" shift ;; *) echo "Invalid argument: $1" show_usage exit 1 ;; esac done tmp=$(mktemp "${TMPDIR:-/tmp}"/vuxml.XXXXXXXXXX) || exit 1 tmp_fbsd_sa="" # ------------------------------------- # Define how to clean up temporal files # ------------------------------------- # doclean="yes" cleanup() { if [ "${doclean}" = "yes" ]; then rm -f "${tmp}" "${tmp_fbsd_sa}" > /dev/null fi # Call cleaners for providers - for provider in ${providers}; do + for provider in ${valid_providers}; do cleanup_"${provider}" cleanup_"${provider}" done } trap cleanup EXIT HUP INT PIPE TERM # ----------------------------- # Variables with default values # ----------------------------- vid="$(uuidgen | tr '[:upper:]' '[:lower:]')" [ -z "$vid" ] && exit 1 discovery_date="" cvename="INSERT CVE RECORD IF AVAILABLE" cveurl="INSERT BLOCKQUOTE URL HERE" details="." discovery_date="$(date -u '+%Y-%m')-FIXME" || exit 1 entry_date="$(date -u '+%Y-%m-%d')" || exit 1 package_name="" product_name="" product_range="" package_list=" " references="INSERT URL HERE" topic="" source="SO-AND-SO" upstream_fix="" impact="" DESC_BODY="

${source} reports:

${details}

" # -------------------------------- # Check we have everything we need # -------------------------------- check_dependencies() { if ! command -v jq > /dev/null; then echo textproc/jq is needed for CVE automatic entry fill exit 1 fi } # ------------------------------------------ # List of CVE providers sorted by preference # ------------------------------------------ providers="mitre nvd euvd" +valid_providers="" # ------------------------------------------ # List of fields to query for every provider # ------------------------------------------ fields="cvename cveurl details discovery_date entry_date product_name product_range package_name references source topic" # Some providers only allow for upper case identifiers CVE_ID=$(echo "${CVE_ID}" | tr '[:lower:]' '[:upper:]') || exit 1 # ----------------------------------------------------------------------------- # Generic resolver # # Gets a variable name and the list of providers and returns the value of the # variable. If the first defined provider returns empty or nullm, it tries with # the next one until one provider returns a value or we run out of providers # ----------------------------------------------------------------------------- resolve_field() { field="${1}" shift providers="$@" - for provider in $providers; do + for provider in ${valid_providers}; do func="get_${field}_from_${provider}" if command -v "${func}" >/dev/null 2>&1; then value="$($func)" if [ -n "${value}" ] && [ "${value}" != "null" ] && [ "${value}" != "n/a" ]; then echo "${value}" return 0 fi else echo "Warning: function ${func} not implemented in provider ${provider}" fi done echo "null" } # -------------------------------------------------- # Fill global variables with data from CVE databases # -------------------------------------------------- get_cve_info() { for field in ${fields}; do - value=$(resolve_field "${field}" ${providers}) + value=$(resolve_field "${field}" ${valid_providers}) eval "${field}=\$value" done DESC_BODY="

${source} reports:

${details}

" } # ---------------------------------------------------------------- # Fill global variables with data from FreeBSD Security Advisories # ---------------------------------------------------------------- get_sa_info() { SA_URL_BASE=https://www.freebsd.org/security/advisories/ # Get information from the Project's SA site tmp_fbsd_sa=$(mktemp "${TMPDIR:-/tmp}/fbsd_sa_data.XXXXXXXXXX") || exit 1 fetch -q -o "${tmp_fbsd_sa}" "${SA_URL_BASE}${SA_ID}" || exit 1 # Create variables from SA note if grep -q 'CVE Name' "${tmp_fbsd_sa}"; then cve_tmp=$(grep 'CVE Name' "${tmp_fbsd_sa}" | cut -f2 -d:) || exit 1 cvename="${cve_tmp#"${cve_tmp%%[![:space:]]*}"}" # NVD database only accepts uppercase CVE ids, like CVE-2022-39282, NOT # cve-2022-39282. cvename=$(echo "${cvename}" | tr '[:lower:]' '[:upper:]') || exit 1 cveurl="https://nvd.nist.gov/vuln/detail/${cvename}" fi details=$(awk '/II. Problem Description/ {f=1;next;next} /III. Impact/ {f=0} (f==1) {print}' "${tmp_fbsd_sa}" ) || exit 1 details=$(echo "

${details}

" | fmt -p -s | sed -e 's/

/

/' | sed '1!s/^/\t/') impact=$(awk '/III. Impact/ {f=1;next;next} /IV. Workaround/ {f=0} (f==1) {print}' "${tmp_fbsd_sa}") || exit 1 impact=$(echo "

${impact}

" | fmt -p -s | sed -e 's/

/

/' | sed '1!s/^/\t/') package_name="FreeBSD" if grep -Eq 'Module:.*kernel' "${tmp_fbsd_sa}"; then package_name="${package_name}-kernel" fi upstream_fix="FIXME" references="${SA_URL_BASE}${SA_ID}" source="The FreeBSD Project" topic_tmp=$(grep 'Topic:' "${tmp_fbsd_sa}" | cut -f2 -d:) || exit 1 topic="${topic_tmp#"${topic_tmp%%[![:space:]]*}"}" DESC_BODY="

Problem Description:

${details}

Impact:

${impact} " } init_providers() { - for provider in files/*_provider.sh; do - provider_name=$(basename "${provider}" | cut -f1 -d_) + for provider_name in ${providers}; do . "files/${provider_name}_provider.sh" - init_"${provider_name}" + if init_"${provider_name}"; then + # The provider failed, remove it. + valid_providers="${valid_providers} ${provider_name}" + fi done } create_packages_list() { tmp_prod=$(mktemp "${TMPDIR:-/tmp}"/vuxml.prod.XXXXXXXXXX) || exit 1 tmp_ver=$(mktemp "${TMPDIR:-/tmp}"/vuxml.ver.XXXXXXXXXX) || exit 1 printf "%s" "${product_name}" > "${tmp_prod}" printf "%s" "${product_range}" > "${tmp_ver}" package_list=$(paste "${tmp_prod}" "${tmp_ver}" | sed \ -e 's|\t|\n\t|g' \ -e 's|^| \n\t|g' \ -e 's|$|\n |g') rm "${tmp_prod}" "${tmp_ver}" 2>/dev/null } # Try to retrieve information if a CVE identifier was provided if [ -n "${CVE_ID}" ]; then check_dependencies init_providers get_cve_info "${CVE_ID}" create_packages_list fi if [ -n "${SA_ID}" ]; then get_sa_info fi awk '/^<\?/,/^> "${tmp}" || exit 1 cat << EOF >> "${tmp}" || exit 1 ${package_name} -- ${topic} ${package_list} ${DESC_BODY} ${cvename} ${cveurl} ${discovery_date} ${entry_date} EOF awk '/^[[:space:]]+> "${tmp}" || exit 1 if cp "${tmp}" "${vuxml_file}"; then exec ${EDITOR:-vi} "${vuxml_file}" else doclean="no" exec >&2 echo "Could not overwrite \`${vuxml_file}'." echo "Results are left in \`${tmp}'." exit 1 fi diff --git a/security/vuxml/files/nvd_provider.sh b/security/vuxml/files/nvd_provider.sh index 8a383a7d6752..7bd608562388 100644 --- a/security/vuxml/files/nvd_provider.sh +++ b/security/vuxml/files/nvd_provider.sh @@ -1,72 +1,72 @@ # Provider for the National Vulnerability Database # https://nvd.nist.gov/ tmp_nvd="" init_nvd() { tmp_nvd=$(mktemp "${TMPDIR:-/tmp}"/nvd_json_data.XXXXXXXXXX) || exit 1 - fetch -q -o "${tmp_nvd}" https://services.nvd.nist.gov/rest/json/cves/2.0?cveId="${CVE_ID}" || exit 1 + fetch -q -o "${tmp_nvd}" https://services.nvd.nist.gov/rest/json/cves/2.0?cveId="${CVE_ID}" } cleanup_nvd() { rm "${tmp_nvd}" 2>/dev/null } get_cvename_from_nvd() { cvename="${CVE_ID}" echo "${cvename}" } get_cveurl_from_nvd() { cveurl=https://nvd.nist.gov/vuln/detail/${CVE_ID} echo "${cveurl}" } get_details_from_nvd() { pref=".vulnerabilities[0]?.cve?" jq -r "${pref}.descriptions[0]?.value|@html" "${tmp_nvd}" | fmt -p -s | sed '1!s/^/\t/' } get_discovery_date_from_nvd() { pref=".vulnerabilities[0]?.cve?" jq -r "${pref}.published|@html" "${tmp_nvd}" | cut -f1 -dT } get_entry_date_from_nvd() { echo "${entry_date}" } get_product_name_from_nvd() { jq -r '.vulnerabilities[]?.cve?.configurations[]?.nodes[]?.cpeMatch[]? | (.criteria | split(":")[4])' "${tmp_nvd}" } get_product_range_from_nvd() { jq -r '.vulnerabilities[]?.cve.configurations[]?.nodes[]?.cpeMatch[]?.versionEndExcluding ' "${tmp_nvd}" } get_package_name_from_nvd() { jq -r '.vulnerabilities[]?.cve?.configurations[]?.nodes[]?.cpeMatch[0]?.criteria' "${tmp_nvd}" | cut -f5 -d: } get_references_from_nvd() { pref=".vulnerabilities[0]?.cve?.references[0]?" jq -r "${pref}.url|@html" "${tmp_nvd}" | tr " " "\n" } get_source_from_nvd() { pref=".vulnerabilities[0]?.cve?.references[0]?" jq -r "${pref}.source|@html" "${tmp_nvd}" | tr " " "\n" } get_topic_from_nvd() { # NVD does not provide a nice summary. Let's use the first sentence from # the details instead pref=".vulnerabilities[0]?.cve?" jq -r "${pref}.descriptions[0]?.value|@html" "${tmp_nvd}" | cut -f1 -d. }