Assessor_2.0/tests/server/nvd-cve-client.test.ts

67 lines
3.6 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { NvdCveClient, NvdCveClientError, parseNvdCveResponse } from '../../server/cve/nvd-cve-client';
const payload = {
version: '2.0',
vulnerabilities: [{ cve: {
id: 'CVE-2012-3037', sourceIdentifier: 'nvd@nist.gov', published: '2012-08-25T10:55:01.000',
lastModified: '2025-04-11T00:51:21.963', vulnStatus: 'Deferred',
descriptions: [{ lang: 'en', value: 'Synthetic vulnerability description.' }],
metrics: {
cvssMetricV31: [{ source: 'nvd@nist.gov', type: 'Primary', exploitabilityScore: 3.9, impactScore: 5.9,
cvssData: { version: '3.1', vectorString: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H', baseScore: 9.8, baseSeverity: 'CRITICAL' } }],
cvssMetricV2: [{ source: 'nvd@nist.gov', type: 'Primary', baseSeverity: 'MEDIUM', exploitabilityScore: 8.6, impactScore: 2.9,
cvssData: { version: '2.0', vectorString: 'AV:N/AC:M/Au:N/C:P/I:N/A:N', baseScore: 4.3 } }]
},
weaknesses: [{ source: 'nvd@nist.gov', type: 'Primary', description: [{ lang: 'en', value: 'CWE-119' }] }],
references: [{ url: 'https://vendor.example/advisory', source: 'nvd@nist.gov', tags: ['Vendor Advisory', 'Patch'] }],
configurations: [{ operator: 'AND', nodes: [
{ cpeMatch: [{
vulnerable: true, criteria: 'cpe:2.3:a:vendor:product:*:*:*:*:*:*:*:*', versionEndExcluding: '2.0'
}] },
{ cpeMatch: [{ vulnerable: false, criteria: 'cpe:2.3:h:vendor:device:*:*:*:*:*:*:*:*' }] }
] }],
cisaRequiredAction: 'Apply vendor guidance.'
} }]
};
describe('NVD CVE enrichment', () => {
it('normalizes the bounded NVD 2.0 response into shared CVE knowledge', () => {
const record = parseNvdCveResponse(payload, 'CVE-2012-3037', '2026-09-10T08:00:00.000Z');
expect(record).toMatchObject({
cveId: 'CVE-2012-3037', source: 'nvd', description: 'Synthetic vulnerability description.',
metrics: [
{ version: '3.1', baseScore: 9.8, baseSeverity: 'CRITICAL' },
{ version: '2.0', baseScore: 4.3, baseSeverity: 'MEDIUM' }
],
weaknesses: [{ id: 'CWE-119' }],
references: [{ url: 'https://vendor.example/advisory', tags: ['Vendor Advisory', 'Patch'] }],
affectedConfigurations: [{
operator: 'AND',
vulnerableMatches: [{ criteria: 'cpe:2.3:a:vendor:product:*:*:*:*:*:*:*:*', versionEndExcluding: '2.0' }],
runningOnWith: [{ criteria: 'cpe:2.3:h:vendor:device:*:*:*:*:*:*:*:*' }]
}],
attributes: { cisaRequiredAction: 'Apply vendor guidance.' }
});
});
it('calls only the fixed NVD endpoint with an encoded exact CVE and optional API key', async () => {
const fetcher = vi.fn<typeof fetch>().mockResolvedValue(new Response(JSON.stringify(payload), {
status: 200, headers: { 'Content-Type': 'application/json' }
}));
const client = new NvdCveClient({ fetcher, apiKey: 'test-key', now: () => new Date('2026-09-10T08:00:00.000Z') });
await expect(client.fetchCve('cve-2012-3037')).resolves.toMatchObject({ cveId: 'CVE-2012-3037' });
const url = fetcher.mock.calls[0][0] as URL;
expect(url.origin + url.pathname).toBe('https://services.nvd.nist.gov/rest/json/cves/2.0');
expect(url.searchParams.get('cveId')).toBe('CVE-2012-3037');
expect(new Headers(fetcher.mock.calls[0][1]?.headers).get('apiKey')).toBe('test-key');
});
it('rejects invalid identifiers before making a network request', async () => {
const fetcher = vi.fn<typeof fetch>();
const client = new NvdCveClient({ fetcher });
await expect(client.fetchCve('not-a-cve')).rejects.toMatchObject({ code: 'invalid_cve_id' } satisfies Partial<NvdCveClientError>);
expect(fetcher).not.toHaveBeenCalled();
});
});