← Voltar ao blog

Porque é que o seu SBOM falha na validação PURL para pacotes npm com scope

· Verimu
PURLCycloneDXNTIASBOMnpm

Se está a gerar SBOMs CycloneDX para projetos Node.js, pode ter visto o seu SBOM passar na validação de esquema mas falhar nas verificações de elementos mínimos NTIA com este erro:

Component requires at least one valid identifier (PURL or CPE). Both are currently missing or invalid. Path: components.pkg:npm/@types/node@20.11.5.identifiers Rule: Component.identifiers

O componente está lá. A versão está lá. O PURL parece correto: pkg:npm/@types/node@20.11.5. Então qual é o problema?

O problema: dois sinais @

Um Package URL (PURL) usa o carácter @ como separador de versão. O formato é:

pkg:<type>/<namespace>/<n>@<version>

Quando escreve pkg:npm/@types/node@20.11.5, existem dois caracteres @ na string. Um parser PURL precisa de saber qual deles separa a versão. É @types/node na versão 20.11.5, ou é @types/node@20.11.5 sem versão alguma?

Esta ambiguidade faz com que os parsers PURL rejeitem o identificador completamente. O seu componente efetivamente não tem nenhum PURL válido, o que significa que falha no requisito NTIA de "identificador único".

A especificação é explícita

A especificação PURL para pacotes npm aborda este caso diretamente:

"O prefixo @ do scope npm é sempre codificado em percentagem, como era nos primeiros tempos dos scopes npm."

A codificação correta para pacotes npm com scope é:

Pacote ❌ PURL incorreto ✅ PURL correto
@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

O @ do scope torna-se %40, deixando exatamente um único @ literal em todo o PURL — inequivocamente o separador de versão.

Pacotes sem scope como express@4.18.2 não são afetados: pkg:npm/express@4.18.2 já está correto.

Porque é que este erro é tão comum

É um erro natural. Quando olha para @types/node, o prefixo @ faz parte da forma como o npm apresenta e referencia o pacote. Todos os programadores escrevem npm install @types/node — com o @ literal. Codificá-lo parece errado.

Aumentando a confusão, alguns exemplos de PURL que circulam na Internet (e até em alguns ficheiros de exemplo CycloneDX) mostram o sinal @ não codificado. Os exemplos canónicos da especificação no GitHub mostram pkg:npm/@angular/animation@12.3.1 — mas o texto normativo desse mesmo documento diz que o @ deve ser codificado. O texto da especificação prevalece.

A correção

Se está a desenvolver ferramentas SBOM, a correção é simples. Ao construir um PURL para um pacote npm com scope, codifique o @ inicial como %40:

function buildPurl(name: string, version: string): string {
  if (name.startsWith('@')) {
    return `pkg:npm/%40${name.slice(1)}@${version}`;
  }
  return `pkg:npm/${name}@${version}`;
}

Um erro comum é usar um name.replace('/', '%2F') genérico ou encodeURIComponent no nome completo. Não faça isso também — o / entre namespace e nome é um separador estrutural PURL e deve permanecer não codificado.

Uma forma rápida de verificar

Após corrigir a sua geração de PURL, pode validar o seu SBOM com:

Ambos detetarão PURLs inválidos antes de se tornarem um problema de conformidade.

Após a correção

Com o prefixo de scope @ corretamente codificado em percentagem, os seus pacotes npm com scope terão PURLs válidos e a verificação de identificador NTIA passa sem problemas:

✓ 12/12 verificações de elementos mínimos NTIA aprovadas

Como o Verimu lida com isto

O gerador de SBOM do Verimu codifica corretamente os PURLs npm com scope segundo a especificação PURL — %40 para o prefixo de scope, / não codificado para o separador de namespace. Cada SBOM que geramos passa na validação de elementos mínimos NTIA desde o primeiro momento.

Se está a trabalhar na conformidade CRA para acesso ao mercado europeu, ou simplesmente quer SBOMs que não falhem na validação em produção, já resolvemos estes casos especiais para que não tenha de o fazer.

Comece grátis → ou marque uma demo para ver o Verimu em ação.