@cencori/scan 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -88,6 +88,12 @@ var SECRET_PATTERNS = [
88
88
  pattern: /sk_test_[0-9a-zA-Z]{24,}/g,
89
89
  severity: "medium"
90
90
  },
91
+ {
92
+ name: "Stripe Webhook Secret",
93
+ provider: "Stripe",
94
+ pattern: /whsec_[a-zA-Z0-9]{24,}/g,
95
+ severity: "critical"
96
+ },
91
97
  // AWS
92
98
  {
93
99
  name: "AWS Access Key ID",
@@ -114,6 +120,12 @@ var SECRET_PATTERNS = [
114
120
  pattern: /gho_[a-zA-Z0-9]{36}/g,
115
121
  severity: "critical"
116
122
  },
123
+ {
124
+ name: "GitHub Webhook Secret",
125
+ provider: "GitHub",
126
+ pattern: /sha256=[a-fA-F0-9]{64}/g,
127
+ severity: "high"
128
+ },
117
129
  // Telegram
118
130
  {
119
131
  name: "Telegram Bot Token",
@@ -196,13 +208,56 @@ var SECRET_PATTERNS = [
196
208
  pattern: /hf_[a-zA-Z0-9]{34}/g,
197
209
  severity: "critical"
198
210
  },
199
- // Cohere
211
+ // JWT Secrets
200
212
  {
201
- name: "Cohere API Key",
202
- provider: "Cohere",
203
- pattern: /[a-zA-Z0-9]{40}/g,
204
- // Less specific, check context
205
- severity: "medium"
213
+ name: "JWT Secret Assignment",
214
+ provider: "Generic",
215
+ pattern: /JWT_SECRET\s*[:=]\s*["'][^"']{16,}["']/gi,
216
+ severity: "critical"
217
+ },
218
+ {
219
+ name: "Hardcoded JWT Sign",
220
+ provider: "Generic",
221
+ pattern: /jwt\.(sign|verify)\s*\([^,]+,\s*["'][^"']{10,}["']/gi,
222
+ severity: "critical"
223
+ },
224
+ // OAuth Secrets
225
+ {
226
+ name: "OAuth Client Secret",
227
+ provider: "Generic",
228
+ pattern: /client_secret\s*[:=]\s*["'][a-zA-Z0-9_-]{20,}["']/gi,
229
+ severity: "critical"
230
+ },
231
+ {
232
+ name: "Google Client Secret",
233
+ provider: "Google",
234
+ pattern: /GOOGLE_CLIENT_SECRET\s*[:=]\s*["'][^"']+["']/gi,
235
+ severity: "critical"
236
+ },
237
+ // Database Connection Strings
238
+ {
239
+ name: "MongoDB Connection String",
240
+ provider: "MongoDB",
241
+ pattern: /mongodb(\+srv)?:\/\/[^@\s]+@[^\s"']+/g,
242
+ severity: "critical"
243
+ },
244
+ {
245
+ name: "PostgreSQL Connection String",
246
+ provider: "PostgreSQL",
247
+ pattern: /postgres(ql)?:\/\/[^\s"']+/g,
248
+ severity: "critical"
249
+ },
250
+ {
251
+ name: "MySQL Connection String",
252
+ provider: "MySQL",
253
+ pattern: /mysql:\/\/[^\s"']+/g,
254
+ severity: "critical"
255
+ },
256
+ {
257
+ name: "Redis Connection String",
258
+ provider: "Redis",
259
+ pattern: /redis:\/\/[^\s"']+/g,
260
+ severity: "high"
206
261
  }
207
262
  ];
208
263
  var PII_PATTERNS = [
@@ -238,7 +293,7 @@ var PII_PATTERNS = [
238
293
  }
239
294
  ];
240
295
  var ROUTE_PATTERNS = [
241
- // Next.js API routes without auth
296
+ // Next.js API routes
242
297
  {
243
298
  name: "Next.js API Route (check for auth)",
244
299
  framework: "Next.js",
@@ -253,6 +308,120 @@ var ROUTE_PATTERNS = [
253
308
  pattern: /app\.(get|post|put|delete|patch)\s*\(\s*["'`][^"'`]+["'`]\s*,\s*(?!.*auth)/gi,
254
309
  severity: "medium",
255
310
  description: "Express route - check if auth middleware is applied"
311
+ },
312
+ // Admin routes
313
+ {
314
+ name: "Admin Route Exposed",
315
+ framework: "Generic",
316
+ pattern: /["'`](\/admin|\/dashboard|\/internal|\/private)[^"'`]*["'`]/gi,
317
+ severity: "high",
318
+ description: "Sensitive route - ensure proper authentication"
319
+ }
320
+ ];
321
+ var VULNERABILITY_PATTERNS = [
322
+ // Hardcoded URLs
323
+ {
324
+ name: "Localhost URL in Code",
325
+ category: "hardcoded-url",
326
+ pattern: /https?:\/\/localhost[:\d]*/gi,
327
+ severity: "medium",
328
+ description: "Development URL - should use environment variables"
329
+ },
330
+ {
331
+ name: "Staging/Dev URL in Code",
332
+ category: "hardcoded-url",
333
+ pattern: /https?:\/\/(staging\.|dev\.|test\.)[^\s"']+/gi,
334
+ severity: "medium",
335
+ description: "Non-production URL in code"
336
+ },
337
+ // Debug artifacts (skip console.log - too many false positives for CLI tools)
338
+ {
339
+ name: "Debug Flag Enabled",
340
+ category: "debug",
341
+ pattern: /DEBUG\s*[:=]\s*(true|1|["']true["'])/gi,
342
+ severity: "medium",
343
+ description: "Debug mode enabled - disable in production"
344
+ },
345
+ {
346
+ name: "Hardcoded Development Mode",
347
+ category: "debug",
348
+ pattern: /NODE_ENV\s*[:=]\s*["']development["']/gi,
349
+ severity: "medium",
350
+ description: "Hardcoded development mode"
351
+ },
352
+ // CORS issues
353
+ {
354
+ name: "CORS Wildcard Origin",
355
+ category: "cors",
356
+ pattern: /Access-Control-Allow-Origin['":\s]+\*/g,
357
+ severity: "high",
358
+ description: "Allows requests from any origin - security risk"
359
+ },
360
+ {
361
+ name: "Permissive CORS Config",
362
+ category: "cors",
363
+ pattern: /cors\s*\(\s*\)/g,
364
+ severity: "medium",
365
+ description: "CORS with default (permissive) settings"
366
+ },
367
+ // SQL Injection
368
+ {
369
+ name: "SQL String Concatenation",
370
+ category: "injection",
371
+ pattern: /query\s*\(\s*[`'"].*\$\{.*\}/g,
372
+ severity: "critical",
373
+ description: "Potential SQL injection - use parameterized queries"
374
+ },
375
+ {
376
+ name: "SQL String Addition",
377
+ category: "injection",
378
+ pattern: /(SELECT|INSERT|UPDATE|DELETE).*["']\s*\+\s*\w+/gi,
379
+ severity: "critical",
380
+ description: "SQL built with string concatenation"
381
+ },
382
+ // XSS Vulnerabilities
383
+ {
384
+ name: "React dangerouslySetInnerHTML",
385
+ category: "xss",
386
+ pattern: /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html/g,
387
+ severity: "high",
388
+ description: "Renders raw HTML - ensure input is sanitized"
389
+ },
390
+ {
391
+ name: "Direct innerHTML Assignment",
392
+ category: "xss",
393
+ pattern: /\.innerHTML\s*=/g,
394
+ severity: "high",
395
+ description: "Direct HTML injection - use textContent instead"
396
+ },
397
+ {
398
+ name: "Vue v-html Directive",
399
+ category: "xss",
400
+ pattern: /v-html\s*=\s*["'][^"']+["']/g,
401
+ severity: "high",
402
+ description: "Vue raw HTML binding - ensure input is sanitized"
403
+ },
404
+ {
405
+ name: "Document Write",
406
+ category: "xss",
407
+ pattern: /document\.write\s*\(/g,
408
+ severity: "high",
409
+ description: "Deprecated and potentially dangerous"
410
+ },
411
+ // Eval and code execution
412
+ {
413
+ name: "Eval Usage",
414
+ category: "injection",
415
+ pattern: /\beval\s*\(/g,
416
+ severity: "critical",
417
+ description: "Code execution - major security risk"
418
+ },
419
+ {
420
+ name: "Function Constructor",
421
+ category: "injection",
422
+ pattern: /new\s+Function\s*\(/g,
423
+ severity: "high",
424
+ description: "Dynamic code execution risk"
256
425
  }
257
426
  ];
258
427
  var IGNORE_PATTERNS = [
@@ -293,7 +462,9 @@ var SCANNABLE_EXTENSIONS = [
293
462
  ".sql",
294
463
  ".sh",
295
464
  ".bash",
296
- ".zsh"
465
+ ".zsh",
466
+ ".vue",
467
+ ".svelte"
297
468
  ];
298
469
 
299
470
  // src/scanner/index.ts
@@ -323,9 +494,14 @@ function isScannable(filePath) {
323
494
  const ext = path.extname(filePath).toLowerCase();
324
495
  return SCANNABLE_EXTENSIONS.includes(ext);
325
496
  }
497
+ function isDocOrTestFile(filePath) {
498
+ const lower = filePath.toLowerCase();
499
+ return lower.includes(".test.") || lower.includes(".spec.") || lower.includes("__tests__") || lower.includes("/test/") || lower.includes("/tests/") || lower.endsWith(".md") || lower.includes("/docs/");
500
+ }
326
501
  function scanFile(filePath, content) {
327
502
  const issues = [];
328
503
  const relativePath = filePath;
504
+ const isDocFile = isDocOrTestFile(filePath);
329
505
  for (const pattern of SECRET_PATTERNS) {
330
506
  pattern.pattern.lastIndex = 0;
331
507
  let match;
@@ -343,47 +519,73 @@ function scanFile(filePath, content) {
343
519
  });
344
520
  }
345
521
  }
346
- for (const pattern of PII_PATTERNS) {
522
+ if (!isDocFile) {
523
+ for (const pattern of PII_PATTERNS) {
524
+ pattern.pattern.lastIndex = 0;
525
+ let match;
526
+ while ((match = pattern.pattern.exec(content)) !== null) {
527
+ const matchStr = match[0];
528
+ if (isLikelyFalsePositive(matchStr, pattern.name, filePath)) {
529
+ continue;
530
+ }
531
+ const pos = getPosition(content, match.index);
532
+ issues.push({
533
+ type: "pii",
534
+ severity: pattern.severity,
535
+ name: pattern.name,
536
+ file: relativePath,
537
+ line: pos.line,
538
+ column: pos.column,
539
+ match: redact(matchStr, 3)
540
+ });
541
+ }
542
+ }
543
+ }
544
+ for (const pattern of ROUTE_PATTERNS) {
347
545
  pattern.pattern.lastIndex = 0;
348
546
  let match;
349
547
  while ((match = pattern.pattern.exec(content)) !== null) {
350
- const matchStr = match[0];
351
- if (isLikelyFalsePositive(matchStr, pattern.name)) {
352
- continue;
353
- }
354
548
  const pos = getPosition(content, match.index);
355
549
  issues.push({
356
- type: "pii",
550
+ type: "route",
357
551
  severity: pattern.severity,
358
552
  name: pattern.name,
359
553
  file: relativePath,
360
554
  line: pos.line,
361
555
  column: pos.column,
362
- match: redact(matchStr, 3)
556
+ match: match[0],
557
+ description: pattern.description
363
558
  });
364
559
  }
365
560
  }
366
- for (const pattern of ROUTE_PATTERNS) {
561
+ for (const pattern of VULNERABILITY_PATTERNS) {
562
+ if (pattern.category === "debug" && isDocFile) {
563
+ continue;
564
+ }
367
565
  pattern.pattern.lastIndex = 0;
368
566
  let match;
369
567
  while ((match = pattern.pattern.exec(content)) !== null) {
568
+ if (pattern.category === "debug" && pattern.name === "Console Log Statement") {
569
+ if (match[0].includes("error") || match[0].includes("warn")) {
570
+ continue;
571
+ }
572
+ }
370
573
  const pos = getPosition(content, match.index);
371
574
  issues.push({
372
- type: "route",
575
+ type: "vulnerability",
576
+ category: pattern.category,
373
577
  severity: pattern.severity,
374
578
  name: pattern.name,
375
579
  file: relativePath,
376
580
  line: pos.line,
377
581
  column: pos.column,
378
- match: match[0],
582
+ match: match[0].length > 50 ? match[0].slice(0, 50) + "..." : match[0],
379
583
  description: pattern.description
380
584
  });
381
585
  }
382
586
  }
383
587
  const fileName = path.basename(filePath);
384
588
  if (fileName.startsWith(".env") && !fileName.includes(".example")) {
385
- const gitignorePath = path.join(path.dirname(filePath), ".gitignore");
386
- const gitignoreExists = fs.existsSync(gitignorePath);
387
589
  issues.push({
388
590
  type: "config",
389
591
  severity: "high",
@@ -392,20 +594,14 @@ function scanFile(filePath, content) {
392
594
  line: 1,
393
595
  column: 1,
394
596
  match: fileName,
395
- description: gitignoreExists ? "Verify this file is in .gitignore" : "Add .env* to .gitignore"
597
+ description: "Add .env* to .gitignore"
396
598
  });
397
599
  }
398
600
  return issues;
399
601
  }
400
- function isLikelyFalsePositive(match, patternName) {
602
+ function isLikelyFalsePositive(match, patternName, filePath) {
401
603
  if (patternName === "Email Address") {
402
- const falseDomains = [
403
- "example.com",
404
- "example.org",
405
- "test.com",
406
- "localhost",
407
- "placeholder.com"
408
- ];
604
+ const falseDomains = ["example.com", "example.org", "test.com", "localhost", "placeholder.com"];
409
605
  if (falseDomains.some((d) => match.includes(d))) {
410
606
  return true;
411
607
  }
@@ -447,13 +643,12 @@ function calculateScore(issues) {
447
643
  const critical = issues.filter((i) => i.severity === "critical").length;
448
644
  const high = issues.filter((i) => i.severity === "high").length;
449
645
  const medium = issues.filter((i) => i.severity === "medium").length;
450
- const total = issues.length;
451
646
  if (critical > 0) return "F";
452
647
  if (high >= 3) return "F";
453
648
  if (high >= 2) return "D";
454
649
  if (high >= 1 || medium >= 5) return "C";
455
650
  if (medium >= 2) return "B";
456
- if (total === 0) return "A";
651
+ if (issues.length === 0) return "A";
457
652
  return "B";
458
653
  }
459
654
  function getTierDescription(score) {
@@ -467,7 +662,7 @@ function getTierDescription(score) {
467
662
  case "D":
468
663
  return "Poor. Significant security issues detected.";
469
664
  case "F":
470
- return "Critical! Your app is leaking secrets.";
665
+ return "Critical! Major security vulnerabilities found.";
471
666
  default:
472
667
  return "";
473
668
  }
@@ -510,6 +705,7 @@ async function scan(targetPath) {
510
705
  pii: issues.filter((i) => i.type === "pii").length,
511
706
  routes: issues.filter((i) => i.type === "route").length,
512
707
  config: issues.filter((i) => i.type === "config").length,
708
+ vulnerabilities: issues.filter((i) => i.type === "vulnerability").length,
513
709
  critical: issues.filter((i) => i.severity === "critical").length,
514
710
  high: issues.filter((i) => i.severity === "high").length,
515
711
  medium: issues.filter((i) => i.severity === "medium").length,
@@ -519,7 +715,7 @@ async function scan(targetPath) {
519
715
  }
520
716
 
521
717
  // src/cli.ts
522
- var VERSION = "0.1.0";
718
+ var VERSION = "0.2.0";
523
719
  var scoreStyles = {
524
720
  A: { color: import_chalk.default.green },
525
721
  B: { color: import_chalk.default.blue },
@@ -537,7 +733,8 @@ var typeLabels = {
537
733
  secret: "SECRETS",
538
734
  pii: "PII",
539
735
  route: "ROUTES",
540
- config: "CONFIG"
736
+ config: "CONFIG",
737
+ vulnerability: "VULNERABILITIES"
541
738
  };
542
739
  function printBanner() {
543
740
  console.log();
@@ -617,6 +814,11 @@ function printFixes(issues) {
617
814
  const hasSecrets = issues.some((i) => i.type === "secret");
618
815
  const hasPII = issues.some((i) => i.type === "pii");
619
816
  const hasConfig = issues.some((i) => i.type === "config");
817
+ const hasVulnerabilities = issues.some((i) => i.type === "vulnerability");
818
+ const hasXSS = issues.some((i) => i.category === "xss");
819
+ const hasInjection = issues.some((i) => i.category === "injection");
820
+ const hasCORS = issues.some((i) => i.category === "cors");
821
+ const hasDebug = issues.some((i) => i.category === "debug");
620
822
  if (hasSecrets) {
621
823
  console.log(import_chalk.default.gray(" - Use environment variables for secrets"));
622
824
  console.log(import_chalk.default.gray(" - Never commit API keys to version control"));
@@ -627,6 +829,21 @@ function printFixes(issues) {
627
829
  if (hasPII) {
628
830
  console.log(import_chalk.default.gray(" - Remove personal data from source code"));
629
831
  }
832
+ if (hasXSS) {
833
+ console.log(import_chalk.default.gray(" - Sanitize user input before rendering HTML"));
834
+ console.log(import_chalk.default.gray(" - Use textContent instead of innerHTML"));
835
+ }
836
+ if (hasInjection) {
837
+ console.log(import_chalk.default.gray(" - Use parameterized queries for SQL"));
838
+ console.log(import_chalk.default.gray(" - Avoid using eval and dynamic code execution"));
839
+ }
840
+ if (hasCORS) {
841
+ console.log(import_chalk.default.gray(" - Configure CORS with specific allowed origins"));
842
+ }
843
+ if (hasDebug) {
844
+ console.log(import_chalk.default.gray(" - Remove console.log statements before production"));
845
+ console.log(import_chalk.default.gray(" - Use environment variables for debug flags"));
846
+ }
630
847
  console.log();
631
848
  }
632
849
  function printFooter() {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/scanner/index.ts","../src/scanner/patterns.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { scan, type ScanResult, type ScanIssue } from './scanner/index.js';\n\nconst VERSION = '0.1.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};\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 fix suggestions\n */\nfunction printFixes(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\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\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 * 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-color', 'Disable colored output')\n .action(async (targetPath: string, options: { json?: boolean; quiet?: boolean }) => {\n if (options.json) {\n // JSON output mode\n const result = await scan(targetPath);\n console.log(JSON.stringify(result, null, 2));\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 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 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 printFixes(result.issues);\n printFooter();\n\n // Exit with error code if issues found\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 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 IGNORE_PATTERNS,\n SCANNABLE_EXTENSIONS,\n type SecretPattern,\n type PIIPattern,\n type RoutePattern,\n} from './patterns';\n\nexport interface ScanIssue {\n type: 'secret' | 'pii' | 'route' | 'config';\n severity: 'critical' | 'high' | 'medium' | 'low';\n name: string;\n provider?: string;\n file: string;\n line: number;\n column: number;\n match: string; // Redacted version\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 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 * Scan a single file for issues\n */\nfunction scanFile(filePath: string, content: string): ScanIssue[] {\n const issues: ScanIssue[] = [];\n const relativePath = filePath;\n\n // Scan for secrets\n for (const pattern of SECRET_PATTERNS) {\n // Reset regex lastIndex\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\n for (const pattern of PII_PATTERNS) {\n pattern.pattern.lastIndex = 0;\n let match;\n while ((match = pattern.pattern.exec(content)) !== null) {\n // Skip common false positives\n const matchStr = match[0];\n if (isLikelyFalsePositive(matchStr, pattern.name)) {\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 // 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 // Check for .env files in wrong places\n const fileName = path.basename(filePath);\n if (fileName.startsWith('.env') && !fileName.includes('.example')) {\n // If we're scanning a .env file, it might be committed\n const gitignorePath = path.join(path.dirname(filePath), '.gitignore');\n const gitignoreExists = fs.existsSync(gitignorePath);\n\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: gitignoreExists\n ? 'Verify this file is in .gitignore'\n : '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): boolean {\n // Common email false positives\n if (patternName === 'Email Address') {\n const falseDomains = [\n 'example.com',\n 'example.org',\n 'test.com',\n 'localhost',\n 'placeholder.com',\n ];\n if (falseDomains.some(d => match.includes(d))) {\n return true;\n }\n\n // Common public email prefixes (not personal PII)\n const publicPrefixes = [\n 'support@',\n 'help@',\n 'info@',\n 'contact@',\n 'sales@',\n 'admin@',\n 'noreply@',\n 'no-reply@',\n 'hello@',\n 'team@',\n 'partners@',\n 'enterprise@',\n 'security@',\n 'privacy@',\n 'legal@',\n ];\n if (publicPrefixes.some(p => match.toLowerCase().startsWith(p))) {\n return true;\n }\n }\n\n // IP address false positives (version numbers, etc)\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 (example numbers in docs)\n if (patternName.includes('Phone Number')) {\n // Common example phone numbers\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 fragility score based on issues\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 const total = issues.length;\n\n // Scoring algorithm\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 (total === 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':\n return 'Excellent! No security issues detected.';\n case 'B':\n return 'Good, but minor improvements recommended.';\n case 'C':\n return 'Fair. Some security concerns need attention.';\n case 'D':\n return 'Poor. Significant security issues detected.';\n case 'F':\n return 'Critical! Your app is leaking secrets.';\n default:\n 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 // Get all files\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 // Skip files that can't be read\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 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 // 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 // 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 // Cohere\n {\n name: 'Cohere API Key',\n provider: 'Cohere',\n pattern: /[a-zA-Z0-9]{40}/g, // Less specific, check context\n severity: 'medium',\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 without auth\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];\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];\n"],"mappings":"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","names":["chalk","ora"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/scanner/index.ts","../src/scanner/patterns.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { scan, type ScanResult, type ScanIssue } from './scanner/index.js';\n\nconst VERSION = '0.2.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 fix suggestions\n */\nfunction printFixes(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 hasVulnerabilities = issues.some(i => i.type === 'vulnerability');\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 const hasDebug = issues.some(i => i.category === 'debug');\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 console.log(chalk.gray(' - Use textContent instead of innerHTML'));\n }\n if (hasInjection) {\n console.log(chalk.gray(' - Use parameterized queries for SQL'));\n console.log(chalk.gray(' - Avoid using eval and dynamic code execution'));\n }\n if (hasCORS) {\n console.log(chalk.gray(' - Configure CORS with specific allowed origins'));\n }\n if (hasDebug) {\n console.log(chalk.gray(' - Remove console.log statements before production'));\n console.log(chalk.gray(' - Use environment variables for debug flags'));\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 * 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-color', 'Disable colored output')\n .action(async (targetPath: string, options: { json?: boolean; quiet?: boolean }) => {\n if (options.json) {\n // JSON output mode\n const result = await scan(targetPath);\n console.log(JSON.stringify(result, null, 2));\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 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 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 printFixes(result.issues);\n printFooter();\n\n // Exit with error code if issues found\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 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"],"mappings":"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","names":["chalk","ora"]}