From 6fdcbbbbc3c2ba2c6450fb1222f7b571c1c949a8 Mon Sep 17 00:00:00 2001 From: Erlend Oftedal Date: Tue, 20 Aug 2024 11:06:14 +0200 Subject: [PATCH] Correctly encode PURLs in SBOM output --- node/CHANGELOG.md | 6 ++++++ node/lib/retire.js | 2 +- node/package-lock.json | 4 ++-- node/package.json | 2 +- node/spec/tests/purl.spec.ts | 16 ++++++++++++++++ node/src/repo.ts | 2 +- node/src/reporters/utils.ts | 13 +++++++++++-- 7 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 node/spec/tests/purl.spec.ts diff --git a/node/CHANGELOG.md b/node/CHANGELOG.md index 062a1b664..ab615d742 100644 --- a/node/CHANGELOG.md +++ b/node/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [5.2.2] + +### Bugfix + +- Encode purls correctly in SBOM + ## [5.2.1] ### Bugfix diff --git a/node/lib/retire.js b/node/lib/retire.js index fee8bbc6e..2c56cb64d 100644 --- a/node/lib/retire.js +++ b/node/lib/retire.js @@ -4,7 +4,7 @@ */ var exports = exports || {}; -exports.version = '5.2.1'; +exports.version = '5.2.2'; function isDefined(o) { return typeof o !== 'undefined'; diff --git a/node/package-lock.json b/node/package-lock.json index b12e5f33c..690e9432a 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "retire", - "version": "5.2.1", + "version": "5.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "retire", - "version": "5.2.1", + "version": "5.2.2", "license": "Apache-2.0", "dependencies": { "ansi-colors": "^4.1.1", diff --git a/node/package.json b/node/package.json index 09ab4509c..88c6c4879 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "author": "Erlend Oftedal ", "name": "retire", "description": "Retire is a tool for detecting use of vulnerable libraries", - "version": "5.2.1", + "version": "5.2.2", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/node/spec/tests/purl.spec.ts b/node/spec/tests/purl.spec.ts new file mode 100644 index 000000000..ed9f55d46 --- /dev/null +++ b/node/spec/tests/purl.spec.ts @@ -0,0 +1,16 @@ +import { should } from 'chai'; +import 'mocha'; +should(); +import { generatePURL } from '../../lib/reporters/utils'; + +describe('purl encoding', () => { + it('should not touch a simple string', () => { + generatePURL({ component: 'jquery', version: '1.2.3' }).should.equal('pkg:npm/jquery@1.2.3'); + }); + it('should encode @ in package scopes', () => { + generatePURL({ component: '@angular/core', version: '1.2.3' }).should.equal('pkg:npm/%40angular/core@1.2.3'); + }); + it('should not doulbe encode', () => { + generatePURL({ component: '%40angular/core', version: '1.2.3' }).should.equal('pkg:npm/%40angular/core@1.2.3'); + }); +}); diff --git a/node/src/repo.ts b/node/src/repo.ts index 4c666234e..ecf95eb6b 100644 --- a/node/src/repo.ts +++ b/node/src/repo.ts @@ -104,7 +104,7 @@ export function validateRepository( bowername: z.array(z.string().regex(/^[a-z0-9.-]+$/i)).optional(), basePurl: z .string() - .regex(/^pkg:[a-z0-9/]+$/i) + .regex(/^pkg:[a-z0-9%.-/]+$/i) .optional(), npmname: z .string() diff --git a/node/src/reporters/utils.ts b/node/src/reporters/utils.ts index e2e2b8ba4..c00e7451a 100644 --- a/node/src/reporters/utils.ts +++ b/node/src/reporters/utils.ts @@ -1,9 +1,18 @@ import { Component } from '../types'; +function encodePURLchars(str: string): string { + return str.replace( + /[^A-Za-z0-9.+/=-%]/g, + (match) => '%' + ('0' + match.charCodeAt(0).toString(16).toUpperCase()).slice(-2), + ); +} + export function generatePURL(component: Component): string { if (component.basePurl) { - return component.basePurl + '@' + component.version; + const [pType, ...rest] = component.basePurl.split(':'); + const pathElements = rest.join(':').split('/').map(encodePURLchars).join('/'); + return `${pType}:${pathElements}@${encodePURLchars(component.version)}`; } const compName = component.npmname || component.component; - return `pkg:npm/${compName}@${component.version}`; + return `pkg:npm/${encodePURLchars(compName)}@${encodePURLchars(component.version)}`; }