UNPKG

ubon

Version:

Security scanner for AI-generated apps (Cursor, Lovable, Windsurf, v0). Catches hardcoded secrets, prompt injection, hallucinated imports, Server Actions / Edge runtime mistakes, and the vibe-coded vulnerabilities traditional linters miss.

105 lines 4.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ViteScanner = void 0; const glob_1 = require("glob"); const fs_1 = require("fs"); const path_1 = require("path"); const rules_1 = require("../rules"); class ViteScanner { name = 'Vite Security Scanner'; async scan(options) { const results = []; // Check if this is a Vite project const viteConfigExists = (0, fs_1.existsSync)((0, path_1.join)(options.directory, 'vite.config.ts')) || (0, fs_1.existsSync)((0, path_1.join)(options.directory, 'vite.config.js')); const files = await (0, glob_1.glob)('**/*.{js,jsx,ts,tsx,svelte}', { cwd: options.directory, ignore: ['node_modules/**', 'dist/**', 'build/**', '.next/**', 'examples/**', 'vite.config.*'] }); // If no vite config, check if any files use Vite features if (!viteConfigExists) { let hasViteUsage = false; for (const file of files) { try { const filePath = `${options.directory}/${file}`; const content = (0, fs_1.readFileSync)(filePath, 'utf-8'); if (/import\.meta\.(env|glob|hot)|import\s*\(/i.test(content)) { hasViteUsage = true; break; } } catch { } } if (!hasViteUsage) { return results; // Skip if not a Vite project } } // Vite-specific rules const viteRuleIds = [ 'VITE001', // Client-Side Environment Variable Exposure 'VITE002', // Development-Only Code 'VITE003' // Unsafe Dynamic Imports ]; for (const file of files) { try { const filePath = `${options.directory}/${file}`; const content = (0, fs_1.readFileSync)(filePath, 'utf-8'); const lines = content.split('\n'); // Check if file uses Vite-specific features const hasViteUsage = /import\.meta\.(env|glob|hot)|import\s*\(/i.test(content); // If no vite.config, only scan files with Vite usage if (!viteConfigExists && !hasViteUsage) { continue; } // Skip non-Vite files in node_modules if (!hasViteUsage && file.includes('node_modules')) { continue; } // Apply each Vite rule for (const ruleId of viteRuleIds) { const rule = (0, rules_1.getRule)(ruleId); if (!rule || !rule.impl.detect) continue; // Check file type restrictions if (rule.impl.fileTypes) { const fileExt = file.split('.').pop() || ''; if (!rule.impl.fileTypes.includes(fileExt)) continue; } // Run detection const detections = rule.impl.detect(content, file, lines); // Convert detections to ScanResults for (const detection of detections) { const line = lines[detection.line - 1] || ''; results.push({ type: rule.meta.severity === 'high' ? 'error' : 'warning', category: rule.meta.category, message: rule.meta.message, file, line: detection.line, range: { startLine: detection.line, startColumn: 1, endLine: detection.line, endColumn: Math.max(1, line.length) }, severity: rule.meta.severity, ruleId: rule.meta.id, confidence: detection.confidence || 0.85, match: detection.match, fix: rule.meta.fix, helpUri: rule.meta.helpUri, ...(detection.fixEdits ? { fixEdits: detection.fixEdits } : {}) }); } } } catch (error) { // Skip files that can't be read } } return results; } } exports.ViteScanner = ViteScanner; //# sourceMappingURL=vite-scanner.js.map