UNPKG

pact-audit-free

Version:

Free smart contract security scanner using Slither static analysis

183 lines 6.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkSlitherInstallation = checkSlitherInstallation; exports.validatePath = validatePath; exports.runSlitherScan = runSlitherScan; const child_process_1 = require("child_process"); const fs_1 = require("fs"); const path_1 = require("path"); async function checkSlitherInstallation() { return new Promise((resolve) => { const slither = (0, child_process_1.spawn)('slither', ['--version'], { stdio: 'pipe', }); slither.on('close', (code) => { resolve(code === 0); }); slither.on('error', () => { resolve(false); }); }); } function validatePath(targetPath) { if (!(0, fs_1.existsSync)(targetPath)) { return { valid: false, error: `Path does not exist: ${targetPath}` }; } const stats = (0, fs_1.statSync)(targetPath); if (stats.isFile()) { if ((0, path_1.extname)(targetPath) !== '.sol') { return { valid: false, error: 'File must have .sol extension' }; } } else if (stats.isDirectory()) { const hasSolFiles = checkForSolidityFiles(targetPath); if (!hasSolFiles) { return { valid: false, error: 'Directory does not contain any .sol files' }; } } return { valid: true }; } function checkForSolidityFiles(dirPath) { try { const files = (0, fs_1.readdirSync)(dirPath, { withFileTypes: true }); for (const file of files) { if (file.isFile() && file.name.endsWith('.sol')) { return true; } if (file.isDirectory()) { const subDirPath = (0, path_1.join)(dirPath, file.name); if (checkForSolidityFiles(subDirPath)) { return true; } } } return false; } catch { return false; } } async function runSlitherScan(targetPath) { return new Promise((resolve) => { const slither = (0, child_process_1.spawn)('slither', [targetPath, '--json', '-'], { stdio: ['pipe', 'pipe', 'pipe'], }); let stdout = ''; let stderr = ''; slither.stdout?.on('data', (data) => { stdout += data.toString(); }); slither.stderr?.on('data', (data) => { stderr += data.toString(); }); slither.on('close', (code) => { if (code !== 0 && !stdout) { resolve({ success: false, vulnerabilities: [], error: `Slither failed with exit code ${code}: ${stderr}`, }); return; } try { const vulnerabilities = parseSlitherOutput(stdout); resolve({ success: true, vulnerabilities, rawOutput: stdout, }); } catch (error) { resolve({ success: false, vulnerabilities: [], error: `Failed to parse Slither output: ${error}`, rawOutput: stdout, }); } }); slither.on('error', (error) => { resolve({ success: false, vulnerabilities: [], error: `Failed to execute Slither: ${error.message}`, }); }); }); } function parseSlitherOutput(output) { if (!output.trim()) { return []; } try { const jsonOutput = JSON.parse(output); const vulnerabilities = []; if (jsonOutput.results && jsonOutput.results.detectors) { for (const detector of jsonOutput.results.detectors) { const vulnerability = { type: detector.check || 'Unknown', severity: mapSeverity(detector.impact), description: detector.description || detector.markdown || 'No description available', confidence: detector.confidence, impact: detector.impact, }; // Extract source mapping if available if (detector.elements && detector.elements.length > 0) { const element = detector.elements[0]; if (element.source_mapping) { vulnerability.sourceMapping = { filename: element.source_mapping.filename_relative || element.source_mapping.filename || 'Unknown file', lines: element.source_mapping.lines || [], }; } } vulnerabilities.push(vulnerability); } } return vulnerabilities; } catch (error) { // Fallback: try to parse line-by-line for older Slither versions return parseSlitherOutputFallback(output); } } function parseSlitherOutputFallback(output) { const lines = output.split('\n'); const vulnerabilities = []; for (const line of lines) { if (line.includes('INFO:') || line.includes('LOW:') || line.includes('MEDIUM:') || line.includes('HIGH:')) { const severity = extractSeverityFromLine(line); const description = line.replace(/^.*?(INFO:|LOW:|MEDIUM:|HIGH:)/, '').trim(); if (description) { vulnerabilities.push({ type: 'General Finding', severity, description, }); } } } return vulnerabilities; } function mapSeverity(impact) { if (!impact) return 'INFORMATIONAL'; const impactLower = impact.toLowerCase(); if (impactLower === 'high') return 'HIGH'; if (impactLower === 'medium') return 'MEDIUM'; if (impactLower === 'low') return 'LOW'; return 'INFORMATIONAL'; } function extractSeverityFromLine(line) { if (line.includes('HIGH:')) return 'HIGH'; if (line.includes('MEDIUM:')) return 'MEDIUM'; if (line.includes('LOW:')) return 'LOW'; return 'INFORMATIONAL'; } //# sourceMappingURL=slither.js.map