Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 24 Oct 2025 15:55:18 GMT
From:      Fernando =?utf-8?Q?Apestegu=C3=ADa?= <fernape@FreeBSD.org>
To:        ports-committers@FreeBSD.org, dev-commits-ports-all@FreeBSD.org, dev-commits-ports-main@FreeBSD.org
Subject:   git: da37086a88a5 - main - security/vuxml: Improve newentry
Message-ID:  <202510241555.59OFtI7V073334@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by fernape:

URL: https://cgit.FreeBSD.org/ports/commit/?id=da37086a88a5e544ab63b1825236823277f1feae

commit da37086a88a5e544ab63b1825236823277f1feae
Author:     Fernando ApesteguĂ­a <fernape@FreeBSD.org>
AuthorDate: 2024-09-08 17:06:16 +0000
Commit:     Fernando ApesteguĂ­a <fernape@FreeBSD.org>
CommitDate: 2025-10-24 15:47:49 +0000

    security/vuxml: Improve newentry
    
    Rearrange the code a bit by introducing providers.
    Fields are retrieved from providers in an orderly fashion.
    Should a provider fail to return a value, the next in the list is queried.
    
    This should improve our chances of getting proper reports from different
    providers.
    
    Differential Revision:  https://reviews.freebsd.org/D52903
---
 security/vuxml/Makefile                |   4 -
 security/vuxml/files/euvd_provider.sh  |  69 +++++++++++++
 security/vuxml/files/mitre_provider.sh |  61 ++++++++++++
 security/vuxml/files/newentry.sh       | 174 ++++++++++++++++++++++++---------
 security/vuxml/files/nvd_provider.sh   |  72 ++++++++++++++
 5 files changed, 330 insertions(+), 50 deletions(-)

diff --git a/security/vuxml/Makefile b/security/vuxml/Makefile
index 9a3ef8b7a291..243b5cd5723e 100644
--- a/security/vuxml/Makefile
+++ b/security/vuxml/Makefile
@@ -102,10 +102,6 @@ newentry:
 	@${ECHO_CMD} 'Also, <gt> tags are usually wrong in ranges. Use <ge> where adequate.'
 	@${ECHO_CMD}
 	@${SH} ${FILESDIR}/newentry.sh "${VUXML_CURRENT_FILE}" "CVE_ID=${CVE_ID}" "SA_ID=${SA_ID}"
-	@${ECHO_CMD}
-	@${ECHO_CMD} 'Be sure to get versioning right for PORTEPOCH and remember possible linux-* ports!'
-	@${ECHO_CMD} 'Also, <gt> tags are usually wrong in ranges. Use <ge> where adequate.'
-	@${ECHO_CMD}
 
 .if defined(VID) && !empty(VID)
 html: work/${VID}.html
diff --git a/security/vuxml/files/euvd_provider.sh b/security/vuxml/files/euvd_provider.sh
new file mode 100644
index 000000000000..821d2fcc06a2
--- /dev/null
+++ b/security/vuxml/files/euvd_provider.sh
@@ -0,0 +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
+}
+
+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("<";"&lt;") | gsub(">";"&gt;") | gsub("&";"&amp;"))"' "${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/mitre_provider.sh b/security/vuxml/files/mitre_provider.sh
new file mode 100644
index 000000000000..c2b1f8ffa73e
--- /dev/null
+++ b/security/vuxml/files/mitre_provider.sh
@@ -0,0 +1,61 @@
+# Provider for MITRE
+# https://www.mitre.org/
+
+tmp_mitre=""
+
+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="<package>
+<name></name>
+<range><lt></lt></range>"
 references="INSERT URL HERE"
 topic=""
 source="SO-AND-SO"
@@ -67,38 +88,65 @@ DESC_BODY="<body xmlns=\"http://www.w3.org/1999/xhtml\">;
 	</blockquote>
 	</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="<body xmlns=\"http://www.w3.org/1999/xhtml\">;
 	<p>${source} reports:</p>
@@ -106,14 +154,17 @@ DESC_BODY="<body xmlns=\"http://www.w3.org/1999/xhtml\">;
 	  <p>${details}</p>
 	</blockquote>
 	</body>"
-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="<body xmlns=\"http://www.w3.org/1999/xhtml\">;
 	<h1>Impact:</h1>
 	  ${impact}
       </body>"
+}
+
+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|</name>\n\t<range><lt>|g'  \
+	-e 's|^|    <package>\n\t<name>|g' \
+	-e 's|$|</lt></range>\n    </package>|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 '/^<\?/,/^<vuxml/ { print }' "${vuxml_file}" >> "${tmp}" || exit 1
@@ -155,21 +240,18 @@ cat << EOF >> "${tmp}" || exit 1
   <vuln vid="${vid}">
     <topic>${package_name} -- ${topic}</topic>
     <affects>
-      <package>
-	<name>${package_name}</name>
-	<range><lt>${upstream_fix}</lt></range>
-      </package>
+${package_list}
     </affects>
     <description>
-	${DESC_BODY}
+        ${DESC_BODY}
     </description>
     <references>
       <cvename>${cvename}</cvename>
       <url>${cveurl}</url>
     </references>
     <dates>
-      <discovery>${discovery}</discovery>
-      <entry>${entry}</entry>
+      <discovery>${discovery_date}</discovery>
+      <entry>${entry_date}</entry>
     </dates>
   </vuln>
 
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.
+}
+


help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202510241555.59OFtI7V073334>