Index: head/security/vuxml/Makefile =================================================================== --- head/security/vuxml/Makefile (revision 491044) +++ head/security/vuxml/Makefile (revision 491045) @@ -1,99 +1,99 @@ # Created by: nectar@FreeBSD.org # $FreeBSD$ PORTNAME= vuxml PORTVERSION= 1.1 -PORTREVISION= 3 +PORTREVISION= 4 CATEGORIES= security textproc MASTER_SITES= http://www.vuxml.org/dtd/vuxml-1/ DISTFILES= vuxml-10.dtd vuxml-model-10.mod \ vuxml-11.dtd vuxml-model-11.mod \ xml1.dcl catalog catalog.xml DIST_SUBDIR= vuxml MAINTAINER= ports-secteam@FreeBSD.org COMMENT= Vulnerability and eXposure Markup Language DTD LICENSE= BSD2CLAUSE RUN_DEPENDS= xmlcatmgr:textproc/xmlcatmgr \ xsltproc:textproc/libxslt \ ${LOCALBASE}/share/xml/dtd/xhtml-modularization/VERSION:textproc/xhtml-modularization \ ${LOCALBASE}/share/xml/dtd/xhtml-basic/xhtml-basic10.dtd:textproc/xhtml-basic USES= python:run NO_MTREE= yes NO_ARCH= yes NO_BUILD= yes WRKSRC= ${WRKDIR} dir_DTD= share/xml/dtd/vuxml VUXML_FILE?= ${PKGDIR}/vuln.xml do-extract: @${RM} -r ${WRKDIR} @${MKDIR} ${WRKDIR} .for f in ${DISTFILES} ${CP} ${_DISTDIR}/${f} ${WRKDIR}/${f} .endfor do-install: @${MKDIR} ${STAGEDIR}${PREFIX}/${dir_DTD} .for f in ${DISTFILES} ${INSTALL_DATA} ${WRKSRC}/${f} ${STAGEDIR}${PREFIX}/${dir_DTD}/${f} .endfor do-test: @${MKDIR} ${WRKDIR}/test @${CP} ${.CURDIR}/vuln.xml ${WRKDIR}/test @cd ${.CURDIR} && make validate PKGDIR=${WRKDIR}/test validate: tidy @${SH} ${FILESDIR}/validate.sh "${VUXML_FILE}" @${ECHO_MSG} Checking if tidy differs... @if ${DIFF} -u "${VUXML_FILE}" "${VUXML_FILE}.tidy"; \ then \ ${ECHO_MSG} ... seems okay; \ ${RM} "${VUXML_FILE}.tidy"; \ else \ return 1; \ fi @${ECHO_MSG} Checking for space/tab... @unexpand "${VUXML_FILE}" | ${SED} -E 's,[[:space:]]*$$,,g' > "${VUXML_FILE}.unexpanded" @if ${DIFF} -u "${VUXML_FILE}" "${VUXML_FILE}.unexpanded"; \ then \ ${ECHO_MSG} ... seems okay; \ ${RM} "${VUXML_FILE}.unexpanded"; \ else \ ${ECHO_MSG} ... see above; \ ${ECHO_CMD} Consider using ${VUXML_FILE}.unexpanded for final commit; \ return 1; \ fi ${PYTHON_CMD} ${FILESDIR}/extra-validation.py ${VUXML_FILE} tidy: vuln.xml @if [ ! -e ${LOCALBASE}/share/xml/dtd/vuxml/catalog.xml ]; \ then \ echo "Please install the VuXML port prior to running make validate/tidy."; \ exit 1; \ fi ${SH} ${FILESDIR}/tidy.sh "${FILESDIR}/tidy.xsl" "${VUXML_FILE}" > "${VUXML_FILE}.tidy" newentry: @${SH} ${FILESDIR}/newentry.sh "${VUXML_FILE}" .include .if defined(VID) && !empty(VID) html: work/${VID}.html work/${VID}.html: ${FILESDIR}/html.xsl ${FILESDIR}/common.css ${VUXML_FILE} ${MKDIR} work xsltproc --stringparam vid "${VID}" \ --output ${.TARGET} \ ${FILESDIR}/html.xsl ${VUXML_FILE} ${INSTALL_DATA} ${FILESDIR}/common.css work .endif .include Index: head/security/vuxml/files/extra-validation.py =================================================================== --- head/security/vuxml/files/extra-validation.py (revision 491044) +++ head/security/vuxml/files/extra-validation.py (revision 491045) @@ -1,92 +1,104 @@ #!/usr/bin/env python # $FreeBSD$ import datetime import xml.etree.ElementTree as ET import sys import re if len(sys.argv) != 2: print("Usage: %s vuln.xml" % (sys.argv[0])) sys.exit(1) re_date = re.compile(r'^(19|20)[0-9]{2}-[0-9]{2}-[0-9]{2}$') +re_invalid_package_name = re.compile('[@!#$%^&*()<>?/\|}{~:]') # warn if description has more than X characters DESCRIPTION_LENGTH = 5000 tree = ET.parse(sys.argv[1]) root = tree.getroot() namespace = "{http://www.vuxml.org/apps/vuxml-1}" ret = 0 def dateof(string): return datetime.datetime.strptime(string, "%Y-%m-%d") all_vids = set() for vuln in root: vid = vuln.get("vid") cancelled = False if vuln.find(namespace+"cancelled") is None else True if cancelled: continue # Validate Vids if vid in all_vids: print("Error: duplicate vid : {0}".format(vid)) all_vids.add(vid) # Validate References references = vuln.find(namespace+"references") if references is None: print("Error: references is None : {0}".format(vid)) ret = 1 else: prev = references[0] for reference in references: if reference.tag < prev.tag: #print("Warn: tags out of order ({1} and {2}): {0}".format(vid, prev.tag[len(namespace):], reference.tag[len(namespace):])) pass prev = reference # Validate Dates dates = vuln.find(namespace+"dates") if dates is None: print("Error: no date : {0}".format(vid)) ret = 1 else: discovery = dates.find(namespace+"discovery") entry = dates.find(namespace+"entry") modified = dates.find(namespace+"modified") if discovery is None: print("Error: discovery is None : {0}".format(vid)) ret = 1 elif entry is None: print("Error: entry is None : {0}".format(vid)) ret = 1 else: if modified is None: modified = entry if not (dateof(discovery.text) <= dateof(entry.text) <= dateof(modified.text)): print("Error: dates are insane : {0}".format(vid)) ret = 1 # Make sure the dates are in YYYY-MM-DD format datelist = [discovery.text, entry.text] + ([modified.text] if modified is not None else []) for d in datelist: if not re_date.match(d): print("Warning: dates must be in YYYY-MM-DD format: {0}".format(d)) # Check description lengths description = vuln.find(namespace + "description") description_len = len(ET.tostring(description)) if description_len > DESCRIPTION_LENGTH: print("Warning: description too long ({0} chars, {1} is warning threshold): {2})" \ .format(description_len, DESCRIPTION_LENGTH, vid)) + + # Walk and validate package names + affects = vuln.find(namespace + "affects") + packages = affects.findall(namespace + "package") + for package in packages: + names = package.findall(namespace + "name") + + for name in names: + if (re_invalid_package_name.search(name.text) is not None): + print("Error: invalid package name: " + name.text + " for VID " + format(vid)) + ret = 1 sys.exit(ret)