UNPKG

dependency-guardian

Version:

A powerful dependency management and analysis tool for Node.js projects

163 lines (144 loc) 4.9 kB
const fs = require('fs').promises; const path = require('path'); const logger = require('../utils/logger'); const cache = require('../utils/cache'); class PolicyChecker { constructor() { this.cache = cache; this.defaultPolicyPath = path.join(process.cwd(), 'policies', 'default.policy.json'); } async loadPolicy(policyPath = this.defaultPolicyPath) { const cacheKey = `policy-${policyPath}`; const cached = this.cache.get(cacheKey); if (cached) { return cached; } try { const content = await fs.readFile(policyPath, 'utf8'); const policy = JSON.parse(content); this.cache.set(cacheKey, policy); return policy; } catch (error) { logger.debug(`Failed to load policy from ${policyPath}:`, error); return this.getDefaultPolicy(); } } getDefaultPolicy() { return { dependencies: { maxAge: 365, // days allowedUpdateTypes: ['patch', 'minor'], blockedPackages: [], allowedPackages: [] }, security: { maxVulnerabilityLevel: 'moderate', requireAudit: true, ignoreVulnerabilities: [] }, licenses: { allowed: ['MIT', 'ISC', 'Apache-2.0', 'BSD-3-Clause'], blocked: ['GPL', 'AGPL'], unknown: 'warn' } }; } async checkDependency(dependency, policy = null) { if (!policy) { policy = await this.loadPolicy(); } const issues = []; // Check if package is blocked if (policy.dependencies.blockedPackages.includes(dependency.name)) { issues.push({ type: 'policy', level: 'high', message: `Package ${dependency.name} is blocked by policy` }); } // Check update type against policy if (dependency.updateType && !policy.dependencies.allowedUpdateTypes.includes(dependency.updateType)) { issues.push({ type: 'updates', level: 'warning', message: `Update type ${dependency.updateType} is not allowed for ${dependency.name}` }); } // Check license against policy if (dependency.license) { if (policy.licenses.blocked.includes(dependency.license)) { issues.push({ type: 'license', level: 'high', message: `License ${dependency.license} is blocked for ${dependency.name}` }); } else if (!policy.licenses.allowed.includes(dependency.license)) { issues.push({ type: 'license', level: policy.licenses.unknown === 'warn' ? 'warning' : 'high', message: `License ${dependency.license} is not allowed for ${dependency.name}` }); } } // Check vulnerability level against policy if (dependency.vulnLevel && this.getVulnerabilityScore(dependency.vulnLevel) > this.getVulnerabilityScore(policy.security.maxVulnerabilityLevel)) { issues.push({ type: 'security', level: 'high', message: `Vulnerability level ${dependency.vulnLevel} exceeds maximum allowed level for ${dependency.name}` }); } return issues; } getVulnerabilityScore(level) { const scores = { 'none': 0, 'low': 1, 'moderate': 2, 'high': 3, 'critical': 4 }; return scores[level.toLowerCase()] || 0; } async validatePolicy(policy) { const errors = []; const warnings = []; // Validate dependencies section if (!policy.dependencies) { errors.push('Missing dependencies section in policy'); } else { if (!Array.isArray(policy.dependencies.allowedUpdateTypes)) { errors.push('allowedUpdateTypes must be an array'); } if (!Array.isArray(policy.dependencies.blockedPackages)) { errors.push('blockedPackages must be an array'); } } // Validate security section if (!policy.security) { errors.push('Missing security section in policy'); } else { if (!['none', 'low', 'moderate', 'high', 'critical'].includes(policy.security.maxVulnerabilityLevel)) { errors.push('Invalid maxVulnerabilityLevel value'); } } // Validate licenses section if (!policy.licenses) { errors.push('Missing licenses section in policy'); } else { if (!Array.isArray(policy.licenses.allowed)) { errors.push('allowed licenses must be an array'); } if (!Array.isArray(policy.licenses.blocked)) { errors.push('blocked licenses must be an array'); } if (!['warn', 'block'].includes(policy.licenses.unknown)) { warnings.push('unknown license handling should be either "warn" or "block"'); } } return { errors, warnings }; } } module.exports = new PolicyChecker();