vibe-secure 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHECKLIST.md ADDED
@@ -0,0 +1,12 @@
1
+ # Top 10 Dangerous Things to Check For
2
+
3
+ 1. **API keys in code** (like `const key = "sk_live_123"`)
4
+ 2. **Passwords hardcoded**
5
+ 3. **Database URLs exposed**
6
+ 4. **Unsafe API endpoints** (no authentication)
7
+ 5. **Missing input validation**
8
+ 6. **SQL injection risks**
9
+ 7. **Suspicious npm packages**
10
+ 8. **Missing error handling**
11
+ 9. **Exposed environment variables**
12
+ 10. **Insecure file uploads**
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ Vibe Secure
2
+ ===========
3
+
4
+ > The security scanner for AI-generated applications.
5
+
6
+ Vibe Secure is a lightweight, CLI-based security scanner designed for the "vibe coding" era. It catches common mistakes that AI coding assistants (like ChatGPT, Claude, or Cursor) might accidentally leave behind in your React, Node.js, and Next.js applications.
7
+
8
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
9
+
10
+ Features
11
+ --------
12
+
13
+ - Zero Config: Just run it. No complex setup files.
14
+ - Security Checks: Scans for common errors:
15
+ - Hardcoded Secrets: API keys (Stripe, AWS, OpenAI, etc.).
16
+ - Exposed Databases: Postgres, MongoDB, Redis connection strings.
17
+ - Unsafe Endpoints: Risky API routes (POST/DELETE) lacking authentication middleware.
18
+ - SQL Injection: Detects unsafe string concatenation in SQL queries.
19
+ - XSS Vulnerabilities: Detects dangerous React patterns.
20
+ - Clean Output: Readable CLI reports with clickable file links.
21
+
22
+ How it Works
23
+ ------------
24
+
25
+ Vibe Secure is a static analysis tool, not an AI model.
26
+
27
+ 1. Fast and Privacy-First: It runs entirely on your machine. No code leaves your computer.
28
+ 2. Pattern Matching: It uses Regular Expressions (Regex) and heuristic rules to find patterns that look dangerous.
29
+ 3. Deterministic: Unlike AI models which can hallucinate, this tool uses rigid pattern matching to identify specific security violations.
30
+
31
+ Usage
32
+ -----
33
+
34
+ You can run Vibe Secure directly in your project using npx:
35
+
36
+ ```bash
37
+ npx vibe-secure
38
+ ```
39
+
40
+ Or install it globally:
41
+
42
+ ```bash
43
+ npm install -g vibe-secure
44
+ vibe-secure
45
+ ```
46
+
47
+ Run on a specific folder:
48
+
49
+ ```bash
50
+ vibe-secure ./src/api
51
+ ```
52
+
53
+ What it Checks
54
+ --------------
55
+
56
+ | Check Type | Description |
57
+ | :--- | :--- |
58
+ | Secrets | Scans for sk_live_, AWS_ACCESS_KEY, and other credential patterns. |
59
+ | Databases | Detects hardcoded connection strings like postgres:// or mongodb+srv://. |
60
+ | Auth | Flags API routes like app.delete('/user/:id') that appear to have no middleware. |
61
+ | SQL Injection | Flags SELECT statements using string concatenation or template literals. |
62
+ | XSS | Flags dangerouslySetInnerHTML, javascript: links, and eval(). |
63
+
64
+ Development
65
+ -----------
66
+
67
+ 1. Clone the repo
68
+ 2. Install dependencies:
69
+ ```bash
70
+ npm install
71
+ ```
72
+ 3. Run the scanner on itself:
73
+ ```bash
74
+ node index.js
75
+ ```
76
+
77
+ License
78
+ -------
79
+
80
+ MIT.
package/index.js ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const chalk = require('chalk');
5
+ const figlet = require('figlet');
6
+ const { scanFolder } = require('./src/scanner');
7
+ const { checkForSecrets } = require('./src/checks/secrets');
8
+ const { checkForDatabaseUrls } = require('./src/checks/dbCheck');
9
+ const { checkForUnsafeEndpoints } = require('./src/checks/endpointCheck');
10
+ const { checkForSqlInjection } = require('./src/checks/sqlInjection.js');
11
+ const { checkForDangerousReact } = require('./src/checks/unsafeReact.js');
12
+ const fs = require('fs').promises;
13
+ const path = require('path');
14
+
15
+ const program = new Command();
16
+
17
+ console.log(chalk.cyan(figlet.textSync('Vibe Secure', { horizontalLayout: 'full' })));
18
+
19
+ program
20
+ .version('1.0.0')
21
+ .description('Security scanner for vibe-coded AI applications')
22
+ .argument('[directory]', 'Directory to scan', '.')
23
+ .action(async (directory) => {
24
+ const targetDir = path.resolve(directory);
25
+ console.log(chalk.blue(`\n🔍 Scanning directory: ${targetDir}\n`));
26
+
27
+ try {
28
+ const files = await scanFolder(targetDir);
29
+
30
+ if (files.length === 0) {
31
+ console.log(chalk.yellow('⚠️ No relevant implementation files found (.js, .jsx, .ts, .tsx).'));
32
+ return;
33
+ }
34
+
35
+ console.log(chalk.gray(`Found ${files.length} files to check...`));
36
+
37
+ let allIssues = [];
38
+
39
+ for (const file of files) {
40
+ // Skip scanning the checker files themselves to avoid false positives during dev
41
+ if (file.includes('src/checks/')) continue;
42
+
43
+ const content = await fs.readFile(file, 'utf8');
44
+ const relativePath = path.relative(process.cwd(), file);
45
+
46
+ const issues = [
47
+ ...checkForSecrets(content, relativePath),
48
+ ...checkForDatabaseUrls(content, relativePath),
49
+ ...checkForUnsafeEndpoints(content, relativePath),
50
+ ...checkForSqlInjection(content, relativePath),
51
+ ...checkForDangerousReact(content, relativePath)
52
+ ];
53
+
54
+ allIssues = [...allIssues, ...issues];
55
+ }
56
+
57
+ console.log(chalk.gray('────────────────────────────────────────'));
58
+
59
+ if (allIssues.length > 0) {
60
+ console.log(chalk.red(`\n❌ Scan failed! Found ${allIssues.length} issues:\n`));
61
+
62
+ allIssues.forEach(issue => {
63
+ const color = issue.type === 'Critical' ? chalk.red.bold : chalk.yellow.bold;
64
+
65
+ // Standard terminal format: /absolute/path/to/file:line:col
66
+ // VS Code and other terminals auto-link this pattern to open the file at the line
67
+ const absolutePath = path.resolve(process.cwd(), issue.file);
68
+
69
+ console.log(`${color(`[${issue.type}]`)} ${chalk.underline(`${absolutePath}:${issue.line}:1`)}`);
70
+ console.log(` ${chalk.yellow(issue.message)}`);
71
+ console.log(` ${chalk.gray('>')} ${chalk.cyan(issue.code.substring(0, 60))}${issue.code.length > 60 ? '...' : ''}\n`);
72
+ });
73
+
74
+ console.log(chalk.red(`\n💥 Fix these ${allIssues.length} issues before shipping!`));
75
+ process.exit(1);
76
+ } else {
77
+ console.log(chalk.green('\n✨ Vibes are pristine! No obvious security issues found.'));
78
+ console.log(chalk.green('🚀 Ready to ship!'));
79
+ }
80
+
81
+ } catch (error) {
82
+ console.error(chalk.red('\n❌ Error scanning folder:'), error.message);
83
+ process.exit(1);
84
+ }
85
+ });
86
+
87
+ program.parse(process.argv);
88
+
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "vibe-secure",
3
+ "version": "1.0.0",
4
+ "description": "The security scanner for AI-generated applications code",
5
+ "bin": {
6
+ "vibe-secure": "index.js"
7
+ },
8
+ "main": "index.js",
9
+ "scripts": {
10
+ "test": "node index.js"
11
+ },
12
+ "keywords": [
13
+ "security",
14
+ "scanner",
15
+ "ai",
16
+ "vibe-coding",
17
+ "cli"
18
+ ],
19
+ "author": "Vibe Secure Team",
20
+ "license": "MIT",
21
+ "type": "commonjs",
22
+ "dependencies": {
23
+ "chalk": "^4.1.2",
24
+ "commander": "^14.0.2",
25
+ "figlet": "^1.10.0"
26
+ }
27
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Checks for exposed database URLs
3
+ * @param {string} content - File content
4
+ * @param {string} filePath - Path to file
5
+ * @returns {Array} - Array of issues found
6
+ */
7
+ function checkForDatabaseUrls(content, filePath) {
8
+ const issues = [];
9
+ const lines = content.split('\n');
10
+
11
+ // postgres://user:password@localhost:5432/mydb
12
+ // mongodb+srv://user:password@cluster.mongodb.net
13
+ const dbPattern = /((postgres|mysql|mongodb|redis)(\+[a-z]+)?):\/\/[a-zA-Z0-9_]+:[^@]+@/i;
14
+
15
+ lines.forEach((line, index) => {
16
+ if (dbPattern.test(line) && !line.includes('process.env')) {
17
+ issues.push({
18
+ file: filePath,
19
+ line: index + 1,
20
+ type: 'Critical',
21
+ message: 'Exposed Database Connection String',
22
+ code: line.trim()
23
+ });
24
+ }
25
+ });
26
+
27
+ return issues;
28
+ }
29
+
30
+ module.exports = { checkForDatabaseUrls };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Checks for potentially unsafe API endpoints (no auth)
3
+ * @param {string} content - File content
4
+ * @param {string} filePath - Path to file
5
+ * @returns {Array} - Array of issues found
6
+ */
7
+ function checkForUnsafeEndpoints(content, filePath) {
8
+ const issues = [];
9
+ const lines = content.split('\n');
10
+
11
+ // Look for Express/Next.js route definitions
12
+ // app.get('/api/users', (req, res) => ...)
13
+ // export default function handler(req, res) ...
14
+
15
+ // This is a naive check: looks for route definitions that DON'T look like they use middleware
16
+ // Or simply flagging all routes for manual review (which might be too noisy)
17
+
18
+ // Better approach for "vibe coded" apps: Look for 'delete' or 'update' routes without some 'auth' keyword nearby
19
+
20
+ lines.forEach((line, index) => {
21
+ // Check for dangerous operations
22
+ if ((line.includes('app.delete') || line.includes('app.post') || line.includes('app.put')) &&
23
+ !line.includes('auth') &&
24
+ !line.includes('middleware') &&
25
+ !line.includes('verify')) {
26
+
27
+ // Look ahead a few lines for auth checks? (Start simple)
28
+ issues.push({
29
+ file: filePath,
30
+ line: index + 1,
31
+ type: 'Warning',
32
+ message: 'Potential Unsafe Endpoint (Mutation without Auth)',
33
+ code: line.trim()
34
+ });
35
+ }
36
+ });
37
+
38
+ return issues;
39
+ }
40
+
41
+ module.exports = { checkForUnsafeEndpoints };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Checks for common hardcoded secrets and patterns
3
+ * @param {string} content - File content
4
+ * @param {string} filePath - Path to file
5
+ * @returns {Array} - Array of issues found
6
+ */
7
+ function checkForSecrets(content, filePath) {
8
+ const issues = [];
9
+ const lines = content.split('\n');
10
+
11
+ const patterns = [
12
+ { name: 'Stripe Secret Key', regex: /sk_live_[a-zA-Z0-9]{24}/ },
13
+ { name: 'Generic API Key', regex: /api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9_\-]{20,}['"]/i },
14
+ { name: 'Hardcoded Password', regex: /password\s*[:=]\s*['"][^'"]{6,}['"]/i },
15
+ { name: 'AWS Access Key', regex: /AKI[A-Z0-9]{17}/ },
16
+ { name: 'Private Key Block', regex: /-----BEGIN PRIVATE KEY-----/ }
17
+ ];
18
+
19
+ lines.forEach((line, index) => {
20
+ // Basic check for very long strings that look like keys (entropy check heuristic simplified)
21
+ // Only check lines that look like assignments
22
+
23
+ for (const pattern of patterns) {
24
+ if (pattern.regex.test(line)) {
25
+ issues.push({
26
+ file: filePath,
27
+ line: index + 1,
28
+ type: 'Critical',
29
+ message: `Possible ${pattern.name} found`,
30
+ code: line.trim()
31
+ });
32
+ }
33
+ }
34
+ });
35
+
36
+ return issues;
37
+ }
38
+
39
+ module.exports = { checkForSecrets };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Checks for potential SQL injection vulnerabilities
3
+ * @param {string} content - File content
4
+ * @param {string} filePath - Path to file
5
+ * @returns {Array} - Array of issues found
6
+ */
7
+ function checkForSqlInjection(content, filePath) {
8
+ const issues = [];
9
+ const lines = content.split('\n');
10
+
11
+ // Regex to look for:
12
+ // 1. Strings starting with SELECT/INSERT/UPDATE/DELETE (case insensitive)
13
+ // 2. That contain variable interpolation ${...} or string concatenation ' + '
14
+
15
+ // Heuristic:
16
+ // const query = `SELECT * FROM users WHERE id = ${id}`; <-- BAD
17
+ // db.query('SELECT * FROM users WHERE id = ' + id); <-- BAD
18
+
19
+ lines.forEach((line, index) => {
20
+ // Check for template literal interpolation in SQL strings
21
+ if (/\`\s*(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER)\s+.*?\$\{.*?\}/i.test(line)) {
22
+ issues.push({
23
+ file: filePath,
24
+ line: index + 1,
25
+ type: 'Critical',
26
+ message: 'Potential SQL Injection (Template Literal)',
27
+ code: line.trim()
28
+ });
29
+ }
30
+
31
+ // Check for string concatenation in SQL strings (simple check)
32
+ // Matches: 'SELECT ... ' + variable
33
+ if (/['"]\s*(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER)\s+.*?['"]\s*\+/i.test(line) ||
34
+ /\+\s*['"]\s*(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER)\s+.*?['"]/i.test(line)) {
35
+ issues.push({
36
+ file: filePath,
37
+ line: index + 1,
38
+ type: 'Critical',
39
+ message: 'Potential SQL Injection (String Concatenation)',
40
+ code: line.trim()
41
+ });
42
+ }
43
+ });
44
+
45
+ return issues;
46
+ }
47
+
48
+ module.exports = { checkForSqlInjection };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Checks for dangerous React patterns (XSS risks)
3
+ * @param {string} content - File content
4
+ * @param {string} filePath - Path to file
5
+ * @returns {Array} - Array of issues found
6
+ */
7
+ function checkForDangerousReact(content, filePath) {
8
+ const issues = [];
9
+ const lines = content.split('\n');
10
+
11
+ lines.forEach((line, index) => {
12
+ // 1. dangerouslySetInnerHTML
13
+ if (line.includes('dangerouslySetInnerHTML')) {
14
+ issues.push({
15
+ file: filePath,
16
+ line: index + 1,
17
+ type: 'Warning',
18
+ message: 'Dangerous React Pattern (XSS Risk)',
19
+ code: line.trim()
20
+ });
21
+ }
22
+
23
+ // 2. javascript: URIs in href or src
24
+ if (/['"]javascript:/i.test(line)) {
25
+ issues.push({
26
+ file: filePath,
27
+ line: index + 1,
28
+ type: 'Critical',
29
+ message: 'Inline JavaScript URI (XSS Risk)',
30
+ code: line.trim()
31
+ });
32
+ }
33
+
34
+ // 3. eval()
35
+ if (/\beval\s*\(/.test(line)) {
36
+ issues.push({
37
+ file: filePath,
38
+ line: index + 1,
39
+ type: 'Critical',
40
+ message: 'Use of eval() detected',
41
+ code: line.trim()
42
+ });
43
+ }
44
+ });
45
+
46
+ return issues;
47
+ }
48
+
49
+ module.exports = { checkForDangerousReact };
package/src/scanner.js ADDED
@@ -0,0 +1,26 @@
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+
4
+ async function scanFolder(dir, fileList = []) {
5
+ const files = await fs.readdir(dir);
6
+
7
+ for (const file of files) {
8
+ const filePath = path.join(dir, file);
9
+ const stat = await fs.stat(filePath);
10
+
11
+ if (stat.isDirectory()) {
12
+ if (file !== 'node_modules' && file !== '.git') {
13
+ await scanFolder(filePath, fileList);
14
+ }
15
+ } else {
16
+ const ext = path.extname(file).toLowerCase();
17
+ if (['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
18
+ fileList.push(filePath);
19
+ }
20
+ }
21
+ }
22
+
23
+ return fileList;
24
+ }
25
+
26
+ module.exports = { scanFolder };