UNPKG

vibe-guard

Version:

██ Vibe-Guard Security Scanner - 28 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, contain

239 lines 8.62 kB
"use strict"; 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 for scanning the files for security issues 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' ]; // The patterns to exclude from the scan this.excludePatterns = [ '**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '**/coverage/**', '**/*.min.js', '**/*.bundle.js', '**/vendor/**', '**/__pycache__/**', '**/*.pyc', '**/target/**', '**/bin/**', '**/obj/**' ]; // The maximum file size to scan this.maxFileSize = 5 * 1024 * 1024; // The binary extensions to exclude 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' ]; } // Scans a directory for security issues 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); } // Scans a file for security issues async scanFile(filePath, rules) { const issues = []; let filesScanned = 0; try { const fileContent = await this.readFile(filePath); if (fileContent) { filesScanned = 1; for (const rule of rules) { const ruleIssues = rule.check(fileContent); 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'}`); } return this.createScanResult(issues, filesScanned); } // Finds the files to scan 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)); } // Checks if the file is supported isSupportedFile(filePath) { const ext = path.extname(filePath).toLowerCase(); if (this.binaryExtensions.includes(ext)) { return false; } return this.supportedExtensions.includes(ext); } // Reads the file content 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; } } // Checks if the file is a binary file 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; } } // Creates the scan result 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