scanpack
Version:
Dependency scanner to detect unknown or malicious packages in Node.js and Bun projects
74 lines • 3.46 kB
JavaScript
export class ValidateDependenciesUseCase {
npmRegistry;
maliciousPackageRepository;
constructor(npmRegistry, maliciousPackageRepository) {
this.npmRegistry = npmRegistry;
this.maliciousPackageRepository = maliciousPackageRepository;
}
async execute(dependencies, options) {
const results = [];
// Filter ignored dependencies
const ignoreSet = new Set(options?.ignore?.map(name => name.toLowerCase()) || []);
const filteredDependencies = dependencies.filter(dep => !ignoreSet.has(dep.name.toLowerCase()));
// Dynamic batch size based on latency
let batchSize = options?.batchSize || 10;
const minBatchSize = 5;
const maxBatchSize = 50;
for (let i = 0; i < filteredDependencies.length; i += batchSize) {
const batchStart = Date.now();
const batch = filteredDependencies.slice(i, i + batchSize);
const batchResults = await Promise.all(batch.map(dep => this.validateDependency(dep)));
results.push(...batchResults);
// Calculate latency and adjust batch size
const batchLatency = Date.now() - batchStart;
// Adjust batch size: if fast, increase; if slow, decrease
if (batchLatency < 500 && batchSize < maxBatchSize) {
batchSize = Math.min(batchSize + 5, maxBatchSize);
}
else if (batchLatency > 2000 && batchSize > minBatchSize) {
batchSize = Math.max(batchSize - 5, minBatchSize);
}
// Progress callback
if (options?.onProgress) {
options.onProgress(results.length, filteredDependencies.length);
}
}
const validDependencies = results.filter(r => r.isValid).length;
const invalidDependencies = results.filter(r => !r.isValid).length;
const maliciousDependencies = results.filter(r => r.isKnownMalicious || r.isSecurityHolding).length;
const unknownDependencies = results.filter(r => !r.existsOnNpm && !r.isKnownMalicious && !r.isSecurityHolding).length;
return {
totalDependencies: dependencies.length,
validDependencies,
invalidDependencies,
maliciousDependencies,
unknownDependencies,
results
};
}
async validateDependency(dependency) {
const npmCheck = await this.npmRegistry.checkPackage(dependency.name);
const maliciousCheck = this.maliciousPackageRepository.isKnownMalicious(dependency.name);
const isSecurityHolding = npmCheck.isSecurityHolding || false;
const isMalicious = maliciousCheck.isMalicious || isSecurityHolding;
const isValid = npmCheck.exists && !isMalicious;
const isUnknown = !npmCheck.exists && !isMalicious;
let reason = maliciousCheck.reason;
if (isSecurityHolding && !maliciousCheck.isMalicious) {
reason = 'Security holding package - original package was removed by npm for security reasons';
}
else if (isUnknown) {
reason = 'Package not found on npm';
}
return {
dependency,
isValid,
existsOnNpm: npmCheck.exists,
isKnownMalicious: isMalicious,
isSecurityHolding,
reason,
npmUrl: npmCheck.url
};
}
}
//# sourceMappingURL=validate-dependencies.use-case.js.map