@cencori/scan 0.3.8 → 0.4.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/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/scanner/index.ts","../src/scanner/patterns.ts","../src/ai/index.ts","../src/telemetry.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { confirm, password } from '@inquirer/prompts';\nimport { scan, type ScanResult, type ScanIssue } from './scanner/index.js';\nimport {\n getApiKey,\n setSessionApiKey,\n saveApiKey,\n validateApiKey,\n analyzeIssues,\n generateFixes,\n applyFixes,\n} from './ai/index.js';\nimport { sendTelemetry, buildTelemetryData, flushTelemetry } from './telemetry.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst VERSION = '0.3.8';\n\n// Score colors\nconst scoreStyles: Record<string, { color: typeof chalk.green }> = {\n A: { color: chalk.green },\n B: { color: chalk.blue },\n C: { color: chalk.yellow },\n D: { color: chalk.red },\n F: { color: chalk.bgRed.white },\n};\n\nconst severityColors: Record<string, typeof chalk.red> = {\n critical: chalk.bgRed.white,\n high: chalk.red,\n medium: chalk.yellow,\n low: chalk.blue,\n};\n\nconst typeLabels: Record<string, string> = {\n secret: 'SECRETS',\n pii: 'PII',\n route: 'ROUTES',\n config: 'CONFIG',\n vulnerability: 'VULNERABILITIES',\n};\n\n/**\n * Print the banner\n */\nfunction printBanner(): void {\n console.log();\n console.log(chalk.cyan.bold(' Cencori Scan'));\n console.log(chalk.gray(` v${VERSION}`));\n console.log();\n}\n\n/**\n * Print the score box\n */\nfunction printScore(result: ScanResult): void {\n const style = scoreStyles[result.score];\n const scoreText = `${result.score}-Tier`;\n const content = ` Security Score: ${scoreText}`;\n\n console.log();\n console.log(chalk.gray(' ┌─────────────────────────────────────────────┐'));\n console.log(chalk.gray(' │') + style.color.bold(content.padEnd(45)) + chalk.gray('│'));\n console.log(chalk.gray(' └─────────────────────────────────────────────┘'));\n console.log();\n console.log(chalk.gray(` ${result.tierDescription}`));\n console.log();\n}\n\n/**\n * Print issues grouped by type\n */\nfunction printIssues(issues: ScanIssue[]): void {\n if (issues.length === 0) {\n console.log(chalk.green(' No security issues found.'));\n console.log();\n return;\n }\n\n // Group by type\n const grouped: Record<string, ScanIssue[]> = {};\n for (const issue of issues) {\n if (!grouped[issue.type]) {\n grouped[issue.type] = [];\n }\n grouped[issue.type].push(issue);\n }\n\n // Print each group\n for (const [type, typeIssues] of Object.entries(grouped)) {\n const label = typeLabels[type] || type.toUpperCase();\n\n console.log(` ${chalk.bold(label)} (${typeIssues.length})`);\n\n for (let i = 0; i < typeIssues.length; i++) {\n const issue = typeIssues[i];\n const isLast = i === typeIssues.length - 1;\n const prefix = isLast ? ' └─' : ' ├─';\n const severityColor = severityColors[issue.severity];\n\n console.log(\n chalk.gray(prefix) + ' ' +\n chalk.gray(`${issue.file}:${issue.line}`) + ' ' +\n severityColor(issue.match)\n );\n\n if (issue.description) {\n const descPrefix = isLast ? ' ' : ' │ ';\n console.log(chalk.gray(descPrefix) + chalk.dim(issue.description));\n }\n }\n console.log();\n }\n}\n\n/**\n * Print summary stats\n */\nfunction printSummary(result: ScanResult): void {\n const { summary } = result;\n\n console.log(chalk.gray(' ─────────────────────────────────────────────'));\n console.log();\n console.log(` ${chalk.bold('Summary')}`);\n console.log(` Files scanned: ${chalk.cyan(result.filesScanned)}`);\n console.log(` Scan time: ${chalk.cyan(result.scanDuration + 'ms')}`);\n console.log();\n\n if (summary.critical > 0) {\n console.log(` ${chalk.bgRed.white(' CRITICAL ')} ${summary.critical} issues`);\n }\n if (summary.high > 0) {\n console.log(` ${chalk.red(' HIGH ')} ${summary.high} issues`);\n }\n if (summary.medium > 0) {\n console.log(` ${chalk.yellow(' MEDIUM ')} ${summary.medium} issues`);\n }\n if (summary.low > 0) {\n console.log(` ${chalk.blue(' LOW ')} ${summary.low} issues`);\n }\n console.log();\n}\n\n/**\n * Print recommendations\n */\nfunction printRecommendations(issues: ScanIssue[]): void {\n if (issues.length === 0) return;\n\n console.log(` ${chalk.bold('Recommendations:')}`);\n\n const hasSecrets = issues.some(i => i.type === 'secret');\n const hasPII = issues.some(i => i.type === 'pii');\n const hasConfig = issues.some(i => i.type === 'config');\n const hasXSS = issues.some(i => i.category === 'xss');\n const hasInjection = issues.some(i => i.category === 'injection');\n const hasCORS = issues.some(i => i.category === 'cors');\n\n if (hasSecrets) {\n console.log(chalk.gray(' - Use environment variables for secrets'));\n console.log(chalk.gray(' - Never commit API keys to version control'));\n }\n if (hasConfig) {\n console.log(chalk.gray(' - Add .env* to .gitignore'));\n }\n if (hasPII) {\n console.log(chalk.gray(' - Remove personal data from source code'));\n }\n if (hasXSS) {\n console.log(chalk.gray(' - Sanitize user input before rendering HTML'));\n }\n if (hasInjection) {\n console.log(chalk.gray(' - Use parameterized queries for SQL'));\n }\n if (hasCORS) {\n console.log(chalk.gray(' - Configure CORS with specific allowed origins'));\n }\n\n console.log();\n}\n\n/**\n * Print footer with links\n */\nfunction printFooter(): void {\n console.log(chalk.gray(' ─────────────────────────────────────────────'));\n console.log();\n console.log(` Share: ${chalk.cyan('https://scan.cencori.com')}`);\n console.log(` Docs: ${chalk.cyan('https://cencori.com/docs')}`);\n console.log();\n}\n\n/**\n * Load file contents for AI analysis\n */\nfunction loadFileContents(issues: ScanIssue[], basePath: string): Map<string, string> {\n const contents = new Map<string, string>();\n const uniqueFiles = [...new Set(issues.map(i => i.file))];\n\n for (const file of uniqueFiles) {\n try {\n const fullPath = path.resolve(basePath, file);\n const content = fs.readFileSync(fullPath, 'utf-8');\n contents.set(file, content);\n } catch {\n // Skip files that can't be read\n }\n }\n\n return contents;\n}\n\n/**\n * Prompt user for API key (hidden input)\n */\nasync function promptForApiKey(): Promise<string | undefined> {\n console.log();\n console.log(chalk.gray(' ─────────────────────────────────────────────'));\n console.log();\n console.log(` ${chalk.cyan.bold('Cencori Pro')}`);\n console.log(chalk.gray(' AI-powered auto-fix requires an API key.'));\n console.log();\n console.log(` Get your free API key at:`);\n console.log(` ${chalk.cyan('https://cencori.com/dashboard')} → API Keys`);\n console.log();\n\n try {\n const apiKey = await password({\n message: 'Enter your Cencori API key:',\n mask: '*',\n });\n\n if (!apiKey || apiKey.trim() === '') {\n console.log(chalk.yellow(' No API key entered. Skipping auto-fix.'));\n return undefined;\n }\n\n return apiKey.trim();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Handle AI auto-fix flow\n */\nasync function handleAutoFix(\n result: ScanResult,\n targetPath: string\n): Promise<void> {\n if (result.issues.length === 0) return;\n\n console.log();\n\n // Ask user if they want to auto-fix\n const shouldFix = await confirm({\n message: 'Would you like Cencori to auto-fix these issues?',\n default: false,\n });\n\n if (!shouldFix) {\n console.log();\n console.log(chalk.gray(' Skipped auto-fix. Run again anytime to fix issues.'));\n console.log();\n return;\n }\n\n // Check if we have an API key\n let apiKey = getApiKey();\n\n if (!apiKey) {\n // Prompt for API key\n apiKey = await promptForApiKey();\n\n if (!apiKey) {\n console.log();\n return;\n }\n\n // Validate the API key\n const validatingSpinner = ora({\n text: 'Validating API key...',\n color: 'cyan',\n }).start();\n\n const isValid = await validateApiKey(apiKey);\n\n if (!isValid) {\n validatingSpinner.fail('Invalid API key');\n console.log(chalk.red(' The API key could not be validated. Please check and try again.'));\n console.log();\n return;\n }\n\n validatingSpinner.succeed('API key validated');\n\n // Save the API key for future use\n try {\n saveApiKey(apiKey);\n console.log(chalk.green(' ✔ API key saved to ~/.cencorirc'));\n } catch {\n // Non-fatal, just won't be saved\n }\n\n // Set for current session\n setSessionApiKey(apiKey);\n } else {\n console.log(chalk.gray(' Using saved API key...'));\n }\n\n // Load file contents\n const fileContents = loadFileContents(result.issues, targetPath);\n\n // Analyze with AI\n const analyzeSpinner = ora({\n text: 'Analyzing issues with AI...',\n color: 'cyan',\n }).start();\n\n try {\n const analysis = await analyzeIssues(result.issues, fileContents);\n\n // Filter out false positives\n const realIssues = analysis.filter(a => !a.isFalsePositive);\n const falsePositives = analysis.filter(a => a.isFalsePositive);\n\n if (falsePositives.length > 0) {\n analyzeSpinner.succeed(`${chalk.green(falsePositives.length)} false positives filtered`);\n } else {\n analyzeSpinner.succeed('Analysis complete');\n }\n\n if (realIssues.length === 0) {\n console.log(chalk.green(' All issues were false positives!'));\n return;\n }\n\n // Generate fixes\n const fixSpinner = ora({\n text: 'Generating fixes...',\n color: 'cyan',\n }).start();\n\n const fixes = await generateFixes(\n realIssues.map(a => a.issue),\n fileContents\n );\n\n fixSpinner.succeed(`Generated ${fixes.length} fixes`);\n\n // Apply fixes\n const applySpinner = ora({\n text: 'Applying fixes...',\n color: 'cyan',\n }).start();\n\n const appliedFixes = await applyFixes(fixes, fileContents);\n const appliedCount = appliedFixes.filter(f => f.applied).length;\n\n applySpinner.succeed(`Applied ${appliedCount}/${fixes.length} fixes`);\n\n // Show what was fixed\n console.log();\n console.log(` ${chalk.bold('Applied fixes:')}`);\n for (const fix of appliedFixes.filter(f => f.applied)) {\n console.log(chalk.green(` ✔ ${fix.issue.file}:${fix.issue.line}`));\n console.log(chalk.gray(` ${fix.explanation}`));\n }\n\n const notApplied = appliedFixes.filter(f => !f.applied);\n if (notApplied.length > 0) {\n console.log();\n console.log(` ${chalk.yellow(`${notApplied.length} issues require manual review`)}`);\n }\n\n console.log();\n } catch (error) {\n analyzeSpinner.fail('Auto-fix failed');\n console.error(chalk.red(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`));\n console.log();\n }\n}\n\n/**\n * Main CLI function\n */\nasync function main(): Promise<void> {\n program\n .name('cencori-scan')\n .description('Security scanner for AI apps. Detect secrets, PII, and exposed routes.')\n .version(VERSION)\n .argument('[path]', 'Path to scan', '.')\n .option('-j, --json', 'Output results as JSON')\n .option('-q, --quiet', 'Only output the score')\n .option('--no-prompt', 'Skip interactive prompts')\n .option('--no-color', 'Disable colored output')\n .action(async (targetPath: string, options: { json?: boolean; quiet?: boolean; prompt?: boolean }) => {\n if (options.json) {\n const result = await scan(targetPath);\n // Send telemetry for JSON mode too\n sendTelemetry(buildTelemetryData(result, VERSION, !!getApiKey()));\n console.log(JSON.stringify(result, null, 2));\n // Wait for telemetry to complete before exiting\n await flushTelemetry();\n process.exit(result.score === 'A' || result.score === 'B' ? 0 : 1);\n return;\n }\n\n printBanner();\n\n const spinner = ora({\n text: 'Scanning for security issues...',\n color: 'cyan',\n }).start();\n\n try {\n const result = await scan(targetPath);\n\n // Send telemetry silently in background\n sendTelemetry(buildTelemetryData(result, VERSION, !!getApiKey()));\n\n spinner.succeed(`Scanned ${result.filesScanned} files`);\n\n if (options.quiet) {\n const style = scoreStyles[result.score];\n console.log(`\\n Score: ${style.color.bold(result.score + '-Tier')}\\n`);\n // Wait for telemetry to complete before exiting\n await flushTelemetry();\n process.exit(result.score === 'A' || result.score === 'B' ? 0 : 1);\n return;\n }\n\n printScore(result);\n printIssues(result.issues);\n printSummary(result);\n printRecommendations(result.issues);\n\n // Interactive auto-fix prompt (unless --no-prompt)\n if (options.prompt !== false && result.issues.length > 0) {\n await handleAutoFix(result, targetPath);\n }\n\n printFooter();\n\n // Wait for telemetry to complete before exiting\n await flushTelemetry();\n process.exit(result.score === 'A' || result.score === 'B' ? 0 : 1);\n } catch (error) {\n spinner.fail('Scan failed');\n console.error(chalk.red(`\\n Error: ${error instanceof Error ? error.message : 'Unknown error'}`));\n // Wait for any pending telemetry before exiting\n await flushTelemetry();\n process.exit(1);\n }\n });\n\n program.parse();\n}\n\nmain();\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport { glob } from 'glob';\nimport {\n SECRET_PATTERNS,\n PII_PATTERNS,\n ROUTE_PATTERNS,\n VULNERABILITY_PATTERNS,\n IGNORE_PATTERNS,\n SCANNABLE_EXTENSIONS,\n} from './patterns';\n\nexport type IssueType = 'secret' | 'pii' | 'route' | 'config' | 'vulnerability';\nexport type IssueSeverity = 'critical' | 'high' | 'medium' | 'low';\n\nexport interface ScanIssue {\n type: IssueType;\n category?: string;\n severity: IssueSeverity;\n name: string;\n provider?: string;\n file: string;\n line: number;\n column: number;\n match: string;\n description?: string;\n}\n\nexport interface ScanResult {\n score: 'A' | 'B' | 'C' | 'D' | 'F';\n tierDescription: string;\n issues: ScanIssue[];\n filesScanned: number;\n scanDuration: number;\n summary: {\n secrets: number;\n pii: number;\n routes: number;\n config: number;\n vulnerabilities: number;\n critical: number;\n high: number;\n medium: number;\n low: number;\n };\n}\n\n/**\n * Redact sensitive content for display\n */\nfunction redact(match: string, showChars: number = 4): string {\n if (match.length <= showChars * 2) {\n return '*'.repeat(match.length);\n }\n return match.slice(0, showChars) + '****' + match.slice(-showChars);\n}\n\n/**\n * Get line and column number for a match index\n */\nfunction getPosition(content: string, index: number): { line: number; column: number } {\n const lines = content.slice(0, index).split('\\n');\n return {\n line: lines.length,\n column: lines[lines.length - 1].length + 1,\n };\n}\n\n/**\n * Check if a file should be ignored\n */\nfunction shouldIgnore(filePath: string): boolean {\n const normalized = filePath.replace(/\\\\/g, '/');\n return IGNORE_PATTERNS.some(pattern => {\n if (pattern.startsWith('*')) {\n return normalized.endsWith(pattern.slice(1));\n }\n return normalized.includes(pattern);\n });\n}\n\n/**\n * Check if file has scannable extension\n */\nfunction isScannable(filePath: string): boolean {\n const ext = path.extname(filePath).toLowerCase();\n return SCANNABLE_EXTENSIONS.includes(ext);\n}\n\n/**\n * Check if file is a documentation or test file\n */\nfunction isDocOrTestFile(filePath: string): boolean {\n const lower = filePath.toLowerCase();\n return (\n lower.includes('.test.') ||\n lower.includes('.spec.') ||\n lower.includes('__tests__') ||\n lower.includes('/test/') ||\n lower.includes('/tests/') ||\n lower.endsWith('.md') ||\n lower.includes('/docs/')\n );\n}\n\n/**\n * Scan a single file for issues\n */\nfunction scanFile(filePath: string, content: string): ScanIssue[] {\n const issues: ScanIssue[] = [];\n const relativePath = filePath;\n const isDocFile = isDocOrTestFile(filePath);\n\n // Scan for secrets\n for (const pattern of SECRET_PATTERNS) {\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'secret',\n severity: pattern.severity,\n name: pattern.name,\n provider: pattern.provider,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: redact(match[0]),\n });\n }\n }\n\n // Scan for PII (skip in doc files)\n if (!isDocFile) {\n for (const pattern of PII_PATTERNS) {\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n const matchStr = match[0];\n if (isLikelyFalsePositive(matchStr, pattern.name, filePath)) {\n continue;\n }\n\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'pii',\n severity: pattern.severity,\n name: pattern.name,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: redact(matchStr, 3),\n });\n }\n }\n }\n\n // Scan for exposed routes\n for (const pattern of ROUTE_PATTERNS) {\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'route',\n severity: pattern.severity,\n name: pattern.name,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: match[0],\n description: pattern.description,\n });\n }\n }\n\n // Scan for vulnerabilities (skip debug checks in test files)\n for (const pattern of VULNERABILITY_PATTERNS) {\n // Skip debug pattern checks in test/doc files\n if (pattern.category === 'debug' && isDocFile) {\n continue;\n }\n\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n // Skip console.log false positives\n if (pattern.category === 'debug' && pattern.name === 'Console Log Statement') {\n // Allow console.error and console.warn\n if (match[0].includes('error') || match[0].includes('warn')) {\n continue;\n }\n }\n\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'vulnerability',\n category: pattern.category,\n severity: pattern.severity,\n name: pattern.name,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: match[0].length > 50 ? match[0].slice(0, 50) + '...' : match[0],\n description: pattern.description,\n });\n }\n }\n\n // Check for .env files\n const fileName = path.basename(filePath);\n if (fileName.startsWith('.env') && !fileName.includes('.example')) {\n issues.push({\n type: 'config',\n severity: 'high',\n name: 'Environment file in repository',\n file: relativePath,\n line: 1,\n column: 1,\n match: fileName,\n description: 'Add .env* to .gitignore',\n });\n }\n\n return issues;\n}\n\n/**\n * Filter out likely false positives\n */\nfunction isLikelyFalsePositive(match: string, patternName: string, filePath: string): boolean {\n // Email false positives\n if (patternName === 'Email Address') {\n const falseDomains = ['example.com', 'example.org', 'test.com', 'localhost', 'placeholder.com'];\n if (falseDomains.some(d => match.includes(d))) {\n return true;\n }\n\n const publicPrefixes = [\n 'support@', 'help@', 'info@', 'contact@', 'sales@', 'admin@',\n 'noreply@', 'no-reply@', 'hello@', 'team@', 'partners@',\n 'enterprise@', 'security@', 'privacy@', 'legal@',\n ];\n if (publicPrefixes.some(p => match.toLowerCase().startsWith(p))) {\n return true;\n }\n }\n\n // IP address false positives\n if (patternName === 'IP Address') {\n const falseIPs = ['0.0.0.0', '127.0.0.1', '192.168.', '10.0.', '172.16.'];\n if (falseIPs.some(ip => match.startsWith(ip))) {\n return true;\n }\n }\n\n // Phone number false positives\n if (patternName.includes('Phone Number')) {\n if (match.includes('555') || match.includes('123-456') || match.includes('000-000')) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Calculate the security score\n */\nfunction calculateScore(issues: ScanIssue[]): 'A' | 'B' | 'C' | 'D' | 'F' {\n const critical = issues.filter(i => i.severity === 'critical').length;\n const high = issues.filter(i => i.severity === 'high').length;\n const medium = issues.filter(i => i.severity === 'medium').length;\n\n if (critical > 0) return 'F';\n if (high >= 3) return 'F';\n if (high >= 2) return 'D';\n if (high >= 1 || medium >= 5) return 'C';\n if (medium >= 2) return 'B';\n if (issues.length === 0) return 'A';\n return 'B';\n}\n\n/**\n * Get tier description\n */\nfunction getTierDescription(score: string): string {\n switch (score) {\n case 'A': return 'Excellent! No security issues detected.';\n case 'B': return 'Good, but minor improvements recommended.';\n case 'C': return 'Fair. Some security concerns need attention.';\n case 'D': return 'Poor. Significant security issues detected.';\n case 'F': return 'Critical! Major security vulnerabilities found.';\n default: return '';\n }\n}\n\n/**\n * Main scan function\n */\nexport async function scan(targetPath: string): Promise<ScanResult> {\n const startTime = Date.now();\n const absolutePath = path.resolve(targetPath);\n\n const files = await glob('**/*', {\n cwd: absolutePath,\n nodir: true,\n ignore: IGNORE_PATTERNS,\n absolute: true,\n });\n\n const issues: ScanIssue[] = [];\n let filesScanned = 0;\n\n for (const file of files) {\n if (!isScannable(file) || shouldIgnore(file)) {\n continue;\n }\n\n try {\n const content = fs.readFileSync(file, 'utf-8');\n const relativePath = path.relative(absolutePath, file);\n const fileIssues = scanFile(relativePath, content);\n issues.push(...fileIssues);\n filesScanned++;\n } catch {\n continue;\n }\n }\n\n const score = calculateScore(issues);\n const scanDuration = Date.now() - startTime;\n\n return {\n score,\n tierDescription: getTierDescription(score),\n issues,\n filesScanned,\n scanDuration,\n summary: {\n secrets: issues.filter(i => i.type === 'secret').length,\n pii: issues.filter(i => i.type === 'pii').length,\n routes: issues.filter(i => i.type === 'route').length,\n config: issues.filter(i => i.type === 'config').length,\n vulnerabilities: issues.filter(i => i.type === 'vulnerability').length,\n critical: issues.filter(i => i.severity === 'critical').length,\n high: issues.filter(i => i.severity === 'high').length,\n medium: issues.filter(i => i.severity === 'medium').length,\n low: issues.filter(i => i.severity === 'low').length,\n },\n };\n}\n","/**\n * Secret detection patterns for common API keys and tokens\n */\nexport interface SecretPattern {\n name: string;\n provider: string;\n pattern: RegExp;\n severity: 'critical' | 'high' | 'medium' | 'low';\n}\n\nexport const SECRET_PATTERNS: SecretPattern[] = [\n // OpenAI\n {\n name: 'OpenAI API Key',\n provider: 'OpenAI',\n pattern: /sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20}/g,\n severity: 'critical',\n },\n {\n name: 'OpenAI Project Key',\n provider: 'OpenAI',\n pattern: /sk-proj-[a-zA-Z0-9_-]{80,}/g,\n severity: 'critical',\n },\n // Anthropic\n {\n name: 'Anthropic API Key',\n provider: 'Anthropic',\n pattern: /sk-ant-[a-zA-Z0-9-]{90,}/g,\n severity: 'critical',\n },\n // Google\n {\n name: 'Google API Key',\n provider: 'Google',\n pattern: /AIza[0-9A-Za-z_-]{35}/g,\n severity: 'critical',\n },\n // Supabase\n {\n name: 'Supabase Service Role Key',\n provider: 'Supabase',\n pattern: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\\.[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n severity: 'critical',\n },\n {\n name: 'Supabase Anon Key (if hardcoded)',\n provider: 'Supabase',\n pattern: /SUPABASE_ANON_KEY\\s*[:=]\\s*[\"']eyJ[^\"']+[\"']/g,\n severity: 'medium',\n },\n // Stripe\n {\n name: 'Stripe Secret Key',\n provider: 'Stripe',\n pattern: /sk_live_[0-9a-zA-Z]{24,}/g,\n severity: 'critical',\n },\n {\n name: 'Stripe Test Key',\n provider: 'Stripe',\n pattern: /sk_test_[0-9a-zA-Z]{24,}/g,\n severity: 'medium',\n },\n {\n name: 'Stripe Webhook Secret',\n provider: 'Stripe',\n pattern: /whsec_[a-zA-Z0-9]{24,}/g,\n severity: 'critical',\n },\n // AWS\n {\n name: 'AWS Access Key ID',\n provider: 'AWS',\n pattern: /AKIA[0-9A-Z]{16}/g,\n severity: 'critical',\n },\n {\n name: 'AWS Secret Access Key',\n provider: 'AWS',\n pattern: /aws_secret_access_key\\s*[:=]\\s*[\"'][A-Za-z0-9/+=]{40}[\"']/gi,\n severity: 'critical',\n },\n // GitHub\n {\n name: 'GitHub Personal Access Token',\n provider: 'GitHub',\n pattern: /ghp_[a-zA-Z0-9]{36}/g,\n severity: 'critical',\n },\n {\n name: 'GitHub OAuth Token',\n provider: 'GitHub',\n pattern: /gho_[a-zA-Z0-9]{36}/g,\n severity: 'critical',\n },\n {\n name: 'GitHub Webhook Secret',\n provider: 'GitHub',\n pattern: /sha256=[a-fA-F0-9]{64}/g,\n severity: 'high',\n },\n // Telegram\n {\n name: 'Telegram Bot Token',\n provider: 'Telegram',\n pattern: /[0-9]{9,10}:[a-zA-Z0-9_-]{35}/g,\n severity: 'high',\n },\n // Discord\n {\n name: 'Discord Bot Token',\n provider: 'Discord',\n pattern: /[MN][A-Za-z\\d]{23,}\\.[\\w-]{6}\\.[\\w-]{27}/g,\n severity: 'high',\n },\n // Slack\n {\n name: 'Slack Bot Token',\n provider: 'Slack',\n pattern: /xoxb-[0-9]{11}-[0-9]{11}-[a-zA-Z0-9]{24}/g,\n severity: 'high',\n },\n // SendGrid\n {\n name: 'SendGrid API Key',\n provider: 'SendGrid',\n pattern: /SG\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9_-]{43}/g,\n severity: 'high',\n },\n // Twilio\n {\n name: 'Twilio API Key',\n provider: 'Twilio',\n pattern: /SK[a-fA-F0-9]{32}/g,\n severity: 'high',\n },\n // Mailgun\n {\n name: 'Mailgun API Key',\n provider: 'Mailgun',\n pattern: /key-[a-zA-Z0-9]{32}/g,\n severity: 'high',\n },\n // Firebase\n {\n name: 'Firebase Database URL',\n provider: 'Firebase',\n pattern: /https:\\/\\/[a-z0-9-]+\\.firebaseio\\.com/g,\n severity: 'medium',\n },\n // Generic patterns\n {\n name: 'Private Key',\n provider: 'Generic',\n pattern: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,\n severity: 'critical',\n },\n {\n name: 'Generic API Key Assignment',\n provider: 'Generic',\n pattern: /(api_key|apikey|api_secret|secret_key)\\s*[:=]\\s*[\"'][a-zA-Z0-9_-]{20,}[\"']/gi,\n severity: 'high',\n },\n {\n name: 'Password Assignment',\n provider: 'Generic',\n pattern: /(password|passwd|pwd)\\s*[:=]\\s*[\"'][^\"']{8,}[\"']/gi,\n severity: 'high',\n },\n // Replicate\n {\n name: 'Replicate API Token',\n provider: 'Replicate',\n pattern: /r8_[a-zA-Z0-9]{38}/g,\n severity: 'critical',\n },\n // Hugging Face\n {\n name: 'Hugging Face Token',\n provider: 'Hugging Face',\n pattern: /hf_[a-zA-Z0-9]{34}/g,\n severity: 'critical',\n },\n // JWT Secrets\n {\n name: 'JWT Secret Assignment',\n provider: 'Generic',\n pattern: /JWT_SECRET\\s*[:=]\\s*[\"'][^\"']{16,}[\"']/gi,\n severity: 'critical',\n },\n {\n name: 'Hardcoded JWT Sign',\n provider: 'Generic',\n pattern: /jwt\\.(sign|verify)\\s*\\([^,]+,\\s*[\"'][^\"']{10,}[\"']/gi,\n severity: 'critical',\n },\n // OAuth Secrets\n {\n name: 'OAuth Client Secret',\n provider: 'Generic',\n pattern: /client_secret\\s*[:=]\\s*[\"'][a-zA-Z0-9_-]{20,}[\"']/gi,\n severity: 'critical',\n },\n {\n name: 'Google Client Secret',\n provider: 'Google',\n pattern: /GOOGLE_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']+[\"']/gi,\n severity: 'critical',\n },\n // Database Connection Strings\n {\n name: 'MongoDB Connection String',\n provider: 'MongoDB',\n pattern: /mongodb(\\+srv)?:\\/\\/[^@\\s]+@[^\\s\"']+/g,\n severity: 'critical',\n },\n {\n name: 'PostgreSQL Connection String',\n provider: 'PostgreSQL',\n pattern: /postgres(ql)?:\\/\\/[^\\s\"']+/g,\n severity: 'critical',\n },\n {\n name: 'MySQL Connection String',\n provider: 'MySQL',\n pattern: /mysql:\\/\\/[^\\s\"']+/g,\n severity: 'critical',\n },\n {\n name: 'Redis Connection String',\n provider: 'Redis',\n pattern: /redis:\\/\\/[^\\s\"']+/g,\n severity: 'high',\n },\n];\n\n/**\n * PII detection patterns\n */\nexport interface PIIPattern {\n name: string;\n pattern: RegExp;\n severity: 'high' | 'medium' | 'low';\n}\n\nexport const PII_PATTERNS: PIIPattern[] = [\n {\n name: 'Email Address',\n pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g,\n severity: 'medium',\n },\n {\n name: 'Phone Number (US)',\n pattern: /(\\+1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g,\n severity: 'medium',\n },\n {\n name: 'Phone Number (International)',\n pattern: /\\+[1-9]\\d{1,14}/g,\n severity: 'medium',\n },\n {\n name: 'Social Security Number',\n pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n severity: 'high',\n },\n {\n name: 'Credit Card Number',\n pattern: /\\b(?:\\d{4}[-\\s]?){3}\\d{4}\\b/g,\n severity: 'high',\n },\n {\n name: 'IP Address',\n pattern: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n severity: 'low',\n },\n];\n\n/**\n * Exposed route patterns for common frameworks\n */\nexport interface RoutePattern {\n name: string;\n framework: string;\n pattern: RegExp;\n severity: 'high' | 'medium' | 'low';\n description: string;\n}\n\nexport const ROUTE_PATTERNS: RoutePattern[] = [\n // Next.js API routes\n {\n name: 'Next.js API Route (check for auth)',\n framework: 'Next.js',\n pattern: /export\\s+(async\\s+)?function\\s+(GET|POST|PUT|DELETE|PATCH)\\s*\\(/g,\n severity: 'medium',\n description: 'API route handler - verify authentication is implemented',\n },\n // Express routes\n {\n name: 'Express Route without Auth Middleware',\n framework: 'Express',\n pattern: /app\\.(get|post|put|delete|patch)\\s*\\(\\s*[\"'`][^\"'`]+[\"'`]\\s*,\\s*(?!.*auth)/gi,\n severity: 'medium',\n description: 'Express route - check if auth middleware is applied',\n },\n // Admin routes\n {\n name: 'Admin Route Exposed',\n framework: 'Generic',\n pattern: /[\"'`](\\/admin|\\/dashboard|\\/internal|\\/private)[^\"'`]*[\"'`]/gi,\n severity: 'high',\n description: 'Sensitive route - ensure proper authentication',\n },\n];\n\n/**\n * Security vulnerability patterns\n */\nexport interface VulnerabilityPattern {\n name: string;\n category: string;\n pattern: RegExp;\n severity: 'critical' | 'high' | 'medium' | 'low';\n description: string;\n}\n\nexport const VULNERABILITY_PATTERNS: VulnerabilityPattern[] = [\n // Hardcoded URLs\n {\n name: 'Localhost URL in Code',\n category: 'hardcoded-url',\n pattern: /https?:\\/\\/localhost[:\\d]*/gi,\n severity: 'medium',\n description: 'Development URL - should use environment variables',\n },\n {\n name: 'Staging/Dev URL in Code',\n category: 'hardcoded-url',\n pattern: /https?:\\/\\/(staging\\.|dev\\.|test\\.)[^\\s\"']+/gi,\n severity: 'medium',\n description: 'Non-production URL in code',\n },\n // Debug artifacts (skip console.log - too many false positives for CLI tools)\n {\n name: 'Debug Flag Enabled',\n category: 'debug',\n pattern: /DEBUG\\s*[:=]\\s*(true|1|[\"']true[\"'])/gi,\n severity: 'medium',\n description: 'Debug mode enabled - disable in production',\n },\n {\n name: 'Hardcoded Development Mode',\n category: 'debug',\n pattern: /NODE_ENV\\s*[:=]\\s*[\"']development[\"']/gi,\n severity: 'medium',\n description: 'Hardcoded development mode',\n },\n // CORS issues\n {\n name: 'CORS Wildcard Origin',\n category: 'cors',\n pattern: /Access-Control-Allow-Origin['\":\\s]+\\*/g,\n severity: 'high',\n description: 'Allows requests from any origin - security risk',\n },\n {\n name: 'Permissive CORS Config',\n category: 'cors',\n pattern: /cors\\s*\\(\\s*\\)/g,\n severity: 'medium',\n description: 'CORS with default (permissive) settings',\n },\n // SQL Injection\n {\n name: 'SQL String Concatenation',\n category: 'injection',\n pattern: /query\\s*\\(\\s*[`'\"].*\\$\\{.*\\}/g,\n severity: 'critical',\n description: 'Potential SQL injection - use parameterized queries',\n },\n {\n name: 'SQL String Addition',\n category: 'injection',\n pattern: /(SELECT|INSERT|UPDATE|DELETE).*[\"']\\s*\\+\\s*\\w+/gi,\n severity: 'critical',\n description: 'SQL built with string concatenation',\n },\n // XSS Vulnerabilities\n {\n name: 'React dangerouslySetInnerHTML',\n category: 'xss',\n pattern: /dangerouslySetInnerHTML\\s*=\\s*\\{\\s*\\{\\s*__html/g,\n severity: 'high',\n description: 'Renders raw HTML - ensure input is sanitized',\n },\n {\n name: 'Direct innerHTML Assignment',\n category: 'xss',\n pattern: /\\.innerHTML\\s*=/g,\n severity: 'high',\n description: 'Direct HTML injection - use textContent instead',\n },\n {\n name: 'Vue v-html Directive',\n category: 'xss',\n pattern: /v-html\\s*=\\s*[\"'][^\"']+[\"']/g,\n severity: 'high',\n description: 'Vue raw HTML binding - ensure input is sanitized',\n },\n {\n name: 'Document Write',\n category: 'xss',\n pattern: /document\\.write\\s*\\(/g,\n severity: 'high',\n description: 'Deprecated and potentially dangerous',\n },\n // Eval and code execution\n {\n name: 'Eval Usage',\n category: 'injection',\n pattern: /\\beval\\s*\\(/g,\n severity: 'critical',\n description: 'Code execution - major security risk',\n },\n {\n name: 'Function Constructor',\n category: 'injection',\n pattern: /new\\s+Function\\s*\\(/g,\n severity: 'high',\n description: 'Dynamic code execution risk',\n },\n];\n\n/**\n * Files/patterns to ignore\n */\nexport const IGNORE_PATTERNS = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n '.venv',\n '__pycache__',\n '*.min.js',\n '*.min.css',\n '*.map',\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n];\n\n/**\n * File extensions to scan\n */\nexport const SCANNABLE_EXTENSIONS = [\n '.js',\n '.jsx',\n '.ts',\n '.tsx',\n '.mjs',\n '.cjs',\n '.py',\n '.rb',\n '.go',\n '.java',\n '.php',\n '.env',\n '.json',\n '.yaml',\n '.yml',\n '.toml',\n '.xml',\n '.md',\n '.txt',\n '.sql',\n '.sh',\n '.bash',\n '.zsh',\n '.vue',\n '.svelte',\n];\n","/**\n * AI-powered analysis and auto-fix module\n * Uses Cencori API for LLM intelligence\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport type { ScanIssue } from '../scanner/index.js';\n\nconst CENCORI_API_URL = 'https://api.cencori.com/v1';\nconst CONFIG_FILE = '.cencorirc';\n\nexport interface AnalysisResult {\n issue: ScanIssue;\n isFalsePositive: boolean;\n confidence: number;\n reason: string;\n}\n\nexport interface FixResult {\n issue: ScanIssue;\n originalCode: string;\n fixedCode: string;\n explanation: string;\n applied: boolean;\n}\n\n/**\n * Get the config file path\n */\nfunction getConfigPath(): string {\n return path.join(os.homedir(), CONFIG_FILE);\n}\n\n/**\n * Load API key from config file\n */\nfunction loadApiKeyFromConfig(): string | undefined {\n try {\n const configPath = getConfigPath();\n if (fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, 'utf-8');\n const lines = content.split('\\n');\n for (const line of lines) {\n if (line.startsWith('api_key=')) {\n return line.slice('api_key='.length).trim();\n }\n }\n }\n } catch {\n // Ignore config read errors\n }\n return undefined;\n}\n\n/**\n * Save API key to config file\n */\nexport function saveApiKey(apiKey: string): void {\n const configPath = getConfigPath();\n fs.writeFileSync(configPath, `api_key=${apiKey}\\n`, { mode: 0o600 });\n}\n\n/**\n * Get API key (from env var, config file, or undefined)\n */\nexport function getApiKey(): string | undefined {\n // Priority: env var > config file\n return process.env.CENCORI_API_KEY || loadApiKeyFromConfig();\n}\n\n/**\n * Set API key for current session (used after prompting user)\n */\nlet sessionApiKey: string | undefined;\n\nexport function setSessionApiKey(apiKey: string): void {\n sessionApiKey = apiKey;\n}\n\n/**\n * Get API key including session key\n */\nfunction getEffectiveApiKey(): string | undefined {\n return sessionApiKey || getApiKey();\n}\n\n/**\n * Check if AI features are available\n */\nexport function isAIAvailable(): boolean {\n return !!getEffectiveApiKey();\n}\n\n/**\n * Validate API key by making a test request\n */\nexport async function validateApiKey(apiKey: string): Promise<boolean> {\n try {\n const response = await fetch(`${CENCORI_API_URL}/models`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n },\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Analyze issues with AI to filter false positives\n */\nexport async function analyzeIssues(\n issues: ScanIssue[],\n fileContents: Map<string, string>\n): Promise<AnalysisResult[]> {\n const apiKey = getEffectiveApiKey();\n if (!apiKey) {\n throw new Error('No API key available');\n }\n\n const results: AnalysisResult[] = [];\n\n for (const issue of issues) {\n const content = fileContents.get(issue.file) || '';\n const lines = content.split('\\n');\n const startLine = Math.max(0, issue.line - 3);\n const endLine = Math.min(lines.length, issue.line + 3);\n const context = lines.slice(startLine, endLine).join('\\n');\n\n try {\n const response = await fetch(`${CENCORI_API_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: 'meta-llama/llama-4-scout-17b-16e-instruct',\n messages: [\n {\n role: 'system',\n content: `You are a security analyst. Analyze code findings and determine if they are real security issues or false positives. Respond in JSON format: {\"isFalsePositive\": boolean, \"confidence\": number (0-100), \"reason\": \"brief explanation\"}`,\n },\n {\n role: 'user',\n content: `Analyze this security finding:\nType: ${issue.type}\nName: ${issue.name}\nMatch: ${issue.match}\nFile: ${issue.file}:${issue.line}\nContext:\n\\`\\`\\`\n${context}\n\\`\\`\\`\n\nIs this a real security issue or a false positive (e.g., test data, example code, documentation)?`,\n },\n ],\n temperature: 0,\n max_tokens: 150,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n };\n const content_response = data.choices[0]?.message?.content || '{}';\n\n // Parse JSON response\n const parsed = JSON.parse(content_response);\n results.push({\n issue,\n isFalsePositive: parsed.isFalsePositive || false,\n confidence: parsed.confidence || 50,\n reason: parsed.reason || 'Unable to analyze',\n });\n } catch {\n // If analysis fails, assume it's a real issue\n results.push({\n issue,\n isFalsePositive: false,\n confidence: 50,\n reason: 'Analysis failed - treating as potential issue',\n });\n }\n }\n\n return results;\n}\n\n/**\n * Generate fixes for issues using AI\n */\nexport async function generateFixes(\n issues: ScanIssue[],\n fileContents: Map<string, string>\n): Promise<FixResult[]> {\n const apiKey = getEffectiveApiKey();\n if (!apiKey) {\n throw new Error('No API key available');\n }\n\n const results: FixResult[] = [];\n\n for (const issue of issues) {\n const content = fileContents.get(issue.file) || '';\n const lines = content.split('\\n');\n const startLine = Math.max(0, issue.line - 5);\n const endLine = Math.min(lines.length, issue.line + 5);\n const codeSnippet = lines.slice(startLine, endLine).join('\\n');\n\n try {\n const response = await fetch(`${CENCORI_API_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: 'meta-llama/llama-4-scout-17b-16e-instruct',\n messages: [\n {\n role: 'system',\n content: `You are a security engineer. Generate secure code fixes. For secrets, use environment variables. For XSS, use sanitization. Respond in JSON: {\"fixedCode\": \"the fixed code snippet\", \"explanation\": \"what was changed\"}`,\n },\n {\n role: 'user',\n content: `Fix this security issue:\nType: ${issue.type}\nName: ${issue.name}\nFile: ${issue.file}:${issue.line}\n\nCode to fix:\n\\`\\`\\`\n${codeSnippet}\n\\`\\`\\`\n\nGenerate a secure fix.`,\n },\n ],\n temperature: 0,\n max_tokens: 500,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n };\n const content_response = data.choices[0]?.message?.content || '{}';\n\n const parsed = JSON.parse(content_response);\n results.push({\n issue,\n originalCode: codeSnippet,\n fixedCode: parsed.fixedCode || codeSnippet,\n explanation: parsed.explanation || 'No explanation provided',\n applied: false,\n });\n } catch {\n results.push({\n issue,\n originalCode: codeSnippet,\n fixedCode: codeSnippet,\n explanation: 'Unable to generate fix - manual review required',\n applied: false,\n });\n }\n }\n\n return results;\n}\n\n/**\n * Apply fixes to files\n */\nexport async function applyFixes(\n fixes: FixResult[],\n fileContents: Map<string, string>\n): Promise<FixResult[]> {\n for (const fix of fixes) {\n if (fix.fixedCode === fix.originalCode) {\n continue;\n }\n\n const content = fileContents.get(fix.issue.file);\n if (!content) {\n continue;\n }\n\n // Replace the original code with the fixed code\n const newContent = content.replace(fix.originalCode, fix.fixedCode);\n\n if (newContent !== content) {\n const filePath = path.resolve(fix.issue.file);\n fs.writeFileSync(filePath, newContent, 'utf-8');\n fix.applied = true;\n }\n }\n\n return fixes;\n}\n","/**\n * Silent telemetry module for Cencori Scan\n * Sends anonymous usage metrics - no code or sensitive data\n */\n\nconst TELEMETRY_URL = 'https://api.cencori.com/v1/telemetry/scan';\n\nexport interface TelemetryData {\n event: 'scan_completed';\n version: string;\n platform: string;\n filesScanned: number;\n issuesFound: number;\n score: string;\n hasApiKey: boolean;\n scanDuration: number;\n issueBreakdown: {\n secrets: number;\n pii: number;\n routes: number;\n config: number;\n vulnerabilities: number;\n };\n}\n\n// Store the pending telemetry promise so we can ensure it completes before exit\nlet pendingTelemetry: Promise<void> | null = null;\n\n/**\n * Send telemetry data in the background\n * Returns a promise that resolves when the request completes\n */\nexport function sendTelemetry(data: TelemetryData): Promise<void> {\n pendingTelemetry = fetch(TELEMETRY_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data),\n })\n .then(() => {\n // Success - do nothing\n })\n .catch(() => {\n // Silently ignore any errors\n // Telemetry should never affect user experience\n });\n\n return pendingTelemetry;\n}\n\n/**\n * Wait for any pending telemetry to complete\n * Call this before process exit to ensure telemetry is sent\n */\nexport async function flushTelemetry(): Promise<void> {\n if (pendingTelemetry) {\n await pendingTelemetry;\n pendingTelemetry = null;\n }\n}\n\n/**\n * Build telemetry data from scan result\n */\nexport function buildTelemetryData(\n result: {\n filesScanned: number;\n issues: Array<{ type: string }>;\n score: string;\n scanDuration: number;\n },\n version: string,\n hasApiKey: boolean\n): TelemetryData {\n // Count issues by type\n const breakdown = {\n secrets: 0,\n pii: 0,\n routes: 0,\n config: 0,\n vulnerabilities: 0,\n };\n\n for (const issue of result.issues) {\n const type = issue.type as keyof typeof breakdown;\n if (type in breakdown) {\n breakdown[type]++;\n }\n }\n\n return {\n event: 'scan_completed',\n version,\n platform: process.platform,\n filesScanned: result.filesScanned,\n issuesFound: result.issues.length,\n score: result.score,\n hasApiKey,\n scanDuration: result.scanDuration,\n issueBreakdown: breakdown,\n };\n}\n"],"mappings":"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","names":["fs","path","fs","path"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/scanner/index.ts","../src/scanner/patterns.ts","../src/ai/index.ts","../src/telemetry.ts","../src/changelog/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { confirm, password } from '@inquirer/prompts';\nimport { scan, type ScanResult, type ScanIssue } from './scanner/index.js';\nimport {\n getApiKey,\n setSessionApiKey,\n saveApiKey,\n validateApiKey,\n analyzeIssues,\n generateFixes,\n applyFixes,\n} from './ai/index.js';\nimport { sendTelemetry, buildTelemetryData, flushTelemetry } from './telemetry.js';\nimport { generateChangelog } from './changelog/index.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst VERSION = '0.4.0';\n\n// Score colors\nconst scoreStyles: Record<string, { color: typeof chalk.green }> = {\n A: { color: chalk.green },\n B: { color: chalk.blue },\n C: { color: chalk.yellow },\n D: { color: chalk.red },\n F: { color: chalk.bgRed.white },\n};\n\nconst severityColors: Record<string, typeof chalk.red> = {\n critical: chalk.bgRed.white,\n high: chalk.red,\n medium: chalk.yellow,\n low: chalk.blue,\n};\n\nconst typeLabels: Record<string, string> = {\n secret: 'SECRETS',\n pii: 'PII',\n route: 'ROUTES',\n config: 'CONFIG',\n vulnerability: 'VULNERABILITIES',\n};\n\n/**\n * Print the banner\n */\nfunction printBanner(): void {\n console.log();\n console.log(chalk.cyan.bold(' Cencori Scan'));\n console.log(chalk.gray(` v${VERSION}`));\n console.log();\n}\n\n/**\n * Print the score box\n */\nfunction printScore(result: ScanResult): void {\n const style = scoreStyles[result.score];\n const scoreText = `${result.score}-Tier`;\n const content = ` Security Score: ${scoreText}`;\n\n console.log();\n console.log(chalk.gray(' ┌─────────────────────────────────────────────┐'));\n console.log(chalk.gray(' │') + style.color.bold(content.padEnd(45)) + chalk.gray('│'));\n console.log(chalk.gray(' └─────────────────────────────────────────────┘'));\n console.log();\n console.log(chalk.gray(` ${result.tierDescription}`));\n console.log();\n}\n\n/**\n * Print issues grouped by type\n */\nfunction printIssues(issues: ScanIssue[]): void {\n if (issues.length === 0) {\n console.log(chalk.green(' No security issues found.'));\n console.log();\n return;\n }\n\n // Group by type\n const grouped: Record<string, ScanIssue[]> = {};\n for (const issue of issues) {\n if (!grouped[issue.type]) {\n grouped[issue.type] = [];\n }\n grouped[issue.type].push(issue);\n }\n\n // Print each group\n for (const [type, typeIssues] of Object.entries(grouped)) {\n const label = typeLabels[type] || type.toUpperCase();\n\n console.log(` ${chalk.bold(label)} (${typeIssues.length})`);\n\n for (let i = 0; i < typeIssues.length; i++) {\n const issue = typeIssues[i];\n const isLast = i === typeIssues.length - 1;\n const prefix = isLast ? ' └─' : ' ├─';\n const severityColor = severityColors[issue.severity];\n\n console.log(\n chalk.gray(prefix) + ' ' +\n chalk.gray(`${issue.file}:${issue.line}`) + ' ' +\n severityColor(issue.match)\n );\n\n if (issue.description) {\n const descPrefix = isLast ? ' ' : ' │ ';\n console.log(chalk.gray(descPrefix) + chalk.dim(issue.description));\n }\n }\n console.log();\n }\n}\n\n/**\n * Print summary stats\n */\nfunction printSummary(result: ScanResult): void {\n const { summary } = result;\n\n console.log(chalk.gray(' ─────────────────────────────────────────────'));\n console.log();\n console.log(` ${chalk.bold('Summary')}`);\n console.log(` Files scanned: ${chalk.cyan(result.filesScanned)}`);\n console.log(` Scan time: ${chalk.cyan(result.scanDuration + 'ms')}`);\n console.log();\n\n if (summary.critical > 0) {\n console.log(` ${chalk.bgRed.white(' CRITICAL ')} ${summary.critical} issues`);\n }\n if (summary.high > 0) {\n console.log(` ${chalk.red(' HIGH ')} ${summary.high} issues`);\n }\n if (summary.medium > 0) {\n console.log(` ${chalk.yellow(' MEDIUM ')} ${summary.medium} issues`);\n }\n if (summary.low > 0) {\n console.log(` ${chalk.blue(' LOW ')} ${summary.low} issues`);\n }\n console.log();\n}\n\n/**\n * Print recommendations\n */\nfunction printRecommendations(issues: ScanIssue[]): void {\n if (issues.length === 0) return;\n\n console.log(` ${chalk.bold('Recommendations:')}`);\n\n const hasSecrets = issues.some(i => i.type === 'secret');\n const hasPII = issues.some(i => i.type === 'pii');\n const hasConfig = issues.some(i => i.type === 'config');\n const hasXSS = issues.some(i => i.category === 'xss');\n const hasInjection = issues.some(i => i.category === 'injection');\n const hasCORS = issues.some(i => i.category === 'cors');\n\n if (hasSecrets) {\n console.log(chalk.gray(' - Use environment variables for secrets'));\n console.log(chalk.gray(' - Never commit API keys to version control'));\n }\n if (hasConfig) {\n console.log(chalk.gray(' - Add .env* to .gitignore'));\n }\n if (hasPII) {\n console.log(chalk.gray(' - Remove personal data from source code'));\n }\n if (hasXSS) {\n console.log(chalk.gray(' - Sanitize user input before rendering HTML'));\n }\n if (hasInjection) {\n console.log(chalk.gray(' - Use parameterized queries for SQL'));\n }\n if (hasCORS) {\n console.log(chalk.gray(' - Configure CORS with specific allowed origins'));\n }\n\n console.log();\n}\n\n/**\n * Print footer with links\n */\nfunction printFooter(): void {\n console.log(chalk.gray(' ─────────────────────────────────────────────'));\n console.log();\n console.log(` Share: ${chalk.cyan('https://scan.cencori.com')}`);\n console.log(` Docs: ${chalk.cyan('https://cencori.com/docs')}`);\n console.log();\n}\n\n/**\n * Load file contents for AI analysis\n */\nfunction loadFileContents(issues: ScanIssue[], basePath: string): Map<string, string> {\n const contents = new Map<string, string>();\n const uniqueFiles = [...new Set(issues.map(i => i.file))];\n\n for (const file of uniqueFiles) {\n try {\n const fullPath = path.resolve(basePath, file);\n const content = fs.readFileSync(fullPath, 'utf-8');\n contents.set(file, content);\n } catch {\n // Skip files that can't be read\n }\n }\n\n return contents;\n}\n\n/**\n * Prompt user for API key (hidden input)\n */\nasync function promptForApiKey(): Promise<string | undefined> {\n console.log();\n console.log(chalk.gray(' ─────────────────────────────────────────────'));\n console.log();\n console.log(` ${chalk.cyan.bold('Cencori Pro')}`);\n console.log(chalk.gray(' AI-powered auto-fix requires an API key.'));\n console.log();\n console.log(` Get your free API key at:`);\n console.log(` ${chalk.cyan('https://cencori.com/dashboard')} → API Keys`);\n console.log();\n\n try {\n const apiKey = await password({\n message: 'Enter your Cencori API key:',\n mask: '*',\n });\n\n if (!apiKey || apiKey.trim() === '') {\n console.log(chalk.yellow(' No API key entered. Skipping auto-fix.'));\n return undefined;\n }\n\n return apiKey.trim();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Handle AI auto-fix flow\n */\nasync function handleAutoFix(\n result: ScanResult,\n targetPath: string\n): Promise<void> {\n if (result.issues.length === 0) return;\n\n console.log();\n\n // Ask user if they want to auto-fix\n const shouldFix = await confirm({\n message: 'Would you like Cencori to auto-fix these issues?',\n default: false,\n });\n\n if (!shouldFix) {\n console.log();\n console.log(chalk.gray(' Skipped auto-fix. Run again anytime to fix issues.'));\n console.log();\n return;\n }\n\n // Check if we have an API key\n let apiKey = getApiKey();\n\n if (!apiKey) {\n // Prompt for API key\n apiKey = await promptForApiKey();\n\n if (!apiKey) {\n console.log();\n return;\n }\n\n // Validate the API key\n const validatingSpinner = ora({\n text: 'Validating API key...',\n color: 'cyan',\n }).start();\n\n const isValid = await validateApiKey(apiKey);\n\n if (!isValid) {\n validatingSpinner.fail('Invalid API key');\n console.log(chalk.red(' The API key could not be validated. Please check and try again.'));\n console.log();\n return;\n }\n\n validatingSpinner.succeed('API key validated');\n\n // Save the API key for future use\n try {\n saveApiKey(apiKey);\n console.log(chalk.green(' ✔ API key saved to ~/.cencorirc'));\n } catch {\n // Non-fatal, just won't be saved\n }\n\n // Set for current session\n setSessionApiKey(apiKey);\n } else {\n console.log(chalk.gray(' Using saved API key...'));\n }\n\n // Load file contents\n const fileContents = loadFileContents(result.issues, targetPath);\n\n // Analyze with AI\n const analyzeSpinner = ora({\n text: 'Analyzing issues with AI...',\n color: 'cyan',\n }).start();\n\n try {\n const analysis = await analyzeIssues(result.issues, fileContents);\n\n // Filter out false positives\n const realIssues = analysis.filter(a => !a.isFalsePositive);\n const falsePositives = analysis.filter(a => a.isFalsePositive);\n\n if (falsePositives.length > 0) {\n analyzeSpinner.succeed(`${chalk.green(falsePositives.length)} false positives filtered`);\n } else {\n analyzeSpinner.succeed('Analysis complete');\n }\n\n if (realIssues.length === 0) {\n console.log(chalk.green(' All issues were false positives!'));\n return;\n }\n\n // Generate fixes\n const fixSpinner = ora({\n text: 'Generating fixes...',\n color: 'cyan',\n }).start();\n\n const fixes = await generateFixes(\n realIssues.map(a => a.issue),\n fileContents\n );\n\n fixSpinner.succeed(`Generated ${fixes.length} fixes`);\n\n // Apply fixes\n const applySpinner = ora({\n text: 'Applying fixes...',\n color: 'cyan',\n }).start();\n\n const appliedFixes = await applyFixes(fixes, fileContents);\n const appliedCount = appliedFixes.filter(f => f.applied).length;\n\n applySpinner.succeed(`Applied ${appliedCount}/${fixes.length} fixes`);\n\n // Show what was fixed\n console.log();\n console.log(` ${chalk.bold('Applied fixes:')}`);\n for (const fix of appliedFixes.filter(f => f.applied)) {\n console.log(chalk.green(` ✔ ${fix.issue.file}:${fix.issue.line}`));\n console.log(chalk.gray(` ${fix.explanation}`));\n }\n\n const notApplied = appliedFixes.filter(f => !f.applied);\n if (notApplied.length > 0) {\n console.log();\n console.log(` ${chalk.yellow(`${notApplied.length} issues require manual review`)}`);\n }\n\n console.log();\n } catch (error) {\n analyzeSpinner.fail('Auto-fix failed');\n console.error(chalk.red(` Error: ${error instanceof Error ? error.message : 'Unknown error'}`));\n console.log();\n }\n}\n\n/**\n * Main CLI function\n */\nasync function main(): Promise<void> {\n program\n .name('cencori-scan')\n .description('Security scanner for AI apps. Detect secrets, PII, and exposed routes.')\n .version(VERSION)\n .argument('[path]', 'Path to scan', '.')\n .option('-j, --json', 'Output results as JSON')\n .option('-q, --quiet', 'Only output the score')\n .option('--no-prompt', 'Skip interactive prompts')\n .option('--no-color', 'Disable colored output')\n .action(async (targetPath: string, options: { json?: boolean; quiet?: boolean; prompt?: boolean }) => {\n if (options.json) {\n const result = await scan(targetPath);\n // Send telemetry for JSON mode too\n sendTelemetry(buildTelemetryData(result, VERSION, !!getApiKey()));\n console.log(JSON.stringify(result, null, 2));\n // Wait for telemetry to complete before exiting\n await flushTelemetry();\n process.exit(result.score === 'A' || result.score === 'B' ? 0 : 1);\n return;\n }\n\n printBanner();\n\n const spinner = ora({\n text: 'Scanning for security issues...',\n color: 'cyan',\n }).start();\n\n try {\n const result = await scan(targetPath);\n\n // Send telemetry silently in background\n sendTelemetry(buildTelemetryData(result, VERSION, !!getApiKey()));\n\n spinner.succeed(`Scanned ${result.filesScanned} files`);\n\n if (options.quiet) {\n const style = scoreStyles[result.score];\n console.log(`\\n Score: ${style.color.bold(result.score + '-Tier')}\\n`);\n // Wait for telemetry to complete before exiting\n await flushTelemetry();\n process.exit(result.score === 'A' || result.score === 'B' ? 0 : 1);\n return;\n }\n\n printScore(result);\n printIssues(result.issues);\n printSummary(result);\n printRecommendations(result.issues);\n\n // Interactive auto-fix prompt (unless --no-prompt)\n if (options.prompt !== false && result.issues.length > 0) {\n await handleAutoFix(result, targetPath);\n }\n\n printFooter();\n\n // Wait for telemetry to complete before exiting\n await flushTelemetry();\n process.exit(result.score === 'A' || result.score === 'B' ? 0 : 1);\n } catch (error) {\n spinner.fail('Scan failed');\n console.error(chalk.red(`\\n Error: ${error instanceof Error ? error.message : 'Unknown error'}`));\n // Wait for any pending telemetry before exiting\n await flushTelemetry();\n process.exit(1);\n }\n });\n\n // Changelog command\n program\n .command('changelog')\n .description('Generate AI-powered changelog from git commits')\n .option('-s, --since <time>', 'Time range for commits', '1 week ago')\n .option('-o, --output <file>', 'Output to file instead of stdout')\n .option('-f, --format <format>', 'Output format (markdown or json)', 'markdown')\n .action(async (options: { since: string; output?: string; format: 'markdown' | 'json' }) => {\n printBanner();\n\n const spinner = ora({\n text: 'Reading git history...',\n color: 'cyan',\n }).start();\n\n try {\n const apiKey = getApiKey();\n const result = await generateChangelog(options.since, apiKey || undefined);\n\n if (result.commitCount === 0) {\n spinner.warn('No commits found in the specified period');\n console.log(chalk.yellow(`\\n Try a larger time range with --since \"2 weeks ago\"\\n`));\n process.exit(0);\n return;\n }\n\n spinner.succeed(`Found ${result.commitCount} commits`);\n\n if (options.format === 'json') {\n const output = JSON.stringify(result, null, 2);\n if (options.output) {\n fs.writeFileSync(options.output, output);\n console.log(chalk.green(`\\n ✔ Changelog saved to ${options.output}\\n`));\n } else {\n console.log(output);\n }\n } else {\n if (options.output) {\n fs.writeFileSync(options.output, result.markdown);\n console.log(chalk.green(`\\n ✔ Changelog saved to ${options.output}\\n`));\n } else {\n console.log('\\n' + result.markdown);\n }\n }\n\n // Upsell for Pro tier\n if (!apiKey) {\n console.log(chalk.gray(' ─────────────────────────────────────────────'));\n console.log(chalk.cyan('\\n 💡 Want AI-enhanced changelogs?'));\n console.log(chalk.gray(' Get human-readable summaries with a Cencori API key'));\n console.log(chalk.gray(' Sign up free at https://cencori.com\\n'));\n }\n\n process.exit(0);\n } catch (error) {\n spinner.fail('Changelog generation failed');\n console.error(chalk.red(`\\n Error: ${error instanceof Error ? error.message : 'Unknown error'}`));\n process.exit(1);\n }\n });\n\n program.parse();\n}\n\nmain();\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport { glob } from 'glob';\nimport {\n SECRET_PATTERNS,\n PII_PATTERNS,\n ROUTE_PATTERNS,\n VULNERABILITY_PATTERNS,\n IGNORE_PATTERNS,\n SCANNABLE_EXTENSIONS,\n} from './patterns';\n\nexport type IssueType = 'secret' | 'pii' | 'route' | 'config' | 'vulnerability';\nexport type IssueSeverity = 'critical' | 'high' | 'medium' | 'low';\n\nexport interface ScanIssue {\n type: IssueType;\n category?: string;\n severity: IssueSeverity;\n name: string;\n provider?: string;\n file: string;\n line: number;\n column: number;\n match: string;\n description?: string;\n}\n\nexport interface ScanResult {\n score: 'A' | 'B' | 'C' | 'D' | 'F';\n tierDescription: string;\n issues: ScanIssue[];\n filesScanned: number;\n scanDuration: number;\n summary: {\n secrets: number;\n pii: number;\n routes: number;\n config: number;\n vulnerabilities: number;\n critical: number;\n high: number;\n medium: number;\n low: number;\n };\n}\n\n/**\n * Redact sensitive content for display\n */\nfunction redact(match: string, showChars: number = 4): string {\n if (match.length <= showChars * 2) {\n return '*'.repeat(match.length);\n }\n return match.slice(0, showChars) + '****' + match.slice(-showChars);\n}\n\n/**\n * Get line and column number for a match index\n */\nfunction getPosition(content: string, index: number): { line: number; column: number } {\n const lines = content.slice(0, index).split('\\n');\n return {\n line: lines.length,\n column: lines[lines.length - 1].length + 1,\n };\n}\n\n/**\n * Check if a file should be ignored\n */\nfunction shouldIgnore(filePath: string): boolean {\n const normalized = filePath.replace(/\\\\/g, '/');\n return IGNORE_PATTERNS.some(pattern => {\n if (pattern.startsWith('*')) {\n return normalized.endsWith(pattern.slice(1));\n }\n return normalized.includes(pattern);\n });\n}\n\n/**\n * Check if file has scannable extension\n */\nfunction isScannable(filePath: string): boolean {\n const ext = path.extname(filePath).toLowerCase();\n return SCANNABLE_EXTENSIONS.includes(ext);\n}\n\n/**\n * Check if file is a documentation or test file\n */\nfunction isDocOrTestFile(filePath: string): boolean {\n const lower = filePath.toLowerCase();\n return (\n lower.includes('.test.') ||\n lower.includes('.spec.') ||\n lower.includes('__tests__') ||\n lower.includes('/test/') ||\n lower.includes('/tests/') ||\n lower.endsWith('.md') ||\n lower.includes('/docs/')\n );\n}\n\n/**\n * Scan a single file for issues\n */\nfunction scanFile(filePath: string, content: string): ScanIssue[] {\n const issues: ScanIssue[] = [];\n const relativePath = filePath;\n const isDocFile = isDocOrTestFile(filePath);\n\n // Scan for secrets\n for (const pattern of SECRET_PATTERNS) {\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'secret',\n severity: pattern.severity,\n name: pattern.name,\n provider: pattern.provider,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: redact(match[0]),\n });\n }\n }\n\n // Scan for PII (skip in doc files)\n if (!isDocFile) {\n for (const pattern of PII_PATTERNS) {\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n const matchStr = match[0];\n if (isLikelyFalsePositive(matchStr, pattern.name, filePath)) {\n continue;\n }\n\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'pii',\n severity: pattern.severity,\n name: pattern.name,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: redact(matchStr, 3),\n });\n }\n }\n }\n\n // Scan for exposed routes\n for (const pattern of ROUTE_PATTERNS) {\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'route',\n severity: pattern.severity,\n name: pattern.name,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: match[0],\n description: pattern.description,\n });\n }\n }\n\n // Scan for vulnerabilities (skip debug checks in test files)\n for (const pattern of VULNERABILITY_PATTERNS) {\n // Skip debug pattern checks in test/doc files\n if (pattern.category === 'debug' && isDocFile) {\n continue;\n }\n\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n // Skip console.log false positives\n if (pattern.category === 'debug' && pattern.name === 'Console Log Statement') {\n // Allow console.error and console.warn\n if (match[0].includes('error') || match[0].includes('warn')) {\n continue;\n }\n }\n\n const pos = getPosition(content, match.index);\n issues.push({\n type: 'vulnerability',\n category: pattern.category,\n severity: pattern.severity,\n name: pattern.name,\n file: relativePath,\n line: pos.line,\n column: pos.column,\n match: match[0].length > 50 ? match[0].slice(0, 50) + '...' : match[0],\n description: pattern.description,\n });\n }\n }\n\n // Check for .env files\n const fileName = path.basename(filePath);\n if (fileName.startsWith('.env') && !fileName.includes('.example')) {\n issues.push({\n type: 'config',\n severity: 'high',\n name: 'Environment file in repository',\n file: relativePath,\n line: 1,\n column: 1,\n match: fileName,\n description: 'Add .env* to .gitignore',\n });\n }\n\n return issues;\n}\n\n/**\n * Filter out likely false positives\n */\nfunction isLikelyFalsePositive(match: string, patternName: string, filePath: string): boolean {\n // Email false positives\n if (patternName === 'Email Address') {\n const falseDomains = ['example.com', 'example.org', 'test.com', 'localhost', 'placeholder.com'];\n if (falseDomains.some(d => match.includes(d))) {\n return true;\n }\n\n const publicPrefixes = [\n 'support@', 'help@', 'info@', 'contact@', 'sales@', 'admin@',\n 'noreply@', 'no-reply@', 'hello@', 'team@', 'partners@',\n 'enterprise@', 'security@', 'privacy@', 'legal@',\n ];\n if (publicPrefixes.some(p => match.toLowerCase().startsWith(p))) {\n return true;\n }\n }\n\n // IP address false positives\n if (patternName === 'IP Address') {\n const falseIPs = ['0.0.0.0', '127.0.0.1', '192.168.', '10.0.', '172.16.'];\n if (falseIPs.some(ip => match.startsWith(ip))) {\n return true;\n }\n }\n\n // Phone number false positives\n if (patternName.includes('Phone Number')) {\n if (match.includes('555') || match.includes('123-456') || match.includes('000-000')) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Calculate the security score\n */\nfunction calculateScore(issues: ScanIssue[]): 'A' | 'B' | 'C' | 'D' | 'F' {\n const critical = issues.filter(i => i.severity === 'critical').length;\n const high = issues.filter(i => i.severity === 'high').length;\n const medium = issues.filter(i => i.severity === 'medium').length;\n\n if (critical > 0) return 'F';\n if (high >= 3) return 'F';\n if (high >= 2) return 'D';\n if (high >= 1 || medium >= 5) return 'C';\n if (medium >= 2) return 'B';\n if (issues.length === 0) return 'A';\n return 'B';\n}\n\n/**\n * Get tier description\n */\nfunction getTierDescription(score: string): string {\n switch (score) {\n case 'A': return 'Excellent! No security issues detected.';\n case 'B': return 'Good, but minor improvements recommended.';\n case 'C': return 'Fair. Some security concerns need attention.';\n case 'D': return 'Poor. Significant security issues detected.';\n case 'F': return 'Critical! Major security vulnerabilities found.';\n default: return '';\n }\n}\n\n/**\n * Main scan function\n */\nexport async function scan(targetPath: string): Promise<ScanResult> {\n const startTime = Date.now();\n const absolutePath = path.resolve(targetPath);\n\n const files = await glob('**/*', {\n cwd: absolutePath,\n nodir: true,\n ignore: IGNORE_PATTERNS,\n absolute: true,\n });\n\n const issues: ScanIssue[] = [];\n let filesScanned = 0;\n\n for (const file of files) {\n if (!isScannable(file) || shouldIgnore(file)) {\n continue;\n }\n\n try {\n const content = fs.readFileSync(file, 'utf-8');\n const relativePath = path.relative(absolutePath, file);\n const fileIssues = scanFile(relativePath, content);\n issues.push(...fileIssues);\n filesScanned++;\n } catch {\n continue;\n }\n }\n\n const score = calculateScore(issues);\n const scanDuration = Date.now() - startTime;\n\n return {\n score,\n tierDescription: getTierDescription(score),\n issues,\n filesScanned,\n scanDuration,\n summary: {\n secrets: issues.filter(i => i.type === 'secret').length,\n pii: issues.filter(i => i.type === 'pii').length,\n routes: issues.filter(i => i.type === 'route').length,\n config: issues.filter(i => i.type === 'config').length,\n vulnerabilities: issues.filter(i => i.type === 'vulnerability').length,\n critical: issues.filter(i => i.severity === 'critical').length,\n high: issues.filter(i => i.severity === 'high').length,\n medium: issues.filter(i => i.severity === 'medium').length,\n low: issues.filter(i => i.severity === 'low').length,\n },\n };\n}\n","/**\n * Secret detection patterns for common API keys and tokens\n */\nexport interface SecretPattern {\n name: string;\n provider: string;\n pattern: RegExp;\n severity: 'critical' | 'high' | 'medium' | 'low';\n}\n\nexport const SECRET_PATTERNS: SecretPattern[] = [\n // OpenAI\n {\n name: 'OpenAI API Key',\n provider: 'OpenAI',\n pattern: /sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20}/g,\n severity: 'critical',\n },\n {\n name: 'OpenAI Project Key',\n provider: 'OpenAI',\n pattern: /sk-proj-[a-zA-Z0-9_-]{80,}/g,\n severity: 'critical',\n },\n // Anthropic\n {\n name: 'Anthropic API Key',\n provider: 'Anthropic',\n pattern: /sk-ant-[a-zA-Z0-9-]{90,}/g,\n severity: 'critical',\n },\n // Google\n {\n name: 'Google API Key',\n provider: 'Google',\n pattern: /AIza[0-9A-Za-z_-]{35}/g,\n severity: 'critical',\n },\n // Supabase\n {\n name: 'Supabase Service Role Key',\n provider: 'Supabase',\n pattern: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\\.[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n severity: 'critical',\n },\n {\n name: 'Supabase Anon Key (if hardcoded)',\n provider: 'Supabase',\n pattern: /SUPABASE_ANON_KEY\\s*[:=]\\s*[\"']eyJ[^\"']+[\"']/g,\n severity: 'medium',\n },\n // Stripe\n {\n name: 'Stripe Secret Key',\n provider: 'Stripe',\n pattern: /sk_live_[0-9a-zA-Z]{24,}/g,\n severity: 'critical',\n },\n {\n name: 'Stripe Test Key',\n provider: 'Stripe',\n pattern: /sk_test_[0-9a-zA-Z]{24,}/g,\n severity: 'medium',\n },\n {\n name: 'Stripe Webhook Secret',\n provider: 'Stripe',\n pattern: /whsec_[a-zA-Z0-9]{24,}/g,\n severity: 'critical',\n },\n // AWS\n {\n name: 'AWS Access Key ID',\n provider: 'AWS',\n pattern: /AKIA[0-9A-Z]{16}/g,\n severity: 'critical',\n },\n {\n name: 'AWS Secret Access Key',\n provider: 'AWS',\n pattern: /aws_secret_access_key\\s*[:=]\\s*[\"'][A-Za-z0-9/+=]{40}[\"']/gi,\n severity: 'critical',\n },\n // GitHub\n {\n name: 'GitHub Personal Access Token',\n provider: 'GitHub',\n pattern: /ghp_[a-zA-Z0-9]{36}/g,\n severity: 'critical',\n },\n {\n name: 'GitHub OAuth Token',\n provider: 'GitHub',\n pattern: /gho_[a-zA-Z0-9]{36}/g,\n severity: 'critical',\n },\n {\n name: 'GitHub Webhook Secret',\n provider: 'GitHub',\n pattern: /sha256=[a-fA-F0-9]{64}/g,\n severity: 'high',\n },\n // Telegram\n {\n name: 'Telegram Bot Token',\n provider: 'Telegram',\n pattern: /[0-9]{9,10}:[a-zA-Z0-9_-]{35}/g,\n severity: 'high',\n },\n // Discord\n {\n name: 'Discord Bot Token',\n provider: 'Discord',\n pattern: /[MN][A-Za-z\\d]{23,}\\.[\\w-]{6}\\.[\\w-]{27}/g,\n severity: 'high',\n },\n // Slack\n {\n name: 'Slack Bot Token',\n provider: 'Slack',\n pattern: /xoxb-[0-9]{11}-[0-9]{11}-[a-zA-Z0-9]{24}/g,\n severity: 'high',\n },\n // SendGrid\n {\n name: 'SendGrid API Key',\n provider: 'SendGrid',\n pattern: /SG\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9_-]{43}/g,\n severity: 'high',\n },\n // Twilio\n {\n name: 'Twilio API Key',\n provider: 'Twilio',\n pattern: /SK[a-fA-F0-9]{32}/g,\n severity: 'high',\n },\n // Mailgun\n {\n name: 'Mailgun API Key',\n provider: 'Mailgun',\n pattern: /key-[a-zA-Z0-9]{32}/g,\n severity: 'high',\n },\n // Firebase\n {\n name: 'Firebase Database URL',\n provider: 'Firebase',\n pattern: /https:\\/\\/[a-z0-9-]+\\.firebaseio\\.com/g,\n severity: 'medium',\n },\n // Generic patterns\n {\n name: 'Private Key',\n provider: 'Generic',\n pattern: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,\n severity: 'critical',\n },\n {\n name: 'Generic API Key Assignment',\n provider: 'Generic',\n pattern: /(api_key|apikey|api_secret|secret_key)\\s*[:=]\\s*[\"'][a-zA-Z0-9_-]{20,}[\"']/gi,\n severity: 'high',\n },\n {\n name: 'Password Assignment',\n provider: 'Generic',\n pattern: /(password|passwd|pwd)\\s*[:=]\\s*[\"'][^\"']{8,}[\"']/gi,\n severity: 'high',\n },\n // Replicate\n {\n name: 'Replicate API Token',\n provider: 'Replicate',\n pattern: /r8_[a-zA-Z0-9]{38}/g,\n severity: 'critical',\n },\n // Hugging Face\n {\n name: 'Hugging Face Token',\n provider: 'Hugging Face',\n pattern: /hf_[a-zA-Z0-9]{34}/g,\n severity: 'critical',\n },\n // JWT Secrets\n {\n name: 'JWT Secret Assignment',\n provider: 'Generic',\n pattern: /JWT_SECRET\\s*[:=]\\s*[\"'][^\"']{16,}[\"']/gi,\n severity: 'critical',\n },\n {\n name: 'Hardcoded JWT Sign',\n provider: 'Generic',\n pattern: /jwt\\.(sign|verify)\\s*\\([^,]+,\\s*[\"'][^\"']{10,}[\"']/gi,\n severity: 'critical',\n },\n // OAuth Secrets\n {\n name: 'OAuth Client Secret',\n provider: 'Generic',\n pattern: /client_secret\\s*[:=]\\s*[\"'][a-zA-Z0-9_-]{20,}[\"']/gi,\n severity: 'critical',\n },\n {\n name: 'Google Client Secret',\n provider: 'Google',\n pattern: /GOOGLE_CLIENT_SECRET\\s*[:=]\\s*[\"'][^\"']+[\"']/gi,\n severity: 'critical',\n },\n // Database Connection Strings\n {\n name: 'MongoDB Connection String',\n provider: 'MongoDB',\n pattern: /mongodb(\\+srv)?:\\/\\/[^@\\s]+@[^\\s\"']+/g,\n severity: 'critical',\n },\n {\n name: 'PostgreSQL Connection String',\n provider: 'PostgreSQL',\n pattern: /postgres(ql)?:\\/\\/[^\\s\"']+/g,\n severity: 'critical',\n },\n {\n name: 'MySQL Connection String',\n provider: 'MySQL',\n pattern: /mysql:\\/\\/[^\\s\"']+/g,\n severity: 'critical',\n },\n {\n name: 'Redis Connection String',\n provider: 'Redis',\n pattern: /redis:\\/\\/[^\\s\"']+/g,\n severity: 'high',\n },\n];\n\n/**\n * PII detection patterns\n */\nexport interface PIIPattern {\n name: string;\n pattern: RegExp;\n severity: 'high' | 'medium' | 'low';\n}\n\nexport const PII_PATTERNS: PIIPattern[] = [\n {\n name: 'Email Address',\n pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g,\n severity: 'medium',\n },\n {\n name: 'Phone Number (US)',\n pattern: /(\\+1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g,\n severity: 'medium',\n },\n {\n name: 'Phone Number (International)',\n pattern: /\\+[1-9]\\d{1,14}/g,\n severity: 'medium',\n },\n {\n name: 'Social Security Number',\n pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n severity: 'high',\n },\n {\n name: 'Credit Card Number',\n pattern: /\\b(?:\\d{4}[-\\s]?){3}\\d{4}\\b/g,\n severity: 'high',\n },\n {\n name: 'IP Address',\n pattern: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n severity: 'low',\n },\n];\n\n/**\n * Exposed route patterns for common frameworks\n */\nexport interface RoutePattern {\n name: string;\n framework: string;\n pattern: RegExp;\n severity: 'high' | 'medium' | 'low';\n description: string;\n}\n\nexport const ROUTE_PATTERNS: RoutePattern[] = [\n // Next.js API routes\n {\n name: 'Next.js API Route (check for auth)',\n framework: 'Next.js',\n pattern: /export\\s+(async\\s+)?function\\s+(GET|POST|PUT|DELETE|PATCH)\\s*\\(/g,\n severity: 'medium',\n description: 'API route handler - verify authentication is implemented',\n },\n // Express routes\n {\n name: 'Express Route without Auth Middleware',\n framework: 'Express',\n pattern: /app\\.(get|post|put|delete|patch)\\s*\\(\\s*[\"'`][^\"'`]+[\"'`]\\s*,\\s*(?!.*auth)/gi,\n severity: 'medium',\n description: 'Express route - check if auth middleware is applied',\n },\n // Admin routes\n {\n name: 'Admin Route Exposed',\n framework: 'Generic',\n pattern: /[\"'`](\\/admin|\\/dashboard|\\/internal|\\/private)[^\"'`]*[\"'`]/gi,\n severity: 'high',\n description: 'Sensitive route - ensure proper authentication',\n },\n];\n\n/**\n * Security vulnerability patterns\n */\nexport interface VulnerabilityPattern {\n name: string;\n category: string;\n pattern: RegExp;\n severity: 'critical' | 'high' | 'medium' | 'low';\n description: string;\n}\n\nexport const VULNERABILITY_PATTERNS: VulnerabilityPattern[] = [\n // Hardcoded URLs\n {\n name: 'Localhost URL in Code',\n category: 'hardcoded-url',\n pattern: /https?:\\/\\/localhost[:\\d]*/gi,\n severity: 'medium',\n description: 'Development URL - should use environment variables',\n },\n {\n name: 'Staging/Dev URL in Code',\n category: 'hardcoded-url',\n pattern: /https?:\\/\\/(staging\\.|dev\\.|test\\.)[^\\s\"']+/gi,\n severity: 'medium',\n description: 'Non-production URL in code',\n },\n // Debug artifacts (skip console.log - too many false positives for CLI tools)\n {\n name: 'Debug Flag Enabled',\n category: 'debug',\n pattern: /DEBUG\\s*[:=]\\s*(true|1|[\"']true[\"'])/gi,\n severity: 'medium',\n description: 'Debug mode enabled - disable in production',\n },\n {\n name: 'Hardcoded Development Mode',\n category: 'debug',\n pattern: /NODE_ENV\\s*[:=]\\s*[\"']development[\"']/gi,\n severity: 'medium',\n description: 'Hardcoded development mode',\n },\n // CORS issues\n {\n name: 'CORS Wildcard Origin',\n category: 'cors',\n pattern: /Access-Control-Allow-Origin['\":\\s]+\\*/g,\n severity: 'high',\n description: 'Allows requests from any origin - security risk',\n },\n {\n name: 'Permissive CORS Config',\n category: 'cors',\n pattern: /cors\\s*\\(\\s*\\)/g,\n severity: 'medium',\n description: 'CORS with default (permissive) settings',\n },\n // SQL Injection\n {\n name: 'SQL String Concatenation',\n category: 'injection',\n pattern: /query\\s*\\(\\s*[`'\"].*\\$\\{.*\\}/g,\n severity: 'critical',\n description: 'Potential SQL injection - use parameterized queries',\n },\n {\n name: 'SQL String Addition',\n category: 'injection',\n pattern: /(SELECT|INSERT|UPDATE|DELETE).*[\"']\\s*\\+\\s*\\w+/gi,\n severity: 'critical',\n description: 'SQL built with string concatenation',\n },\n // XSS Vulnerabilities\n {\n name: 'React dangerouslySetInnerHTML',\n category: 'xss',\n pattern: /dangerouslySetInnerHTML\\s*=\\s*\\{\\s*\\{\\s*__html/g,\n severity: 'high',\n description: 'Renders raw HTML - ensure input is sanitized',\n },\n {\n name: 'Direct innerHTML Assignment',\n category: 'xss',\n pattern: /\\.innerHTML\\s*=/g,\n severity: 'high',\n description: 'Direct HTML injection - use textContent instead',\n },\n {\n name: 'Vue v-html Directive',\n category: 'xss',\n pattern: /v-html\\s*=\\s*[\"'][^\"']+[\"']/g,\n severity: 'high',\n description: 'Vue raw HTML binding - ensure input is sanitized',\n },\n {\n name: 'Document Write',\n category: 'xss',\n pattern: /document\\.write\\s*\\(/g,\n severity: 'high',\n description: 'Deprecated and potentially dangerous',\n },\n // Eval and code execution\n {\n name: 'Eval Usage',\n category: 'injection',\n pattern: /\\beval\\s*\\(/g,\n severity: 'critical',\n description: 'Code execution - major security risk',\n },\n {\n name: 'Function Constructor',\n category: 'injection',\n pattern: /new\\s+Function\\s*\\(/g,\n severity: 'high',\n description: 'Dynamic code execution risk',\n },\n];\n\n/**\n * Files/patterns to ignore\n */\nexport const IGNORE_PATTERNS = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n '.venv',\n '__pycache__',\n '*.min.js',\n '*.min.css',\n '*.map',\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n];\n\n/**\n * File extensions to scan\n */\nexport const SCANNABLE_EXTENSIONS = [\n '.js',\n '.jsx',\n '.ts',\n '.tsx',\n '.mjs',\n '.cjs',\n '.py',\n '.rb',\n '.go',\n '.java',\n '.php',\n '.env',\n '.json',\n '.yaml',\n '.yml',\n '.toml',\n '.xml',\n '.md',\n '.txt',\n '.sql',\n '.sh',\n '.bash',\n '.zsh',\n '.vue',\n '.svelte',\n];\n","/**\n * AI-powered analysis and auto-fix module\n * Uses Cencori API for LLM intelligence\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport type { ScanIssue } from '../scanner/index.js';\n\nconst CENCORI_API_URL = 'https://api.cencori.com/v1';\nconst CONFIG_FILE = '.cencorirc';\n\nexport interface AnalysisResult {\n issue: ScanIssue;\n isFalsePositive: boolean;\n confidence: number;\n reason: string;\n}\n\nexport interface FixResult {\n issue: ScanIssue;\n originalCode: string;\n fixedCode: string;\n explanation: string;\n applied: boolean;\n}\n\n/**\n * Get the config file path\n */\nfunction getConfigPath(): string {\n return path.join(os.homedir(), CONFIG_FILE);\n}\n\n/**\n * Load API key from config file\n */\nfunction loadApiKeyFromConfig(): string | undefined {\n try {\n const configPath = getConfigPath();\n if (fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, 'utf-8');\n const lines = content.split('\\n');\n for (const line of lines) {\n if (line.startsWith('api_key=')) {\n return line.slice('api_key='.length).trim();\n }\n }\n }\n } catch {\n // Ignore config read errors\n }\n return undefined;\n}\n\n/**\n * Save API key to config file\n */\nexport function saveApiKey(apiKey: string): void {\n const configPath = getConfigPath();\n fs.writeFileSync(configPath, `api_key=${apiKey}\\n`, { mode: 0o600 });\n}\n\n/**\n * Get API key (from env var, config file, or undefined)\n */\nexport function getApiKey(): string | undefined {\n // Priority: env var > config file\n return process.env.CENCORI_API_KEY || loadApiKeyFromConfig();\n}\n\n/**\n * Set API key for current session (used after prompting user)\n */\nlet sessionApiKey: string | undefined;\n\nexport function setSessionApiKey(apiKey: string): void {\n sessionApiKey = apiKey;\n}\n\n/**\n * Get API key including session key\n */\nfunction getEffectiveApiKey(): string | undefined {\n return sessionApiKey || getApiKey();\n}\n\n/**\n * Check if AI features are available\n */\nexport function isAIAvailable(): boolean {\n return !!getEffectiveApiKey();\n}\n\n/**\n * Validate API key by making a test request\n */\nexport async function validateApiKey(apiKey: string): Promise<boolean> {\n try {\n const response = await fetch(`${CENCORI_API_URL}/models`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n },\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Analyze issues with AI to filter false positives\n */\nexport async function analyzeIssues(\n issues: ScanIssue[],\n fileContents: Map<string, string>\n): Promise<AnalysisResult[]> {\n const apiKey = getEffectiveApiKey();\n if (!apiKey) {\n throw new Error('No API key available');\n }\n\n const results: AnalysisResult[] = [];\n\n for (const issue of issues) {\n const content = fileContents.get(issue.file) || '';\n const lines = content.split('\\n');\n const startLine = Math.max(0, issue.line - 3);\n const endLine = Math.min(lines.length, issue.line + 3);\n const context = lines.slice(startLine, endLine).join('\\n');\n\n try {\n const response = await fetch(`${CENCORI_API_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: 'meta-llama/llama-4-scout-17b-16e-instruct',\n messages: [\n {\n role: 'system',\n content: `You are a security analyst. Analyze code findings and determine if they are real security issues or false positives. Respond in JSON format: {\"isFalsePositive\": boolean, \"confidence\": number (0-100), \"reason\": \"brief explanation\"}`,\n },\n {\n role: 'user',\n content: `Analyze this security finding:\nType: ${issue.type}\nName: ${issue.name}\nMatch: ${issue.match}\nFile: ${issue.file}:${issue.line}\nContext:\n\\`\\`\\`\n${context}\n\\`\\`\\`\n\nIs this a real security issue or a false positive (e.g., test data, example code, documentation)?`,\n },\n ],\n temperature: 0,\n max_tokens: 150,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n };\n const content_response = data.choices[0]?.message?.content || '{}';\n\n // Parse JSON response\n const parsed = JSON.parse(content_response);\n results.push({\n issue,\n isFalsePositive: parsed.isFalsePositive || false,\n confidence: parsed.confidence || 50,\n reason: parsed.reason || 'Unable to analyze',\n });\n } catch {\n // If analysis fails, assume it's a real issue\n results.push({\n issue,\n isFalsePositive: false,\n confidence: 50,\n reason: 'Analysis failed - treating as potential issue',\n });\n }\n }\n\n return results;\n}\n\n/**\n * Generate fixes for issues using AI\n */\nexport async function generateFixes(\n issues: ScanIssue[],\n fileContents: Map<string, string>\n): Promise<FixResult[]> {\n const apiKey = getEffectiveApiKey();\n if (!apiKey) {\n throw new Error('No API key available');\n }\n\n const results: FixResult[] = [];\n\n for (const issue of issues) {\n const content = fileContents.get(issue.file) || '';\n const lines = content.split('\\n');\n const startLine = Math.max(0, issue.line - 5);\n const endLine = Math.min(lines.length, issue.line + 5);\n const codeSnippet = lines.slice(startLine, endLine).join('\\n');\n\n try {\n const response = await fetch(`${CENCORI_API_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model: 'meta-llama/llama-4-scout-17b-16e-instruct',\n messages: [\n {\n role: 'system',\n content: `You are a security engineer. Generate secure code fixes. For secrets, use environment variables. For XSS, use sanitization. Respond in JSON: {\"fixedCode\": \"the fixed code snippet\", \"explanation\": \"what was changed\"}`,\n },\n {\n role: 'user',\n content: `Fix this security issue:\nType: ${issue.type}\nName: ${issue.name}\nFile: ${issue.file}:${issue.line}\n\nCode to fix:\n\\`\\`\\`\n${codeSnippet}\n\\`\\`\\`\n\nGenerate a secure fix.`,\n },\n ],\n temperature: 0,\n max_tokens: 500,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`API error: ${response.status}`);\n }\n\n const data = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n };\n const content_response = data.choices[0]?.message?.content || '{}';\n\n const parsed = JSON.parse(content_response);\n results.push({\n issue,\n originalCode: codeSnippet,\n fixedCode: parsed.fixedCode || codeSnippet,\n explanation: parsed.explanation || 'No explanation provided',\n applied: false,\n });\n } catch {\n results.push({\n issue,\n originalCode: codeSnippet,\n fixedCode: codeSnippet,\n explanation: 'Unable to generate fix - manual review required',\n applied: false,\n });\n }\n }\n\n return results;\n}\n\n/**\n * Apply fixes to files\n */\nexport async function applyFixes(\n fixes: FixResult[],\n fileContents: Map<string, string>\n): Promise<FixResult[]> {\n for (const fix of fixes) {\n if (fix.fixedCode === fix.originalCode) {\n continue;\n }\n\n const content = fileContents.get(fix.issue.file);\n if (!content) {\n continue;\n }\n\n // Replace the original code with the fixed code\n const newContent = content.replace(fix.originalCode, fix.fixedCode);\n\n if (newContent !== content) {\n const filePath = path.resolve(fix.issue.file);\n fs.writeFileSync(filePath, newContent, 'utf-8');\n fix.applied = true;\n }\n }\n\n return fixes;\n}\n","/**\n * Silent telemetry module for Cencori Scan\n * Sends anonymous usage metrics - no code or sensitive data\n */\n\nconst TELEMETRY_URL = 'https://api.cencori.com/v1/telemetry/scan';\n\nexport interface TelemetryData {\n event: 'scan_completed';\n version: string;\n platform: string;\n filesScanned: number;\n issuesFound: number;\n score: string;\n hasApiKey: boolean;\n scanDuration: number;\n issueBreakdown: {\n secrets: number;\n pii: number;\n routes: number;\n config: number;\n vulnerabilities: number;\n };\n}\n\n// Store the pending telemetry promise so we can ensure it completes before exit\nlet pendingTelemetry: Promise<void> | null = null;\n\n/**\n * Send telemetry data in the background\n * Returns a promise that resolves when the request completes\n */\nexport function sendTelemetry(data: TelemetryData): Promise<void> {\n pendingTelemetry = fetch(TELEMETRY_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data),\n })\n .then(() => {\n // Success - do nothing\n })\n .catch(() => {\n // Silently ignore any errors\n // Telemetry should never affect user experience\n });\n\n return pendingTelemetry;\n}\n\n/**\n * Wait for any pending telemetry to complete\n * Call this before process exit to ensure telemetry is sent\n */\nexport async function flushTelemetry(): Promise<void> {\n if (pendingTelemetry) {\n await pendingTelemetry;\n pendingTelemetry = null;\n }\n}\n\n/**\n * Build telemetry data from scan result\n */\nexport function buildTelemetryData(\n result: {\n filesScanned: number;\n issues: Array<{ type: string }>;\n score: string;\n scanDuration: number;\n },\n version: string,\n hasApiKey: boolean\n): TelemetryData {\n // Count issues by type\n const breakdown = {\n secrets: 0,\n pii: 0,\n routes: 0,\n config: 0,\n vulnerabilities: 0,\n };\n\n for (const issue of result.issues) {\n const type = issue.type as keyof typeof breakdown;\n if (type in breakdown) {\n breakdown[type]++;\n }\n }\n\n return {\n event: 'scan_completed',\n version,\n platform: process.platform,\n filesScanned: result.filesScanned,\n issuesFound: result.issues.length,\n score: result.score,\n hasApiKey,\n scanDuration: result.scanDuration,\n issueBreakdown: breakdown,\n };\n}\n","/**\n * Changelog generation module\n * Reads git commits and generates AI-powered changelogs\n */\n\nimport { execSync } from 'child_process';\n\nconst CENCORI_API_URL = 'https://api.cencori.com/v1';\n\nexport interface GitCommit {\n hash: string;\n date: string;\n author: string;\n message: string;\n type: 'feat' | 'fix' | 'chore' | 'docs' | 'style' | 'refactor' | 'test' | 'other';\n}\n\nexport interface ChangelogEntry {\n type: 'feature' | 'fix' | 'improvement' | 'docs' | 'other';\n title: string;\n description?: string;\n commits: string[];\n}\n\nexport interface ChangelogResult {\n period: {\n start: string;\n end: string;\n };\n entries: ChangelogEntry[];\n markdown: string;\n commitCount: number;\n}\n\n/**\n * Parse conventional commit type from message\n */\nfunction parseCommitType(message: string): GitCommit['type'] {\n const lower = message.toLowerCase();\n if (lower.startsWith('feat') || lower.startsWith('feature')) return 'feat';\n if (lower.startsWith('fix') || lower.startsWith('bugfix')) return 'fix';\n if (lower.startsWith('chore')) return 'chore';\n if (lower.startsWith('docs')) return 'docs';\n if (lower.startsWith('style')) return 'style';\n if (lower.startsWith('refactor')) return 'refactor';\n if (lower.startsWith('test')) return 'test';\n return 'other';\n}\n\n/**\n * Get commits from git log\n */\nexport function getCommits(since: string = '1 week ago', cwd?: string): GitCommit[] {\n try {\n // Get git log in a parseable format\n const output = execSync(\n `git log --since=\"${since}\" --pretty=format:\"%H|%aI|%an|%s\" --no-merges`,\n {\n cwd: cwd || process.cwd(),\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }\n );\n\n if (!output.trim()) {\n return [];\n }\n\n return output\n .trim()\n .split('\\n')\n .map((line) => {\n const [hash, date, author, ...messageParts] = line.split('|');\n const message = messageParts.join('|'); // Handle messages with | in them\n return {\n hash: hash.substring(0, 7),\n date,\n author,\n message,\n type: parseCommitType(message),\n };\n });\n } catch (error) {\n // Not a git repo or git not available\n return [];\n }\n}\n\n/**\n * Group commits by type (free tier)\n */\nfunction groupCommitsByType(commits: GitCommit[]): ChangelogEntry[] {\n const groups: Record<string, GitCommit[]> = {\n feat: [],\n fix: [],\n docs: [],\n other: [],\n };\n\n for (const commit of commits) {\n if (commit.type === 'feat') {\n groups.feat.push(commit);\n } else if (commit.type === 'fix') {\n groups.fix.push(commit);\n } else if (commit.type === 'docs') {\n groups.docs.push(commit);\n } else {\n groups.other.push(commit);\n }\n }\n\n const entries: ChangelogEntry[] = [];\n\n if (groups.feat.length > 0) {\n entries.push({\n type: 'feature',\n title: 'Features',\n commits: groups.feat.map((c) => c.message),\n });\n }\n\n if (groups.fix.length > 0) {\n entries.push({\n type: 'fix',\n title: 'Bug Fixes',\n commits: groups.fix.map((c) => c.message),\n });\n }\n\n if (groups.docs.length > 0) {\n entries.push({\n type: 'docs',\n title: 'Documentation',\n commits: groups.docs.map((c) => c.message),\n });\n }\n\n if (groups.other.length > 0) {\n entries.push({\n type: 'other',\n title: 'Other Changes',\n commits: groups.other.map((c) => c.message),\n });\n }\n\n return entries;\n}\n\n/**\n * Generate markdown from entries (free tier)\n */\nfunction generateMarkdownFree(entries: ChangelogEntry[], period: { start: string; end: string }): string {\n const startDate = new Date(period.start).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n const endDate = new Date(period.end).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n\n let md = `## Changelog (${startDate} - ${endDate})\\n\\n`;\n\n const typeEmojis: Record<string, string> = {\n feature: '✨',\n fix: '🐛',\n docs: '📚',\n improvement: '🔧',\n other: '📦',\n };\n\n for (const entry of entries) {\n const emoji = typeEmojis[entry.type] || '📦';\n md += `### ${emoji} ${entry.title}\\n\\n`;\n for (const commit of entry.commits) {\n // Clean up conventional commit prefixes\n const cleanMessage = commit\n .replace(/^(feat|fix|chore|docs|style|refactor|test)(\\(.+?\\))?:\\s*/i, '')\n .trim();\n md += `- ${cleanMessage}\\n`;\n }\n md += '\\n';\n }\n\n return md;\n}\n\n/**\n * Generate changelog using AI (Pro tier)\n */\nasync function generateChangelogAI(\n commits: GitCommit[],\n apiKey: string,\n period: { start: string; end: string }\n): Promise<{ entries: ChangelogEntry[]; markdown: string }> {\n const commitMessages = commits.map((c) => c.message).join('\\n');\n\n try {\n const response = await fetch(`${CENCORI_API_URL}/changelog/generate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n commits: commitMessages,\n period,\n }),\n });\n\n if (!response.ok) {\n throw new Error('API request failed');\n }\n\n const data = await response.json() as { entries?: ChangelogEntry[]; markdown?: string };\n return {\n entries: data.entries || [],\n markdown: data.markdown || '',\n };\n } catch {\n // Fallback to free tier if AI fails\n const entries = groupCommitsByType(commits);\n return {\n entries,\n markdown: generateMarkdownFree(entries, period),\n };\n }\n}\n\n/**\n * Generate changelog from commits\n */\nexport async function generateChangelog(\n since: string = '1 week ago',\n apiKey?: string,\n cwd?: string\n): Promise<ChangelogResult> {\n const commits = getCommits(since, cwd);\n\n const now = new Date();\n const sinceDate = new Date();\n\n // Parse \"X weeks ago\" format\n const match = since.match(/(\\d+)\\s*(week|day|month)s?\\s*ago/i);\n if (match) {\n const amount = parseInt(match[1], 10);\n const unit = match[2].toLowerCase();\n if (unit === 'week') {\n sinceDate.setDate(sinceDate.getDate() - amount * 7);\n } else if (unit === 'day') {\n sinceDate.setDate(sinceDate.getDate() - amount);\n } else if (unit === 'month') {\n sinceDate.setMonth(sinceDate.getMonth() - amount);\n }\n } else {\n // Default to 1 week\n sinceDate.setDate(sinceDate.getDate() - 7);\n }\n\n const period = {\n start: sinceDate.toISOString(),\n end: now.toISOString(),\n };\n\n if (commits.length === 0) {\n return {\n period,\n entries: [],\n markdown: `## Changelog\\n\\nNo commits found in the specified period.\\n`,\n commitCount: 0,\n };\n }\n\n if (apiKey) {\n // Pro tier: Use AI\n const { entries, markdown } = await generateChangelogAI(commits, apiKey, period);\n return {\n period,\n entries,\n markdown,\n commitCount: commits.length,\n };\n } else {\n // Free tier: Basic grouping\n const entries = groupCommitsByType(commits);\n return {\n period,\n entries,\n markdown: generateMarkdownFree(entries, period),\n commitCount: commits.length,\n };\n }\n}\n"],"mappings":"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","names":["fs","path","CENCORI_API_URL","fs","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cencori/scan",
3
- "version": "0.3.8",
3
+ "version": "0.4.0",
4
4
  "description": "Security scanner for AI apps. Detect hardcoded secrets, PII leaks, and exposed routes.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",