supamend
Version:
Pluggable DevSecOps Security Scanner with 10+ scanners and multiple reporting channels
100 lines • 4.14 kB
JavaScript
;
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