vibe-guard
Version:
🛡️ Vibe-Guard Security Scanner - 25 essential security rules to catch vulnerabilities before they catch you! Zero dependencies, instant setup, works everywhere, optimized performance. Detects SQL injection, XSS, exposed secrets, CSRF, CORS issues, and mo
234 lines • 8.65 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileScanner = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const glob_1 = require("glob");
class FileScanner {
constructor() {
this.supportedExtensions = [
'.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte',
'.py', '.php', '.rb', '.go', '.java', '.cs',
'.cpp', '.c', '.h', '.hpp', '.rs', '.kt',
'.swift', '.dart', '.scala', '.clj', '.hs',
'.json', '.yaml', '.yml', '.xml', '.env',
'.config', '.conf', '.ini', '.toml'
];
this.excludePatterns = [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'**/.git/**',
'**/coverage/**',
'**/*.min.js',
'**/*.bundle.js',
'**/vendor/**',
'**/__pycache__/**',
'**/*.pyc',
'**/target/**',
'**/bin/**',
'**/obj/**'
];
this.maxFileSize = 5 * 1024 * 1024;
this.binaryExtensions = [
'.exe', '.dll', '.so', '.dylib', '.bin', '.dat',
'.img', '.iso', '.dmg', '.pkg', '.deb', '.rpm',
'.zip', '.tar', '.gz', '.bz2', '.7z', '.rar',
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.ico',
'.mp3', '.mp4', '.avi', '.mov', '.wmv', '.flv',
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt',
'.sqlite', '.db', '.mdb', '.accdb',
'.ttf', '.otf', '.woff', '.woff2', '.eot',
'.class', '.jar', '.war', '.ear',
'.o', '.obj', '.lib', '.a'
];
}
async scanDirectory(targetPath, rules) {
const files = await this.findFiles(targetPath);
const issues = [];
let filesScanned = 0;
let filesSkipped = 0;
for (const filePath of files) {
try {
const fileContent = await this.readFile(filePath);
if (fileContent) {
filesScanned++;
for (const rule of rules) {
const ruleIssues = rule.check(fileContent);
issues.push(...ruleIssues);
}
}
else {
filesSkipped++;
}
}
catch (error) {
console.warn(`Warning: Could not scan file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
filesSkipped++;
}
}
if (filesSkipped > 0) {
console.log(`📋 Skipped ${filesSkipped} files (binary, too large, or unreadable)`);
}
return this.createScanResult(issues, filesScanned);
}
async scanFile(filePath, rules) {
const issues = [];
let filesScanned = 0;
console.log('DEBUG: scanFile called with', rules.length, 'rules');
console.log('DEBUG: Rule names:', rules.map(r => r.name));
try {
const fileContent = await this.readFile(filePath);
if (fileContent) {
filesScanned = 1;
console.log('DEBUG: File content read, length:', fileContent.content.length);
for (const rule of rules) {
console.log('DEBUG: Checking rule:', rule.name);
const ruleIssues = rule.check(fileContent);
console.log('DEBUG: Rule', rule.name, 'found', ruleIssues.length, 'issues');
issues.push(...ruleIssues);
}
}
else {
console.log(`📋 Skipped file: ${filePath} (binary, too large, or unreadable)`);
}
}
catch (error) {
throw new Error(`Could not scan file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
console.log('DEBUG: Total issues found:', issues.length);
return this.createScanResult(issues, filesScanned);
}
async findFiles(targetPath) {
const stats = await fs.promises.stat(targetPath);
if (stats.isFile()) {
return [targetPath];
}
if (!stats.isDirectory()) {
throw new Error(`Target path is neither a file nor a directory: ${targetPath}`);
}
const pattern = path.join(targetPath, '**/*');
const allFiles = glob_1.glob.sync(pattern, {
ignore: this.excludePatterns,
nodir: true,
absolute: true
});
return allFiles.filter((file) => this.isSupportedFile(file));
}
isSupportedFile(filePath) {
const ext = path.extname(filePath).toLowerCase();
if (this.binaryExtensions.includes(ext)) {
return false;
}
return this.supportedExtensions.includes(ext);
}
async readFile(filePath) {
try {
const stats = await fs.promises.stat(filePath);
if (stats.size > this.maxFileSize) {
console.warn(`Skipping large file: ${filePath} (${Math.round(stats.size / 1024 / 1024)}MB > 5MB limit)`);
return null;
}
if (await this.isBinaryFile(filePath)) {
return null;
}
const content = await fs.promises.readFile(filePath, 'utf-8');
const lines = content.split('\n');
return {
path: filePath,
content,
lines
};
}
catch (error) {
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
return null;
}
if (error instanceof Error && error.message.includes('invalid')) {
return null;
}
throw error;
}
}
async isBinaryFile(filePath) {
try {
const fd = await fs.promises.open(filePath, 'r');
const buffer = Buffer.alloc(512);
const { bytesRead } = await fd.read(buffer, 0, 512, 0);
await fd.close();
if (bytesRead === 0) {
return false;
}
for (let i = 0; i < bytesRead; i++) {
if (buffer[i] === 0) {
return true;
}
}
let nonPrintable = 0;
for (let i = 0; i < bytesRead; i++) {
const byte = buffer[i];
if (byte !== undefined) {
if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
nonPrintable++;
}
}
}
return (nonPrintable / bytesRead) > 0.3;
}
catch {
return false;
}
}
createScanResult(issues, filesScanned) {
const summary = {
critical: 0,
high: 0,
medium: 0,
low: 0
};
issues.forEach(issue => {
summary[issue.severity]++;
});
return {
issues,
filesScanned,
issuesFound: issues.length,
summary
};
}
}
exports.FileScanner = FileScanner;
//# sourceMappingURL=scanner.js.map