UNPKG

snyk-mvn-plugin

Version:
221 lines 9.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getMavenRepositoryPath = getMavenRepositoryPath; exports.dependencyIdToArtifactPath = dependencyIdToArtifactPath; exports.generateFingerprints = generateFingerprints; exports.reportFingerprintTiming = reportFingerprintTiming; exports.generateMavenFingerprints = generateMavenFingerprints; exports.createMavenPurlWithChecksum = createMavenPurlWithChecksum; const crypto = require("crypto"); const fs = require("fs"); const path = require("path"); const os = require("os"); const perf_hooks_1 = require("perf_hooks"); const util_1 = require("util"); const child_process_1 = require("child_process"); const packageurl_js_1 = require("packageurl-js"); const dependency_1 = require("./parse/dependency"); const index_1 = require("./index"); const execAsync = (0, util_1.promisify)(child_process_1.exec); /** * Get the Maven local repository path * Uses the same Maven command that's being used for dependency resolution */ async function getMavenRepositoryPath(mavenCommand, providedPath) { if (providedPath) { return providedPath; } try { // Use the same Maven command that's being used for dependency resolution const { stdout } = await execAsync(`${mavenCommand} help:evaluate -Dexpression=settings.localRepository -DforceStdout -q`); const repoPath = stdout.trim(); if (repoPath && fs.existsSync(repoPath)) { return repoPath; } } catch { // Fall back to default if Maven command fails } // Default Maven repository locations const homeDir = os.homedir(); const defaultPath = path.join(homeDir, '.m2', 'repository'); return defaultPath; } /** * Convert dependency ID to the expected artifact file path in Maven repository */ function dependencyIdToArtifactPath(dependencyId, repositoryPath) { const dep = (0, dependency_1.parseDependency)(dependencyId); // Convert groupId dots to path separators const groupPath = dep.groupId.replace(/\./g, path.sep); // Build the artifact filename // Format: artifactId-version[-classifier].type const classifierPart = dep.classifier ? `-${dep.classifier}` : ''; const artifactFileName = `${dep.artifactId}-${dep.version}${classifierPart}.${dep.type}`; // Construct full path const artifactPath = path.join(repositoryPath, groupPath, dep.artifactId, dep.version, artifactFileName); return artifactPath; } /** * Calculate fingerprint for a single file */ async function calculateFileFingerprint(filePath, algorithm) { const hash = crypto.createHash(algorithm); const stream = fs.createReadStream(filePath); return new Promise((resolve, reject) => { stream.on('data', (data) => hash.update(data)); stream.on('end', () => resolve(hash.digest('hex'))); stream.on('error', reject); }); } /** * Check if artifact file exists in the repository */ async function checkArtifactExists(artifactPath) { try { const stats = await fs.promises.stat(artifactPath); return { exists: true, size: stats.size }; } catch { return { exists: false }; } } /** * Sanitize artifact path to show only the relative path within the repository */ function sanitizeArtifactPath(artifactPath, repositoryPath) { if (artifactPath.startsWith(repositoryPath)) { // Remove the repository path prefix and any leading path separator return artifactPath.slice(repositoryPath.length).replace(/^[/\\]+/, ''); } // Fallback to the full path if sanitization isn't possible return artifactPath; } /** * Process a single dependency ID to generate fingerprint */ async function processSingleDependency(dependencyId, repositoryPath, algorithm) { const startTime = perf_hooks_1.performance.now(); try { const artifactPath = dependencyIdToArtifactPath(dependencyId, repositoryPath); const { exists, size } = await checkArtifactExists(artifactPath); // Sanitize the artifact path to show only the relative path within the repository const sanitizedPath = sanitizeArtifactPath(artifactPath, repositoryPath); if (!exists) { const endTime = perf_hooks_1.performance.now(); return { hash: '', algorithm, filePath: sanitizedPath, fileSize: 0, processingTime: endTime - startTime, error: 'Artifact not found in repository', }; } const hash = await calculateFileFingerprint(artifactPath, algorithm); const endTime = perf_hooks_1.performance.now(); return { hash, algorithm, filePath: sanitizedPath, fileSize: size || 0, processingTime: endTime - startTime, }; } catch (error) { const endTime = perf_hooks_1.performance.now(); const artifactPath = dependencyIdToArtifactPath(dependencyId, repositoryPath); const sanitizedPath = sanitizeArtifactPath(artifactPath, repositoryPath); return { hash: '', algorithm, filePath: sanitizedPath, fileSize: 0, processingTime: endTime - startTime, error: error instanceof Error ? error.message : 'Unknown error', }; } } /** * Process multiple dependency IDs with concurrency control */ async function processDependenciesConcurrently(dependencyIds, repositoryPath, algorithm, concurrency = 5) { const results = []; // Process dependencies in batches to control concurrency for (let i = 0; i < dependencyIds.length; i += concurrency) { const batch = dependencyIds.slice(i, i + concurrency); const batchPromises = batch.map((id) => processSingleDependency(id, repositoryPath, algorithm)); const batchResults = await Promise.all(batchPromises); results.push(...batchResults); } return results; } /** * Generate fingerprints for all unique dependencies in Maven graphs */ async function generateFingerprints(mavenGraphs, options, mavenCommand) { const fingerprintMap = new Map(); // Extract all unique node IDs from all graphs const allNodeIds = new Set(); for (const graph of mavenGraphs) { Object.keys(graph.nodes).forEach((nodeId) => allNodeIds.add(nodeId)); } // Get Maven repository path using the same command const repositoryPath = await getMavenRepositoryPath(mavenCommand, options.mavenRepository); // Process all unique dependencies with concurrency control const nodeIdArray = Array.from(allNodeIds); const results = await processDependenciesConcurrently(nodeIdArray, repositoryPath, options.algorithm, 5); // Build the map for (let i = 0; i < nodeIdArray.length; i++) { fingerprintMap.set(nodeIdArray[i], results[i]); } return fingerprintMap; } function reportFingerprintTiming(fingerprintMap) { const results = Array.from(fingerprintMap.values()); // Don't report timing if there are no results if (results.length === 0) { return; } const processingTimes = results.map((r) => r.processingTime); const totalTime = processingTimes.reduce((sum, time) => sum + time, 0); const successful = results.filter((r) => !r.error).length; const failed = results.filter((r) => r.error).length; (0, index_1.debug)('=== Fingerprint Timing Summary ==='); (0, index_1.debug)(`Total artifacts: ${results.length}`); (0, index_1.debug)(`Successful: ${successful}`); (0, index_1.debug)(`Failed: ${failed}`); (0, index_1.debug)(`Total time: ${totalTime.toFixed(2)}ms`); (0, index_1.debug)(`Average time per artifact: ${(totalTime / results.length).toFixed(2)}ms`); (0, index_1.debug)(`Fastest: ${Math.min(...processingTimes).toFixed(2)}ms`); (0, index_1.debug)(`Slowest: ${Math.max(...processingTimes).toFixed(2)}ms`); (0, index_1.debug)('====================================='); } /** * Generate Maven fingerprints with reporting */ async function generateMavenFingerprints(mavenGraphs, fingerprintOptions, mavenCommand) { const fingerprintMap = await generateFingerprints(mavenGraphs, fingerprintOptions, mavenCommand); reportFingerprintTiming(fingerprintMap); return fingerprintMap; } /** * Create a Maven PURL with checksum qualifier from dependency information and fingerprint data */ function createMavenPurlWithChecksum(groupId, artifactId, version, fingerprintData, classifier, type) { const standardQualifiers = {}; if (classifier) { standardQualifiers.classifier = classifier; } if (type && type !== 'jar') { standardQualifiers.type = type; } // Add checksum qualifier to standard qualifiers if (fingerprintData && !fingerprintData.error && fingerprintData.hash) { const checksumValue = `${fingerprintData.algorithm.toLowerCase()}:${fingerprintData.hash.toLowerCase()}`; standardQualifiers.checksum = checksumValue; } const purl = new packageurl_js_1.PackageURL('maven', groupId, artifactId, version, Object.keys(standardQualifiers).length > 0 ? standardQualifiers : undefined, undefined); return purl.toString(); } //# sourceMappingURL=fingerprint.js.map