@bryan-thompson/inspector-assessment-client 1.35.1 → 1.35.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.
- package/dist/assets/{OAuthCallback-DC1cIXHT.js → OAuthCallback-jfmizOMH.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-C3gqJjgQ.js → OAuthDebugCallback-bU5kKvnt.js} +1 -1
- package/dist/assets/{index-Dn2w887x.js → index-Ce63ds7G.js} +4 -4
- package/dist/index.html +1 -1
- package/lib/lib/assessment/coreTypes.d.ts +23 -0
- package/lib/lib/assessment/coreTypes.d.ts.map +1 -1
- package/lib/lib/assessment/extendedTypes.d.ts +64 -7
- package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
- package/lib/lib/assessment/jsonlEventSchemas.d.ts +4 -4
- package/lib/lib/assessment/resultTypes.d.ts +12 -1
- package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/lib/lib/aupPatterns.d.ts +50 -0
- package/lib/lib/aupPatterns.d.ts.map +1 -1
- package/lib/lib/aupPatterns.js +140 -0
- package/lib/lib/securityPatterns.d.ts.map +1 -1
- package/lib/lib/securityPatterns.js +92 -0
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts +26 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.js +160 -1
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +40 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ManifestValidationAssessor.js +269 -28
- package/lib/services/assessment/modules/securityTests/ConfidenceScorer.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/ConfidenceScorer.js +28 -0
- package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts +95 -0
- package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.js +174 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +15 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +40 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +143 -131
- package/package.json +1 -1
|
@@ -14,7 +14,137 @@ import { BaseAssessor } from "./BaseAssessor.js";
|
|
|
14
14
|
const REQUIRED_FIELDS = ["name", "version", "mcp_config"];
|
|
15
15
|
const RECOMMENDED_FIELDS = ["description", "author", "repository"];
|
|
16
16
|
const CURRENT_MANIFEST_VERSION = "0.3";
|
|
17
|
+
const SEMVER_PATTERN = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
|
|
18
|
+
/**
|
|
19
|
+
* Calculate Levenshtein distance between two strings
|
|
20
|
+
* Uses space-optimized two-row algorithm for O(min(n,m)) memory
|
|
21
|
+
* Used for "did you mean?" suggestions on mismatched tool names (Issue #140)
|
|
22
|
+
* Exported for testing (Issue #141 - ISSUE-002)
|
|
23
|
+
*/
|
|
24
|
+
export function levenshteinDistance(a, b, maxDist) {
|
|
25
|
+
// Early termination optimizations
|
|
26
|
+
if (a === b)
|
|
27
|
+
return 0;
|
|
28
|
+
if (a.length === 0)
|
|
29
|
+
return b.length;
|
|
30
|
+
if (b.length === 0)
|
|
31
|
+
return a.length;
|
|
32
|
+
// If length difference exceeds max distance, no need to compute
|
|
33
|
+
if (maxDist && Math.abs(a.length - b.length) > maxDist) {
|
|
34
|
+
return maxDist + 1;
|
|
35
|
+
}
|
|
36
|
+
// Ensure a is the shorter string (optimize space)
|
|
37
|
+
if (a.length > b.length) {
|
|
38
|
+
[a, b] = [b, a];
|
|
39
|
+
}
|
|
40
|
+
// Two-row algorithm: only keep previous and current row
|
|
41
|
+
let prev = Array.from({ length: a.length + 1 }, (_, i) => i);
|
|
42
|
+
let curr = new Array(a.length + 1);
|
|
43
|
+
for (let i = 1; i <= b.length; i++) {
|
|
44
|
+
curr[0] = i;
|
|
45
|
+
for (let j = 1; j <= a.length; j++) {
|
|
46
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
47
|
+
curr[j] = prev[j - 1];
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
curr[j] = Math.min(prev[j - 1] + 1, // substitution
|
|
51
|
+
curr[j - 1] + 1, // insertion
|
|
52
|
+
prev[j] + 1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Swap rows
|
|
56
|
+
[prev, curr] = [curr, prev];
|
|
57
|
+
}
|
|
58
|
+
return prev[a.length];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Find closest matching tool name from a set
|
|
62
|
+
* Returns match if distance <= threshold (default: 10 chars or 40% of length)
|
|
63
|
+
* Generous threshold to catch common renames like "data" -> "resources"
|
|
64
|
+
*/
|
|
65
|
+
function findClosestMatch(name, candidates, threshold) {
|
|
66
|
+
const maxDist = threshold ?? Math.max(10, Math.floor(name.length * 0.4));
|
|
67
|
+
let closest = null;
|
|
68
|
+
let minDist = Infinity;
|
|
69
|
+
for (const candidate of candidates) {
|
|
70
|
+
const dist = levenshteinDistance(name.toLowerCase(), candidate.toLowerCase(), maxDist);
|
|
71
|
+
if (dist < minDist && dist <= maxDist) {
|
|
72
|
+
minDist = dist;
|
|
73
|
+
closest = candidate;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return closest;
|
|
77
|
+
}
|
|
17
78
|
export class ManifestValidationAssessor extends BaseAssessor {
|
|
79
|
+
/**
|
|
80
|
+
* Get mcp_config from manifest (supports both root and nested v0.3 format)
|
|
81
|
+
* Issue #138: Manifest v0.3 places mcp_config under server object
|
|
82
|
+
*
|
|
83
|
+
* @param manifest - The parsed manifest JSON
|
|
84
|
+
* @returns The mcp_config object or undefined if not found in either location
|
|
85
|
+
*/
|
|
86
|
+
getMcpConfig(manifest) {
|
|
87
|
+
// Check root level first (legacy format)
|
|
88
|
+
if (manifest.mcp_config) {
|
|
89
|
+
return manifest.mcp_config;
|
|
90
|
+
}
|
|
91
|
+
// Check nested under server (v0.3 format)
|
|
92
|
+
if (manifest.server?.mcp_config) {
|
|
93
|
+
return manifest.server.mcp_config;
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Extract contact information from manifest (Issue #141 - D4 check)
|
|
99
|
+
* Supports: author object, author string (email parsing), repository fallback
|
|
100
|
+
*
|
|
101
|
+
* @param manifest - The parsed manifest JSON
|
|
102
|
+
* @returns Extracted contact info or undefined if no contact info found
|
|
103
|
+
*/
|
|
104
|
+
extractContactInfo(manifest) {
|
|
105
|
+
// 1. Check author object format (npm-style)
|
|
106
|
+
if (typeof manifest.author === "object" && manifest.author !== null) {
|
|
107
|
+
const authorObj = manifest.author;
|
|
108
|
+
return {
|
|
109
|
+
email: authorObj.email,
|
|
110
|
+
url: authorObj.url,
|
|
111
|
+
name: authorObj.name,
|
|
112
|
+
source: "author_object",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// 2. Check author string (may contain email: "Name <email@example.com>")
|
|
116
|
+
if (typeof manifest.author === "string" && manifest.author.trim()) {
|
|
117
|
+
const emailMatch = manifest.author.match(/<([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>/);
|
|
118
|
+
return {
|
|
119
|
+
name: manifest.author.replace(/<[^>]+>/, "").trim() || undefined,
|
|
120
|
+
email: emailMatch?.[1],
|
|
121
|
+
source: "author_string",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// 3. Fallback to repository (provides contact via issues)
|
|
125
|
+
if (manifest.repository) {
|
|
126
|
+
return {
|
|
127
|
+
url: manifest.repository,
|
|
128
|
+
source: "repository",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Extract version information from manifest (Issue #141 - D5 check)
|
|
135
|
+
*
|
|
136
|
+
* @param manifest - The parsed manifest JSON
|
|
137
|
+
* @returns Extracted version info or undefined if no version found
|
|
138
|
+
*/
|
|
139
|
+
extractVersionInfo(manifest) {
|
|
140
|
+
if (!manifest.version)
|
|
141
|
+
return undefined;
|
|
142
|
+
return {
|
|
143
|
+
version: manifest.version,
|
|
144
|
+
valid: true,
|
|
145
|
+
semverCompliant: SEMVER_PATTERN.test(manifest.version),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
18
148
|
/**
|
|
19
149
|
* Run manifest validation assessment
|
|
20
150
|
*/
|
|
@@ -58,11 +188,35 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
58
188
|
// Validate required fields
|
|
59
189
|
for (const field of REQUIRED_FIELDS) {
|
|
60
190
|
this.testCount++;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
191
|
+
// Special handling for mcp_config - can be nested under server (Issue #138)
|
|
192
|
+
if (field === "mcp_config") {
|
|
193
|
+
const mcpConfig = this.getMcpConfig(manifest);
|
|
194
|
+
if (!mcpConfig) {
|
|
195
|
+
validationResults.push({
|
|
196
|
+
field: "mcp_config",
|
|
197
|
+
valid: false,
|
|
198
|
+
issue: "Missing required field: mcp_config (checked root and server.mcp_config)",
|
|
199
|
+
severity: "ERROR",
|
|
200
|
+
});
|
|
201
|
+
hasRequiredFields = false;
|
|
202
|
+
missingFields.push(field);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
validationResults.push({
|
|
206
|
+
field: "mcp_config",
|
|
207
|
+
valid: true,
|
|
208
|
+
value: mcpConfig,
|
|
209
|
+
severity: "INFO",
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const result = this.validateRequiredField(manifest, field);
|
|
215
|
+
validationResults.push(result);
|
|
216
|
+
if (!result.valid) {
|
|
217
|
+
hasRequiredFields = false;
|
|
218
|
+
missingFields.push(field);
|
|
219
|
+
}
|
|
66
220
|
}
|
|
67
221
|
}
|
|
68
222
|
// Validate recommended fields
|
|
@@ -70,10 +224,11 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
70
224
|
this.testCount++;
|
|
71
225
|
validationResults.push(this.validateRecommendedField(manifest, field));
|
|
72
226
|
}
|
|
73
|
-
// Validate mcp_config structure
|
|
74
|
-
|
|
227
|
+
// Validate mcp_config structure (using helper to support both root and nested paths)
|
|
228
|
+
const mcpConfig = this.getMcpConfig(manifest);
|
|
229
|
+
if (mcpConfig) {
|
|
75
230
|
this.testCount++;
|
|
76
|
-
validationResults.push(this.validateMcpConfig(
|
|
231
|
+
validationResults.push(this.validateMcpConfig(mcpConfig));
|
|
77
232
|
}
|
|
78
233
|
// Check for icon
|
|
79
234
|
this.testCount++;
|
|
@@ -86,6 +241,11 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
86
241
|
// Validate version format
|
|
87
242
|
this.testCount++;
|
|
88
243
|
validationResults.push(this.validateVersionFormat(manifest.version));
|
|
244
|
+
// Validate tool names match server (Issue #140)
|
|
245
|
+
if (manifest.tools && context.tools.length > 0) {
|
|
246
|
+
const toolResults = this.validateToolNamesMatch(manifest, context.tools);
|
|
247
|
+
validationResults.push(...toolResults);
|
|
248
|
+
}
|
|
89
249
|
// Validate privacy policy URLs if present
|
|
90
250
|
let privacyPolicies;
|
|
91
251
|
if (manifest.privacy_policies &&
|
|
@@ -121,6 +281,9 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
121
281
|
const status = this.determineManifestStatus(validationResults, hasRequiredFields);
|
|
122
282
|
const explanation = this.generateExplanation(validationResults, hasRequiredFields, hasIcon, privacyPolicies);
|
|
123
283
|
const recommendations = this.generateRecommendations(validationResults, privacyPolicies);
|
|
284
|
+
// Extract D4/D5 fields (Issue #141)
|
|
285
|
+
const contactInfo = this.extractContactInfo(manifest);
|
|
286
|
+
const versionInfo = this.extractVersionInfo(manifest);
|
|
124
287
|
this.logger.info(`Assessment complete: ${validationResults.filter((r) => r.valid).length}/${validationResults.length} checks passed`);
|
|
125
288
|
return {
|
|
126
289
|
hasManifest: true,
|
|
@@ -130,6 +293,8 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
130
293
|
hasRequiredFields,
|
|
131
294
|
missingFields,
|
|
132
295
|
privacyPolicies,
|
|
296
|
+
contactInfo,
|
|
297
|
+
versionInfo,
|
|
133
298
|
status,
|
|
134
299
|
explanation,
|
|
135
300
|
recommendations,
|
|
@@ -383,9 +548,7 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
383
548
|
severity: "ERROR",
|
|
384
549
|
};
|
|
385
550
|
}
|
|
386
|
-
|
|
387
|
-
const semverPattern = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/;
|
|
388
|
-
if (!semverPattern.test(version)) {
|
|
551
|
+
if (!SEMVER_PATTERN.test(version)) {
|
|
389
552
|
return {
|
|
390
553
|
field: "version (format)",
|
|
391
554
|
valid: false,
|
|
@@ -401,6 +564,98 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
401
564
|
severity: "INFO",
|
|
402
565
|
};
|
|
403
566
|
}
|
|
567
|
+
/**
|
|
568
|
+
* Validate manifest tool declarations against actual server tools (Issue #140)
|
|
569
|
+
* Compares tool names in manifest.tools against context.tools from tools/list
|
|
570
|
+
* Uses Levenshtein distance for "did you mean?" suggestions
|
|
571
|
+
*/
|
|
572
|
+
validateToolNamesMatch(manifest, serverTools) {
|
|
573
|
+
const results = [];
|
|
574
|
+
// Skip if manifest doesn't declare tools
|
|
575
|
+
if (!manifest.tools || manifest.tools.length === 0) {
|
|
576
|
+
return results;
|
|
577
|
+
}
|
|
578
|
+
this.testCount++;
|
|
579
|
+
const manifestToolNames = new Set(manifest.tools.map((t) => t.name));
|
|
580
|
+
const serverToolNames = new Set(serverTools.map((t) => t.name));
|
|
581
|
+
// Check for tools declared in manifest but not on server
|
|
582
|
+
const mismatches = [];
|
|
583
|
+
for (const name of manifestToolNames) {
|
|
584
|
+
if (!serverToolNames.has(name)) {
|
|
585
|
+
const suggestion = findClosestMatch(name, serverToolNames);
|
|
586
|
+
mismatches.push({ manifest: name, suggestion });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// Check for tools on server but not declared in manifest
|
|
590
|
+
const undeclaredTools = [];
|
|
591
|
+
for (const name of serverToolNames) {
|
|
592
|
+
if (!manifestToolNames.has(name)) {
|
|
593
|
+
undeclaredTools.push(name);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// Report mismatches with suggestions
|
|
597
|
+
if (mismatches.length > 0) {
|
|
598
|
+
const issueLines = mismatches.map((m) => m.suggestion
|
|
599
|
+
? `"${m.manifest}" (did you mean "${m.suggestion}"?)`
|
|
600
|
+
: `"${m.manifest}"`);
|
|
601
|
+
results.push({
|
|
602
|
+
field: "tools (manifest vs server)",
|
|
603
|
+
valid: false,
|
|
604
|
+
value: mismatches,
|
|
605
|
+
issue: `Manifest declares tools not found on server: ${issueLines.join(", ")}`,
|
|
606
|
+
severity: "WARNING",
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
if (undeclaredTools.length > 0) {
|
|
610
|
+
results.push({
|
|
611
|
+
field: "tools (undeclared)",
|
|
612
|
+
valid: false,
|
|
613
|
+
value: undeclaredTools,
|
|
614
|
+
issue: `Server has tools not declared in manifest: ${undeclaredTools.join(", ")}`,
|
|
615
|
+
severity: "INFO",
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
// All matched
|
|
619
|
+
if (mismatches.length === 0 && undeclaredTools.length === 0) {
|
|
620
|
+
results.push({
|
|
621
|
+
field: "tools (manifest vs server)",
|
|
622
|
+
valid: true,
|
|
623
|
+
value: `${manifestToolNames.size} tools matched`,
|
|
624
|
+
severity: "INFO",
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
return results;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Fetch with retry logic for transient network failures
|
|
631
|
+
*/
|
|
632
|
+
async fetchWithRetry(url, method, retries = 2, backoffMs = 100) {
|
|
633
|
+
let lastError = null;
|
|
634
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
635
|
+
const controller = new AbortController();
|
|
636
|
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
637
|
+
try {
|
|
638
|
+
const response = await fetch(url, {
|
|
639
|
+
method,
|
|
640
|
+
signal: controller.signal,
|
|
641
|
+
redirect: "follow",
|
|
642
|
+
});
|
|
643
|
+
clearTimeout(timeoutId);
|
|
644
|
+
return response;
|
|
645
|
+
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
clearTimeout(timeoutId);
|
|
648
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
649
|
+
// Don't retry on last attempt
|
|
650
|
+
if (attempt < retries) {
|
|
651
|
+
// Exponential backoff
|
|
652
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs * Math.pow(2, attempt)));
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
// All retries exhausted
|
|
657
|
+
throw lastError;
|
|
658
|
+
}
|
|
404
659
|
/**
|
|
405
660
|
* Validate privacy policy URLs are accessible
|
|
406
661
|
*/
|
|
@@ -424,15 +679,8 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
424
679
|
continue;
|
|
425
680
|
}
|
|
426
681
|
try {
|
|
427
|
-
// Use HEAD request for efficiency
|
|
428
|
-
const
|
|
429
|
-
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
430
|
-
const response = await fetch(url, {
|
|
431
|
-
method: "HEAD",
|
|
432
|
-
signal: controller.signal,
|
|
433
|
-
redirect: "follow",
|
|
434
|
-
});
|
|
435
|
-
clearTimeout(timeoutId);
|
|
682
|
+
// Use HEAD request for efficiency with retry logic
|
|
683
|
+
const response = await this.fetchWithRetry(url, "HEAD");
|
|
436
684
|
results.push({
|
|
437
685
|
url,
|
|
438
686
|
accessible: response.ok,
|
|
@@ -446,14 +694,7 @@ export class ManifestValidationAssessor extends BaseAssessor {
|
|
|
446
694
|
error: headError instanceof Error ? headError.message : String(headError),
|
|
447
695
|
});
|
|
448
696
|
try {
|
|
449
|
-
const
|
|
450
|
-
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
451
|
-
const response = await fetch(url, {
|
|
452
|
-
method: "GET",
|
|
453
|
-
signal: controller.signal,
|
|
454
|
-
redirect: "follow",
|
|
455
|
-
});
|
|
456
|
-
clearTimeout(timeoutId);
|
|
697
|
+
const response = await this.fetchWithRetry(url, "GET");
|
|
457
698
|
results.push({
|
|
458
699
|
url,
|
|
459
700
|
accessible: response.ok,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfidenceScorer.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/ConfidenceScorer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAmCD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B;;;;;;;;;;;;;;;;OAgBG;IACH,mBAAmB,CACjB,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,OAAO,EACrB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,EACxB,kBAAkB,CAAC,EAAE,2BAA2B,GAC/C,gBAAgB;
|
|
1
|
+
{"version":3,"file":"ConfidenceScorer.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/ConfidenceScorer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAmCD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B;;;;;;;;;;;;;;;;OAgBG;IACH,mBAAmB,CACjB,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,OAAO,EACrB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,EACxB,kBAAkB,CAAC,EAAE,2BAA2B,GAC/C,gBAAgB;IAgMnB;;;;;OAKG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO;IAOxE;;;;;OAKG;IACH,mBAAmB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO;CAOtD"}
|
|
@@ -58,6 +58,34 @@ export class ConfidenceScorer {
|
|
|
58
58
|
* @returns Confidence result with manual review requirements
|
|
59
59
|
*/
|
|
60
60
|
calculateConfidence(tool, isVulnerable, evidence, responseText, payload, sanitizationResult) {
|
|
61
|
+
// Issue #146: Extract execution context from evidence if present
|
|
62
|
+
// This handles context classification from SecurityResponseAnalyzer
|
|
63
|
+
const contextMatch = evidence.match(/\[Context: (CONFIRMED|LIKELY_FALSE_POSITIVE|SUSPECTED)/);
|
|
64
|
+
if (contextMatch) {
|
|
65
|
+
const context = contextMatch[1];
|
|
66
|
+
// LIKELY_FALSE_POSITIVE: Payload reflected in error message, not executed
|
|
67
|
+
// Mark as low confidence requiring manual review
|
|
68
|
+
if (context === "LIKELY_FALSE_POSITIVE") {
|
|
69
|
+
return {
|
|
70
|
+
confidence: "low",
|
|
71
|
+
requiresManualReview: true,
|
|
72
|
+
manualReviewReason: "Payload reflected in error message, operation failed",
|
|
73
|
+
reviewGuidance: "The server rejected the operation but echoed the payload in the error. " +
|
|
74
|
+
"Verify if the tool actually processed the payload or just reflected it in the error message. " +
|
|
75
|
+
"Check the HTTP status code and error type to confirm the operation was rejected.",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// CONFIRMED: Operation succeeded, payload was executed
|
|
79
|
+
// High confidence vulnerability
|
|
80
|
+
if (context === "CONFIRMED") {
|
|
81
|
+
return {
|
|
82
|
+
confidence: "high",
|
|
83
|
+
requiresManualReview: false,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// SUSPECTED: Ambiguous case - continue with normal scoring but add review flag
|
|
87
|
+
// Will be handled by downstream logic with medium confidence
|
|
88
|
+
}
|
|
61
89
|
// Issue #56: If sanitization is detected, reduce confidence for vulnerabilities
|
|
62
90
|
// This helps reduce false positives on well-protected servers
|
|
63
91
|
if (isVulnerable && sanitizationResult?.detected) {
|
|
@@ -25,6 +25,37 @@ export declare const HTTP_ERROR_PATTERNS: {
|
|
|
25
25
|
* Used by: isMCPValidationError()
|
|
26
26
|
*/
|
|
27
27
|
export declare const VALIDATION_ERROR_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
28
|
+
/**
|
|
29
|
+
* Issue #146: Error context patterns indicating operation failure
|
|
30
|
+
* Used to detect when payload appears in error message (likely false positive)
|
|
31
|
+
* These patterns indicate the server rejected/failed the operation
|
|
32
|
+
*/
|
|
33
|
+
export declare const ERROR_CONTEXT_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
34
|
+
/**
|
|
35
|
+
* Issue #146: Success context patterns indicating operation completion
|
|
36
|
+
* Used to confirm operation actually executed (high confidence vulnerability)
|
|
37
|
+
* These patterns indicate the server processed and returned results
|
|
38
|
+
*/
|
|
39
|
+
export declare const SUCCESS_CONTEXT_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
40
|
+
/**
|
|
41
|
+
* Issue #146: Check if payload appears in error context (likely false positive)
|
|
42
|
+
* @param responseText The full response text from the tool
|
|
43
|
+
* @param payload The payload that was sent to the tool
|
|
44
|
+
* @returns true if payload is reflected in an error context
|
|
45
|
+
*/
|
|
46
|
+
export declare function isPayloadInErrorContext(responseText: string, payload: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Issue #146: Check if response indicates successful operation (high confidence)
|
|
49
|
+
* @param responseText The full response text from the tool
|
|
50
|
+
* @returns true if response indicates operation succeeded
|
|
51
|
+
*/
|
|
52
|
+
export declare function hasSuccessContext(responseText: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Issue #146: Check if response indicates failed operation (error context)
|
|
55
|
+
* @param responseText The full response text from the tool
|
|
56
|
+
* @returns true if response indicates operation failed
|
|
57
|
+
*/
|
|
58
|
+
export declare function hasErrorContext(responseText: string): boolean;
|
|
28
59
|
/**
|
|
29
60
|
* Patterns indicating actual code/command execution
|
|
30
61
|
* Used by: hasExecutionEvidence()
|
|
@@ -380,6 +411,70 @@ export declare const OUTPUT_INJECTION_PATTERNS: {
|
|
|
380
411
|
/** Canary markers for echo detection */
|
|
381
412
|
readonly canaryMarkers: readonly [RegExp];
|
|
382
413
|
};
|
|
414
|
+
/**
|
|
415
|
+
* Patterns indicating scope violation in tool response
|
|
416
|
+
* Detects when tools exceed their declared annotations at runtime
|
|
417
|
+
* CWE-250: Execution with Unnecessary Privileges
|
|
418
|
+
* CWE-269: Improper Privilege Management
|
|
419
|
+
*/
|
|
420
|
+
export declare const SCOPE_VIOLATION_PATTERNS: readonly [{
|
|
421
|
+
readonly pattern: RegExp;
|
|
422
|
+
readonly evidence: "scope_violation: true (tool exceeded declared scope)";
|
|
423
|
+
}, {
|
|
424
|
+
readonly pattern: RegExp;
|
|
425
|
+
readonly evidence: "scope_escalation: true (privilege escalation via input)";
|
|
426
|
+
}, {
|
|
427
|
+
readonly pattern: RegExp;
|
|
428
|
+
readonly evidence: "actual_scope indicates privileged operation";
|
|
429
|
+
}, {
|
|
430
|
+
readonly pattern: RegExp;
|
|
431
|
+
readonly evidence: "privileged_data exposed in response";
|
|
432
|
+
}, {
|
|
433
|
+
readonly pattern: RegExp;
|
|
434
|
+
readonly evidence: "system_secrets leaked in response";
|
|
435
|
+
}, {
|
|
436
|
+
readonly pattern: RegExp;
|
|
437
|
+
readonly evidence: "Admin mode activated via keyword trigger";
|
|
438
|
+
}];
|
|
439
|
+
/**
|
|
440
|
+
* Patterns indicating safe scope enforcement (hardened behavior)
|
|
441
|
+
* Used to confirm tools properly reject privileged actions
|
|
442
|
+
*/
|
|
443
|
+
export declare const SCOPE_ENFORCED_PATTERNS: readonly [{
|
|
444
|
+
readonly pattern: RegExp;
|
|
445
|
+
readonly evidence: "scope_enforced: true (safe)";
|
|
446
|
+
}, {
|
|
447
|
+
readonly pattern: RegExp;
|
|
448
|
+
readonly evidence: "Action blocked by allowlist";
|
|
449
|
+
}, {
|
|
450
|
+
readonly pattern: RegExp;
|
|
451
|
+
readonly evidence: "Allowlist enforcement";
|
|
452
|
+
}, {
|
|
453
|
+
readonly pattern: RegExp;
|
|
454
|
+
readonly evidence: "Explicit rejection: action not in allowlist";
|
|
455
|
+
}, {
|
|
456
|
+
readonly pattern: RegExp;
|
|
457
|
+
readonly evidence: "No escalation attempted (safe)";
|
|
458
|
+
}, {
|
|
459
|
+
readonly pattern: RegExp;
|
|
460
|
+
readonly evidence: "Allowlist defined and enforced";
|
|
461
|
+
}];
|
|
462
|
+
/**
|
|
463
|
+
* Privileged actions that should be blocked by readOnlyHint=True tools
|
|
464
|
+
*/
|
|
465
|
+
export declare const PRIVILEGED_ACTIONS: readonly ["write", "write_file", "delete", "delete_data", "execute", "execute_command", "network", "network_request", "modify", "modify_config", "admin", "environment_access"];
|
|
466
|
+
/**
|
|
467
|
+
* Escalation keywords that may trigger hidden privilege escalation
|
|
468
|
+
*/
|
|
469
|
+
export declare const ESCALATION_KEYWORDS: readonly ["admin", "sudo", "elevate", "root", "superuser", "privilege"];
|
|
470
|
+
/**
|
|
471
|
+
* Check if response contains scope violation indicators (Issue #144)
|
|
472
|
+
*/
|
|
473
|
+
export declare function hasScopeViolation(text: string): boolean;
|
|
474
|
+
/**
|
|
475
|
+
* Check if response contains scope enforcement indicators (Issue #144)
|
|
476
|
+
*/
|
|
477
|
+
export declare function hasScopeEnforcement(text: string): boolean;
|
|
383
478
|
/**
|
|
384
479
|
* Check if any pattern in array matches text
|
|
385
480
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityPatternLibrary.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPatternLibrary.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SecurityPatternLibrary.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPatternLibrary.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;GAGG;AACH,eAAO,MAAM,mBAAmB;IAC9B,kEAAkE;;IAIlE,8DAA8D;;IAG9D,kCAAkC;;IAGlC,gCAAgC;;CAExB,CAAC;AAMX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,2JAmB5B,CAAC;AAMX;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,2GAazB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,mFAU3B,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAWT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAE7D;AAMD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,2LAuBvB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,2BAA2B;IACtC,iCAAiC;;IAejC,0DAA0D;;CAElD,CAAC;AAMX;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,2KA4BxB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,yBAAyB;IACpC,uDAAuD;;IAOvD,oDAAoD;;CAO5C,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,yBAAyB;IACpC,oCAAoC;;IAqBpC,4DAA4D;;IAW5D,+BAA+B;;CAEvB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,6BAA6B;;;;CAMhC,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,eAAe,mJAkBlB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB,2rBAwGtB,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+B1B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAc5B,CAAC;AAMX;;;;GAIG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;EAiCjC,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;EAyB3B,CAAC;AAMX;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,EAAE,oBAAoB,EA0FnE,CAAC;AAEF;;;;;;;;GAQG;AAKH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,IAAM,CAAC;AAMxC;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,MAAM,EACN;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,CAgCxC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB5E;AAED,eAAO,MAAM,2BAA2B,EAAE,oBAAoB,EAuE7D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,2FAWzB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,iBAAiB,mHAcpB,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,uBAAuB,mFAU1B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,2BAA2B,mDAM9B,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,uBAAuB,2DAO1B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,yBAAyB,2DAO5B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,6BAA6B,yKAWhC,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,kBAAkB,mGAYrB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,2BAA2B,QACO,CAAC;AAMhD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAC8B,CAAC;AAE/D;;;GAGG;AACH,eAAO,MAAM,wBAAwB,2EAS3B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,2BAA2B,oRA4B9B,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;CAK7B,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,uBAAuB;IAClC,iCAAiC;;IAQjC,mDAAmD;;IAInD,gDAAgD;;IAIhD,oCAAoC;;IAEpC,6CAA6C;;CAIrC,CAAC;AAMX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;IACpC,oDAAoD;;IAOpD,wCAAwC;;CAEhC,CAAC;AAMX;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;EAyB3B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;EAyB1B,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,kBAAkB,iLAarB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,mBAAmB,yEAOtB,CAAC;AAEX;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAMD;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOjD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKrE"}
|