+init_mitre()
+{
+ tmp_mitre=$(mktemp "${TMPDIR:-/tmp}"/mitre.XXXXXXXXXX) || exit 1
+ fetch -q -o "${tmp_mitre}" https://cveawg.mitre.org/api/cve/"${CVE_ID}"
+}
+
+cleanup_mitre()
+{
+ rm "${tmp_mitre}" 2>/dev/null
+}
+
+get_cvename_from_mitre()
+{
+ cvename="${CVE_ID}"
+ echo "${cvename}"
+}
+
+get_cveurl_from_mitre() {
+ echo https://cveawg.mitre.org/api/cve/"${CVE_ID}"
+}
+
+get_details_from_mitre() {
+ jq -r '.containers?.cna?.descriptions[0]?.value' "${tmp_mitre}" | fmt -p -s
+}
+
+get_discovery_date_from_mitre() {
+ jq -r '.cveMetadata?.datePublished?' "${tmp_mitre}" | cut -f1 -dT
+}
+
+get_entry_date_from_mitre() {
+ echo "${entry_date}"
+}
+
+get_product_name_from_mitre() {
+ jq -r '.containers?.cna?.affected[]?.product' "${tmp_mitre}"
+}
+
+get_product_range_from_mitre() {
+ jq -r '.containers?.cna?.affected[]??.versions[0]?.lessThan' "${tmp_mitre}"
+}
+
+get_package_name_from_mitre() {
+ jq -r '.containers?.cna?.affected[0]?.product' "${tmp_mitre}"
+}
+
+get_references_from_mitre() {
+ jq -r '.containers?.cna?.references[0]?.url' "${tmp_mitre}" | fmt -p -s
+}
+
+get_source_from_mitre() {
+ jq -r '.containers?.cna?.references[0]?.url' "${tmp_mitre}"
+}
+
+get_topic_from_mitre() {
+ jq -r ".containers?.cna?.problemTypes[0]?.descriptions[0]?.description" "${tmp_mitre}"
+}
diff --git a/security/vuxml/files/newentry.sh b/security/vuxml/files/newentry.sh
index 0298a5376a9e..4c8b09636112 100644
--- a/security/vuxml/files/newentry.sh
+++ b/security/vuxml/files/newentry.sh
@@ -15,6 +15,9 @@ if [ -z "${vuxml_file}" ]; then
show_usage
fi
+# -----------------
+# Process arguments
+# -----------------
shift
while [ $# -gt 0 ]; do
case "$1" in
@@ -34,27 +37,45 @@ case "$1" in
esac
done
-tmp="`mktemp ${TMPDIR:-/tmp}/vuxml.XXXXXXXXXX`" || exit 1
+tmp=$(mktemp "${TMPDIR:-/tmp}"/vuxml.XXXXXXXXXX) || exit 1
tmp_fbsd_sa=""
-tmp_mitre=""
-tmp_nvd=""
+# -------------------------------------
+# Define how to clean up temporal files
+# -------------------------------------
+#
doclean="yes"
cleanup() {
if [ "${doclean}" = "yes" ]; then
- rm -f "${tmp}" "${tmp_fbsd_sa}" "${tmp_mitre}" "${tmp_nvd}" > /dev/null
+ rm -f "${tmp}" "${tmp_fbsd_sa}" > /dev/null
fi
+
+ # Call cleaners for providers
+ for provider in ${providers}; do
+ cleanup_"${provider}"
+ cleanup_"${provider}"
+ done
}
-trap cleanup EXIT 1 2 13 15
+trap cleanup EXIT HUP INT PIPE TERM
-vid="`uuidgen | tr '[:upper:]' '[:lower:]'`"
+# -----------------------------
+# 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 -u '+%Y-%m'`-FIXME" || exit 1
-entry="`date -u '+%Y-%m-%d'`" || exit 1
+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"
@@ -67,38 +88,65 @@ DESC_BODY="
"
-
-# Try to retrieve information if a CVE identifier was provided
-if [ -n "${CVE_ID}" ]; then
+# --------------------------------
+# 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"
+
+# ------------------------------------------
+# 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"
- # NVD database only accepts uppercase CVE ids, like CVE-2022-39282, NOT
- # cve-2022-39282.
- CVE_ID=$(echo "${CVE_ID}" | tr '[:lower:]' '[:upper:]') || exit 1
-
- # Get information from the NVD database JSON format
- 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
- # Get information from MITRE database (they provide a nice "topic")
- tmp_mitre="`mktemp ${TMPDIR:-/tmp}/mitre.XXXXXXXXXX`" || exit 1
- fetch -q -o "${tmp_mitre}" https://cveawg.mitre.org/api/cve/"${CVE_ID}"
-
- # Create variables from input and online sources
- cvename="${CVE_ID}"
- cveurl=https://nvd.nist.gov/vuln/detail/${CVE_ID}
- pref=.vulnerabilities[0].cve
- details=$(jq -r "${pref}.descriptions[0].value|@html" "${tmp_nvd}" | fmt -p -s | sed '1!s/^/\t/') || exit 1
- discovery=$(jq -r "${pref}.published|@html" "${tmp_nvd}" | cut -f1 -dT) || exit 1
- pref=.vulnerabilities[0].cve.configurations[0].nodes[0].cpeMatch[0]
- package_name=$(jq -r "${pref}.criteria|@html" "${tmp_nvd}" | cut -f4 -d:) || exit 1
- upstream_fix=$(jq -r "${pref}.versionEndExcluding|@html" "${tmp_nvd}") || exit 1
- pref=.vulnerabilities[0].cve.references[0]
- references=$(jq -r "${pref}.url|@html" "${tmp_nvd}" | tr " " "\n") || exit 1
- source=$(jq -r "${pref}.source|@html" "${tmp_nvd}" | tr " " "\n") || exit 1
- topic=$(jq -r ".containers.cna.title|@html" "${tmp_mitre}" ) || exit 1
+# 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
+ 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})
+ eval "${field}=\$value"
+ done
DESC_BODY="
${source} reports:
@@ -106,14 +154,17 @@ DESC_BODY="
${details}
"
-fi
+}
-if [ -n "${SA_ID}" ]; then
+# ----------------------------------------------------------------
+# 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
+ 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
@@ -148,6 +199,40 @@ DESC_BODY="
Impact:
${impact}
"
+}
+
+init_providers() {
+ for provider in files/*_provider.sh; do
+ provider_name=$(basename "${provider}" | cut -f1 -d_)
+ . "files/${provider_name}_provider.sh"
+ init_"${provider_name}"
+ 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
@@ -155,21 +240,18 @@ cat << EOF >> "${tmp}" || exit 1
${package_name} -- ${topic}
-
- ${package_name}
- ${upstream_fix}
-
+${package_list}
- ${DESC_BODY}
+ ${DESC_BODY}
${cvename}
${cveurl}
- ${discovery}
- ${entry}
+ ${discovery_date}
+ ${entry_date}
diff --git a/security/vuxml/files/nvd_provider.sh b/security/vuxml/files/nvd_provider.sh
new file mode 100644
index 000000000000..8a383a7d6752
--- /dev/null
+++ b/security/vuxml/files/nvd_provider.sh
@@ -0,0 +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
+}
+
+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.
+}
+