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
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 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