UNPKG

supamend

Version:

Pluggable DevSecOps Security Scanner with 10+ scanners and multiple reporting channels

100 lines 4.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SafetyScanner = void 0; const child_process_1 = require("child_process"); class SafetyScanner { constructor() { this.name = 'safety'; this.description = 'Scan Python dependencies for security vulnerabilities using Safety'; this.version = '1.0.0'; } async init(config) { // Check if safety is available const isAvailable = await this.isAvailable(); if (!isAvailable) { throw new Error('Safety is not installed. Please install it with: pip install safety'); } } async scan(repoPath, options) { return new Promise((resolve, reject) => { const results = []; // Prepare safety command const args = ['check', '--json']; // Add custom database if provided if (options?.db) { args.push('--db', options.db); } const safety = (0, child_process_1.spawn)('safety', args, { cwd: repoPath }); let stdout = ''; let stderr = ''; safety.stdout.on('data', (data) => { stdout += data.toString(); }); safety.stderr.on('data', (data) => { stderr += data.toString(); }); safety.on('close', (code) => { try { // Safety returns 0 for no issues, 1 when vulnerabilities are found if (code === 0 || code === 1) { const vulns = JSON.parse(stdout || '[]'); for (const vuln of vulns) { const result = { id: `${this.name}-${vuln.package}-${vuln.installed_version}`, type: 'vulnerability', severity: this.mapSeverity(vuln.severity), title: `Safety: ${vuln.package} vulnerability`, description: vuln.description || `Vulnerability in ${vuln.package}`, file: 'requirements.txt', line: 0, column: 0, rule: vuln.vulnerability_id, scanner: this.name, timestamp: new Date(), metadata: { package: vuln.package, installedVersion: vuln.installed_version, affectedVersions: vuln.affected, fixedVersions: vuln.fixed_versions, advisory: vuln.advisory, cve: vuln.vulnerability_id } }; results.push(result); } } resolve(results); } catch (error) { reject(new Error(`Failed to parse safety results: ${error}`)); } }); safety.on('error', (error) => { reject(new Error(`Safety execution failed: ${error.message}`)); }); }); } async isAvailable() { return new Promise((resolve) => { const safety = (0, child_process_1.spawn)('safety', ['--version']); safety.on('close', (code) => { resolve(code === 0); }); safety.on('error', () => { resolve(false); }); }); } mapSeverity(severity) { switch (severity.toLowerCase()) { case 'critical': return 'critical'; case 'high': return 'high'; case 'medium': return 'medium'; case 'low': return 'low'; default: return 'low'; } } } exports.SafetyScanner = SafetyScanner; exports.default = new SafetyScanner(); //# sourceMappingURL=safety.js.map