@bryan-thompson/inspector-assessment-client 1.29.0 → 1.30.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/assets/{OAuthCallback-9Gbb39Ii.js → OAuthCallback-BbE88qbF.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-B76J2MBn.js → OAuthDebugCallback-CfRYq1JG.js} +1 -1
- package/dist/assets/{index-CHTOR9VI.js → index-CsUB73MT.js} +4 -4
- package/dist/index.html +1 -1
- package/lib/lib/assessment/configTypes.d.ts +38 -0
- package/lib/lib/assessment/configTypes.d.ts.map +1 -1
- package/lib/lib/assessment/configTypes.js +30 -7
- package/lib/lib/assessment/coreTypes.d.ts +14 -0
- package/lib/lib/assessment/coreTypes.d.ts.map +1 -1
- package/lib/lib/assessment/coreTypes.js +3 -0
- package/lib/lib/assessment/index.d.ts +1 -0
- package/lib/lib/assessment/index.d.ts.map +1 -1
- package/lib/lib/assessment/index.js +1 -0
- package/lib/lib/assessment/progressTypes.d.ts +13 -0
- package/lib/lib/assessment/progressTypes.d.ts.map +1 -1
- package/lib/lib/assessment/progressTypes.js +1 -0
- package/lib/lib/assessment/resultTypes.d.ts +8 -0
- package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/lib/lib/moduleScoring.d.ts +11 -0
- package/lib/lib/moduleScoring.d.ts.map +1 -1
- package/lib/lib/moduleScoring.js +11 -0
- package/lib/lib/securityPatterns.d.ts +3 -2
- package/lib/lib/securityPatterns.d.ts.map +1 -1
- package/lib/lib/securityPatterns.js +150 -2
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +31 -0
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.js +23 -0
- package/lib/services/assessment/ResponseValidator.d.ts +3 -0
- package/lib/services/assessment/ResponseValidator.d.ts.map +1 -1
- package/lib/services/assessment/ResponseValidator.js +3 -0
- package/lib/services/assessment/TestDataGenerator.d.ts +3 -0
- package/lib/services/assessment/TestDataGenerator.d.ts.map +1 -1
- package/lib/services/assessment/TestDataGenerator.js +3 -0
- package/lib/services/assessment/TestScenarioEngine.d.ts +3 -0
- package/lib/services/assessment/TestScenarioEngine.d.ts.map +1 -1
- package/lib/services/assessment/TestScenarioEngine.js +3 -0
- package/lib/services/assessment/config/performanceConfig.d.ts +8 -0
- package/lib/services/assessment/config/performanceConfig.d.ts.map +1 -1
- package/lib/services/assessment/config/performanceConfig.js +7 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts +3 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -1
- package/lib/services/assessment/lib/claudeCodeBridge.js +3 -0
- package/lib/services/assessment/lib/errors.d.ts +3 -0
- package/lib/services/assessment/lib/errors.d.ts.map +1 -1
- package/lib/services/assessment/lib/errors.js +3 -0
- package/lib/services/assessment/lib/timeoutUtils.d.ts +3 -0
- package/lib/services/assessment/lib/timeoutUtils.d.ts.map +1 -1
- package/lib/services/assessment/lib/timeoutUtils.js +3 -0
- package/lib/services/assessment/modules/annotations/index.d.ts +6 -1
- package/lib/services/assessment/modules/annotations/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/annotations/index.js +6 -1
- package/lib/services/assessment/modules/index.d.ts +5 -0
- package/lib/services/assessment/modules/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/index.js +5 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.js +86 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +26 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +58 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +402 -1
- package/lib/services/assessment/modules/securityTests/index.d.ts +6 -1
- package/lib/services/assessment/modules/securityTests/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/index.js +6 -1
- package/lib/services/assessment/orchestratorHelpers.d.ts +3 -0
- package/lib/services/assessment/orchestratorHelpers.d.ts.map +1 -1
- package/lib/services/assessment/orchestratorHelpers.js +8 -3
- package/package.json +1 -1
|
@@ -372,7 +372,10 @@ export class SecurityResponseAnalyzer {
|
|
|
372
372
|
{ pattern: /<\|system\|>/i, name: "<|system|>" },
|
|
373
373
|
{ pattern: /<\|user\|>/i, name: "<|user|>" },
|
|
374
374
|
{ pattern: /\{\{SYSTEM_PROMPT\}\}/i, name: "{{SYSTEM_PROMPT}}" },
|
|
375
|
-
{
|
|
375
|
+
{
|
|
376
|
+
pattern: /ignore.*previous.*instructions/i,
|
|
377
|
+
name: "ignore instructions",
|
|
378
|
+
},
|
|
376
379
|
];
|
|
377
380
|
for (const { pattern, name } of markerPatterns) {
|
|
378
381
|
if (pattern.test(responseText)) {
|
|
@@ -441,6 +444,404 @@ export class SecurityResponseAnalyzer {
|
|
|
441
444
|
}
|
|
442
445
|
return { detected: false, injectionType: "UNKNOWN" };
|
|
443
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* Analyze response for session management vulnerabilities (Issue #111, Challenge #12)
|
|
449
|
+
* Detects 5 CWEs from mcp-vulnerable-testbed:
|
|
450
|
+
* - CWE-384: Session Fixation (accepts external session ID, no regeneration)
|
|
451
|
+
* - CWE-330: Predictable Tokens (session_{user}_{timestamp}_{counter})
|
|
452
|
+
* - CWE-613: No Session Timeout (expires_at: null, timeout_checked: false)
|
|
453
|
+
* - CWE-200: ID Exposure in URL (session_url contains session_id=)
|
|
454
|
+
*
|
|
455
|
+
* @param response The tool response to analyze
|
|
456
|
+
* @returns Analysis result with session management detection status
|
|
457
|
+
*/
|
|
458
|
+
analyzeSessionManagementResponse(response) {
|
|
459
|
+
const responseText = this.extractResponseContent(response);
|
|
460
|
+
const cweIds = [];
|
|
461
|
+
let vulnerabilityType = "UNKNOWN";
|
|
462
|
+
let evidence;
|
|
463
|
+
// Check for safe patterns first (hardened server)
|
|
464
|
+
const safePatterns = [
|
|
465
|
+
{
|
|
466
|
+
pattern: /"fixation_prevented"\s*:\s*true/i,
|
|
467
|
+
name: "fixation_prevented",
|
|
468
|
+
},
|
|
469
|
+
{ pattern: /"token_secure"\s*:\s*true/i, name: "token_secure" },
|
|
470
|
+
{ pattern: /"timeout_enforced"\s*:\s*true/i, name: "timeout_enforced" },
|
|
471
|
+
{ pattern: /"id_in_url"\s*:\s*false/i, name: "id_in_url: false" },
|
|
472
|
+
{
|
|
473
|
+
pattern: /"regeneration_on_auth"\s*:\s*true/i,
|
|
474
|
+
name: "regeneration_on_auth",
|
|
475
|
+
},
|
|
476
|
+
{ pattern: /"attack_blocked"\s*:\s*true/i, name: "attack_blocked" },
|
|
477
|
+
{ pattern: /"cwe_384_mitigated"\s*:\s*true/i, name: "cwe_384_mitigated" },
|
|
478
|
+
];
|
|
479
|
+
for (const { pattern, name } of safePatterns) {
|
|
480
|
+
if (pattern.test(responseText)) {
|
|
481
|
+
return {
|
|
482
|
+
detected: false,
|
|
483
|
+
vulnerabilityType: "UNKNOWN",
|
|
484
|
+
cweIds: [],
|
|
485
|
+
evidence: `Secure session management: ${name}`,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// CWE-384: Session Fixation (external ID accepted)
|
|
490
|
+
const fixationPatterns = [
|
|
491
|
+
{
|
|
492
|
+
pattern: /"attacker_controlled"\s*:\s*true/i,
|
|
493
|
+
evidence: "attacker_controlled: true (session fixation)",
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
pattern: /"fixed"\s*:\s*true/i,
|
|
497
|
+
evidence: "fixed: true (fixated session)",
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
pattern: /session\s*fixation\s*accepted/i,
|
|
501
|
+
evidence: "session fixation attack accepted",
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
pattern: /"fixation_url"\s*:/i,
|
|
505
|
+
evidence: "fixation_url present (attack vector)",
|
|
506
|
+
},
|
|
507
|
+
];
|
|
508
|
+
for (const { pattern, evidence: evidenceText } of fixationPatterns) {
|
|
509
|
+
if (pattern.test(responseText)) {
|
|
510
|
+
if (!cweIds.includes("CWE-384"))
|
|
511
|
+
cweIds.push("CWE-384");
|
|
512
|
+
vulnerabilityType = "SESSION_FIXATION";
|
|
513
|
+
evidence = evidenceText;
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// CWE-384: No Regeneration after authentication
|
|
518
|
+
if (/"session_regenerated"\s*:\s*false/i.test(responseText)) {
|
|
519
|
+
if (!cweIds.includes("CWE-384"))
|
|
520
|
+
cweIds.push("CWE-384");
|
|
521
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
522
|
+
vulnerabilityType = "NO_REGENERATION";
|
|
523
|
+
evidence = "session_regenerated: false (CWE-384)";
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// CWE-330: Predictable Token Pattern
|
|
527
|
+
const predictablePatterns = [
|
|
528
|
+
{
|
|
529
|
+
pattern: /"token_pattern"\s*:\s*"session_\{user\}_\{timestamp\}_\{counter\}"/i,
|
|
530
|
+
evidence: "Predictable token pattern exposed: session_{user}_{timestamp}_{counter}",
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
pattern: /"session_id"\s*:\s*"session_[a-z0-9]+_\d{9,}_\d+"/i,
|
|
534
|
+
evidence: "Predictable session ID format detected",
|
|
535
|
+
},
|
|
536
|
+
];
|
|
537
|
+
for (const { pattern, evidence: evidenceText } of predictablePatterns) {
|
|
538
|
+
if (pattern.test(responseText)) {
|
|
539
|
+
if (!cweIds.includes("CWE-330"))
|
|
540
|
+
cweIds.push("CWE-330");
|
|
541
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
542
|
+
vulnerabilityType = "PREDICTABLE_TOKEN";
|
|
543
|
+
evidence = evidenceText;
|
|
544
|
+
}
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
// CWE-613: No Session Timeout
|
|
549
|
+
const noTimeoutPatterns = [
|
|
550
|
+
{
|
|
551
|
+
pattern: /"expires_at"\s*:\s*null/i,
|
|
552
|
+
evidence: "expires_at: null (sessions never expire)",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
pattern: /"timeout_checked"\s*:\s*false/i,
|
|
556
|
+
evidence: "timeout_checked: false (no expiration validation)",
|
|
557
|
+
},
|
|
558
|
+
];
|
|
559
|
+
for (const { pattern, evidence: evidenceText } of noTimeoutPatterns) {
|
|
560
|
+
if (pattern.test(responseText)) {
|
|
561
|
+
if (!cweIds.includes("CWE-613"))
|
|
562
|
+
cweIds.push("CWE-613");
|
|
563
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
564
|
+
vulnerabilityType = "NO_TIMEOUT";
|
|
565
|
+
evidence = evidenceText;
|
|
566
|
+
}
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
// CWE-200: Session ID in URL
|
|
571
|
+
const idInUrlPatterns = [
|
|
572
|
+
{
|
|
573
|
+
pattern: /"session_url"\s*:\s*"[^"]*[?&]session_id=/i,
|
|
574
|
+
evidence: "Session ID exposed in URL query parameter",
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
pattern: /"fixation_url"\s*:\s*"[^"]*[?&]session_id=/i,
|
|
578
|
+
evidence: "Session ID exposed in fixation URL",
|
|
579
|
+
},
|
|
580
|
+
];
|
|
581
|
+
for (const { pattern, evidence: evidenceText } of idInUrlPatterns) {
|
|
582
|
+
if (pattern.test(responseText)) {
|
|
583
|
+
if (!cweIds.includes("CWE-200"))
|
|
584
|
+
cweIds.push("CWE-200");
|
|
585
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
586
|
+
vulnerabilityType = "ID_IN_URL";
|
|
587
|
+
evidence = evidenceText;
|
|
588
|
+
}
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
detected: cweIds.length > 0,
|
|
594
|
+
vulnerabilityType,
|
|
595
|
+
cweIds: [...new Set(cweIds)], // Dedupe
|
|
596
|
+
evidence,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Analyze response for cryptographic failures (Issue #112, Challenge #13)
|
|
601
|
+
* Detects OWASP A02:2021 Cryptographic Failures from mcp-vulnerable-testbed:
|
|
602
|
+
* - CWE-328: Weak Hash (MD5/SHA1 for password hashing)
|
|
603
|
+
* - CWE-916: Static Salt / Weak KDF
|
|
604
|
+
* - CWE-330: Predictable RNG (random.random() with timestamp seed)
|
|
605
|
+
* - CWE-208: Timing Attack (non-constant-time comparison)
|
|
606
|
+
* - CWE-327: Broken Cipher (ECB mode, XOR)
|
|
607
|
+
* - CWE-321: Hardcoded Key (key_source: "hardcoded")
|
|
608
|
+
* - CWE-326: Weak Key Length (key_length < 16)
|
|
609
|
+
*
|
|
610
|
+
* @param response The tool response to analyze
|
|
611
|
+
* @returns Analysis result with cryptographic failure detection status
|
|
612
|
+
*/
|
|
613
|
+
analyzeCryptographicFailures(response) {
|
|
614
|
+
const responseText = this.extractResponseContent(response);
|
|
615
|
+
const cweIds = [];
|
|
616
|
+
let vulnerabilityType = "UNKNOWN";
|
|
617
|
+
let evidence;
|
|
618
|
+
// Check for safe patterns first (hardened server)
|
|
619
|
+
const safePatterns = [
|
|
620
|
+
{
|
|
621
|
+
pattern: /"status"\s*:\s*"stored"/i,
|
|
622
|
+
name: "request stored (not executed)",
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
pattern: /"note"\s*:\s*".*blocked.*secure/i,
|
|
626
|
+
name: "weak crypto blocked",
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
pattern: /"recommendation"\s*:\s*"Use\s+(?:bcrypt|scrypt|Argon2|AES-GCM)/i,
|
|
630
|
+
name: "secure alternative recommended",
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
pattern: /"vulnerable"\s*:\s*false/i,
|
|
634
|
+
name: "vulnerable: false",
|
|
635
|
+
},
|
|
636
|
+
];
|
|
637
|
+
for (const { pattern, name } of safePatterns) {
|
|
638
|
+
if (pattern.test(responseText)) {
|
|
639
|
+
return {
|
|
640
|
+
detected: false,
|
|
641
|
+
vulnerabilityType: "UNKNOWN",
|
|
642
|
+
cweIds: [],
|
|
643
|
+
evidence: `Secure crypto handling: ${name}`,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
// CWE-328: Weak Hash Algorithm (MD5/SHA1)
|
|
648
|
+
const weakHashPatterns = [
|
|
649
|
+
{
|
|
650
|
+
pattern: /"algorithm"\s*:\s*"MD5"/i,
|
|
651
|
+
evidence: 'algorithm: "MD5" (weak hash for passwords)',
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
pattern: /"algorithm"\s*:\s*"SHA1"/i,
|
|
655
|
+
evidence: 'algorithm: "SHA1" (weak hash for passwords)',
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
pattern: /"algorithm_secure"\s*:\s*false/i,
|
|
659
|
+
evidence: "algorithm_secure: false",
|
|
660
|
+
},
|
|
661
|
+
];
|
|
662
|
+
for (const { pattern, evidence: evidenceText } of weakHashPatterns) {
|
|
663
|
+
if (pattern.test(responseText)) {
|
|
664
|
+
if (!cweIds.includes("CWE-328"))
|
|
665
|
+
cweIds.push("CWE-328");
|
|
666
|
+
vulnerabilityType = "WEAK_HASH";
|
|
667
|
+
evidence = evidenceText;
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// CWE-916: Static Salt
|
|
672
|
+
const staticSaltPatterns = [
|
|
673
|
+
{
|
|
674
|
+
pattern: /"salt_type"\s*:\s*"static"/i,
|
|
675
|
+
evidence: 'salt_type: "static" (same salt for all passwords)',
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
pattern: /"salt"\s*:\s*"static_salt_123"/i,
|
|
679
|
+
evidence: 'salt: "static_salt_123" (hardcoded static salt)',
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
pattern: /"salt_secure"\s*:\s*false/i,
|
|
683
|
+
evidence: "salt_secure: false",
|
|
684
|
+
},
|
|
685
|
+
];
|
|
686
|
+
for (const { pattern, evidence: evidenceText } of staticSaltPatterns) {
|
|
687
|
+
if (pattern.test(responseText)) {
|
|
688
|
+
if (!cweIds.includes("CWE-916"))
|
|
689
|
+
cweIds.push("CWE-916");
|
|
690
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
691
|
+
vulnerabilityType = "STATIC_SALT";
|
|
692
|
+
evidence = evidenceText;
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
// CWE-330: Predictable RNG
|
|
698
|
+
const predictableRngPatterns = [
|
|
699
|
+
{
|
|
700
|
+
pattern: /"rng_type"\s*:\s*"random\.random\(\)"/i,
|
|
701
|
+
evidence: 'rng_type: "random.random()" (non-cryptographic RNG)',
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
pattern: /"seed"\s*:\s*"timestamp"/i,
|
|
705
|
+
evidence: 'seed: "timestamp" (predictable seed)',
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
pattern: /"cryptographically_secure"\s*:\s*false/i,
|
|
709
|
+
evidence: "cryptographically_secure: false",
|
|
710
|
+
},
|
|
711
|
+
];
|
|
712
|
+
for (const { pattern, evidence: evidenceText } of predictableRngPatterns) {
|
|
713
|
+
if (pattern.test(responseText)) {
|
|
714
|
+
if (!cweIds.includes("CWE-330"))
|
|
715
|
+
cweIds.push("CWE-330");
|
|
716
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
717
|
+
vulnerabilityType = "PREDICTABLE_RNG";
|
|
718
|
+
evidence = evidenceText;
|
|
719
|
+
}
|
|
720
|
+
break;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
// CWE-208: Timing Attack
|
|
724
|
+
const timingPatterns = [
|
|
725
|
+
{
|
|
726
|
+
pattern: /"timing_safe"\s*:\s*false/i,
|
|
727
|
+
evidence: "timing_safe: false (vulnerable to timing attacks)",
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
pattern: /"comparison_type"\s*:\s*"direct_equality"/i,
|
|
731
|
+
evidence: 'comparison_type: "direct_equality" (non-constant-time)',
|
|
732
|
+
},
|
|
733
|
+
];
|
|
734
|
+
for (const { pattern, evidence: evidenceText } of timingPatterns) {
|
|
735
|
+
if (pattern.test(responseText)) {
|
|
736
|
+
if (!cweIds.includes("CWE-208"))
|
|
737
|
+
cweIds.push("CWE-208");
|
|
738
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
739
|
+
vulnerabilityType = "TIMING_ATTACK";
|
|
740
|
+
evidence = evidenceText;
|
|
741
|
+
}
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// CWE-327: Broken Cipher Mode (ECB/XOR)
|
|
746
|
+
const brokenCipherPatterns = [
|
|
747
|
+
{
|
|
748
|
+
pattern: /"mode"\s*:\s*"ECB"/i,
|
|
749
|
+
evidence: 'mode: "ECB" (pattern leakage in ciphertext)',
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
pattern: /"algorithm"\s*:\s*"XOR"/i,
|
|
753
|
+
evidence: 'algorithm: "XOR" (weak cipher)',
|
|
754
|
+
},
|
|
755
|
+
];
|
|
756
|
+
for (const { pattern, evidence: evidenceText } of brokenCipherPatterns) {
|
|
757
|
+
if (pattern.test(responseText)) {
|
|
758
|
+
if (!cweIds.includes("CWE-327"))
|
|
759
|
+
cweIds.push("CWE-327");
|
|
760
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
761
|
+
vulnerabilityType = "ECB_MODE";
|
|
762
|
+
evidence = evidenceText;
|
|
763
|
+
}
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
// CWE-321: Hardcoded Key
|
|
768
|
+
const hardcodedKeyPatterns = [
|
|
769
|
+
{
|
|
770
|
+
pattern: /"key_source"\s*:\s*"hardcoded"/i,
|
|
771
|
+
evidence: 'key_source: "hardcoded" (key in source code)',
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
pattern: /"key_preview"\s*:\s*"hardcode/i,
|
|
775
|
+
evidence: "key_preview shows hardcoded key",
|
|
776
|
+
},
|
|
777
|
+
];
|
|
778
|
+
for (const { pattern, evidence: evidenceText } of hardcodedKeyPatterns) {
|
|
779
|
+
if (pattern.test(responseText)) {
|
|
780
|
+
if (!cweIds.includes("CWE-321"))
|
|
781
|
+
cweIds.push("CWE-321");
|
|
782
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
783
|
+
vulnerabilityType = "HARDCODED_KEY";
|
|
784
|
+
evidence = evidenceText;
|
|
785
|
+
}
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
// CWE-916: Weak KDF (MD5 for key derivation)
|
|
790
|
+
const weakKdfPatterns = [
|
|
791
|
+
{
|
|
792
|
+
pattern: /"derivation_function"\s*:\s*"MD5"/i,
|
|
793
|
+
evidence: 'derivation_function: "MD5" (weak KDF)',
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
pattern: /"iterations"\s*:\s*1\b/i,
|
|
797
|
+
evidence: "iterations: 1 (no key stretching)",
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
pattern: /"kdf_secure"\s*:\s*false/i,
|
|
801
|
+
evidence: "kdf_secure: false",
|
|
802
|
+
},
|
|
803
|
+
];
|
|
804
|
+
for (const { pattern, evidence: evidenceText } of weakKdfPatterns) {
|
|
805
|
+
if (pattern.test(responseText)) {
|
|
806
|
+
if (!cweIds.includes("CWE-916"))
|
|
807
|
+
cweIds.push("CWE-916");
|
|
808
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
809
|
+
vulnerabilityType = "WEAK_KDF";
|
|
810
|
+
evidence = evidenceText;
|
|
811
|
+
}
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// CWE-326: Weak Key Length
|
|
816
|
+
const weakKeyPatterns = [
|
|
817
|
+
{
|
|
818
|
+
// Match single digit key_length (1-9 bytes = weak)
|
|
819
|
+
pattern: /"key_length"\s*:\s*[1-9](?!\d)/i,
|
|
820
|
+
evidence: "key_length < 10 bytes (weak key)",
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
pattern: /"key_secure"\s*:\s*false/i,
|
|
824
|
+
evidence: "key_secure: false (weak key)",
|
|
825
|
+
},
|
|
826
|
+
];
|
|
827
|
+
for (const { pattern, evidence: evidenceText } of weakKeyPatterns) {
|
|
828
|
+
if (pattern.test(responseText)) {
|
|
829
|
+
if (!cweIds.includes("CWE-326"))
|
|
830
|
+
cweIds.push("CWE-326");
|
|
831
|
+
if (vulnerabilityType === "UNKNOWN") {
|
|
832
|
+
vulnerabilityType = "WEAK_KEY_LENGTH";
|
|
833
|
+
evidence = evidenceText;
|
|
834
|
+
}
|
|
835
|
+
break;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return {
|
|
839
|
+
detected: cweIds.length > 0,
|
|
840
|
+
vulnerabilityType,
|
|
841
|
+
cweIds: [...new Set(cweIds)], // Dedupe
|
|
842
|
+
evidence,
|
|
843
|
+
};
|
|
844
|
+
}
|
|
444
845
|
/**
|
|
445
846
|
* Analyze response for chain exploitation vulnerabilities (Issue #93, Challenge #6)
|
|
446
847
|
* Detects multi-tool chained exploitation attacks including:
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Security Assessment Module
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Exports all security-related components for testing MCP servers.
|
|
5
|
+
* Includes payload generation, testing, and response analysis.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
* @module assessment/security
|
|
4
9
|
*/
|
|
5
10
|
export { SecurityResponseAnalyzer, type ConfidenceResult, type AnalysisResult, type ErrorClassification, type StateBasedAuthResult, type ChainExploitationAnalysis, type ChainExecutionType, type ChainVulnerabilityCategory, } from "./SecurityResponseAnalyzer.js";
|
|
6
11
|
export { SecurityPayloadTester, type TestProgressCallback, type PayloadTestConfig, type TestLogger, } from "./SecurityPayloadTester.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,GAChC,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,UAAU,GAChB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EACL,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Security Assessment Module
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Exports all security-related components for testing MCP servers.
|
|
5
|
+
* Includes payload generation, testing, and response analysis.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
* @module assessment/security
|
|
4
9
|
*/
|
|
5
10
|
export { SecurityResponseAnalyzer, } from "./SecurityResponseAnalyzer.js";
|
|
6
11
|
export { SecurityPayloadTester, } from "./SecurityPayloadTester.js";
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* - Module progress/started event emission
|
|
8
8
|
* - Overall status determination
|
|
9
9
|
* - Summary and recommendations generation
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
* @module orchestratorHelpers
|
|
10
13
|
*/
|
|
11
14
|
import { MCPDirectoryAssessment, AssessmentStatus } from "../../lib/assessmentTypes.js";
|
|
12
15
|
export declare const moduleStartTimes: Map<string, number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestratorHelpers.d.ts","sourceRoot":"","sources":["../../../src/services/assessment/orchestratorHelpers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"orchestratorHelpers.d.ts","sourceRoot":"","sources":["../../../src/services/assessment/orchestratorHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAW/B,eAAO,MAAM,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,GAChB,IAAI,CAeN;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EACf,QAAQ,GAAE,MAAU,GACnB,IAAI,CAkCN;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE;IACT,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IACH,gBAAgB,CAAC,EAAE;QACjB,SAAS,EAAE,OAAO,CAAC;QACnB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B,EACD,UAAU,GAAE,MAAW,GACtB;IACD,gBAAgB,EAAE,KAAK,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;IACF,gBAAgB,EAAE;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAkEA;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACvC,gBAAgB,CAsBlB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACvC,MAAM,CA8ER;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACvC,MAAM,EAAE,CAiBV"}
|
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
* - Module progress/started event emission
|
|
8
8
|
* - Overall status determination
|
|
9
9
|
* - Summary and recommendations generation
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
* @module orchestratorHelpers
|
|
10
13
|
*/
|
|
11
14
|
// Import score calculation helpers from shared module
|
|
12
|
-
import { calculateModuleScore, normalizeModuleKey, INSPECTOR_VERSION, } from "../../lib/moduleScoring.js";
|
|
15
|
+
import { calculateModuleScore, normalizeModuleKey, INSPECTOR_VERSION, SCHEMA_VERSION, } from "../../lib/moduleScoring.js";
|
|
13
16
|
// Track module start times for duration calculation
|
|
14
17
|
export const moduleStartTimes = new Map();
|
|
15
18
|
/**
|
|
@@ -19,13 +22,14 @@ export const moduleStartTimes = new Map();
|
|
|
19
22
|
export function emitModuleStartedEvent(moduleName, estimatedTests, toolCount) {
|
|
20
23
|
const moduleKey = normalizeModuleKey(moduleName);
|
|
21
24
|
moduleStartTimes.set(moduleKey, Date.now());
|
|
22
|
-
// Emit JSONL to stderr with version
|
|
25
|
+
// Emit JSONL to stderr with version and schemaVersion fields
|
|
23
26
|
console.error(JSON.stringify({
|
|
24
27
|
event: "module_started",
|
|
25
28
|
module: moduleKey,
|
|
26
29
|
estimatedTests,
|
|
27
30
|
toolCount,
|
|
28
31
|
version: INSPECTOR_VERSION,
|
|
32
|
+
schemaVersion: SCHEMA_VERSION,
|
|
29
33
|
}));
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
@@ -53,13 +57,14 @@ export function emitModuleProgress(moduleName, status, result, testsRun = 0) {
|
|
|
53
57
|
testsRun,
|
|
54
58
|
duration,
|
|
55
59
|
version: INSPECTOR_VERSION,
|
|
60
|
+
schemaVersion: SCHEMA_VERSION,
|
|
56
61
|
};
|
|
57
62
|
// Add AUP enrichment when module is AUP
|
|
58
63
|
if (moduleKey === "aup" && result) {
|
|
59
64
|
const aupEnrichment = buildAUPEnrichment(result);
|
|
60
65
|
Object.assign(event, aupEnrichment);
|
|
61
66
|
}
|
|
62
|
-
// Emit JSONL to stderr with version
|
|
67
|
+
// Emit JSONL to stderr with version and schemaVersion fields
|
|
63
68
|
console.error(JSON.stringify(event));
|
|
64
69
|
}
|
|
65
70
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "Client-side application for the Enhanced MCP Inspector with assessment capabilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|