@bryan-thompson/inspector-assessment-client 1.26.1 → 1.26.3

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.
Files changed (32) hide show
  1. package/dist/assets/{OAuthCallback-BoGrFRVg.js → OAuthCallback-TVHya7KP.js} +1 -1
  2. package/dist/assets/{OAuthDebugCallback-CT-Cn5GD.js → OAuthDebugCallback-Cs3pMLdW.js} +1 -1
  3. package/dist/assets/{index-BdqZvK3a.js → index-jBP9ZYhX.js} +439 -368
  4. package/dist/index.html +1 -1
  5. package/lib/lib/assessment/extendedTypes.d.ts +80 -0
  6. package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
  7. package/lib/lib/assessment/resultTypes.d.ts +9 -0
  8. package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
  9. package/lib/lib/prohibitedLibraries.d.ts +13 -0
  10. package/lib/lib/prohibitedLibraries.d.ts.map +1 -1
  11. package/lib/lib/prohibitedLibraries.js +78 -0
  12. package/lib/lib/securityPatterns.d.ts +2 -1
  13. package/lib/lib/securityPatterns.d.ts.map +1 -1
  14. package/lib/lib/securityPatterns.js +83 -1
  15. package/lib/services/assessment/modules/AuthenticationAssessor.d.ts +14 -0
  16. package/lib/services/assessment/modules/AuthenticationAssessor.d.ts.map +1 -1
  17. package/lib/services/assessment/modules/AuthenticationAssessor.js +359 -2
  18. package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts +7 -0
  19. package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts.map +1 -1
  20. package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.js +62 -22
  21. package/lib/services/assessment/modules/SecurityAssessor.d.ts +5 -0
  22. package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
  23. package/lib/services/assessment/modules/SecurityAssessor.js +42 -0
  24. package/lib/services/assessment/modules/TemporalAssessor.d.ts +44 -0
  25. package/lib/services/assessment/modules/TemporalAssessor.d.ts.map +1 -1
  26. package/lib/services/assessment/modules/TemporalAssessor.js +267 -27
  27. package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
  28. package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +12 -0
  29. package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +17 -0
  30. package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
  31. package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +90 -0
  32. package/package.json +2 -2
@@ -60,6 +60,124 @@ const SECURE_TRANSPORT_PATTERNS = [
60
60
  /helmet/i, // Security middleware
61
61
  /cors.*origin.*string|cors.*origin.*array/i, // Specific CORS origins
62
62
  ];
