carrot-scan
Version:
Command-line tool for detecting vulnerabilities in files and directories.
65 lines (58 loc) • 2.45 kB
JavaScript
import fs from 'node:fs/promises';
import path from 'node:path';
import { Plugin } from '../src/plugin-interface.js';
const BAD_PATTERNS = [
{ pattern: /eval\s*\(/gi, message: 'Use of eval() detected' },
{ pattern: /exec\s*(?:Sync)?\s*\(/gi, message: 'Use of exec() detected' },
{ pattern: /system\s*\(/gi, message: 'Use of system() detected' },
{ pattern: /include\s*\(/gi, message: 'Use of include() detected' },
{ pattern: /require\s*\(/gi, message: 'Use of require() detected' },
{ pattern: /mysqli_query\s*\(/gi, message: 'Use of mysqli_query() detected' },
{ pattern: /\$_(GET|POST|REQUEST)\b/gi, message: 'Use of superglobal GET/POST/REQUEST detected' },
{ pattern: /\bSELECT\s+.*\bFROM\b.*\bWHERE\b/gi, message: 'SQL SELECT statement detected' },
{ pattern: /base64_decode\s*\(/gi, message: 'Use of base64_decode() detected' },
{ pattern: /new Function\s*\(/gi, message: 'Use of new Function() detected' },
{ pattern: /shell_exec\s*\(/gi, message: 'Use of shell_exec() detected' },
{ pattern: /popen\s*\(/gi, message: 'Use of popen() detected' },
{ pattern: /passthru\s*\(/gi, message: 'Use of passthru() detected' },
{ pattern: /proc_open\s*\(/gi, message: 'Use of proc_open() detected' },
{ pattern: /pcntl_exec\s*\(/gi, message: 'Use of pcntl_exec() detected' },
{ pattern: /preg_replace\s*\(.*\/e/gi, message: 'Use of preg_replace() with /e modifier detected' },
];
/**
* Counts the number of matches for all BAD_PATTERNS in the given text.
* @param {string} text
* @returns {number}
*/
function heuristicBadness(text) {
if (typeof text !== 'string' || !text) return 0;
return BAD_PATTERNS.reduce((count, { pattern }) => {
const matches = text.match(pattern);
return count + (matches ? matches.length : 0);
}, 0);
}
export class HeuristicPlugin extends Plugin {
static pluginName = 'heuristic';
static applies() {
return true;
}
async run(filePath, { content }) {
const messages = [];
const lines = content.split('\n');
for (const { pattern, message } of BAD_PATTERNS) {
for (let i = 0; i < lines.length; i++) {
if (pattern.test(lines[i])) {
messages.push({
pluginName: this.constructor.pluginName,
filePath,
line: i + 1,
column: 0,
severity: 'warning',
message: `${message} in line ${i + 1}`,
});
}
}
}
return messages;
}
}