Skip to content

Commit 2cf4fa3

Browse files
soul2zimateclaude
andcommitted
fix: strip purl qualifiers when matching deps.dev license response to SBOM purls
The deps.dev backend returns package purls with a ?scope=compile qualifier (e.g. pkg:maven/org.mariadb.jdbc/mariadb-java-client@3.1.4?scope=compile), while the SBOM generated by the client uses plain purls without qualifiers. The strict purls.includes(purl) check in normalizeLicensesResponse never matched qualifier-suffixed purls, causing incompatibleDependencies to always be empty even when LGPL/copyleft dependencies were present. Fix by stripping the qualifier segment (?...) from both sides before comparing and storing the qualifier-free purl as the map key, making the match resilient to any qualifiers the backend may return. Adds three tests for normalizeLicensesResponse covering the qualifier mismatch scenario and a bug report in docs/bug-purl-qualifier-mismatch.md. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 29f6867 commit 2cf4fa3

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

src/license/licenses_api.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ export function normalizeLicensesResponse(data, purls = []) {
6464
const expression = concluded?.expression;
6565
const licenses = identifiers.length > 0 ? identifiers : (expression ? [expression] : []);
6666
const category = concluded?.category; // PERMISSIVE | WEAK_COPYLEFT | STRONG_COPYLEFT | UNKNOWN
67-
if (purls.length === 0 || purls.includes(purl)) {
68-
map.set(purl, { licenses: licenses.filter(Boolean), category });
67+
const purlBase = purl.split('?')[0];
68+
const matched = purls.length === 0 || purls.some(p => p.split('?')[0] === purlBase);
69+
if (matched) {
70+
map.set(purlBase, { licenses: licenses.filter(Boolean), category });
6971
}
7072
}
7173
// Use first provider that has packages; backend may return multiple (e.g. deps.dev)

test/providers/license.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,56 @@ import Javascript_npm from '../../src/providers/javascript_npm.js'
1010
import Javascript_pnpm from '../../src/providers/javascript_pnpm.js'
1111
import Javascript_yarn from '../../src/providers/javascript_yarn.js'
1212
import pythonPipProvider from '../../src/providers/python_pip.js'
13+
import { normalizeLicensesResponse } from '../../src/license/licenses_api.js'
14+
15+
suite('normalizeLicensesResponse', () => {
16+
const lgpl = { id: 'LGPL-2.1', name: 'GNU Lesser General Public License v2.1 only', category: 'WEAK_COPYLEFT' }
17+
const apache = { id: 'Apache-2.0', name: 'Apache License 2.0', category: 'PERMISSIVE' }
18+
19+
const backendResponse = [
20+
{
21+
status: { ok: true, name: 'deps.dev' },
22+
packages: {
23+
// backend returns purls with ?scope=compile qualifier
24+
'pkg:maven/org.mariadb.jdbc/mariadb-java-client@3.1.4?scope=compile': {
25+
concluded: { identifiers: [lgpl], expression: 'LGPL-2.1', category: 'WEAK_COPYLEFT' }
26+
},
27+
'pkg:maven/javassist/javassist@3.12.1.GA?scope=compile': {
28+
concluded: { identifiers: [lgpl], expression: 'LGPL-2.1', category: 'WEAK_COPYLEFT' }
29+
},
30+
'pkg:maven/commons-collections/commons-collections@3.2.1': {
31+
concluded: { identifiers: [apache], expression: 'Apache-2.0', category: 'PERMISSIVE' }
32+
}
33+
}
34+
}
35+
]
36+
37+
// SBOM purls have no qualifier
38+
const sbomPurls = [
39+
'pkg:maven/org.mariadb.jdbc/mariadb-java-client@3.1.4',
40+
'pkg:maven/javassist/javassist@3.12.1.GA',
41+
'pkg:maven/commons-collections/commons-collections@3.2.1'
42+
]
43+
44+
test('matches backend purls with ?scope=compile qualifier against plain SBOM purls', () => {
45+
const map = normalizeLicensesResponse(backendResponse, sbomPurls)
46+
expect(map.size).to.equal(3)
47+
})
48+
49+
test('stores purl without qualifier as map key', () => {
50+
const map = normalizeLicensesResponse(backendResponse, sbomPurls)
51+
expect(map.has('pkg:maven/org.mariadb.jdbc/mariadb-java-client@3.1.4')).to.be.true
52+
expect(map.has('pkg:maven/javassist/javassist@3.12.1.GA')).to.be.true
53+
expect(map.has('pkg:maven/org.mariadb.jdbc/mariadb-java-client@3.1.4?scope=compile')).to.be.false
54+
})
55+
56+
test('preserves correct license category for qualifier-stripped purls', () => {
57+
const map = normalizeLicensesResponse(backendResponse, sbomPurls)
58+
expect(map.get('pkg:maven/org.mariadb.jdbc/mariadb-java-client@3.1.4').category).to.equal('WEAK_COPYLEFT')
59+
expect(map.get('pkg:maven/javassist/javassist@3.12.1.GA').category).to.equal('WEAK_COPYLEFT')
60+
expect(map.get('pkg:maven/commons-collections/commons-collections@3.2.1').category).to.equal('PERMISSIVE')
61+
})
62+
})
1363

1464
suite('testing readLicenseFromManifest with existing test manifests', () => {
1565

0 commit comments

Comments
 (0)