kafkacode 1.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.0.0] - 2024-09-22
6
+
7
+ ### Added
8
+ - Initial release of KafkaCode Privacy Scanner
9
+ - Pattern-based detection for secrets, API keys, and sensitive data
10
+ - AI-powered contextual analysis using Grok LLM
11
+ - Support for multiple programming languages (Python, JavaScript, TypeScript, Java, Go, Ruby, PHP)
12
+ - Beautiful console reporting with severity levels
13
+ - Privacy grading system (A+ to F)
14
+ - CLI interface with scan command
15
+ - API key obfuscation for commercial distribution
16
+ - Gitignore pattern support
17
+ - Comprehensive test suite
18
+
19
+ ### Features
20
+ - Detects AWS keys, Stripe keys, private keys
21
+ - Identifies PII like emails, phone numbers, IP addresses
22
+ - High entropy string detection for potential secrets
23
+ - Context-aware privacy vulnerability analysis
24
+ - Detailed suggestions for remediation
25
+ - Verbose logging option
26
+ - Exit codes for CI/CD integration
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 KafkaLabs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # KafkaCode Privacy Scanner
2
+
3
+ <div align="center">
4
+ <h3>by <a href="https://kafkalabs.com">KafkaLabs</a></h3>
5
+ <p>šŸ” <strong>Shift-left privacy and compliance scanner for source code</strong></p>
6
+ <p>
7
+ <a href="https://kafkalabs.com/kafka-code">Website</a> •
8
+ <a href="https://github.com/nikhil-kapu/KafkacodeFnpm">GitHub</a> •
9
+ <a href="https://www.npmjs.com/package/kafkacode">npm</a>
10
+ </p>
11
+ </div>
12
+
13
+ ---
14
+
15
+ KafkaCode is an AI-powered privacy scanner by **KafkaLabs** that helps developers identify potential privacy issues, PII leaks, and compliance violations in their source code before they reach production.
16
+
17
+ ## Features
18
+
19
+ - šŸ” **Pattern-based Detection**: Identifies hardcoded secrets, API keys, and sensitive data
20
+ - šŸ¤– **AI-powered Analysis**: Uses advanced LLM analysis for contextual privacy issues
21
+ - ⚔ **Fast & Efficient**: Scans entire codebases in seconds
22
+ - šŸŽÆ **Multiple File Types**: Supports Python, JavaScript, TypeScript, Java, Go, Ruby, PHP
23
+ - šŸ“Š **Detailed Reports**: Beautiful console reports with severity levels
24
+ - šŸš€ **CI/CD Ready**: Easy integration with build pipelines
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install -g kafkacode
30
+ ```
31
+
32
+ Or using npx (no installation required):
33
+ ```bash
34
+ npx kafkacode scan /path/to/your/project
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ **Basic Scan:**
40
+ ```bash
41
+ kafkacode scan /path/to/your/project
42
+ ```
43
+
44
+ **Verbose Output:**
45
+ ```bash
46
+ kafkacode scan /path/to/your/project --verbose
47
+ ```
48
+
49
+ ## What it detects
50
+
51
+ - **Critical Issues**: AWS keys, Stripe keys, Private keys
52
+ - **High Severity**: Sensitive keywords in assignment context
53
+ - **Medium Severity**: Email addresses, Phone numbers, High entropy strings
54
+ - **Low Severity**: IP addresses, URLs
55
+
56
+ ## Privacy Grade
57
+
58
+ KafkaCode assigns a privacy grade (A+ to F) based on the severity and number of issues found:
59
+
60
+ - **A+/A/A-**: Excellent privacy practices
61
+ - **B+/B/B-**: Good privacy practices with minor issues
62
+ - **C+/C/C-**: Moderate privacy issues that should be addressed
63
+ - **D**: Multiple high-severity privacy issues
64
+ - **F**: Critical privacy vulnerabilities detected
65
+
66
+ ## Example Output
67
+
68
+ ```
69
+ šŸŽÆ PRIVACY SCAN REPORT
70
+ ═══════════════════════════════════════
71
+
72
+ šŸ“Š SCAN SUMMARY
73
+ šŸ“ Directory: ./src
74
+ ā° Timestamp: 2024-01-15 10:30:45
75
+ šŸ“„ Files Scanned: 25
76
+ šŸ” Total Issues: 3
77
+ šŸ† Privacy Grade: 🟔B-
78
+ ```
79
+
80
+ ## License
81
+
82
+ MIT License - Copyright (c) 2025 KafkaLabs
83
+
84
+ See [LICENSE](LICENSE) file for details.
85
+
86
+ ## About KafkaLabs
87
+
88
+ KafkaCode is built by [KafkaLabs](https://kafkalabs.com), helping developers build privacy-first applications.
89
+
90
+ - 🌐 **Website**: [kafkalabs.com/kafka-code](https://kafkalabs.com/kafka-code)
91
+ - šŸ“§ **Contact**: contact@kafkalabs.com
92
+ - šŸ’¬ **Issues**: [GitHub Issues](https://github.com/nikhil-kapu/KafkacodeFnpm/issues)
93
+
94
+ ---
95
+
96
+ <div align="center">
97
+ Made with ā¤ļø by <a href="https://kafkalabs.com">KafkaLabs</a>
98
+ </div>
@@ -0,0 +1,53 @@
1
+ const fs = require('fs');
2
+ const PatternScanner = require('./PatternScanner');
3
+ const LLMAnalyzer = require('./LLMAnalyzer');
4
+
5
+ class AnalysisEngine {
6
+ constructor(verbose = false) {
7
+ this.verbose = verbose;
8
+ this.patternScanner = new PatternScanner();
9
+ this.llmAnalyzer = new LLMAnalyzer();
10
+ this.llmAnalyzer.verbose = verbose;
11
+ }
12
+
13
+ async analyzeFile(filePath) {
14
+ if (this.verbose) {
15
+ console.log(`Analyzing: ${filePath}`);
16
+ }
17
+
18
+ let content;
19
+ try {
20
+ content = fs.readFileSync(filePath, 'utf-8');
21
+ } catch (error) {
22
+ if (this.verbose) {
23
+ console.log(`Error reading ${filePath}: ${error.message}`);
24
+ }
25
+ return [];
26
+ }
27
+
28
+ const findings = [];
29
+
30
+ // Pattern-based analysis first
31
+ const patternFindings = this.patternScanner.scanContent(filePath, content);
32
+ findings.push(...patternFindings);
33
+
34
+ // LLM-based analysis with pattern findings as context
35
+ const llmFindings = await this.llmAnalyzer.analyzeFile(filePath, content, patternFindings);
36
+ findings.push(...llmFindings);
37
+
38
+ return findings;
39
+ }
40
+
41
+ async analyzeFiles(filePaths) {
42
+ const allFindings = [];
43
+
44
+ for (const filePath of filePaths) {
45
+ const fileFindings = await this.analyzeFile(filePath);
46
+ allFindings.push(...fileFindings);
47
+ }
48
+
49
+ return allFindings;
50
+ }
51
+ }
52
+
53
+ module.exports = AnalysisEngine;
@@ -0,0 +1,96 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { minimatch } = require('minimatch');
4
+
5
+ class FileScanner {
6
+ constructor(rootDir) {
7
+ this.rootDir = path.resolve(rootDir);
8
+ this.supportedExtensions = new Set(['.py', '.js', '.ts', '.java', '.go', '.rb', '.php']);
9
+ this.ignoreDirs = new Set([
10
+ '.git', 'node_modules', 'venv', '__pycache__', '.venv', 'env',
11
+ 'build', 'dist', 'target', 'out', '.next', '.nuxt', 'vendor',
12
+ 'coverage', '.coverage', '.pytest_cache', '.mypy_cache'
13
+ ]);
14
+ this.gitignorePatterns = this._loadGitignore();
15
+ }
16
+
17
+ _loadGitignore() {
18
+ const gitignorePath = path.join(this.rootDir, '.gitignore');
19
+ const patterns = [];
20
+
21
+ if (fs.existsSync(gitignorePath)) {
22
+ try {
23
+ const content = fs.readFileSync(gitignorePath, 'utf-8');
24
+ const lines = content.split('\n');
25
+
26
+ for (const line of lines) {
27
+ const trimmed = line.trim();
28
+ if (trimmed && !trimmed.startsWith('#')) {
29
+ patterns.push(trimmed);
30
+ }
31
+ }
32
+ } catch (error) {
33
+ // Ignore errors loading gitignore
34
+ }
35
+ }
36
+
37
+ return patterns;
38
+ }
39
+
40
+ _shouldIgnorePath(filePath) {
41
+ const relativePath = path.relative(this.rootDir, filePath);
42
+ const pathParts = relativePath.split(path.sep);
43
+
44
+ // Check built-in ignore directories
45
+ for (const part of pathParts) {
46
+ if (this.ignoreDirs.has(part)) {
47
+ return true;
48
+ }
49
+ }
50
+
51
+ // Check gitignore patterns
52
+ for (const pattern of this.gitignorePatterns) {
53
+ if (minimatch(relativePath, pattern) || minimatch(path.basename(filePath), pattern)) {
54
+ return true;
55
+ }
56
+ }
57
+
58
+ return false;
59
+ }
60
+
61
+ _scanDirectory(dir) {
62
+ const files = [];
63
+
64
+ try {
65
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
66
+
67
+ for (const entry of entries) {
68
+ const fullPath = path.join(dir, entry.name);
69
+
70
+ if (this._shouldIgnorePath(fullPath)) {
71
+ continue;
72
+ }
73
+
74
+ if (entry.isDirectory()) {
75
+ files.push(...this._scanDirectory(fullPath));
76
+ } else if (entry.isFile()) {
77
+ const ext = path.extname(entry.name);
78
+ if (this.supportedExtensions.has(ext)) {
79
+ files.push(fullPath);
80
+ }
81
+ }
82
+ }
83
+ } catch (error) {
84
+ // Ignore directory access errors
85
+ }
86
+
87
+ return files;
88
+ }
89
+
90
+ scanFiles() {
91
+ const files = this._scanDirectory(this.rootDir);
92
+ return files.sort();
93
+ }
94
+ }
95
+
96
+ module.exports = FileScanner;
@@ -0,0 +1,286 @@
1
+ const https = require('https');
2
+
3
+ class LLMAnalyzer {
4
+ constructor() {
5
+ this.backendEndpoint = process.env.KAFKACODE_BACKEND_ENDPOINT || 'https://adorable-motivation-production.up.railway.app';
6
+ this.verbose = false;
7
+ this.interestKeywords = new Set([
8
+ 'api', 'db', 'database', 'user', 'password', 'save', 'fetch', 'send', 'log',
9
+ 'auth', 'token', 'key', 'secret', 'credential', 'email', 'phone', 'address',
10
+ 'personal', 'sensitive', 'private', 'encrypt', 'decrypt', 'hash'
11
+ ]);
12
+ }
13
+
14
+ _createSnippetPrompt(codeSnippet, filePath, startLine) {
15
+ return `SYSTEM: You are an automated privacy and compliance analysis engine. Your task is to review the following CODE SNIPPET and identify potential privacy vulnerabilities based ONLY on the provided code. The snippet is from a larger file. Do not infer functionality outside of this snippet. Your analysis must focus on how the code handles data that could be considered sensitive or PII.
16
+
17
+ Your response MUST be a single, valid JSON object. The root object should contain a single key: "vulnerabilities". The value of "vulnerabilities" must be an array of objects. Each object in the array represents a single, distinct vulnerability and must have the following keys:
18
+ - "line_number": The integer line number where the vulnerability is found. This number MUST be relative to the original file, not the snippet.
19
+ - "severity": A string, which must be one of "High", "Medium", or "Low".
20
+ - "description": A concise, one-sentence string describing the vulnerability (e.g., "User email is logged to a publicly accessible file.").
21
+ - "suggestion": A one-sentence string providing an actionable recommendation for the developer (e.g., "Consider logging only non-sensitive user identifiers or hashing the data before logging.").
22
+
23
+ If you find zero vulnerabilities, you MUST return an empty array: {"vulnerabilities": []}.
24
+
25
+ USER: Analyze the following code snippet from the file '${filePath}'. The snippet starts at line ${startLine}:
26
+
27
+ ${codeSnippet}`;
28
+ }
29
+
30
+ _identifyAreasOfInterest(content, patternFindings) {
31
+ const lines = content.split('\n');
32
+ const areas = new Set();
33
+
34
+ // Add lines from pattern findings
35
+ for (const finding of patternFindings) {
36
+ const lineNum = finding.line_number || 1;
37
+ areas.add(lineNum);
38
+ }
39
+
40
+ // Look for function/method definitions and lines with interest keywords
41
+ for (let i = 0; i < lines.length; i++) {
42
+ const line = lines[i];
43
+ const lineLower = line.toLowerCase();
44
+
45
+ // Function/method definitions
46
+ if (/^\s*(def\s+|function\s+|class\s+|\w+\s*\([^)]*\)\s*{)/.test(line.trim())) {
47
+ areas.add(i + 1);
48
+ }
49
+
50
+ // Lines containing interest keywords
51
+ if (Array.from(this.interestKeywords).some(keyword => lineLower.includes(keyword))) {
52
+ areas.add(i + 1);
53
+ }
54
+ }
55
+
56
+ // Convert to ranges with context
57
+ const ranges = [];
58
+ const sortedAreas = Array.from(areas).sort((a, b) => a - b);
59
+
60
+ for (const lineNum of sortedAreas) {
61
+ const start = Math.max(1, lineNum - 10);
62
+ const end = Math.min(lines.length, lineNum + 10);
63
+ ranges.push([start, end]);
64
+ }
65
+
66
+ // Merge overlapping ranges
67
+ const mergedRanges = [];
68
+ for (const [start, end] of ranges) {
69
+ if (mergedRanges.length > 0 && start <= mergedRanges[mergedRanges.length - 1][1] + 10) {
70
+ // Extend previous range
71
+ const lastRange = mergedRanges[mergedRanges.length - 1];
72
+ mergedRanges[mergedRanges.length - 1] = [lastRange[0], Math.max(lastRange[1], end)];
73
+ } else {
74
+ mergedRanges.push([start, end]);
75
+ }
76
+ }
77
+
78
+ return mergedRanges;
79
+ }
80
+
81
+ async callGrokApi(codeSnippet, filePath, startLine) {
82
+ try {
83
+ return await this._callBackendApi(codeSnippet, filePath, startLine);
84
+ } catch (error) {
85
+ if (this.verbose) {
86
+ console.log(` āŒ LLM call failed, using mock: ${error.message}`);
87
+ }
88
+ return this._mockSnippetResponse(codeSnippet, filePath, startLine);
89
+ }
90
+ }
91
+
92
+ async _callBackendApi(codeSnippet, filePath, startLine) {
93
+ const payload = JSON.stringify({
94
+ codeSnippet,
95
+ filePath,
96
+ startLine
97
+ });
98
+
99
+ const url = new URL(this.backendEndpoint);
100
+ url.pathname = '/api/analyze';
101
+
102
+ const options = {
103
+ hostname: url.hostname,
104
+ port: url.port || 443,
105
+ path: url.pathname,
106
+ method: 'POST',
107
+ headers: {
108
+ 'Content-Type': 'application/json',
109
+ 'Content-Length': Buffer.byteLength(payload)
110
+ },
111
+ timeout: 12000 // 12 second timeout for CLI request
112
+ };
113
+
114
+ return new Promise((resolve, reject) => {
115
+ const req = https.request(options, (res) => {
116
+ let data = '';
117
+
118
+ res.on('data', (chunk) => {
119
+ data += chunk;
120
+ });
121
+
122
+ res.on('end', () => {
123
+ try {
124
+ if (res.statusCode === 429) {
125
+ const errorData = JSON.parse(data);
126
+ throw new Error(`Rate limit exceeded: ${errorData.message}`);
127
+ }
128
+
129
+ if (res.statusCode !== 200) {
130
+ throw new Error(`HTTP ${res.statusCode}: ${data}`);
131
+ }
132
+
133
+ const result = JSON.parse(data);
134
+ const content = result.data.choices[0].message.content;
135
+
136
+ try {
137
+ const parsedResponse = JSON.parse(content);
138
+ if (this.verbose) {
139
+ console.log(` āœ… LLM returned ${parsedResponse.vulnerabilities?.length || 0} vulnerabilities`);
140
+ if (result.rateLimitRemaining !== undefined) {
141
+ console.log(` šŸ“Š Rate limit remaining: ${result.rateLimitRemaining}`);
142
+ }
143
+ }
144
+ parsedResponse.__source = 'llm';
145
+ resolve(parsedResponse);
146
+ } catch (jsonError) {
147
+ const jsonStart = content.indexOf('{');
148
+ const jsonEnd = content.lastIndexOf('}') + 1;
149
+ if (jsonStart !== -1 && jsonEnd > jsonStart) {
150
+ const parsed = JSON.parse(content.substring(jsonStart, jsonEnd));
151
+ if (this.verbose) {
152
+ console.log(` āœ… LLM returned ${parsed.vulnerabilities?.length || 0} vulnerabilities (extracted JSON)`);
153
+ }
154
+ parsed.__source = 'llm';
155
+ resolve(parsed);
156
+ } else {
157
+ resolve({ vulnerabilities: [] });
158
+ }
159
+ }
160
+ } catch (error) {
161
+ reject(error);
162
+ }
163
+ });
164
+ });
165
+
166
+ req.on('error', (error) => {
167
+ reject(error);
168
+ });
169
+
170
+ req.on('timeout', () => {
171
+ req.destroy();
172
+ reject(new Error('Backend API request timeout'));
173
+ });
174
+
175
+ req.write(payload);
176
+ req.end();
177
+ });
178
+ }
179
+
180
+
181
+ _mockSnippetResponse(codeSnippet, filePath, startLine) {
182
+ const vulnerabilities = [];
183
+ const lines = codeSnippet.split('\n');
184
+
185
+ // Simple heuristic-based mock analysis
186
+ for (let i = 0; i < lines.length; i++) {
187
+ const line = lines[i];
188
+ const lineLower = line.toLowerCase();
189
+ const actualLineNum = startLine + i;
190
+
191
+ // Look for logging of potentially sensitive data
192
+ if (lineLower.includes('log') && ['email', 'user', 'password', 'token'].some(term => lineLower.includes(term))) {
193
+ vulnerabilities.push({
194
+ line_number: actualLineNum,
195
+ severity: 'Medium',
196
+ description: 'Potential logging of sensitive user data detected.',
197
+ suggestion: 'Consider logging only non-sensitive identifiers or hashing sensitive data before logging.'
198
+ });
199
+ }
200
+
201
+ // Look for insecure data transmission
202
+ if (lineLower.includes('http://') && ['api', 'send', 'post', 'request'].some(term => lineLower.includes(term))) {
203
+ vulnerabilities.push({
204
+ line_number: actualLineNum,
205
+ severity: 'High',
206
+ description: 'Insecure HTTP transmission of potentially sensitive data.',
207
+ suggestion: 'Use HTTPS instead of HTTP for all data transmission.'
208
+ });
209
+ }
210
+ }
211
+
212
+ return { vulnerabilities, __source: 'mock' };
213
+ }
214
+
215
+ async analyzeFile(filePath, content, patternFindings = []) {
216
+ const lines = content.split('\n');
217
+ const areasOfInterest = this._identifyAreasOfInterest(content, patternFindings);
218
+ const findings = [];
219
+
220
+ if (this.verbose && areasOfInterest.length > 0) {
221
+ console.log(` Found ${areasOfInterest.length} areas of interest for LLM analysis`);
222
+ }
223
+
224
+ // Analyze each area of interest
225
+ for (const [startLine, endLine] of areasOfInterest) {
226
+ // Extract snippet
227
+ const snippetLines = lines.slice(startLine - 1, endLine);
228
+ const snippet = snippetLines.join('\n');
229
+
230
+ // Skip very small snippets
231
+ if (snippet.trim().length < 50) {
232
+ continue;
233
+ }
234
+
235
+ try {
236
+ // Call API with rate limiting
237
+ const grokResponse = await this.callGrokApi(snippet, filePath, startLine);
238
+
239
+ // Add delay to prevent rate limiting from free tier
240
+ await new Promise(resolve => setTimeout(resolve, 1000));
241
+
242
+ // Process findings
243
+ for (const vuln of grokResponse.vulnerabilities || []) {
244
+ const finding = {
245
+ file_path: filePath,
246
+ line_number: vuln.line_number || startLine,
247
+ severity: vuln.severity || 'Medium',
248
+ finding_type: 'Context-Based Issue',
249
+ description: vuln.description || 'Privacy vulnerability detected',
250
+ code_snippet: this._getCodeSnippet(content, vuln.line_number || startLine),
251
+ suggestion: vuln.suggestion || 'Review and address the identified issue.',
252
+ source: grokResponse.__source || 'unknown'
253
+ };
254
+ findings.push(finding);
255
+ }
256
+
257
+ } catch (error) {
258
+ // Continue with other snippets if one fails
259
+ continue;
260
+ }
261
+ }
262
+
263
+ // Remove duplicates based on line number and description
264
+ const seen = new Set();
265
+ const uniqueFindings = [];
266
+ for (const finding of findings) {
267
+ const key = `${finding.line_number}:${finding.description}`;
268
+ if (!seen.has(key)) {
269
+ seen.add(key);
270
+ uniqueFindings.push(finding);
271
+ }
272
+ }
273
+
274
+ return uniqueFindings;
275
+ }
276
+
277
+ _getCodeSnippet(content, lineNumber) {
278
+ const lines = content.split('\n');
279
+ if (lineNumber >= 1 && lineNumber <= lines.length) {
280
+ return lines[lineNumber - 1].trim();
281
+ }
282
+ return 'Code snippet unavailable';
283
+ }
284
+ }
285
+
286
+ module.exports = LLMAnalyzer;
@@ -0,0 +1,135 @@
1
+ class PatternScanner {
2
+ constructor() {
3
+ this.patterns = this._initPatterns();
4
+ }
5
+
6
+ _initPatterns() {
7
+ return {
8
+ 'aws_access_key': {
9
+ pattern: /\b(AKIA[0-9A-Z]{16})\b/g,
10
+ severity: 'Critical',
11
+ type: 'Hardcoded Secret',
12
+ description: 'AWS Access Key ID detected'
13
+ },
14
+ 'aws_secret_key': {
15
+ pattern: /\b[A-Za-z0-9/+=]{40}\b/g,
16
+ severity: 'Critical',
17
+ type: 'Hardcoded Secret',
18
+ description: 'Potential AWS Secret Access Key detected'
19
+ },
20
+ 'stripe_key': {
21
+ pattern: /\b(sk_live_[0-9a-zA-Z]{24}|pk_live_[0-9a-zA-Z]{24})\b/g,
22
+ severity: 'Critical',
23
+ type: 'Hardcoded Secret',
24
+ description: 'Stripe API key detected'
25
+ },
26
+ 'private_key': {
27
+ pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/g,
28
+ severity: 'Critical',
29
+ type: 'Hardcoded Secret',
30
+ description: 'Private key detected'
31
+ },
32
+ 'high_entropy': {
33
+ pattern: /\b[A-Za-z0-9+/=]{32,}\b/g,
34
+ severity: 'Medium',
35
+ type: 'Hardcoded Secret',
36
+ description: 'High entropy string (potential secret)'
37
+ },
38
+ 'email': {
39
+ pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/gi,
40
+ severity: 'Medium',
41
+ type: 'PII Detected',
42
+ description: 'Email address detected'
43
+ },
44
+ 'phone': {
45
+ pattern: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b|\b\(\d{3}\)\s?\d{3}[-.]?\d{4}\b/g,
46
+ severity: 'Medium',
47
+ type: 'PII Detected',
48
+ description: 'Phone number detected'
49
+ },
50
+ 'ip_address': {
51
+ pattern: /\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/g,
52
+ severity: 'Low',
53
+ type: 'PII Detected',
54
+ description: 'IP address detected'
55
+ },
56
+ 'sensitive_keywords': {
57
+ pattern: /\b(password|secret|api_key|token|ssn|credit_card|credentials)\s*[=:]\s*["']?[^"'\s]+/gi,
58
+ severity: 'High',
59
+ type: 'Hardcoded Secret',
60
+ description: 'Sensitive keyword in assignment context'
61
+ }
62
+ };
63
+ }
64
+
65
+ _calculateEntropy(data) {
66
+ if (!data) return 0;
67
+
68
+ const counts = {};
69
+ for (const char of data) {
70
+ counts[char] = (counts[char] || 0) + 1;
71
+ }
72
+
73
+ let entropy = 0;
74
+ const length = data.length;
75
+
76
+ for (const count of Object.values(counts)) {
77
+ const probability = count / length;
78
+ entropy -= probability * Math.log2(probability);
79
+ }
80
+
81
+ return entropy;
82
+ }
83
+
84
+ scanContent(filePath, content) {
85
+ const findings = [];
86
+ const lines = content.split('\n');
87
+
88
+ for (let lineNum = 0; lineNum < lines.length; lineNum++) {
89
+ const line = lines[lineNum];
90
+
91
+ for (const [patternName, patternInfo] of Object.entries(this.patterns)) {
92
+ // Reset regex lastIndex for global patterns
93
+ patternInfo.pattern.lastIndex = 0;
94
+
95
+ let match;
96
+ while ((match = patternInfo.pattern.exec(line)) !== null) {
97
+ // Additional filtering for high entropy strings
98
+ if (patternName === 'high_entropy') {
99
+ const matchedText = match[0];
100
+ if (this._calculateEntropy(matchedText) < 4.0) {
101
+ continue;
102
+ }
103
+ if (matchedText.length < 20) {
104
+ continue;
105
+ }
106
+ }
107
+
108
+ // Skip common false positives for AWS secret pattern
109
+ if (patternName === 'aws_secret_key') {
110
+ const matchedText = match[0];
111
+ const falsePositives = ['example', 'dummy', 'test', 'fake'];
112
+ if (falsePositives.some(fp => matchedText.toLowerCase().includes(fp))) {
113
+ continue;
114
+ }
115
+ }
116
+
117
+ const finding = {
118
+ file_path: filePath,
119
+ line_number: lineNum + 1,
120
+ severity: patternInfo.severity,
121
+ finding_type: patternInfo.type,
122
+ description: patternInfo.description,
123
+ code_snippet: line.trim()
124
+ };
125
+
126
+ findings.push(finding);
127
+ }
128
+ }
129
+ }
130
+
131
+ return findings;
132
+ }
133
+ }
134
+
135
+ module.exports = PatternScanner;
@@ -0,0 +1,229 @@
1
+ const chalk = require('chalk');
2
+
3
+ class ReportGenerator {
4
+ constructor() {
5
+ this.reportTime = new Date();
6
+ this.severityIcons = {
7
+ 'Critical': '🚨',
8
+ 'High': 'šŸ”„',
9
+ 'Medium': 'āš ļø',
10
+ 'Low': 'šŸ”µ'
11
+ };
12
+ this.severityOrder = ['Critical', 'High', 'Medium', 'Low'];
13
+ }
14
+
15
+ _calculateGrade(findings) {
16
+ if (!findings.length) {
17
+ return 'A+';
18
+ }
19
+
20
+ const severityCounts = {};
21
+ this.severityOrder.forEach(severity => {
22
+ severityCounts[severity] = 0;
23
+ });
24
+
25
+ for (const finding of findings) {
26
+ const severity = finding.severity || 'Low';
27
+ if (severityCounts.hasOwnProperty(severity)) {
28
+ severityCounts[severity]++;
29
+ }
30
+ }
31
+
32
+ // Grading logic
33
+ if (severityCounts['Critical'] > 0) {
34
+ return 'F';
35
+ } else if (severityCounts['High'] > 3) {
36
+ return 'D';
37
+ } else if (severityCounts['High'] > 0) {
38
+ return 'C-';
39
+ } else if (severityCounts['Medium'] > 5) {
40
+ return 'B-';
41
+ } else if (severityCounts['Medium'] > 0) {
42
+ return 'B';
43
+ } else if (severityCounts['Low'] > 0) {
44
+ return 'A-';
45
+ } else {
46
+ return 'A+';
47
+ }
48
+ }
49
+
50
+ _groupFindingsBySeverity(findings) {
51
+ const groups = {};
52
+ this.severityOrder.forEach(severity => {
53
+ groups[severity] = [];
54
+ });
55
+
56
+ for (const finding of findings) {
57
+ const severity = finding.severity || 'Low';
58
+ if (groups.hasOwnProperty(severity)) {
59
+ groups[severity].push(finding);
60
+ }
61
+ }
62
+
63
+ return groups;
64
+ }
65
+
66
+ _getGradeColor(grade) {
67
+ const colors = {
68
+ 'A+': '🟢', 'A': '🟢', 'A-': '🟢',
69
+ 'B+': '🟔', 'B': '🟔', 'B-': '🟔',
70
+ 'C+': '🟠', 'C': '🟠', 'C-': '🟠',
71
+ 'D': 'šŸ”“', 'F': 'šŸ”“'
72
+ };
73
+ return colors[grade] || '⚪';
74
+ }
75
+
76
+ generateReport(scanDir, findings, fileCount) {
77
+ const reportLines = [];
78
+
79
+ // ASCII Art Header
80
+ reportLines.push(
81
+ '',
82
+ chalk.red('╔═══════════════════════════════════════════════════════════════════════════════╗'),
83
+ chalk.red('ā•‘ ā•‘'),
84
+ chalk.red('ā•‘ ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘'),
85
+ chalk.red('ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā•‘'),
86
+ chalk.red('ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘'),
87
+ chalk.red('ā•‘ ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•— ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•— ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā•‘'),
88
+ chalk.red('ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘'),
89
+ chalk.red('ā•‘ ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā• ā•‘'),
90
+ chalk.red('ā•‘ ā•‘'),
91
+ chalk.red('ā•‘ šŸ” SHIFT-LEFT PRIVACY & COMPLIANCE SCANNER šŸ” ā•‘'),
92
+ chalk.red('ā•‘ Powered by AI • Built for Developers ā•‘'),
93
+ chalk.red('ā•‘ ā•‘'),
94
+ chalk.red('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•'),
95
+ '',
96
+ 'šŸŽÆ PRIVACY SCAN REPORT',
97
+ '═'.repeat(80),
98
+ ''
99
+ );
100
+
101
+ // Summary box
102
+ const severityCounts = {};
103
+ this.severityOrder.forEach(severity => {
104
+ severityCounts[severity] = 0;
105
+ });
106
+
107
+ for (const finding of findings) {
108
+ const severity = finding.severity || 'Low';
109
+ if (severityCounts.hasOwnProperty(severity)) {
110
+ severityCounts[severity]++;
111
+ }
112
+ }
113
+
114
+ const grade = this._calculateGrade(findings);
115
+ const gradeColor = this._getGradeColor(grade);
116
+
117
+ // Count LLM vs Mock findings
118
+ const llmCount = findings.filter(f => f.source === 'llm').length;
119
+ const patternCount = findings.length - llmCount;
120
+
121
+ const timestamp = this.reportTime.toISOString().slice(0, 19).replace('T', ' ');
122
+
123
+ reportLines.push(
124
+ 'ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”',
125
+ '│ šŸ“Š SCAN SUMMARY │',
126
+ 'ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤',
127
+ `│ šŸ“ Directory: ${scanDir.padEnd(60)} │`,
128
+ `│ ā° Timestamp: ${timestamp.padEnd(60)} │`,
129
+ `│ šŸ“„ Files Scanned: ${fileCount.toString().padEnd(57)} │`,
130
+ `│ šŸ” Total Issues: ${findings.length.toString().padEnd(58)} │`,
131
+ `│ šŸ† Privacy Grade: ${gradeColor}${grade.padEnd(56)} │`,
132
+ '│ │',
133
+ '│ šŸŽÆ Detection Methods: │',
134
+ `│ • Pattern-based: ${patternCount} issues │`,
135
+ `│ • AI-powered: ${llmCount} issues (Grok 4 Fast) │`,
136
+ '│ │',
137
+ '│ šŸ“ˆ Severity Breakdown: │',
138
+ `│ 🚨 Critical: ${severityCounts['Critical'].toString().padEnd(10)} šŸ”„ High: ${severityCounts['High'].toString().padEnd(14)} │`,
139
+ `│ āš ļø Medium: ${severityCounts['Medium'].toString().padEnd(11)} šŸ”µ Low: ${severityCounts['Low'].toString().padEnd(15)} │`,
140
+ 'ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜',
141
+ ''
142
+ );
143
+
144
+ // Findings breakdown
145
+ if (!findings.length) {
146
+ reportLines.push(
147
+ 'ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”',
148
+ '│ šŸŽ‰ CONGRATULATIONS! šŸŽ‰ │',
149
+ '│ │',
150
+ '│ ✨ No privacy issues detected! ✨ │',
151
+ '│ │',
152
+ '│ Your codebase follows privacy best practices! │',
153
+ '│ │',
154
+ 'ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜',
155
+ ''
156
+ );
157
+ } else {
158
+ const groupedFindings = this._groupFindingsBySeverity(findings);
159
+
160
+ for (const severity of this.severityOrder) {
161
+ const severityFindings = groupedFindings[severity];
162
+ if (!severityFindings.length) {
163
+ continue;
164
+ }
165
+
166
+ const icon = this.severityIcons[severity];
167
+ const colorBar = 'ā–ˆ'.repeat(Math.min(severityFindings.length, 40));
168
+
169
+ reportLines.push(
170
+ '',
171
+ `ā•­${'─'.repeat(77)}ā•®`,
172
+ `│ ${icon} ${severity.toUpperCase()} SEVERITY ISSUES (${severityFindings.length} found)`.padEnd(76) + '│',
173
+ `│ ${colorBar}`.padEnd(76) + '│',
174
+ `ā•°${'─'.repeat(77)}╯`,
175
+ ''
176
+ );
177
+
178
+ for (let i = 0; i < severityFindings.length; i++) {
179
+ const finding = severityFindings[i];
180
+ const sourceBadge = finding.source === 'llm' ? 'šŸ¤– AI' : 'šŸ” Pattern';
181
+
182
+ reportLines.push(
183
+ `ā”Œā”€ā”€ Issue #${i + 1} ──────────────────────────────────────────────────────────`,
184
+ `│ ${icon} ${finding.description || 'Privacy issue detected'}`,
185
+ '│',
186
+ `│ šŸ“ Location: ${finding.file_path || 'Unknown'}:${finding.line_number || 0}`,
187
+ `│ 🚨 Severity: ${finding.severity || 'Unknown'}`,
188
+ `│ šŸ”Ž Detection: ${sourceBadge}`,
189
+ '│',
190
+ '│ šŸ’¾ Code:',
191
+ `│ ${(finding.line_number || 0).toString().padStart(3)} │ ${finding.code_snippet || 'N/A'}`
192
+ );
193
+
194
+ if (finding.suggestion) {
195
+ reportLines.push(
196
+ '│',
197
+ '│ šŸ’” Suggestion:',
198
+ `│ ${finding.suggestion}`
199
+ );
200
+ }
201
+
202
+ reportLines.push(
203
+ '└────────────────────────────────────────────────────────────────────────',
204
+ ''
205
+ );
206
+ }
207
+ }
208
+ }
209
+
210
+ // Footer
211
+ reportLines.push(
212
+ '╔═══════════════════════════════════════════════════════════════════════════════╗',
213
+ 'ā•‘ šŸš€ GET STARTED ā•‘',
214
+ 'ā•‘ ā•‘',
215
+ 'ā•‘ šŸ“š Documentation: https://kafkacode.dev/docs ā•‘',
216
+ 'ā•‘ šŸ™ GitHub: https://github.com/kafkacode/privacy-scanner ā•‘',
217
+ 'ā•‘ šŸ’¬ Support: https://discord.gg/kafkacode ā•‘',
218
+ 'ā•‘ 🐦 Follow: @KafkaCodeDev ā•‘',
219
+ 'ā•‘ ā•‘',
220
+ 'ā•‘ šŸ›”ļø Keep your code secure, keep your users safe! šŸ›”ļø ā•‘',
221
+ 'ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•',
222
+ ''
223
+ );
224
+
225
+ return reportLines.join('\n');
226
+ }
227
+ }
228
+
229
+ module.exports = ReportGenerator;
package/dist/cli.js ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+ const FileScanner = require('./FileScanner');
7
+ const AnalysisEngine = require('./AnalysisEngine');
8
+ const ReportGenerator = require('./ReportGenerator');
9
+
10
+ const program = new Command();
11
+
12
+ program
13
+ .name('kafkacode')
14
+ .description('KafkaCode - Privacy and Compliance Scanner')
15
+ .version('1.2.0');
16
+
17
+ program
18
+ .command('scan')
19
+ .description('Scan a directory for privacy issues')
20
+ .argument('<directory>', 'Path to the source code directory to scan')
21
+ .option('-v, --verbose', 'Print verbose progress updates during the scan')
22
+ .action(async (directory, options) => {
23
+ await runScan(directory, options);
24
+ });
25
+
26
+ async function runScan(directory, options = {}) {
27
+ const verbose = options.verbose || false;
28
+
29
+ // Validate directory
30
+ if (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory()) {
31
+ console.error(`Error: Directory '${directory}' does not exist or is not a directory.`);
32
+ process.exit(1);
33
+ }
34
+
35
+ if (verbose) {
36
+ console.log('šŸš€ Starting KafkaCode privacy scan...');
37
+ }
38
+
39
+ try {
40
+ // Initialize components
41
+ const fileScanner = new FileScanner(directory);
42
+ const analysisEngine = new AnalysisEngine(verbose);
43
+ const reportGenerator = new ReportGenerator();
44
+
45
+ // Scan for files
46
+ if (verbose) {
47
+ console.log('šŸ“ Discovering source code files...');
48
+ }
49
+ const files = fileScanner.scanFiles();
50
+
51
+ if (!files.length) {
52
+ console.log('No source code files found to analyze.');
53
+ process.exit(0);
54
+ }
55
+
56
+ if (verbose) {
57
+ console.log(`Found ${files.length} files to analyze`);
58
+ }
59
+
60
+ // Analyze files
61
+ if (verbose) {
62
+ console.log('šŸ” Performing privacy analysis...');
63
+ }
64
+ const findings = await analysisEngine.analyzeFiles(files);
65
+
66
+ // Generate and display report
67
+ const report = reportGenerator.generateReport(directory, findings, files.length);
68
+ console.log(report);
69
+
70
+ // Return appropriate exit code
71
+ process.exit(findings.length > 0 ? 1 : 0);
72
+
73
+ } catch (error) {
74
+ if (error.name === 'AbortError' || error.message.includes('aborted')) {
75
+ console.log('\nāš ļø Scan interrupted by user.');
76
+ process.exit(1);
77
+ } else {
78
+ console.error(`āŒ Error during scan: ${error.message}`);
79
+ if (verbose) {
80
+ console.error(error.stack);
81
+ }
82
+ process.exit(1);
83
+ }
84
+ }
85
+ }
86
+
87
+ // Handle SIGINT (Ctrl+C)
88
+ process.on('SIGINT', () => {
89
+ console.log('\nāš ļø Scan interrupted by user.');
90
+ process.exit(1);
91
+ });
92
+
93
+ program.parse();
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ const FileScanner = require('./FileScanner');
2
+ const PatternScanner = require('./PatternScanner');
3
+ const LLMAnalyzer = require('./LLMAnalyzer');
4
+ const AnalysisEngine = require('./AnalysisEngine');
5
+ const ReportGenerator = require('./ReportGenerator');
6
+
7
+ module.exports = {
8
+ FileScanner,
9
+ PatternScanner,
10
+ LLMAnalyzer,
11
+ AnalysisEngine,
12
+ ReportGenerator
13
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "kafkacode",
3
+ "version": "1.2.0",
4
+ "description": "AI-powered privacy and compliance scanner by KafkaLabs - identify PII leaks, secrets, and compliance violations",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "kafkacode": "dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "node build.js",
11
+ "prepublishOnly": "npm run build",
12
+ "test": "node test/test.js"
13
+ },
14
+ "keywords": [
15
+ "privacy",
16
+ "compliance",
17
+ "security",
18
+ "scanner",
19
+ "static-analysis",
20
+ "pii",
21
+ "gdpr",
22
+ "ccpa",
23
+ "secret-detection",
24
+ "code-analysis",
25
+ "privacy-scanner",
26
+ "ai-powered",
27
+ "shift-left",
28
+ "cli-tool",
29
+ "security-scanner",
30
+ "vulnerability-scanner",
31
+ "kafkalabs"
32
+ ],
33
+ "author": "KafkaLabs <contact@kafkalabs.com>",
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "commander": "^9.4.1",
37
+ "minimatch": "^9.0.3",
38
+ "chalk": "^4.1.2"
39
+ },
40
+ "engines": {
41
+ "node": ">=14.0.0"
42
+ },
43
+ "files": [
44
+ "dist/",
45
+ "README.md",
46
+ "LICENSE",
47
+ "CHANGELOG.md"
48
+ ],
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/nikhil-kapu/KafkacodeFnpm.git"
52
+ },
53
+ "homepage": "https://kafkalabs.com/kafka-code",
54
+ "bugs": {
55
+ "url": "https://github.com/nikhil-kapu/KafkacodeFnpm/issues"
56
+ }
57
+ }