UNPKG

mcp-cve-intelligence-server-lite-test

Version:

Lite Model Context Protocol server for comprehensive CVE intelligence gathering with multi-source exploit discovery, designed for security professionals and cybersecurity researchers - Alpha Release

276 lines 10.3 kB
import { createContextLogger } from '../utils/logger.js'; import { getExploitIndicators } from '../utils/validation.js'; import { VERSION, PACKAGE_NAME } from '../version.js'; const logger = createContextLogger('SourceBase'); // Abstract base class for all CVE source implementations // // Custom Authentication: // To implement custom authentication (like MITRE's multi-header system): // 1. Override the getAuthHeaders() method in your source implementation // 2. Return appropriate headers based on your authentication scheme // 3. Configuration fields like authHeaderType and authHeaderName are not needed // when completely overriding the authentication method // // Standard Authentication Types (handled by base class): // - 'apiKey': Simple API key in custom header // - 'bearer': Bearer token in Authorization header // - 'token': Token with 'token' prefix in Authorization header // - 'none': No authentication // export class BaseCVESourceImplementation { // Standard User-Agent for all CVE sources static USER_AGENT = `${PACKAGE_NAME}/${VERSION}`; config; apiKey; constructor(config, apiKey) { this.config = config; this.apiKey = apiKey; } // Validation methods - can be overridden by concrete implementations /** * Validates the source configuration * Default implementation checks basic requirements */ validateConfig() { const errors = []; const warnings = []; // Basic validation that applies to all sources if (!this.config.baseUrl) { errors.push(`Source ${this.config.name}: missing baseUrl`); } if (!this.config.endpoints) { errors.push(`Source ${this.config.name}: missing endpoints`); } if (this.config.priority === undefined || this.config.priority < 0) { errors.push(`Source ${this.config.name}: invalid priority`); } // Check if enabled source has required endpoints if (this.config.enabled) { if (!this.config.endpoints?.search && !this.config.endpoints?.details) { warnings.push(`Source ${this.config.name}: enabled but missing both search and details endpoints`); } } // Allow concrete implementations to add their own validation const specificValidation = this.validateSourceSpecificConfig(); errors.push(...specificValidation.errors); warnings.push(...specificValidation.warnings); return { isValid: errors.length === 0, errors, warnings, }; } /** * Validates API key configuration for this source * Default implementation provides basic API key checks */ validateApiKey() { const errors = []; const warnings = []; // Check if API key is required but missing if (this.config.apiKeyRequired && !this.apiKey) { warnings.push(`Source ${this.config.name}: requires API key but none configured (will use rate-limited access)`); } // Allow concrete implementations to add their own API key validation const specificValidation = this.validateSourceSpecificApiKey(); errors.push(...specificValidation.errors); warnings.push(...specificValidation.warnings); return { isValid: errors.length === 0, errors, warnings, }; } /** * Override this method in concrete implementations for source-specific configuration validation */ validateSourceSpecificConfig() { return { isValid: true, errors: [], warnings: [] }; } /** * Override this method in concrete implementations for source-specific API key validation */ validateSourceSpecificApiKey() { return { isValid: true, errors: [], warnings: [] }; } /** * Gets the expected API key environment variable name for this source * Override in concrete implementations to specify the correct env var */ getApiKeyEnvironmentVariable() { return undefined; } /** * Gets alternative environment variable names that might contain the API key * Override in concrete implementations to specify alternative env vars */ getAlternativeApiKeyEnvironmentVariables() { return []; } /** * Determines if this source can use the provided API key * Override in concrete implementations for source-specific key validation */ canUseApiKey(apiKey) { return !!apiKey; } /** * Gets the source-specific identifier used for API key mapping * Override in concrete implementations to specify the correct identifier */ getApiKeyIdentifier() { return this.config.name.toLowerCase(); } /** * Determines if this source matches the given source name/identifier * Used for API key resolution from external mappings */ matchesSourceIdentifier(identifier) { const lowerIdentifier = identifier.toLowerCase(); const sourceName = this.config.name.toLowerCase(); // Default matching logic - override in concrete implementations for more specific matching return lowerIdentifier === sourceName || lowerIdentifier === this.getApiKeyIdentifier(); } /** * Performs comprehensive validation of the source */ validateFull() { const allErrors = []; const allWarnings = []; // Run all validation checks const configValidation = this.validateConfig(); const apiKeyValidation = this.validateApiKey(); allErrors.push(...configValidation.errors, ...apiKeyValidation.errors); allWarnings.push(...configValidation.warnings, ...apiKeyValidation.warnings); return { isValid: allErrors.length === 0, errors: allErrors, warnings: allWarnings, }; } // Default implementation for connection testing (can be overridden) async testConnection() { try { const response = await fetch(this.getBaseUrl(), { method: 'HEAD', signal: AbortSignal.timeout(5000), }); return response.ok; } catch { return false; } } // Helper methods available to all implementations getBaseUrl() { return this.config.baseUrl; } getTimeout() { return this.config.requestTimeout || 5000; } /** * Gets authentication headers for API requests. * Override this method in concrete implementations for custom authentication schemes. * * @returns Record of header name to header value */ getAuthHeaders() { const headers = {}; // No authentication required if (!this.config.authHeaderType || this.config.authHeaderType === 'none') { return headers; } // Standard single-header authentication types if (!this.apiKey) { logger.debug(`No API key available for ${this.config.name}`); return headers; } if (!this.config.authHeaderName) { const warnMsg = `Auth header name not specified for ${this.config.name} ` + `with auth type ${this.config.authHeaderType}`; logger.warn(warnMsg); return headers; } switch (this.config.authHeaderType) { case 'apiKey': headers[this.config.authHeaderName] = this.apiKey; break; case 'bearer': headers[this.config.authHeaderName] = `Bearer ${this.apiKey}`; break; case 'token': headers[this.config.authHeaderName] = `token ${this.apiKey}`; break; default: logger.warn(`Unknown auth header type: ${this.config.authHeaderType}`); } return headers; } /** * Public method to get authentication headers for external use (e.g., SourceManager) * This method delegates to the protected getAuthHeaders() method that can be overridden by subclasses * * @returns Record of header name to header value */ getRequestAuthHeaders() { return this.getAuthHeaders(); } /** * Gets the standard User-Agent string for all CVE sources */ getUserAgent() { return BaseCVESourceImplementation.USER_AGENT; } // Get source configuration getConfig() { return this.config; } // Configuration getters for convenience getName() { return this.config.name; } isEnabled() { return this.config.enabled; } getPriority() { return this.config.priority; } getFeatures() { return this.config.features || []; } // Check if source supports a specific feature supportsFeature(feature) { return this.config.features?.includes(feature) || false; } // Alias for supportsFeature for backward compatibility hasFeature(feature) { return this.supportsFeature(feature); } /** * Calculate exploit indicators for a CVE based on its references * This method should be called during CVE normalization */ calculateExploitIndicators(references) { if (!references || references.length === 0) { return { hasExploitIndicators: false, indicators: [], calculatedAt: new Date().toISOString(), }; } const indicators = []; const exploitIndicators = getExploitIndicators(); for (const ref of references) { for (const [source, pattern] of Object.entries(exploitIndicators)) { if (pattern.test(ref.url)) { indicators.push({ source, type: source === 'metasploit' ? 'metasploit' : 'exploit', url: ref.url, title: `${source} reference`, verified: false, }); } } } return { hasExploitIndicators: indicators.length > 0, indicators, calculatedAt: new Date().toISOString(), }; } } //# sourceMappingURL=base.js.map