From caee40d77f9839f897c31d28827d14bdb96b7bbe Mon Sep 17 00:00:00 2001 From: Mouad BANI Date: Fri, 26 Jun 2026 11:49:41 +0100 Subject: [PATCH 1/2] feat: enable nuget in osv worker Signed-off-by: Mouad BANI --- backend/.env.dist.local | 2 +- services/apps/packages_worker/src/osv/schedule.ts | 2 +- services/apps/packages_worker/src/osv/versionCompare.ts | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/.env.dist.local b/backend/.env.dist.local index 18c65f92ef..bcba7bc5d3 100755 --- a/backend/.env.dist.local +++ b/backend/.env.dist.local @@ -195,7 +195,7 @@ OSSPCKGS_GCP_CREDENTIALS_B64=e30= # maven/all.zip 404s). The allowlist check and DB storage normalize to lowercase # internally per ADR-0001 §OSV "Ecosystem normalization", so downstream stays lowercase. OSV_BULK_BASE_URL=https://osv-vulnerabilities.storage.googleapis.com -OSV_ECOSYSTEMS=npm,Maven,cargo +OSV_ECOSYSTEMS=npm,Maven,cargo,NuGet OSV_TMP_DIR=/tmp/osv OSV_BATCH_SIZE=500 OSV_DERIVE_BATCH_SIZE=1000 diff --git a/services/apps/packages_worker/src/osv/schedule.ts b/services/apps/packages_worker/src/osv/schedule.ts index 87ced0ed80..0e61307551 100644 --- a/services/apps/packages_worker/src/osv/schedule.ts +++ b/services/apps/packages_worker/src/osv/schedule.ts @@ -11,7 +11,7 @@ const SCHEDULE_ID = 'osv-advisories-sync' // validate the env input against this list and refuse to register the // schedule on a mismatch — better a loud startup error than a silent miss. // Add new entries here when v1 expands beyond npm + Maven. -const VALID_ECOSYSTEMS = ['npm', 'Maven', 'cargo'] as const +const VALID_ECOSYSTEMS = ['npm', 'Maven', 'cargo', 'NuGet'] as const function getEcosystems(): string[] { const raw = process.env.OSV_ECOSYSTEMS diff --git a/services/apps/packages_worker/src/osv/versionCompare.ts b/services/apps/packages_worker/src/osv/versionCompare.ts index 4202efd87a..1fa47c9fd5 100644 --- a/services/apps/packages_worker/src/osv/versionCompare.ts +++ b/services/apps/packages_worker/src/osv/versionCompare.ts @@ -126,11 +126,13 @@ function compareMaven(a: string, b: string): number | null { return 0 } +const SEMVER_ECOSYSTEMS = new Set(['npm', 'cargo', 'nuget']) + // Ecosystem names are stored lowercase in packages-db per ADR-0001 §OSV // "Ecosystem normalization" — 'npm', 'maven', 'cargo'. Callers (deriveCriticalFlag) // pull the value straight from the DB so the literals here must match. export function compareVersion(ecosystem: string, a: string, b: string): number | null { - if (ecosystem === 'npm' || ecosystem === 'cargo') return compareSemver(a, b) + if (SEMVER_ECOSYSTEMS.has(ecosystem)) return compareSemver(a, b) if (ecosystem === 'maven') return compareMaven(a, b) return null } From 4dcc816b7fc6526c971a3a1856fc3862d8f8b45d Mon Sep 17 00:00:00 2001 From: Mouad BANI Date: Fri, 26 Jun 2026 11:58:05 +0100 Subject: [PATCH 2/2] chore: add nuget test cases Signed-off-by: Mouad BANI --- .../src/osv/__tests__/versionCompare.test.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/services/apps/packages_worker/src/osv/__tests__/versionCompare.test.ts b/services/apps/packages_worker/src/osv/__tests__/versionCompare.test.ts index 7836906173..1c114294f8 100644 --- a/services/apps/packages_worker/src/osv/__tests__/versionCompare.test.ts +++ b/services/apps/packages_worker/src/osv/__tests__/versionCompare.test.ts @@ -83,6 +83,34 @@ describe('compareVersion — maven (ComparableVersion-style)', () => { }) }) +describe('compareVersion — nuget (semver)', () => { + it.each([ + ['1.0.0', '2.0.0', -1], + ['2.0.0', '1.0.0', 1], + ['1.0.0', '1.0.0', 0], + ['1.10.0', '1.9.0', 1], // numeric, not lex + ['1.0.0-alpha', '1.0.0', -1], // prerelease < release + ['1.0.0-beta', '1.0.0-rc', -1], + // Real-world CVE boundary: Newtonsoft.Json < 13.0.1 deserialization vuln + ['13.0.0', '13.0.1', -1], + ['13.0.1', '13.0.1', 0], + ['13.0.2', '13.0.1', 1], + ])('compareVersion("nuget", %s, %s) sign = %s', (a, b, expected) => { + expect(sign(compareVersion('nuget', a, b))).toBe(expected) + }) + + it('returns null for unparseable nuget versions', () => { + expect(compareVersion('nuget', 'not-a-version', '1.0.0')).toBeNull() + }) + + it('rejects titlecase "NuGet" — production storage is always lowercase', () => { + // OSV records arrive with ecosystem="NuGet"; parseOsvRecord lowercases to + // "nuget" before writing to packages-db. The comparator is keyed on the + // same lowercase form. A titlecase call means the caller skipped normalization. + expect(compareVersion('NuGet', '1.0.0', '2.0.0')).toBeNull() + }) +}) + describe('compareVersion — unsupported ecosystems', () => { it('returns null for ecosystems we have no comparator for', () => { expect(compareVersion('PyPI', '1.0.0', '2.0.0')).toBeNull()