As a member of the ports-secteam, it is my job, and that of the others on the team, to respond quickly to CVEs. Until now, unfortunately, I did not know of any site that showed me which CVEs are relevant to the FreeBSD ports and which ports have vulnerabilities at all. It was also hard to connect CVEs, VuXML and the ports. So I decided to build my own site that lets me respond quickly and effectively to new vulnerabilities in the ports. The result is portstatus.bsdproject.de.
One example of why the site is needed: print/py-pypdf is at version 6.9.2 in both latest and quarterly. I ask pkg audit whether this package is something to worry about:
# pkg audit -F py312-pypdf-6.9.2
vulnxml file up-to-date
0 problem(s) in 0 package(s) found.
Zero problems. The CVE records disagree: 43 vulnerabilities affect exactly this version, 13 of them with a CVSS score of 7 or higher, and the oldest have been known since April. Upstream fixed all 43 by version 6.21.2. Almost all of them come down to excessive memory or CPU use caused by crafted PDFs. That becomes critical when your service accepts PDFs from outside, such as Paperless, a web shop or a mail filter. pkg audit is not lying here. It only knows what someone has entered in VuXML, and for pypdf nobody has done that yet.
This is exactly the gap portstatus makes visible. The site matches every port it can map to a product against the CVE databases and shows whether there is a VuXML entry and whether upstream has a newer version. Across the whole tree, that currently adds up to 3,295 CVEs in 670 ports that pkg audit does not report. The figure covers all of the roughly 34,000 ports and the packages in latest. On your server it is a fraction of that, but not zero.
Important: portstatus is still my private project and not an official service of the FreeBSD Project or the ports-secteam.

Why VuXML alone is not enough
A VuXML entry says: package X up to version Y is affected, here are the CVEs, here is the source. pkg audit checks that against your installed packages, quickly and with a single download. For everything that is in there, this works very well.
The problem is the word “someone”. A VuXML entry is written by a committer, by the ports-secteam or, through a problem report, by the maintainer. For Firefox, Chromium, OpenSSL or nginx this often happens the same day. For a Perl library, a PHP web project or a Python module like pypdf, sometimes never. There are too many CVEs, and nobody reads them all.
The second catch lies in the ports tree itself. To map a CVE to a port automatically, you need a CPE, the “vendor and product” identifier that the CVE records use as well. Of 34,339 ports, 3,306 carry such an identifier. That is less than ten percent. For maintainers this is the easiest lever: USES=cpe in the Makefile (see the Porter’s Handbook), and the port becomes visible to every tool.
Nobody failed here. The process relies on people looking, and there was no place that showed where nobody had looked.
A tour of a port page
Every pair of CVE and port ends up in exactly one state. Red means unaddressed: the CVE record lists the built package version as affected, and no VuXML entry names the CVE. Yellow means “VuXML only”: pkg audit warns you, only the update is still missing. Anything that cannot be compared cleanly is listed separately and not counted. Better a number that is slightly too low than one that nobody believes.

If you type print/py-pypdf into the search, you get this:

At the top are four tiles: unaddressed, VuXML only, fixed and open problem reports. They refer to the package in latest. Below them is the sentence I consider the most important on the page: “Update to 6.21.2 fixes all 43 unaddressed CVEs.” The line below shows how many CVEs each intermediate version already fixes. That helps when jumping to the newest version is not an option right now. If an old branch no longer gets fixes, the page names the successor, for net/freerdp that is net/freerdp3.
Your package is red, now what?
- Find your ports.
pkg query '%o'lists the origins of all installed packages,pkg info -r <package>shows what pulled a package in. Check first what is reachable from outside or processes untrusted data. - Read the CVEs. If one only affects the Windows client or a feature you do not use, you can take it more calmly.
- Look at the update line. Is there a newer version or a successor port?
- Is the fix only in main? If the MFH has been requested or a problem report is open, wait or follow up politely.
- No update and no problem report? Then open one on bugs.freebsd.org under “Ports & Packages”, with a subject like
print/py-pypdf: Update to 6.21.2 (security)and the CVE numbers plus the link to the portstatus page in the text. A Bugzilla account is enough, you do not need to be a committer. - Need the fix right away? Build the port yourself with poudriere from a quarterly checkout (branch 2026Q4) plus the fix. The page shows the hash on the commit. Mixing single packages from latest into a quarterly system often goes wrong because library versions drift apart.
% git -C ~/ports switch 2026Q4 # checkout on the quarterly branch
% git -C ~/ports cherry-pick -x 1a66662c5b # fix from main, hash from the port page
# poudriere bulk -j 151amd64 -p quarterly mail/roundcube
Quarterly: fixed, but not on your system yet
If you use pkg on a RELEASE, your packages come from quarterly. Only fixes that someone carries over from main by MFH (“merged from head”) end up there. mail/roundcube got the security update to 1.7.5 in main on October 19. The commit requests the MFH to 2026Q4, but it has not been merged yet:

Here VuXML does its job, at least for php82 to php85: pkg audit warns you on quarterly as well.
# pkg audit roundcube-php84-1.7.4,1
roundcube-php84-1.7.4,1 is vulnerable:
Roundcube -- Multiple vulnerabilities
WWW: https://vuxml.FreeBSD.org/freebsd/69adacad-cba3-11f1-8135-8447094a420f.html
1 problem(s) in 1 package(s) found.
The entry, however, lists roundcube-php85 twice and roundcube-php86 not at all. For php86, pkg audit stays silent, and portstatus shows exactly these packages in red, with a note about the missing package name. What pkg audit does not tell you either: that the fix has long been in main, that the MFH has been requested, and that an open problem report is asking for exactly that.
Install and packages per platform
Below the tiles are three tabs. “Port info” sums up maintainer, package state, upstream and quarterly. “Install” shows what you get before you install it: size, dependencies, license, which users the package creates, which options it was built with, and what the pkg-message says.

