From cbba2fb749e8f4b4eadee089607d01da0f7c3de2 Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 24 Jul 2026 14:33:21 -0400 Subject: [PATCH] fix(package): include namespace in shallow score purl output `socket package shallow` (and `socket package score`) dropped the package namespace when rendering the report card purl, so a scoped npm package like `@axe-core/react` was printed as `pkg:npm/react@4.11.0`. The informational log line already showed the correct purl, making the mismatch confusing. formatReportCard() rebuilt the purl from ecosystem/name/version but omitted the namespace segment that preProcess() already tracks on the deduped artifact. Add the namespace back, mirroring how preProcess and the deep-score renderer construct purls. Adds a focused regression test for the scoped-npm case and updates the Go shallow snapshots (which now correctly show the module namespace). Fixes #971 --- .../package/output-purls-shallow-score.mts | 2 +- .../output-purls-shallow-score.test.mts | 50 +++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/package/output-purls-shallow-score.mts b/packages/cli/src/commands/package/output-purls-shallow-score.mts index e2e152e4d9..aa4feb90a1 100644 --- a/packages/cli/src/commands/package/output-purls-shallow-score.mts +++ b/packages/cli/src/commands/package/output-purls-shallow-score.mts @@ -29,7 +29,7 @@ export function formatReportCard( debug(`miss: artifact ecosystem ${JSON.stringify(artifact)}`) } /* c8 ignore stop */ - const purl = `pkg:${artifact.ecosystem}/${artifact.name}${artifact.version ? `@${artifact.version}` : ''}` + const purl = `pkg:${artifact.ecosystem}/${artifact.namespace ? `${artifact.namespace}/` : ''}${artifact.name}${artifact.version ? `@${artifact.version}` : ''}` // Calculate proper padding based on longest label. const maxLabelLength = Math.max( diff --git a/packages/cli/test/unit/commands/package/output-purls-shallow-score.test.mts b/packages/cli/test/unit/commands/package/output-purls-shallow-score.test.mts index 1e02a37da2..409addec4f 100644 --- a/packages/cli/test/unit/commands/package/output-purls-shallow-score.test.mts +++ b/packages/cli/test/unit/commands/package/output-purls-shallow-score.test.mts @@ -34,12 +34,54 @@ import npmShallow from '../../../../src/commands/package/fixtures/npm_shallow.js import nugetShallow from '../../../../src/commands/package/fixtures/nuget_shallow.json' with { type: 'json' } import rubyShallow from '../../../../src/commands/package/fixtures/ruby_shallow.json' with { type: 'json' } import { + formatReportCard, generateMarkdownReport, generateTextReport, preProcess, } from '../../../../src/commands/package/output-purls-shallow-score.mts' +import type { DedupedArtifact } from '../../../../src/commands/package/output-purls-shallow-score.mts' + describe('package score output', async () => { + describe('namespaced packages in shallow report purl', () => { + function makeArtifact( + overrides: Partial, + ): DedupedArtifact { + return { + ecosystem: 'npm', + namespace: '', + name: 'react', + version: '4.11.0', + score: { + supplyChain: 99, + maintenance: 95, + quality: 100, + vulnerability: 100, + license: 70, + }, + alerts: new Map(), + ...overrides, + } + } + + it('should include the npm namespace in the reported purl', () => { + const card = formatReportCard(makeArtifact({ namespace: '@axe-core' }), { + colorize: false, + }) + // Regression: the namespace was dropped, so the card reported + // `pkg:npm/react@4.11.0` instead of `pkg:npm/@axe-core/react@4.11.0`. + expect(card).toContain('Package: pkg:npm/@axe-core/react@4.11.0') + expect(card).not.toContain('Package: pkg:npm/react@4.11.0') + }) + + it('should omit the namespace segment when there is none', () => { + const card = formatReportCard(makeArtifact({ name: 'express' }), { + colorize: false, + }) + expect(card).toContain('Package: pkg:npm/express@4.11.0') + }) + }) + describe('npm', () => { it('should report shallow as text', () => { const { missing, rows } = preProcess(npmShallow.data, []) @@ -101,7 +143,7 @@ describe('package score output', async () => { reflect the scores of any dependencies, transitive or otherwise. - Package: pkg:golang/tlsproxy@v0.0.0-20250304082521-29051ed19c60 + Package: pkg:golang/github.com/steelpoor/tlsproxy@v0.0.0-20250304082521-29051ed19c60 - Supply Chain Risk:  39 - Maintenance: 100 @@ -126,7 +168,7 @@ describe('package score output', async () => { - ## Package: pkg:golang/tlsproxy@v0.0.0-20250304082521-29051ed19c60 + ## Package: pkg:golang/github.com/steelpoor/tlsproxy@v0.0.0-20250304082521-29051ed19c60 - Supply Chain Risk: 39 - Maintenance: 100 @@ -248,7 +290,7 @@ describe('package score output', async () => { reflect the scores of any dependencies, transitive or otherwise. - Package: pkg:maven/beam-runners-flink-1.15-job-server@2.58.0 + Package: pkg:maven/org.apache.beam/beam-runners-flink-1.15-job-server@2.58.0 - Supply Chain Risk:  67 - Maintenance: 100 @@ -273,7 +315,7 @@ describe('package score output', async () => { - ## Package: pkg:maven/beam-runners-flink-1.15-job-server@2.58.0 + ## Package: pkg:maven/org.apache.beam/beam-runners-flink-1.15-job-server@2.58.0 - Supply Chain Risk: 67 - Maintenance: 100