vulnzap-core
Version:
Secure AI-generated code by intercepting vulnerabilities in real-time
186 lines • 7.22 kB
JavaScript
import fs from 'fs/promises';
import path from 'path';
import CONFIG from './serviceConfig.js';
export default class NvdSource {
constructor(options = {}) {
this.isInitialized = false;
this.API_URL = options.apiUrl || CONFIG.SERVICE_ENDPOINTS.NVD_API || 'https://services.nvd.nist.gov/rest/json/cves/2.0';
this.CACHE_TTL = options.cacheTtl || 24 * 60 * 60; // 24 hours in seconds
this.cachePath = path.join(CONFIG.DATA_PATHS.CACHE_DIR, 'nvd-advisories');
}
async initialize() {
if (this.isInitialized)
return true;
try {
// Create cache directory if it doesn't exist
await fs.mkdir(this.cachePath, { recursive: true });
// Test the connection with a simple query
await this.makeApiRequest('?resultsPerPage=1');
this.isInitialized = true;
return true;
}
catch (error) {
console.error('Failed to initialize NVD source:', error);
return false;
}
}
/**
* Save data to cache file
* @private
*/
async _saveToCache(key, data, ttl = this.CACHE_TTL) {
try {
const cacheFile = path.join(this.cachePath, `${key.replace(/[^a-zA-Z0-9]/g, '_')}.json`);
const cacheData = {
data,
expires: Date.now() + (ttl * 1000),
created: Date.now()
};
await fs.writeFile(cacheFile, JSON.stringify(cacheData, null, 2));
}
catch (error) {
console.error('Error saving to NVD cache:', error);
}
}
/**
* Get data from cache
* @private
*/
async _getFromCache(key) {
try {
const cacheFile = path.join(this.cachePath, `${key.replace(/[^a-zA-Z0-9]/g, '_')}.json`);
const data = JSON.parse(await fs.readFile(cacheFile, 'utf8'));
// Check if cache has expired
if (data.expires < Date.now()) {
await fs.unlink(cacheFile);
return null;
}
return data.data;
}
catch (error) {
return null;
}
}
/**
* Clear cache for specific key or all cache
* @private
*/
async _clearCache(key) {
try {
if (key) {
const cacheFile = path.join(this.cachePath, `${key.replace(/[^a-zA-Z0-9]/g, '_')}.json`);
await fs.unlink(cacheFile).catch(() => { });
}
else {
const files = await fs.readdir(this.cachePath);
await Promise.all(files.map(file => fs.unlink(path.join(this.cachePath, file)).catch(err => console.error(`Failed to delete NVD cache file ${file}:`, err))));
}
}
catch (error) {
console.error('Error clearing NVD cache:', error);
}
}
async makeApiRequest(endpoint) {
try {
const response = await fetch(`${this.API_URL}${endpoint}`, {
headers: {
'apiKey': CONFIG.API_KEYS.NVD
}
});
if (!response.ok) {
throw new Error(`NVD API error: ${response.statusText}`);
}
return await response.json();
}
catch (error) {
console.error('Error making NVD API request:', error);
throw error;
}
}
determineSeverity(vuln) {
// Try CVSS v3.1 first
if (vuln.cve.metrics?.cvssMetricV31?.[0]?.cvssData?.baseSeverity) {
return vuln.cve.metrics.cvssMetricV31[0].cvssData.baseSeverity.toLowerCase();
}
// Then try CVSS v3.0
if (vuln.cve.metrics?.cvssMetricV30?.[0]?.cvssData?.baseSeverity) {
return vuln.cve.metrics.cvssMetricV30[0].cvssData.baseSeverity.toLowerCase();
}
// Finally try CVSS v2
if (vuln.cve.metrics?.cvssMetricV2?.[0]?.cvssData?.baseSeverity) {
return vuln.cve.metrics.cvssMetricV2[0].cvssData.baseSeverity.toLowerCase();
}
return 'medium';
}
processNvdResults(results, packageName, version, ecosystem) {
if (!results || results.length === 0) {
return {
isVulnerable: false,
message: 'No vulnerabilities found',
sources: ['nvd']
};
}
const advisories = results.map(vuln => ({
id: vuln.cve.id,
title: vuln.cve.descriptions.find(d => d.lang === 'en')?.value || 'Unknown vulnerability',
severity: this.determineSeverity(vuln),
cve_id: vuln.cve.id,
description: vuln.cve.descriptions.find(d => d.lang === 'en')?.value || 'No description available',
}));
// Extract fixed versions from the first vulnerability
const fixedVersions = [];
if (results[0].cve.configurations && results[0].cve.configurations.length > 0) {
results[0].cve.configurations.forEach(config => {
config.nodes.forEach(node => {
node.cpeMatch.forEach(match => {
if (match.versionEndExcluding) {
fixedVersions.push(match.versionEndExcluding);
}
if (match.versionEndIncluding) {
fixedVersions.push(match.versionEndIncluding);
}
});
});
});
}
return {
isVulnerable: true,
advisories,
fixedVersions: fixedVersions.length > 0 ? fixedVersions : undefined,
sources: ['nvd']
};
}
async findVulnerabilities(packageName, version, ecosystem, options = {}) {
try {
if (!this.isInitialized) {
await this.initialize();
}
const cacheKey = `nvd:${ecosystem}:${packageName}:${version}`;
if (!options.refresh) {
const cached = await this._getFromCache(cacheKey);
if (cached) {
return cached;
}
}
else {
// Clear existing cache if refresh is requested
await this._clearCache(cacheKey);
}
// Construct CPE string for the package
const cpeString = `cpe:2.3:a:${packageName}:${packageName}:${version}:*:*:*:*:*:*:*`;
const results = await this.makeApiRequest(`?cpeName=${encodeURIComponent(cpeString)}`);
const vulnerabilityResult = this.processNvdResults(results.vulnerabilities || [], packageName, version, ecosystem);
await this._saveToCache(cacheKey, vulnerabilityResult, this.CACHE_TTL);
return vulnerabilityResult;
}
catch (error) {
console.error(`Error finding NVD vulnerabilities for ${packageName}@${version}:`, error);
return {
isVulnerable: false,
error: `Error querying NVD API: ${error.message || 'Unknown error'}`,
sources: ['nvd']
};
}
}
}
//# sourceMappingURL=nvd-source.js.map