The options are my favorite. Anyone who has installed a package only to find that the one module they needed is missing knows why. For nginx you see right away: HTTPV3 yes, BROTLI no.
The third tab shows which version is available on which FreeBSD version and architecture, split into latest and quarterly:

On FreeBSD 14 and 16, aarch64 is still at 1.30.5. Package sets that have not been rebuilt for 60 days are hidden. That is why armv6 and armv7 do not show up at all, their indexes date from 2023 and 2025.
No package does not mean “coming soon”
If a port has no package, the page tells you why. www/py-grimoirelab, for example, has no package on FreeBSD 15 amd64 because one of its dependencies has none. But which one?

The chain is longer than you would think: grimoirelab needs py-graal, which needs py-networkx, which in turn needs py-matplotlib, matplotlib needs py-fonttools, and fonttools depends on archivers/py-brotli. py-brotli broke with the update of brotli to 1.2.1. I would have bet on networkx and was wrong. The py-brotli page shows the damage: 350 ports without a package, all because of this one. The fix has been in the tree since October 24. Because package builds lag days behind, the page names both possibilities, a real build failure or a fix that has not been built yet, and shows the latest commit.

For committers and maintainers
If you maintain ports, the maintainer page is the quickest way in. Search for your address and you see all your ports with open CVEs, VuXML hits and available updates on one page. Not always pleasant, but useful.
This is how you resolve an “unaddressed”: update the port past the affected range and, in the same go, write a VuXML entry so users of the old version get warned. If there is no fix yet, the VuXML entry alone is enough. For security fixes, the MFH to quarterly belongs with it. The tree provides the skeleton for the entry, and with CVE_ID it pre-fills the details from the CVE:
% cd ~/ports/security/vuxml
% make newentry CVE_ID=CVE-2026-103000
% make validate
After the next hourly run the port shows yellow instead of red, and once the new package is out, not at all. For every commit the page also shows whether the MFH has been requested or is already done. /vuxml-gaps lists the VuXML entries that are missing CVE numbers or package names.
There is also /updates: 6,318 ports for which upstream has a newer version, by category, type of update (major, minor, patch) and the number of CVEs the update would fix. Ports that deliberately track a branch, like mysql84, do not count as outdated there.
What runs in the background
The site itself computes nothing. Everything happens in a Python collector that runs on its own VM and writes its results to MySQL. The website only reads, sets no cookies, does no tracking and needs no login. All sources are open:
- Package index from pkg.freebsd.org for latest and quarterly, plus the indexes of all FreeBSD versions and architectures that still get packages
- ports.git: every commit to main since 1994, the quarterly branches, VuXML, MOVED
- NVD and cvelistV5 from CVE.org: the CVEs with affected products and version ranges
- Repology: CPEs for ports that have none of their own, and the newest upstream version
- Bugzilla: open problem reports per port

The collector fetches new commits every minute. VuXML, the CVE records from cvelistV5 and the problem reports come hourly, NVD, packages and upstream versions once a day. It compares versions with libpkg, exactly like pkg version -t. Compare 1.30.6_5,3 wrongly against “before 1.30.6” and the list is full of false alarms and therefore worthless. Missing packages are traced back through the dependencies in the package index to the root:
def root(o, path):
# missing dependencies that still exist in main
sub = [d for d in gaps(o) if d not in path]
if not sub:
return {o} # the chain ends here
res = set()
for d in sub:
res |= root(d, path | {o})
return res
Where the site still falls short
- CVE records are often too broad. When a CNA, the organization that publishes the CVE record, writes “before 3.29.0”, that formally applies to every older branch, whether or not the old code has the flaw. That is why net/freerdp 2.x shows 96 CVEs, even though the port is deprecated, will be removed at the end of 2026 and points to net/freerdp3 in its pkg-message. portstatus works with what the record says.
- VuXML does not always list every CVE. If pkg audit already warns about the version and the entry reaches up to the fixed version, portstatus counts the CVE as yellow, because the update fixes it too. Today that affects 582 CVEs from 7 entries, 550 of them in the Chromium entry alone. Without this rule, 3,865 instead of 3,295 CVEs would be on the red list.
- Backports do not show in the version. Especially with the Linux ports (linux-rl9-*), fixes go into the RPM release while the upstream version stays the same. These are therefore listed separately as “possibly backported”.
- No CPE, no reliable match. For more than 90 percent of the ports the site needs fallbacks via Repology or the package name on PyPI and CPAN. That catches a lot, but not everything.
- Package builds lag behind. What is fixed in the tree only reaches the package days later. The site deliberately bases the security state on the built package, because that is what is installed on your system.
How the site counts is explained on the About page. I collect false positives through the comments here. What I still miss: a feed per maintainer or port, so nobody has to visit the site actively, and a way to mark a hit as “checked, does not affect us”, with a reason and visible to everyone.
Conclusion
- pkg audit is as good as VuXML. What is missing there, it does not report: across the whole tree, currently 3,295 CVEs in 670 ports.
- portstatus matches the ports against cvelistV5 hourly and against NVD daily, and tells you per port whether there is a fix and where.
- Quarterly users see what is fixed in main but still open on their systems.
- Committers use the maintainer search to quickly find where a VuXML entry, an update or an MFH is missing, and /vuxml-gaps to see which entries lack CVE numbers or package names.
The site does not replace pkg audit. It shows the gap next to it.
Your turn: look up your most important ports on portstatus.bsdproject.de. Which one shows red for you even though you are sure it is clean? Post it with the port name and CVE in the comments.