CPE (Common Platform Enumeration)


Common Platform Enumeration (CPE) is an open standard for naming hardware and software components. Originally developed by MITRE, it is now maintained by NIST under the aegis of the National Vulnerability Database.

A CPE URI uniquely identifies a device or program by its vendor, product name, version, revision, etc. NIST maintains the official CPE dictionary which lists CPE names for all software versions that have ever been mentioned in an advisory.

Why do we need it?

Once pkg audit and similar tools learn to use this information, they can do a much better job comparing CPE URIs to published vulnerability data, than they can with vuln.xml.

The CPE specification defines a collation algorithm which can be used to compare and sort CPE names, and to match CPE names against glob-like patterns. The NVD uses this to generate machine-readable lists of products affected by a particular vulnerability. Thus, CPE data can be used to audit a system for vulnerabilities by matching the CPE names of all installed software (when available) against a CVE database such as the NVD/CVE XML feed.

Admittedly, the quality of the data included in the NVD/CVE XML feed varies. Newer data is pretty reliable, older data not so much. However, the errors are mostly false positives where older versions are incorrectly identified as vulnerable to a bug that appeared in a newer version, whereas VuXML has too many false negative to count, due to incorrect or missing entries, renamed ports and a poor matching algorithm.

(This happens quite often with older FreeBSD releases, because NVD staff misinterpret “all supported FreeBSD versions” as “all FreeBSD versions”, so the NVD will happily tell you that FreeBSD 1.0 has security vulnerabilities in a 10 GbE driver and in the NAND geom. Compiling a list of false positives affecting FreeBSD and submitting it to the NVD is left as an exercise to the reader.)

The CPE information is included in the package manifest as an annotation which can easily be retrieved from the command line (or by a script) using pkg query, or from a C program using libpkg.

How do I check the CPE URI of a port or package?

In the ports tree:

% make -C /usr/ports/www/apache22 -V CPE_STR

In the package repository:

% pkg rquery '%Av' apache22 | grep '^cpe:'

For an installed package:

% pkg query '%Av' apache22 | grep '^cpe:'

For a package file:

% pkg query -F /usr/ports/packages/All/apache22-2.2.27_3.txz '%Av' | grep '^cpe:'

<insert C example here>

How do I add CPE information to a port?

First, add cpe to the USES list in the port's Makefile. If you're lucky, the defaults are correct and you're done. This is the case for Perl:

% make -C lang/perl5.16 -V CPE_STR

The third-to-last field, freebsd, simply means “this particular version was built to run on FreeBSD” and is automatically inserted by the CPE support code. At some future date, it should be changed to reflect the major release it was built for (freebsd9, freebsd10), and the second-to-last field should contain the target hardware (x86 for i386, x64 for amd64).

The CPE support code assumes that the product name is PORTNAME, the vendor name is identical to the product name and the version PORTVERSION. This is not always correct. For Apache, we need to set the vendor and product explicitly:

CPE_VENDOR=     apache
CPE_PRODUCT=    http_server
% make -C www/apache22 -V CPE_STR

If the vendor name is left blank, the CPE support code automatically sets it to the same string as the product name, under the assumption that most F/OSS projects have only one product and will identify themselves by the name of that product. One example of this is PHP:

% make -C lang/php55 -V CPE_STR

Ruby is more complicated still, because the vendor and product names are not identical, and the CPE name for Ruby keeps the version and patch level separate:

CPE_VENDOR=     ruby-lang
% make -C lang/ruby19 -V CPE_STR

To break it down into small, easy steps:

  1. Do not ever make up CPE data. Find the official CPE entry for the port either by using the NVD's CPE search engine or in the official CPE dictionary (warning, very large XML file).

  2. Add cpe to USES and compare the result of make -V CPE_STR to the CPE dictionary entry. Continue one step at a time until make -V CPE_STR is correct.

  3. If the product name (second field, defaults to PORTNAME) is incorrect, define CPE_PRODUCT to the correct value.

  4. If the vendor name (first field, defaults to CPE_PRODUCT) is incorrect, define CPE_VENDOR to the correct value.

  5. If the version field (third field, defaults to PORTVERSION) is incorrect, define CPE_VERSION to the correct value.

  6. If the update field (fourth field, defaults to empty) is incorrect, define CPE_UPDATE to the correct value.

  7. If it is still not correct, check Mk/Uses/cpe.mk for additional details, or contact the Ports Security Team

  8. To the extent possible, avoid hardcoding elements of the CPE name. If any of it can be derived from existing variables such as PORTNAME or PORTVERSION, try to use variable modifiers to extract the relevant bits.

  9. Always run make -V CPE_STR and check the output before committing anything that changes PORTNAME or PORTVERSION or any other variable which is used to derive CPE_STR.

The CPE string should start with cpe:2.3:a:. An older string found in the details of some CVEs may start with cpe:/a:. Do not trust it!

If an updated string cannot be located, consider contacting cpe_dictionary@nist.gov to request inclusion in the 2.3 dictionary format.

When should CPE information be added?

If you maintain a port for a piece of software that already has an entry in the CPE dictionary, you SHOULD add CPE information to it the next time you upgrade to a new version, and you MAY do it right away.

If you're updating a port because of an advisory that includes a CVE number, you MUST add CPE information to the port.

CategoryPorts CategorySecurity

Ports/CommonPlatformEnumeration (last edited 2022-10-29T23:47:48+0000 by KubilayKocak)