Software composition analysis (SCA) werkt goed voor npm, Maven of PyPI. Voor C en C++ loopt het al jaren achter. Bibliotheken werden in de broncode gekopieerd, vanuit het besturingssysteem geïnstalleerd of met eigen scripts gebouwd, en niets legde vast welke versies in een product terechtkwamen.
Conan heeft dat veranderd. Het geeft C- en C++-projecten een opgeloste dependency graph met versies, en daarmee de basis voor een echte SBOM. Maar tussen weten welke bibliotheken u gebruikt en een melding krijgen als een daarvan een kwetsbaarheid heeft, zit een gat. In de meeste Conan-pipelines die ik heb gezien, is dat gat de ontbrekende CPE-identifier. De SBOM bestaat, de kwetsbaarheidsscanner draait, het dashboard is groen, en de CVE’s in uw versie van OpenSSL of libcurl komen nooit boven.
Dit artikel legt uit waarom dat gebeurt en hoe u het oplost met een kleine, onderhoudbare verrijkingsstap.
Genereer de SBOM uit de build graph
Voor C++ hoort de SBOM uit de dependency graph te komen die Conan tijdens de build oplost. Dat is het gezaghebbende overzicht van wat er in de build is gegaan: elke directe en transitieve afhankelijkheid, met de exacte versie.
Binaire scanning is geen vervanging. Die werkt door versiestrings, bestandshashes of bytepatronen in gecompileerde code te herkennen, en C++-releasebuilds maken dat op verschillende manieren onmogelijk:
- Releasebuilds zijn meestal gestript, dus er zijn geen symbolen om op af te gaan.
- Statisch linken voegt bibliotheekcode samen met uw binary, en de grenzen tussen componenten verdwijnen.
- Bibliotheken die met patches of ongebruikelijke compilerflags zijn gebouwd, lijken niet meer op de referentiebinaries die de scanner kent.
Binaire scanning blijft een nuttige tweede laag voor code die buiten Conan om gaat, zoals gekopieerde broncode of systeembibliotheken. Maar de build graph is uw grondwaarheid.
Twee tools kunnen de SBOM maken:
- cdxgen leest
conan.lock(Conan 1 en 2) ofconanfile.txten schrijft een CycloneDX-SBOM. Het leest geen afhankelijkheden die alleen inconanfile.pyzijn gedeclareerd, dus maak een lockfile metconan lock createen commit die. - Conan 2 heeft eigen experimentele SBOM-tools,
cyclonedx_1_4encyclonedx_1_6, die u vanuit een hook aanroept. Ze werken met de volledige dependency graph zonder lockfile, maar de functie is nog gemarkeerd als onderhevig aan brekende wijzigingen.
Het gat: geen CPE
Dit is wat cdxgen vandaag voor een Conan-component produceert:
{
"type": "library",
"name": "openssl",
"version": "3.3.2",
"purl": "pkg:conan/[email protected]"
}
Er is een Package URL (PURL), maar geen CPE. Dat maakt uit, want kwetsbaarheidsdatabases zoeken componenten niet op naam op. Elke bron gebruikt een eigen identifier:
| Kwetsbaarheidsbron | Matcht op | Dekt Conan-pakketten? |
|---|---|---|
| NIST National Vulnerability Database (NVD) | CPE | Ja, via CPE. Hier staan de meeste CVE's voor C en C++. |
| OSV | PURL | Deels, via het ConanCenter-ecosysteem |
| Sonatype OSS Index | PURL | Deels, voor pakketten die het kent |
| GitHub Advisory Database | PURL | Nee. Het Conan-ecosysteem valt erbuiten. |
Voor de meeste andere ecosystemen geeft een PURL u goede dekking. Voor C++ staan de kwetsbaarheden in bibliotheken als OpenSSL, zlib, libcurl of libxml2 in de NVD geregistreerd onder CPE-identifiers, en de bronnen op basis van PURL dekken daar maar een deel van. Zonder CPE kan uw SBOM niet worden gematcht tegen de bron die het meest telt.
OWASP Dependency-Track laat dit duidelijk zien. Het matcht NVD-data tegen de CPE in uw SBOM. Versie 4 had een optie voor fuzzy matching die CPE’s probeerde te raden op basis van componentnamen. Dependency-Track 5 heeft die geschrapt, dus een component zonder CPE krijgt helemaal geen NVD-bevindingen. Draait u Dependency-Track, dan behandelt onze gids over Dependency-Track 5 uitrollen met Docker Compose de installatie.
Het falen is stil. De scan is klaar, er worden geen kwetsbaarheden gemeld en het dashboard ziet er schoon uit. Niet omdat uw afhankelijkheden veilig zijn, maar omdat de tool ze niet kon opzoeken.
Waarom de CPE niet automatisch te genereren is
Een CPE 2.3-identifier voor een bibliotheek ziet er zo uit:
cpe:2.3:a:<vendor>:<product>:<version>:*:*:*:*:*:*:*
De a staat voor application. De namen van vendor en product komen uit het CPE-woordenboek van de NVD, dat NIST onafhankelijk van welk pakketregister dan ook opbouwt. Veel van die namen zijn jaren vóór Conan vastgelegd, en ze komen vaak niet overeen met de naam van het Conan-pakket:
| Conan-pakket | NVD-vendor | NVD-product |
|---|---|---|
| openssl | openssl | openssl |
| expat | libexpat | expat |
| libcurl | haxx | libcurl |
| libxml2 | xmlsoft | libxml2 |
| libjpeg-turbo | libjpeg-turbo, en d.r.commander voor oudere versies | libjpeg-turbo |
Er bestaat geen regel die u van libcurl naar haxx brengt. En een zoekopdracht op trefwoord in het woordenboek levert ook distributiepakketten op: zoeken naar libpng geeft eerst de redhat:libpng-items van Red Hat, niet de upstream libpng:libpng. De enige betrouwbare manier is dat iemand per bibliotheek één keer het CPE-woordenboek van de NVD controleert, kijkt welke vendor en welk product de recente CVE’s gebruiken, en het resultaat vastlegt.
Daarom doen SBOM-generatoren het ook niet. Voor npm of Maven werkt een heuristische mapping goed genoeg. Voor C++ zou die verkeerde CPE’s opleveren, en een verkeerde CPE is erger dan geen: die faalt net zo stil.
Nog twee dingen om te weten over CPE’s:
- Een bibliotheek zonder CVE-geschiedenis heeft misschien nog geen CPE. NIST maakt CPE-items vooral aan tijdens de analyse van CVE’s. Dat is geen probleem: er valt niets te matchen. Maar de mapping moet opnieuw worden bekeken zodra de eerste CVE verschijnt.
- De NVD verrijkt minder CVE’s. Sinds april 2026 voegt NIST CPE-data vooral toe aan CVE’s in de Known Exploited Vulnerabilities-catalogus van CISA en aan software die de Amerikaanse federale overheid gebruikt; andere CVE’s krijgen de status “Not Scheduled” en krijgen mogelijk nooit CPE-data. CPE-matching blijft essentieel voor C++, maar is op zichzelf niet meer genoeg. Daarom voegt u ook PURL’s en meer dan één kwetsbaarheidsbron toe.
De oplossing: beheerde CPE-metadata plus een nabewerkingsstap
De aanpak bestaat uit twee delen:
- Leg voor elke bibliotheek van derden één keer de geverifieerde CPE-vendor en het CPE-product vast.
- Voeg na het genereren van de SBOM aan elk component de CPE toe, samen met een PURL als de generator die niet heeft toegevoegd.
Sla alleen vendor en product op, nooit de volledige CPE-string. De nabewerking voegt de versie uit de SBOM toe. Als u van OpenSSL 3.3.2 naar 3.4.1 gaat, hoeft er niets in uw CPE-metadata te veranderen, en de CPE kan niet uit de pas lopen met de versie die u werkelijk levert.
Waar u de metadata bewaart
Onderhoudt u eigen Conan-recepten, zet de metadata dan in het recept. Dat bevat al de naam, homepage en licentie van het component, en de CPE hoort daarnaast:
class OpensslConan(ConanFile):
name = "openssl"
homepage = "https://www.openssl.org"
license = "Apache-2.0"
# Verified against the NVD CPE dictionary
cpe_vendor = "openssl"
cpe_product = "openssl"
Conan negeert attributen die het niet kent, dus dit heeft geen invloed op de build. Een kort script kan de waarden uit alle recepten verzamelen zonder ze uit te voeren:
"""Collect cpe_vendor/cpe_product from Conan recipes without executing them."""
import ast
import json
import pathlib
import sys
def recipe_metadata(path):
tree = ast.parse(path.read_text(encoding="utf-8"))
for node in tree.body:
if isinstance(node, ast.ClassDef):
values = {}
for stmt in node.body:
if isinstance(stmt, ast.Assign) and isinstance(stmt.value, ast.Constant):
for target in stmt.targets:
if isinstance(target, ast.Name):
values[target.id] = stmt.value.value
if "name" in values:
return values
return None
mapping = {}
for recipe in pathlib.Path(sys.argv[1]).rglob("conanfile.py"):
meta = recipe_metadata(recipe)
if meta and meta.get("cpe_vendor") and meta.get("cpe_product"):
mapping[meta["name"]] = {"cpe_vendor": meta["cpe_vendor"], "cpe_product": meta["cpe_product"]}
json.dump(mapping, sys.stdout, indent=2, sort_keys=True)
Gebruikt u recepten rechtstreeks uit ConanCenter, dan kunt u er geen attributen aan toevoegen. Houd dan een mappingbestand bij in een repository, dat u reviewt als code:
{
"expat": { "cpe_vendor": "libexpat", "cpe_product": "expat" },
"libcurl": { "cpe_vendor": "haxx", "cpe_product": "libcurl" },
"libxml2": { "cpe_vendor": "xmlsoft", "cpe_product": "libxml2" }
}
Proxyt u ConanCenter via een private remote, bijvoorbeeld in Artifactory, onderhoud dan één centraal mappingbestand voor alles wat erdoorheen gaat, zodat elk project ervan profiteert. Gemengde opzetten werken ook: voeg de waarden uit uw eigen recepten samen met het mappingbestand voor publieke pakketten.
De nabewerking
De verrijkingsstap draait direct na het genereren van de SBOM en vóór die ergens wordt geüpload:
"""Add CPE and Conan PURL identifiers to a CycloneDX SBOM."""
import json
import sys
sbom_path, map_path, out_path = sys.argv[1:4]
sbom = json.load(open(sbom_path, encoding="utf-8"))
cpe_map = json.load(open(map_path, encoding="utf-8"))
missing = []
for comp in sbom.get("components", []):
name, version = comp.get("name"), comp.get("version")
if not name or not version:
continue
comp.setdefault("purl", f"pkg:conan/{name}@{version}")
entry = cpe_map.get(name)
if entry:
comp["cpe"] = f"cpe:2.3:a:{entry['cpe_vendor']}:{entry['cpe_product']}:{version}:*:*:*:*:*:*:*"
elif "cpe" not in comp:
missing.append(f"{name}@{version}")
json.dump(sbom, open(out_path, "w", encoding="utf-8"), indent=2)
for item in missing:
print(f"WARNING: no CPE mapping for {item}", file=sys.stderr)
Na verrijking ziet het component er zo uit:
{
"type": "library",
"name": "openssl",
"version": "3.3.2",
"purl": "pkg:conan/[email protected]",
"cpe": "cpe:2.3:a:openssl:openssl:3.3.2:*:*:*:*:*:*:*"
}
Valideer het resultaat tegen het CycloneDX-schema voordat u het uploadt, bijvoorbeeld met de CycloneDX CLI of cyclonedx-python-lib.
Twee praktische details. Ten eerste komen Conan-versies niet altijd overeen met die van de NVD. ConanCenter gebruikt versies als cci.20230101 voor bibliotheken zonder releases, en de NVD schrijft versies soms anders dan de upstream-tags. Controleer per bibliotheek een paar CVE’s om te zien of de versieformaten overeenkomen, en voeg een versie-override aan de mapping toe waar dat niet zo is. Ten tweede: behandel elke waarschuwing als een gat dat u dicht. Voeg de mapping toe, of leg vast waarom de bibliotheek er geen heeft.
De volledige pipeline
Samengevoegd ziet de pipeline er zo uit:
- Conan lost de dependency graph op en haalt pakketten via uw private remote binnen.
- De build compileert en linkt.
- cdxgen, of de SBOM-hook van Conan, genereert de basis-SBOM.
- De nabewerking voegt CPE’s en PURL’s toe, waarschuwt voor componenten zonder mapping en valideert het resultaat.
- De binaries en de verrijkte SBOM worden samen opgeslagen in uw artefactrepository.
- Dependency-Track, of een ander SCA-platform, neemt de SBOM op, matcht die tegen de NVD, OSV en andere bronnen, en blijft hem bewaken.
Dat laatste punt wordt snel onderschat. Als er een CVE verschijnt voor een bibliotheek die u zes maanden geleden hebt geleverd, matcht Dependency-Track die tegen de SBOM’s die het al heeft en waarschuwt het u, maar alleen als de CPE er al in stond toen de SBOM werd geüpload. Die matching achteraf is precies wat de EU Cyber Resilience Act verwacht: de componenten in uw product identificeren en ze gedurende de ondersteuningsperiode op kwetsbaarheden bewaken. Onze CRA-gids voor het mkb behandelt die verplichtingen.
Voeg ook een build gate toe. Laat de build falen als er een component met een kritieke kwetsbaarheid bijkomt, waarschuw bij hoog en rapporteer de rest. Ontwikkelaars horen dan van een kwetsbare afhankelijkheid in dezelfde pipelinerun die hem introduceerde, wanneer de fix het goedkoopst is. Continue monitoring vangt op wat tijdens de build nog niet bekend was. U hebt beide nodig.
Randgevallen
Interne bibliotheken die andere teams in uw organisatie bouwen en als Conan-pakket publiceren, hebben geen NVD-items en horen geen CPE te krijgen. Geef ze een PURL met een repository_url-qualifier die naar uw private remote wijst, zodat ze zichtbaar blijven in de inventaris en bij licentiecontroles.
Afhankelijkheden buiten de Conan-graph zijn de echte blinde vlek: gekopieerde broncode, bibliotheken die via de pakketbeheerder van de buildmachine zijn geïnstalleerd, en alles wat met FetchContent of ExternalProject van CMake wordt binnengehaald. Die bereiken de SBOM nooit. Verplaats ze waar mogelijk naar Conan-recepten. Waar dat niet kan, voegt u ze in de nabewerkingsstap aan de SBOM toe met een handmatig beheerde CPE, en controleert u regelmatig op nieuwe.
Voor C++-bibliotheken die in geen enkel register staan, is er nog geen goed PURL-type; pkg:generic bestaat, maar weinig kwetsbaarheidsbronnen matchen erop. De PURL-gemeenschap bespreekt een nieuw type voor niet-verpakte software, en PURL zelf werd in december 2025 een internationale norm, ECMA-427. Tot er een type voor niet-verpakte software is, blijft CPE de betrouwbaarste identifier voor die bibliotheken.
Ontwerp het programma rond de mensen die het gebruiken
Verrijking repareert de data. Of het SCA-programma werkt, hangt af van de vraag of het in elke ontwikkelfase de juiste vragen beantwoordt:
- Hoe helpen we ontwikkelaars veilige componenten te kiezen?
- Hoe voorkomen we dat ontwikkelaars slechte componenten importeren?
- Hoe maken we het eenvoudig om een nauwkeurige SBOM voor bestaande software te maken?
- Hoe kan QA vóór de release controleren dat er geen nieuwe SCA-bevindingen zijn?
- Hoe voldoen we aan de beveiligingseisen na de release?
En het moet elk team bedienen dat ervan afhangt, niet alleen het securityteam:
| Team | Wat ze van het SCA-programma nodig hebben |
|---|---|
| GRC en juridisch | Een beeld van het totale risico van componenten van derden in het hele bedrijf |
| AppSec | Een dashboard om te monitoren, te triëren en op te volgen, en antwoorden op vragen van andere afdelingen |
| DevOps | Meldingen over nieuwe bevindingen, en een duidelijk proces om nieuwe repositories toe te voegen |
| Ontwikkelaars | Meldingen over nieuwe bevindingen, en een SCA-tool die elke ontwikkelaar kan gebruiken om ze te beoordelen en te triëren |
| Support | SBOM-exports op verzoek van klanten, vastgelegde beslissingen over kwetsbaarheden om vragen te beantwoorden, en VEX-documenten |
| Sales | Een duidelijke beschrijving van de SCA-aanpak voor aanbestedingen, SBOM's met een acceptabel aantal kwetsbaarheden, en recente VEX-documenten |
Een programma dat alleen bevindingen voor het securityteam oplevert, loopt vast. Een programma dat support een VEX-document geeft om te versturen en sales een antwoord voor een aanbestedingsvragenlijst, wordt gebruikt.
De uitrol
Een realistisch plan voor een bestaande codebase duurt ongeveer acht weken:
- Week 1 en 2, audit. Draai uw SBOM-generator op de huidige build en controleer hoeveel componenten een CPE hebben. Inventariseer uw recepten, controleer welke bibliotheken in het CPE-woordenboek van de NVD staan, en begin met de riskantste: cryptografie, netwerken en parsers.
- Week 3 tot 5, verrijken. Voeg de CPE-vendor en het CPE-product toe aan elk recept van derden of aan het mappingbestand, geverifieerd tegen de NVD. Maak CPE-metadata een reviewvereiste voor elk nieuw recept, en documenteer interne componenten apart.
- Week 6 en 7, de nabewerking bouwen. Voeg die toe aan de pipeline na het genereren van de SBOM, valideer de uitvoer tegen het CycloneDX-schema en test van begin tot eind: upload een verrijkte SBOM en controleer dat bekende CVE’s verschijnen.
- Week 8, monitoring inschakelen. Laat uw SCA-platform de verrijkte SBOM’s opnemen, stel meldingen in voor kritieke en hoge kwetsbaarheden, voeg de build gate toe en draag het proces over aan de teams die het gaan uitvoeren.
Belangrijkste punten
- Genereer C++-SBOM’s uit de dependency graph van Conan en behandel binaire scanning als tweede laag.
- SBOM-generatoren voegen hooguit een
pkg:conan-PURL toe. Ze voegen geen CPE’s toe, en voor C++ is de CPE wat u met de NVD verbindt. - Verifieer CPE-namen voor vendor en product met de hand tegen het NVD-woordenboek, één keer per bibliotheek, en sla ze op zonder versie.
- Voeg de CPE toe in een nabewerkingsstap en behandel elk component zonder mapping als een gat dat u dicht.
- Vertrouw niet alleen op de NVD: sinds 2026 verrijkt die minder CVE’s, dus combineer CPE-matching met bronnen op basis van PURL.
- Houd bij wat buiten Conan om gaat: gekopieerde code, systeembibliotheken en afhankelijkheden die CMake binnenhaalt.
Wilt u hulp bij het ontwerpen of reviewen van uw SCA-programma? Plan een gratis gesprek.