63
+ // ============================================================================
64
+ // Issue #62: Auth Configuration Patterns
65
+ // Detects environment-dependent auth, fail-open patterns, and dev mode warnings
66
+ // ============================================================================
67
+ // Patterns for env vars that control authentication
68
+ const AUTH_ENV_VAR_PATTERNS = [
69
+ /process\.env\.([A-Z_]*SECRET[A-Z_]*)/i,
70
+ /process\.env\.([A-Z_]*AUTH[A-Z_]*)/i,
71
+ /process\.env\.([A-Z_]*TOKEN[A-Z_]*)/i,
72
+ /process\.env\.([A-Z_]*API[_-]?KEY[A-Z_]*)/i,
73
+ /process\.env\.([A-Z_]*PASSWORD[A-Z_]*)/i,
74
+ /process\.env\.([A-Z_]*CREDENTIAL[A-Z_]*)/i,
75
+ /os\.environ\.get\(['"](.*(?:SECRET|AUTH|TOKEN|API[_-]?KEY|PASSWORD|CREDENTIAL).*)['"]/i, // Python
76
+ /os\.getenv\(['"](.*(?:SECRET|AUTH|TOKEN|API[_-]?KEY|PASSWORD|CREDENTIAL).*)['"]/i, // Python
77
+ ];
78
+ // Patterns that indicate fail-open behavior (auth bypassed when env var missing)
79
+ // These capture the context around env var usage with fallback operators
80
+ const FAIL_OPEN_PATTERNS = [
81
+ // JavaScript/TypeScript: process.env.X || 'fallback' or process.env.X ?? 'fallback'
82
+ {
83
+ pattern: /process\.env\.[A-Z_]*(SECRET|AUTH|TOKEN|API[_-]?KEY)[A-Z_]*\s*\|\|/gi,
84
+ name: "OR_FALLBACK",
85
+ },
86
+ {
87
+ pattern: /process\.env\.[A-Z_]*(SECRET|AUTH|TOKEN|API[_-]?KEY)[A-Z_]*\s*\?\?/gi,
88
+ name: "NULLISH_FALLBACK",
89
+ },
90
+ // if (!process.env.X) pattern suggesting bypass
91
+ {
92
+ pattern: /if\s*\(\s*!?\s*process\.env\.[A-Z_]*(SECRET|AUTH|TOKEN|API[_-]?KEY)/gi,
93
+ name: "CONDITIONAL_CHECK",
94
+ },
95
+ // Python: os.environ.get('X', 'default') or os.getenv('X', 'default')
96
+ {
97
+ pattern: /os\.environ\.get\([^,]+(?:SECRET|AUTH|TOKEN|API[_-]?KEY)[^,]*,\s*['"]/gi,
98
+ name: "PYTHON_DEFAULT",
99
+ },
100
+ {
101
+ pattern: /os\.getenv\([^,]+(?:SECRET|AUTH|TOKEN|API[_-]?KEY)[^,]*,\s*['"]/gi,
102
+ name: "PYTHON_GETENV_DEFAULT",
103
+ },
104
+ ];
105
+ // Patterns that indicate dev mode weakening security
106
+ // Warning 2 fix: Added word boundaries and assignment context to reduce false positives
107
+ const DEV_MODE_PATTERNS = [
108
+ // Development mode bypasses
109
+ {
110
+ pattern: /NODE_ENV.*development|development.*NODE_ENV/i,
111
+ severity: "LOW",
112
+ },
113
+ {
114
+ pattern: /if\s*\(\s*(?:process\.env\.)?NODE_ENV\s*[!=]==?\s*['"]development['"]\s*\)/i,
115
+ severity: "MEDIUM",
116
+ },
117
+ {
118
+ // Require word boundary and assignment context to avoid matching unrelated identifiers
119
+ pattern: /\b(isDev|isDevelopment|devMode|debugMode)\s*[=:]/i,
120
+ severity: "LOW",
121
+ },
122
+ // Debug authentication bypasses
123
+ {
124
+ pattern: /skip.*auth.*dev|dev.*skip.*auth/i,
125
+ severity: "HIGH",
126
+ },
127
+ {
128
+ pattern: /disable.*auth.*debug|debug.*disable.*auth/i,
129
+ severity: "HIGH",
130
+ },
131
+ {
132
+ pattern: /auth.*bypass|bypass.*auth/i,
133
+ severity: "HIGH",
134
+ },
135
+ // "authenticate all requests as dev user" pattern from issue
136
+ {
137
+ pattern: /authenticate.*all.*requests|all.*requests.*authenticate/i,
138
+ severity: "HIGH",
139
+ },
140
+ {
141
+ pattern: /as\s+dev\s+user|dev\s+user.*auth/i,
142
+ severity: "HIGH",
143
+ },
144
+ ];
145
+ // Patterns that indicate hardcoded secrets (should be env vars)
146
+ const HARDCODED_SECRET_PATTERNS = [
147
+ {
148
+ pattern: /['"]sk[-_](?:live|test)_[a-zA-Z0-9]{20,}['"]/i,
149
+ name: "STRIPE_KEY",
150
+ }, // Stripe keys
151
+ {
152
+ pattern: /['"]pk[-_](?:live|test)_[a-zA-Z0-9]{20,}['"]/i,
153
+ name: "STRIPE_PUBLISHABLE",
154
+ },
155
+ {
156
+ pattern: /api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9]{20,}['"]/i,
157
+ name: "API_KEY",
158
+ },
159
+ {
160
+ pattern: /secret[_-]?key\s*[:=]\s*['"][a-zA-Z0-9]{16,}['"]/i,
161
+ name: "SECRET_KEY",
162
+ },
163
+ {
164
+ // Warning 3 fix: Exclude env var interpolation and common placeholder values
165
+ pattern: /password\s*[:=]\s*['"](?!\$\{|password|changeme|example|test)[a-zA-Z0-9!@#$%^&*]{8,}['"]/i,
166
+ name: "HARDCODED_PASSWORD",
167
+ },
168
+ {
169
+ pattern: /auth[_-]?token\s*[:=]\s*['"][a-zA-Z0-9._-]{20,}['"]/i,
170
+ name: "AUTH_TOKEN",
171
+ },
172
+ ];
173
+ // ============================================================================
174
+ // Issue #65: Rate Limiting Constants
175
+ // Prevents performance issues when analyzing large codebases
176
+ // ============================================================================
177
+ /** Maximum number of source files to analyze (prevents performance degradation) */
178
+ const MAX_FILES = 500;
179
+ /** Maximum number of findings per type (prevents overwhelming output) */
180
+ const MAX_FINDINGS = 100;
63
181
  export class AuthenticationAssessor extends BaseAssessor {
64
182
  /**
65
183
  * Run authentication assessment
@@ -126,7 +244,23 @@ export class AuthenticationAssessor extends BaseAssessor {
126
244
  if (transportSecurity.hasInsecurePatterns) {
127
245
  appropriateness.concerns.push(...transportSecurity.insecurePatterns.map((p) => `Insecure transport pattern: ${p}`));
128
246
  }
129
- this.log(`Assessment complete: auth=${authMethod}, localDeps=${hasLocalDependencies}, tlsEnforced=${transportSecurity.tlsEnforced}`);
247
+ // Issue #62: Analyze auth configuration for env-dependent auth and fail-open patterns
248
+ const authConfigAnalysis = this.analyzeAuthConfiguration(context);
249
+ // Add auth config findings to concerns
250
+ if (authConfigAnalysis.hasHighSeverity) {
251
+ appropriateness.concerns.push(...authConfigAnalysis.findings
252
+ .filter((f) => f.severity === "HIGH")
253
+ .map((f) => `Auth config issue: ${f.message}`));
254
+ }
255
+ // Update status based on auth config findings
256
+ let finalStatus = status;
257
+ if (authConfigAnalysis.hasHighSeverity) {
258
+ finalStatus = "NEED_MORE_INFO";
259
+ }
260
+ // Generate additional recommendations from auth config findings
261
+ const authConfigRecommendations = authConfigAnalysis.findings.map((f) => f.recommendation ||
262
+ `Review ${f.type}: ${f.message} (${f.file || "unknown file"})`);
263
+ this.log(`Assessment complete: auth=${authMethod}, localDeps=${hasLocalDependencies}, tlsEnforced=${transportSecurity.tlsEnforced}, authConfigFindings=${authConfigAnalysis.totalFindings}`);
130
264
  return {
131
265
  authMethod,
132
266
  hasLocalDependencies,
@@ -139,11 +273,13 @@ export class AuthenticationAssessor extends BaseAssessor {
139
273
  apiKeyIndicators,
140
274
  },
141
275
  transportSecurity,
142
- status,
276
+ authConfigAnalysis,
277
+ status: finalStatus,
143
278
  explanation,
144
279
  recommendations: [
145
280
  ...recommendations,
146
281
  ...transportSecurity.recommendations,
282
+ ...authConfigRecommendations,
147
283
  ],
148
284
  };
149
285
  }
@@ -363,4 +499,225 @@ export class AuthenticationAssessor extends BaseAssessor {
363
499
  }
364
500
  return recommendations;
365
501
  }
502
+ // ============================================================================
503
+ // Issue #62: Authentication Configuration Analysis
504
+ // Detects env-dependent auth, fail-open patterns, and dev mode warnings
505
+ // ============================================================================
506
+ /**
507
+ * Analyze source code for authentication configuration issues (Issue #62)
508
+ *
509
+ * Detects:
510
+ * - Environment-dependent auth (process.env.SECRET, process.env.AUTH_KEY, etc.)
511
+ * - Fail-open patterns (auth bypassed when env var missing with || or ?? fallback)
512
+ * - Development mode warnings (dev mode bypasses that weaken security)
513
+ * - Hardcoded secrets (credentials that should be in env vars)
514
+ */
515
+ analyzeAuthConfiguration(context) {
516
+ const findings = [];
517
+ const envVarsDetected = [];
518
+ if (!context.sourceCodeFiles) {
519
+ return {
520
+ totalFindings: 0,
521
+ envDependentAuthCount: 0,
522
+ failOpenPatternCount: 0,
523
+ devModeWarningCount: 0,
524
+ hardcodedSecretCount: 0,
525
+ findings: [],
526
+ hasHighSeverity: false,
527
+ envVarsDetected: [],
528
+ };
529
+ }
530
+ // Issue #65: Apply file limit to prevent performance issues on large codebases
531
+ let sourceFiles = Array.from(context.sourceCodeFiles);
532
+ if (sourceFiles.length > MAX_FILES) {
533
+ this.log(`Rate limiting: Analyzing ${MAX_FILES} of ${sourceFiles.length} files`);
534
+ sourceFiles = sourceFiles.slice(0, MAX_FILES);
535
+ }
536
+ for (const [filePath, content] of sourceFiles) {
537
+ // Warning 4 fix: Add error handling for malformed files
538
+ try {
539
+ this.testCount++;
540
+ const lines = content.split("\n");
541
+ // 1. Detect env vars used for auth
542
+ for (const pattern of AUTH_ENV_VAR_PATTERNS) {
543
+ const matches = content.match(pattern);
544
+ if (matches) {
545
+ // Extract the env var name from capture group or full match
546
+ for (const match of matches) {
547
+ const envVarMatch = match.match(/(?:process\.env\.|os\.environ\.get\(['"]|os\.getenv\(['"])([A-Z_]+)/i);
548
+ if (envVarMatch && !envVarsDetected.includes(envVarMatch[1])) {
549
+ envVarsDetected.push(envVarMatch[1]);
550
+ }
551
+ }
552
+ }
553
+ }
554
+ // Helper to check if we've hit the findings cap for a type (Issue #65)
555
+ const countByType = (type) => findings.filter((f) => f.type === type).length;
556
+ // Helper to get context lines (Issue #66)
557
+ const getContext = (lineIndex) => {
558
+ const before = lineIndex > 0 ? lines[lineIndex - 1]?.trim() : undefined;
559
+ const after = lineIndex < lines.length - 1
560
+ ? lines[lineIndex + 1]?.trim()
561
+ : undefined;
562
+ return before || after ? { before, after } : undefined;
563
+ };
564
+ // 2. Detect fail-open patterns (auth with fallback values)
565
+ for (const { pattern, name } of FAIL_OPEN_PATTERNS) {
566
+ // Issue #65: Skip if we've hit the cap for this type
567
+ if (countByType("FAIL_OPEN_PATTERN") >= MAX_FINDINGS)
568
+ break;
569
+ // Reset lastIndex for global patterns
570
+ pattern.lastIndex = 0;
571
+ let match;
572
+ while ((match = pattern.exec(content)) !== null) {
573
+ // Issue #65: Check cap before adding
574
+ if (countByType("FAIL_OPEN_PATTERN") >= MAX_FINDINGS)
575
+ break;
576
+ // Find line number
577
+ const beforeMatch = content.substring(0, match.index);
578
+ const lineNumber = beforeMatch.split("\n").length;
579
+ const lineContent = lines[lineNumber - 1]?.trim() || match[0];
580
+ findings.push({
581
+ type: "FAIL_OPEN_PATTERN",
582
+ severity: "MEDIUM",
583
+ message: `Authentication may be bypassed when environment variable is not set (${name} pattern)`,
584
+ evidence: lineContent,
585
+ file: filePath,
586
+ lineNumber,
587
+ recommendation: `Ensure authentication fails securely when credentials are missing. Do not use fallback values for auth secrets.`,
588
+ context: getContext(lineNumber - 1), // Issue #66: Add context
589
+ });
590
+ }
591
+ }
592
+ // 3. Detect dev mode patterns that weaken security
593
+ for (const { pattern, severity } of DEV_MODE_PATTERNS) {
594
+ // Issue #65: Skip if we've hit the cap for this type
595
+ if (countByType("DEV_MODE_WARNING") >= MAX_FINDINGS)
596
+ break;
597
+ if (pattern.test(content)) {
598
+ // Find first occurrence for line number
599
+ const matchResult = content.match(pattern);
600
+ if (matchResult) {
601
+ const matchIndex = content.indexOf(matchResult[0]);
602
+ const beforeMatch = content.substring(0, matchIndex);
603
+ const lineNumber = beforeMatch.split("\n").length;
604
+ const lineContent = lines[lineNumber - 1]?.trim() || matchResult[0];
605
+ findings.push({
606
+ type: "DEV_MODE_WARNING",
607
+ severity,
608
+ message: `Development mode pattern detected that may weaken authentication`,
609
+ evidence: lineContent,
610
+ file: filePath,
611
+ lineNumber,
612
+ recommendation: severity === "HIGH"
613
+ ? `Remove auth bypass logic. Authentication should never be disabled based on environment.`
614
+ : `Ensure development mode does not weaken security controls in production.`,
615
+ context: getContext(lineNumber - 1), // Issue #66: Add context
616
+ });
617
+ }
618
+ }
619
+ }
620
+ // 4. Detect hardcoded secrets
621
+ for (const { pattern, name } of HARDCODED_SECRET_PATTERNS) {
622
+ // Issue #65: Skip if we've hit the cap for this type
623
+ if (countByType("HARDCODED_SECRET") >= MAX_FINDINGS)
624
+ break;
625
+ if (pattern.test(content)) {
626
+ const matchResult = content.match(pattern);
627
+ if (matchResult) {
628
+ const matchIndex = content.indexOf(matchResult[0]);
629
+ const beforeMatch = content.substring(0, matchIndex);
630
+ const lineNumber = beforeMatch.split("\n").length;
631
+ // Redact the actual secret in evidence
632
+ const lineContent = lines[lineNumber - 1]
633
+ ?.trim()
634
+ .replace(/['"][^'"]{8,}['"]/, '"[REDACTED]"') ||
635
+ "[secret value]";
636
+ findings.push({
637
+ type: "HARDCODED_SECRET",
638
+ severity: "HIGH",
639
+ message: `Hardcoded ${name} detected - should use environment variable`,
640
+ evidence: lineContent,
641
+ file: filePath,
642
+ lineNumber,
643
+ recommendation: `Move ${name} to environment variable. Never commit secrets to source control.`,
644
+ context: getContext(lineNumber - 1), // Issue #66: Add context
645
+ });
646
+ }
647
+ }
648
+ }
649
+ // 5. Detect env-dependent auth patterns (env var usage with auth context)
650
+ // Only flag if there's auth context around env var usage
651
+ for (const [index, line] of lines.entries()) {
652
+ // Issue #65: Skip if we've hit the cap for this type
653
+ if (countByType("ENV_DEPENDENT_AUTH") >= MAX_FINDINGS)
654
+ break;
655
+ // Check for env var with auth context in surrounding lines
656
+ const surroundingContext = lines
657
+ .slice(Math.max(0, index - 2), index + 3)
658
+ .join("\n");
659
+ for (const pattern of AUTH_ENV_VAR_PATTERNS) {
660
+ if (pattern.test(line) &&
661
+ /\b(auth|secret|key|token|password|credential)\b/i.test(surroundingContext)) {
662
+ const matchResult = line.match(pattern);
663
+ if (matchResult) {
664
+ // Check if we already have a finding for this line (avoid duplicates)
665
+ const existingFinding = findings.find((f) => f.file === filePath &&
666
+ f.lineNumber === index + 1 &&
667
+ f.type === "ENV_DEPENDENT_AUTH");
668
+ if (!existingFinding) {
669
+ findings.push({
670
+ type: "ENV_DEPENDENT_AUTH",
671
+ severity: "LOW",
672
+ message: `Authentication depends on environment variable that may not be set`,
673
+ evidence: line.trim(),
674
+ file: filePath,
675
+ lineNumber: index + 1,
676
+ recommendation: `Document required environment variables and validate they are set at startup.`,
677
+ context: getContext(index), // Issue #66: Add context
678
+ });
679
+ }
680
+ }
681
+ }
682
+ }
683
+ }
684
+ }
685
+ catch (error) {
686
+ // Warning 4 fix: Handle malformed files gracefully
687
+ this.log(`Error analyzing ${filePath}: ${error}`);
688
+ continue;
689
+ }
690
+ }
691
+ // Deduplicate findings by file+line+type
692
+ const uniqueFindings = this.deduplicateFindings(findings);
693
+ // Count by type
694
+ const envDependentAuthCount = uniqueFindings.filter((f) => f.type === "ENV_DEPENDENT_AUTH").length;
695
+ const failOpenPatternCount = uniqueFindings.filter((f) => f.type === "FAIL_OPEN_PATTERN").length;
696
+ const devModeWarningCount = uniqueFindings.filter((f) => f.type === "DEV_MODE_WARNING").length;
697
+ const hardcodedSecretCount = uniqueFindings.filter((f) => f.type === "HARDCODED_SECRET").length;
698
+ const hasHighSeverity = uniqueFindings.some((f) => f.severity === "HIGH");
699
+ return {
700
+ totalFindings: uniqueFindings.length,
701
+ envDependentAuthCount,
702
+ failOpenPatternCount,
703
+ devModeWarningCount,
704
+ hardcodedSecretCount,
705
+ findings: uniqueFindings,
706
+ hasHighSeverity,
707
+ envVarsDetected,
708
+ };
709
+ }
710
+ /**
711
+ * Deduplicate findings by file, line, and type
712
+ */
713
+ deduplicateFindings(findings) {
714
+ const seen = new Set();
715
+ return findings.filter((f) => {
716
+ const key = `${f.file || ""}:${f.lineNumber || 0}:${f.type}`;
717
+ if (seen.has(key))
718
+ return false;
719
+ seen.add(key);
720
+ return true;
721
+ });
722
+ }
366
723
  }
@@ -27,6 +27,11 @@ export declare class ProhibitedLibrariesAssessor extends BaseAssessor {
27
27
  private deduplicateMatches;
28
28
  /**
29
29
  * Calculate overall status from matches
30
+ *
31
+ * Issue #63: Status now considers dependency usage:
32
+ * - ACTIVE dependencies are actively imported (high risk)
33
+ * - UNUSED dependencies are listed but not imported (lower risk, recommend removal)
34
+ * - UNKNOWN usage falls back to previous behavior
30
35
  */
31
36
  private calculateStatusFromMatches;
32
37
  /**
@@ -35,6 +40,8 @@ export declare class ProhibitedLibrariesAssessor extends BaseAssessor {
35
40
  private generateExplanation;
36
41
  /**
37
42
  * Generate recommendations
43
+ *
44
+ * Issue #63: Recommendations now distinguish between ACTIVE and UNUSED dependencies
38
45
  */
39
46
  private generateRecommendations;
40
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProhibitedLibrariesAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ProhibitedLibrariesAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,6BAA6B,EAG9B,MAAM,uBAAuB,CAAC;AAO/B,qBAAa,2BAA4B,SAAQ,YAAY;IAC3D;;OAEG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,6BAA6B,CAAC;IA4IzC;;OAEG;IACH,OAAO,CAAC,YAAY;IA0BpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAqB1B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAuBlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAoD3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CAqDhC"}
1
+ {"version":3,"file":"ProhibitedLibrariesAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ProhibitedLibrariesAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,6BAA6B,EAG9B,MAAM,uBAAuB,CAAC;AAS/B,qBAAa,2BAA4B,SAAQ,YAAY;IAC3D;;OAEG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,6BAA6B,CAAC;IAiKzC;;OAEG;IACH,OAAO,CAAC,YAAY;IA0BpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAqB1B;;;;;;;OAOG;IACH,OAAO,CAAC,0BAA0B;IAiClC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAoD3B;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;CA2EhC"}
@@ -10,7 +10,7 @@
10
10
  * Reference: Anthropic MCP Directory Policy #28-30
11
11
  */
12
12
  import { BaseAssessor } from "./BaseAssessor.js";
13
- import { checkPackageJsonDependencies, checkRequirementsTxt, checkSourceImports, } from "../../../lib/prohibitedLibraries.js";
13
+ import { checkPackageJsonDependencies, checkRequirementsTxt, checkSourceImports, checkDependencyUsage, } from "../../../lib/prohibitedLibraries.js";
14
14
  export class ProhibitedLibrariesAssessor extends BaseAssessor {
15
15
  /**
16
16
  * Run prohibited libraries assessment
@@ -30,6 +30,17 @@ export class ProhibitedLibrariesAssessor extends BaseAssessor {
30
30
  const packageJson = context.packageJson;
31
31
  const depMatches = checkPackageJsonDependencies(packageJson);
32
32
  for (const match of depMatches) {
33
+ // Issue #63: Check if dependency is actually used in source code
34
+ let usageStatus = "UNKNOWN";
35
+ let importCount = 0;
36
+ let importFiles = [];
37
+ if (context.sourceCodeFiles &&
38
+ context.config.enableSourceCodeAnalysis) {
39
+ const usage = checkDependencyUsage(match.library.name, context.sourceCodeFiles);
40
+ usageStatus = usage.status;
41
+ importCount = usage.importCount;
42
+ importFiles = usage.files;
43
+ }
33
44
  matches.push({
34
45
  name: match.library.name,
35
46
  category: match.library.category,
@@ -37,6 +48,9 @@ export class ProhibitedLibrariesAssessor extends BaseAssessor {
37
48
  severity: match.library.severity,
38
49
  reason: match.library.reason,
39
50
  policyReference: match.library.policyReference,
51
+ usageStatus,
52
+ importCount,
53
+ importFiles,
40
54
  });
41
55
  if (match.library.category === "financial" ||
42
56
  match.library.category === "payments" ||
@@ -169,19 +183,30 @@ export class ProhibitedLibrariesAssessor extends BaseAssessor {
169
183
  }
170
184
  /**
171
185
  * Calculate overall status from matches
186
+ *
187
+ * Issue #63: Status now considers dependency usage:
188
+ * - ACTIVE dependencies are actively imported (high risk)
189
+ * - UNUSED dependencies are listed but not imported (lower risk, recommend removal)
190
+ * - UNKNOWN usage falls back to previous behavior
172
191
  */
173
192
  calculateStatusFromMatches(matches) {
174
- // Any BLOCKING library = FAIL
175
- const blockingMatches = matches.filter((m) => m.severity === "BLOCKING");
176
- if (blockingMatches.length > 0) {
193
+ // Separate matches by usage status
194
+ const activeMatches = matches.filter((m) => m.usageStatus !== "UNUSED");
195
+ const unusedMatches = matches.filter((m) => m.usageStatus === "UNUSED");
196
+ // Only ACTIVE BLOCKING libraries = FAIL (actually imported and dangerous)
197
+ const blockingActive = activeMatches.filter((m) => m.severity === "BLOCKING");
198
+ if (blockingActive.length > 0) {
177
199
  return "FAIL";
178
200
  }
179
- // HIGH severity = NEED_MORE_INFO (requires justification)
180
- const highMatches = matches.filter((m) => m.severity === "HIGH");
181
- if (highMatches.length > 0) {
201
+ // UNUSED BLOCKING = NEED_MORE_INFO (recommend removal, but not actively dangerous)
202
+ if (unusedMatches.some((m) => m.severity === "BLOCKING")) {
203
+ return "NEED_MORE_INFO";
204
+ }
205
+ // ACTIVE HIGH severity = NEED_MORE_INFO (requires justification)
206
+ if (activeMatches.some((m) => m.severity === "HIGH")) {
182
207
  return "NEED_MORE_INFO";
183
208
  }
184
- // MEDIUM severity = PASS with notes
209
+ // Any remaining matches = NEED_MORE_INFO (review recommended)
185
210
  if (matches.length > 0) {
186
211
  return "NEED_MORE_INFO";
187
212
  }
@@ -220,31 +245,46 @@ export class ProhibitedLibrariesAssessor extends BaseAssessor {
220
245
  }
221
246
  /**
222
247
  * Generate recommendations
248
+ *
249
+ * Issue #63: Recommendations now distinguish between ACTIVE and UNUSED dependencies
223
250
  */
224
251
  generateRecommendations(matches) {
225
252
  const recommendations = [];
226
- // Group by severity
227
- const blocking = matches.filter((m) => m.severity === "BLOCKING");
228
- const high = matches.filter((m) => m.severity === "HIGH");
229
- const medium = matches.filter((m) => m.severity === "MEDIUM");
230
- if (blocking.length > 0) {
231
- recommendations.push("BLOCKING - The following libraries must be removed for MCP Directory approval:");
232
- for (const match of blocking) {
233
- recommendations.push(`- ${match.name} (${match.policyReference}): ${match.reason}`);
253
+ // Issue #63: Separate active vs unused dependencies
254
+ const activeMatches = matches.filter((m) => m.usageStatus !== "UNUSED");
255
+ const unusedMatches = matches.filter((m) => m.usageStatus === "UNUSED");
256
+ // Group active matches by severity
257
+ const blockingActive = activeMatches.filter((m) => m.severity === "BLOCKING");
258
+ const highActive = activeMatches.filter((m) => m.severity === "HIGH");
259
+ const mediumActive = activeMatches.filter((m) => m.severity === "MEDIUM");
260
+ if (blockingActive.length > 0) {
261
+ recommendations.push("BLOCKING (ACTIVE) - The following libraries are imported and must be removed:");
262
+ for (const match of blockingActive) {
263
+ const files = match.importFiles && match.importFiles.length > 0
264
+ ? ` (imported in: ${match.importFiles.slice(0, 2).join(", ")})`
265
+ : "";
266
+ recommendations.push(`- ${match.name} (${match.policyReference}): ${match.reason}${files}`);
234
267
  }
235
268
  }
236
- if (high.length > 0) {
237
- recommendations.push("HIGH - The following libraries require strong justification:");
238
- for (const match of high) {
269
+ if (highActive.length > 0) {
270
+ recommendations.push("HIGH (ACTIVE) - The following libraries are imported and require strong justification:");
271
+ for (const match of highActive) {
239
272
  recommendations.push(`- ${match.name} (${match.policyReference}): ${match.reason}`);
240
273
  }
241
274
  }
242
- if (medium.length > 0) {
243
- recommendations.push("MEDIUM - Review the following libraries for necessity:");
244
- for (const match of medium.slice(0, 3)) {
275
+ if (mediumActive.length > 0) {
276
+ recommendations.push("MEDIUM (ACTIVE) - Review the following imported libraries:");
277
+ for (const match of mediumActive.slice(0, 3)) {
245
278
  recommendations.push(`- ${match.name} (${match.policyReference}): ${match.reason}`);
246
279
  }
247
280
  }
281
+ // Issue #63: Add recommendations for unused dependencies
282
+ if (unusedMatches.length > 0) {
283
+ recommendations.push("UNUSED - The following libraries are listed but not imported (consider removing):");
284
+ for (const match of unusedMatches) {
285
+ recommendations.push(`- npm uninstall ${match.name} (${match.policyReference}): Listed in package.json but not imported`);
286
+ }
287
+ }
248
288
  if (matches.length === 0) {
249
289
  recommendations.push("No prohibited libraries detected. Server is compliant with library restrictions.");
250
290
  }
@@ -59,5 +59,10 @@ export declare class SecurityAssessor extends BaseAssessor {
59
59
  * Generate security explanation
60
60
  */
61
61
  private generateSecurityExplanation;
62
+ /**
63
+ * Aggregate auth bypass detection results from security tests (Issue #75)
64
+ * Summarizes fail-open/fail-closed patterns across all tested tools
65
+ */
66
+ private aggregateAuthBypassResults;
62
67
  }
63
68
  //# sourceMappingURL=SecurityAssessor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,kBAAkB,EAInB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAS9D,OAAO,EACL,gBAAgB,EAGjB,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,YAAY,CAAiC;IAErD;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAStD;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAOjC;;;OAGG;YACW,0BAA0B;gBAwBtC,MAAM,EAAE,OAAO,8BAA8B,EAAE,uBAAuB;IA8BlE,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA0JrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;OAEG;YACW,+BAA+B;IAiC7C;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;OAEG;IACH,OAAO,CAAC,2BAA2B;CAiEpC"}
1
+ {"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,kBAAkB,EAInB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAS9D,OAAO,EACL,gBAAgB,EAGjB,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,YAAY,CAAiC;IAErD;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAStD;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAOjC;;;OAGG;YACW,0BAA0B;gBAwBtC,MAAM,EAAE,OAAO,8BAA8B,EAAE,uBAAuB;IA8BlE,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8JrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;OAEG;YACW,+BAA+B;IAiC7C;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAkEnC;;;OAGG;IACH,OAAO,CAAC,0BAA0B;CAgDnC"}
@@ -175,12 +175,15 @@ export class SecurityAssessor extends BaseAssessor {
175
175
  const status = this.determineSecurityStatus(validTests, vulnerabilities.length, validTests.length, connectionErrors.length);
176
176
  // Generate explanation (pass both validTests and connectionErrors)
177
177
  const explanation = this.generateSecurityExplanation(validTests, connectionErrors, vulnerabilities, overallRiskLevel);
178
+ // Issue #75: Aggregate auth bypass detection results
179
+ const authBypassSummary = this.aggregateAuthBypassResults(allTests);
178
180
  return {
179
181
  promptInjectionTests: allTests,
180
182
  vulnerabilities,
181
183
  overallRiskLevel,
182
184
  status,
183
185
  explanation,
186
+ authBypassSummary,
184
187
  };
185
188
  }
186
189
  /**
@@ -308,4 +311,43 @@ export class SecurityAssessor extends BaseAssessor {
308
311
  `Flagged ${lowConfidenceCount} uncertain detection${lowConfidenceCount !== 1 ? "s" : ""} across ${testCount} security tests. Manual verification needed to confirm if these are actual vulnerabilities or false positives.`);
309
312
  }
310
313
  }
314
+ /**
315
+ * Aggregate auth bypass detection results from security tests (Issue #75)
316
+ * Summarizes fail-open/fail-closed patterns across all tested tools
317
+ */
318
+ aggregateAuthBypassResults(tests) {
319
+ const toolsWithAuthBypass = [];
320
+ let failOpenCount = 0;
321
+ let failClosedCount = 0;
322
+ let unknownCount = 0;
323
+ // Filter to Auth Bypass tests only
324
+ const authBypassTests = tests.filter((t) => t.testName === "Auth Bypass" && t.authFailureMode);
325
+ // Track unique tools with auth bypass detected
326
+ const seenTools = new Set();
327
+ for (const test of authBypassTests) {
328
+ const toolName = test.toolName || "unknown";
329
+ if (test.authBypassDetected && !seenTools.has(toolName)) {
330
+ toolsWithAuthBypass.push(toolName);
331
+ seenTools.add(toolName);
332
+ }
333
+ // Count failure modes
334
+ switch (test.authFailureMode) {
335
+ case "FAIL_OPEN":
336
+ failOpenCount++;
337
+ break;
338
+ case "FAIL_CLOSED":
339
+ failClosedCount++;
340
+ break;
341
+ case "UNKNOWN":
342
+ unknownCount++;
343
+ break;
344
+ }
345
+ }
346
+ return {
347
+ toolsWithAuthBypass,
348
+ failOpenCount,
349
+ failClosedCount,
350
+ unknownCount,
351
+ };
352
+ }
311
353
  }