Waarom je SBOM faalt op PURL-validatie voor npm-pakketten met scope
Als je CycloneDX SBOM's genereert voor Node.js-projecten, heb je mogelijk gezien dat je SBOM schemavalidatie doorstaat maar faalt op de NTIA-minimumelementcontroles met deze fout:
Het component is er. De versie is er. De PURL lijkt correct: pkg:npm/@types/node@20.11.5. Dus wat is er mis?
Het probleem: twee @-tekens
Een Package URL (PURL) gebruikt het @-karakter als versieseparator. Het formaat is:
pkg:<type>/<namespace>/<n>@<version>
Wanneer je pkg:npm/@types/node@20.11.5 schrijft, staan er twee @-karakters in de string. Een PURL-parser moet weten welke de versie scheidt. Is het @types/node op versie 20.11.5, of is het @types/node@20.11.5 zonder versie?
Deze ambiguïteit zorgt ervoor dat PURL-parsers de identificator volledig afwijzen. Je component heeft feitelijk geen geldige PURL, wat betekent dat het niet voldoet aan het NTIA-vereiste voor een "uniek identificatienummer".
De specificatie is expliciet
De PURL-specificatie voor npm-pakketten behandelt dit geval direct:
"Het @-teken als prefix voor npm-scope wordt altijd procentgecodeerd, zoals dat was in de begindagen van npm-scopes."
De correcte codering voor npm-pakketten met scope is:
| Pakket | ❌ Onjuiste PURL | ✅ Correcte PURL |
|---|---|---|
@types/node@20.11.5 |
pkg:npm/@types/node@20.11.5 |
pkg:npm/%40types/node@20.11.5 |
@angular/core@17.1.0 |
pkg:npm/@angular/core@17.1.0 |
pkg:npm/%40angular/core@17.1.0 |
@vue/reactivity@3.4.1 |
pkg:npm/@vue/reactivity@3.4.1 |
pkg:npm/%40vue/reactivity@3.4.1 |
Het @ van de scope wordt %40, waardoor er precies één letterlijk @-teken in de hele PURL overblijft — ondubbelzinnig de versieseparator.
Pakketten zonder scope zoals express@4.18.2 worden niet beïnvloed: pkg:npm/express@4.18.2 is al correct.
Waarom deze fout zo vaak voorkomt
Het is een natuurlijke fout. Wanneer je naar @types/node kijkt, is het @-prefix onderdeel van hoe npm het pakket weergeeft en ernaar verwijst. Elke ontwikkelaar typt npm install @types/node — met het letterlijke @. Het coderen ervan voelt verkeerd.
De verwarring wordt vergroot doordat sommige PURL-voorbeelden op het internet (en zelfs in sommige CycloneDX-voorbeeldbestanden) het @-teken ongecodeerd tonen. De canonieke specificatievoorbeelden op GitHub tonen pkg:npm/@angular/animation@12.3.1 — maar de normatieve tekst in datzelfde document zegt dat het @ gecodeerd moet worden. De specificatietekst gaat voor.
De oplossing
Als je SBOM-tooling bouwt, is de oplossing eenvoudig. Bij het construeren van een PURL voor een npm-pakket met scope, codeer je het eerste @ als %40:
function buildPurl(name: string, version: string): string {
if (name.startsWith('@')) {
return `pkg:npm/%40${name.slice(1)}@${version}`;
}
return `pkg:npm/${name}@${version}`;
}
Een veelgemaakte fout is het gebruik van een generiek name.replace('/', '%2F') of encodeURIComponent op de hele naam. Doe dat ook niet — de / tussen namespace en naam is een structureel PURL-scheidingsteken en moet ongecodeerd blijven.
Een snelle manier om te verifiëren
Na het corrigeren van je PURL-generatie kun je je SBOM valideren met:
- FOSSA NTIA SBOM Validator
- De
purl-specreferentieparser op GitHub
Beide zullen ongeldige PURL's detecteren voordat ze een complianceprobleem worden.
Na de correctie
Met het scope-prefix @ correct procentgecodeerd, hebben je npm-pakketten met scope geldige PURL's en slaagt de NTIA-identificatorcontrole probleemloos:
Hoe Verimu dit aanpakt
De SBOM-generator van Verimu codeert npm-PURL's met scope correct volgens de PURL-specificatie — %40 voor het scope-prefix, ongecodeerde / voor het namespacescheidingsteken. Elk SBOM dat we genereren slaagt direct voor NTIA-minimumelementvalidatie.
Als je werkt aan CRA-compliance voor toegang tot de Europese markt, of je gewoon SBOM's wilt die niet falen op validatie in productie, hebben wij deze randgevallen al opgelost zodat jij dat niet hoeft te doen.
Start gratis → of boek een demo om Verimu in actie te zien.