pact-audit-free
Version:
Free smart contract security scanner using Slither static analysis
183 lines • 6.31 kB
JavaScript
;
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