@bryan-thompson/inspector-assessment-client 1.15.0 → 1.16.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.
Files changed (57) hide show
  1. package/dist/assets/{OAuthCallback-BleN4Jjs.js → OAuthCallback-KwMiy-L3.js} +1 -1
  2. package/dist/assets/{OAuthDebugCallback-C__lzEyx.js → OAuthDebugCallback-hckdJlo3.js} +1 -1
  3. package/dist/assets/{index-CPXmfP9b.js → index-C89umkGV.js} +745 -4350
  4. package/dist/index.html +1 -1
  5. package/lib/lib/assessmentTypes.d.ts +123 -0
  6. package/lib/lib/assessmentTypes.d.ts.map +1 -1
  7. package/lib/lib/assessmentTypes.js +20 -0
  8. package/lib/lib/securityPatterns.d.ts +2 -2
  9. package/lib/lib/securityPatterns.d.ts.map +1 -1
  10. package/lib/lib/securityPatterns.js +290 -15
  11. package/lib/services/assessment/AssessmentOrchestrator.d.ts +67 -0
  12. package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
  13. package/lib/services/assessment/AssessmentOrchestrator.js +91 -1
  14. package/lib/services/assessment/ResponseValidator.d.ts +7 -34
  15. package/lib/services/assessment/ResponseValidator.d.ts.map +1 -1
  16. package/lib/services/assessment/ResponseValidator.js +100 -704
  17. package/lib/services/assessment/config/annotationPatterns.js +1 -1
  18. package/lib/services/assessment/lib/RequestHistoryAnalyzer.d.ts +67 -0
  19. package/lib/services/assessment/lib/RequestHistoryAnalyzer.d.ts.map +1 -0
  20. package/lib/services/assessment/lib/RequestHistoryAnalyzer.js +191 -0
  21. package/lib/services/assessment/lib/claudeCodeBridge.d.ts +1 -0
  22. package/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -1
  23. package/lib/services/assessment/lib/claudeCodeBridge.js +5 -4
  24. package/lib/services/assessment/modules/AuthenticationAssessor.d.ts +4 -0
  25. package/lib/services/assessment/modules/AuthenticationAssessor.d.ts.map +1 -1
  26. package/lib/services/assessment/modules/AuthenticationAssessor.js +97 -1
  27. package/lib/services/assessment/modules/CrossCapabilitySecurityAssessor.d.ts +39 -0
  28. package/lib/services/assessment/modules/CrossCapabilitySecurityAssessor.d.ts.map +1 -0
  29. package/lib/services/assessment/modules/CrossCapabilitySecurityAssessor.js +330 -0
  30. package/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
  31. package/lib/services/assessment/modules/FunctionalityAssessor.js +46 -13
  32. package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +5 -0
  33. package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -1
  34. package/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +81 -0
  35. package/lib/services/assessment/modules/ManifestValidationAssessor.js +1 -1
  36. package/lib/services/assessment/modules/PromptAssessor.d.ts +30 -0
  37. package/lib/services/assessment/modules/PromptAssessor.d.ts.map +1 -0
  38. package/lib/services/assessment/modules/PromptAssessor.js +367 -0
  39. package/lib/services/assessment/modules/ResourceAssessor.d.ts +28 -0
  40. package/lib/services/assessment/modules/ResourceAssessor.d.ts.map +1 -0
  41. package/lib/services/assessment/modules/ResourceAssessor.js +296 -0
  42. package/lib/services/assessment/modules/SecurityAssessor.d.ts +4 -2
  43. package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
  44. package/lib/services/assessment/modules/SecurityAssessor.js +10 -41
  45. package/lib/services/assessment/modules/TemporalAssessor.d.ts +1 -0
  46. package/lib/services/assessment/modules/TemporalAssessor.d.ts.map +1 -1
  47. package/lib/services/assessment/modules/TemporalAssessor.js +35 -4
  48. package/lib/utils/jsonUtils.d.ts +68 -0
  49. package/lib/utils/jsonUtils.d.ts.map +1 -0
  50. package/lib/utils/jsonUtils.js +141 -0
  51. package/lib/utils/paramUtils.d.ts +11 -0
  52. package/lib/utils/paramUtils.d.ts.map +1 -0
  53. package/lib/utils/paramUtils.js +37 -0
  54. package/lib/utils/schemaUtils.d.ts +74 -0
  55. package/lib/utils/schemaUtils.d.ts.map +1 -0
  56. package/lib/utils/schemaUtils.js +268 -0
  57. package/package.json +1 -1
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Resource Assessor Module
3
+ * Tests MCP server resources for accessibility, security, and compliance
4
+ *
5
+ * Tests include:
6
+ * - Resource accessibility (can read declared resources)
7
+ * - Path traversal vulnerabilities in resource URIs
8
+ * - Sensitive data exposure detection
9
+ * - URI validation and format compliance
10
+ */
11
+ import { BaseAssessor } from "./BaseAssessor.js";
12
+ // Sensitive file patterns that shouldn't be exposed
13
+ const SENSITIVE_PATTERNS = [
14
+ /\.env$/i,
15
+ /\.pem$/i,
16
+ /\.key$/i,
17
+ /id_rsa/i,
18
+ /id_dsa/i,
19
+ /\.crt$/i,
20
+ /password/i,
21
+ /secret/i,
22
+ /credential/i,
23
+ /\/etc\/passwd/i,
24
+ /\/etc\/shadow/i,
25
+ /\.ssh\//i,
26
+ /aws_access_key/i,
27
+ /api[_-]?key/i,
28
+ /\.htpasswd/i,
29
+ /\.git\/config/i,
30
+ ];
31
+ // Path traversal patterns to test
32
+ const PATH_TRAVERSAL_PAYLOADS = [
33
+ "../../../etc/passwd",
34
+ "..\\..\\..\\windows\\system32\\config\\sam",
35
+ "....//....//....//etc/passwd",
36
+ "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
37
+ "..%252f..%252f..%252fetc/passwd",
38
+ ];
39
+ // Sensitive content patterns in resource content
40
+ const SENSITIVE_CONTENT_PATTERNS = [
41
+ /-----BEGIN.*PRIVATE KEY-----/i,
42
+ /-----BEGIN RSA PRIVATE KEY-----/i,
43
+ /sk-[a-zA-Z0-9]{32,}/i, // OpenAI-style API keys
44
+ /ghp_[a-zA-Z0-9]{36}/i, // GitHub tokens
45
+ /glpat-[a-zA-Z0-9-_]{20}/i, // GitLab tokens
46
+ /xox[baprs]-[a-zA-Z0-9-]+/i, // Slack tokens
47
+ /AKIA[A-Z0-9]{16}/i, // AWS access keys
48
+ /password\s*[:=]\s*['"][^'"]+['"]/i,
49
+ /secret\s*[:=]\s*['"][^'"]+['"]/i,
50
+ ];
51
+ export class ResourceAssessor extends BaseAssessor {
52
+ async assess(context) {
53
+ const results = [];
54
+ // Check if resources are provided
55
+ if (!context.resources && !context.resourceTemplates) {
56
+ return this.createNoResourcesResponse();
57
+ }
58
+ const resources = context.resources || [];
59
+ const templates = context.resourceTemplates || [];
60
+ this.log(`Testing ${resources.length} resources and ${templates.length} resource templates`);
61
+ // Test each resource
62
+ for (const resource of resources) {
63
+ this.testCount++;
64
+ const result = await this.testResource(resource, context);
65
+ results.push(result);
66
+ }
67
+ // Test resource templates with path traversal payloads
68
+ for (const template of templates) {
69
+ this.testCount++;
70
+ const templateResults = await this.testResourceTemplate(template, context);
71
+ results.push(...templateResults);
72
+ }
73
+ // Calculate metrics
74
+ const accessibleResources = results.filter((r) => r.accessible).length;
75
+ const securityIssuesFound = results.filter((r) => r.securityIssues.length > 0).length;
76
+ const pathTraversalVulnerabilities = results.filter((r) => r.pathTraversalVulnerable).length;
77
+ const sensitiveDataExposures = results.filter((r) => r.sensitiveDataExposed).length;
78
+ // Determine status
79
+ const status = this.determineResourceStatus(pathTraversalVulnerabilities, sensitiveDataExposures, securityIssuesFound, results.length);
80
+ // Generate explanation and recommendations
81
+ const explanation = this.generateExplanation(results, pathTraversalVulnerabilities, sensitiveDataExposures);
82
+ const recommendations = this.generateRecommendations(results);
83
+ return {
84
+ resourcesTested: resources.length,
85
+ resourceTemplatesTested: templates.length,
86
+ accessibleResources,
87
+ securityIssuesFound,
88
+ pathTraversalVulnerabilities,
89
+ sensitiveDataExposures,
90
+ results,
91
+ status,
92
+ explanation,
93
+ recommendations,
94
+ };
95
+ }
96
+ createNoResourcesResponse() {
97
+ return {
98
+ resourcesTested: 0,
99
+ resourceTemplatesTested: 0,
100
+ accessibleResources: 0,
101
+ securityIssuesFound: 0,
102
+ pathTraversalVulnerabilities: 0,
103
+ sensitiveDataExposures: 0,
104
+ results: [],
105
+ status: "PASS",
106
+ explanation: "No resources declared by server. Resource assessment skipped.",
107
+ recommendations: [],
108
+ };
109
+ }
110
+ async testResource(resource, context) {
111
+ const result = {
112
+ resourceUri: resource.uri,
113
+ resourceName: resource.name,
114
+ mimeType: resource.mimeType,
115
+ tested: true,
116
+ accessible: false,
117
+ securityIssues: [],
118
+ pathTraversalVulnerable: false,
119
+ sensitiveDataExposed: false,
120
+ validUri: this.isValidUri(resource.uri),
121
+ };
122
+ // Check URI for sensitive patterns
123
+ if (this.isSensitiveUri(resource.uri)) {
124
+ result.securityIssues.push(`Resource URI matches sensitive file pattern: ${resource.uri}`);
125
+ result.sensitiveDataExposed = true;
126
+ }
127
+ // Try to read the resource if readResource function is provided
128
+ if (context.readResource) {
129
+ try {
130
+ const startTime = Date.now();
131
+ const content = await this.executeWithTimeout(context.readResource(resource.uri), 5000);
132
+ result.readTime = Date.now() - startTime;
133
+ result.accessible = true;
134
+ result.contentSizeBytes = content?.length || 0;
135
+ // Check content for sensitive data
136
+ if (content && this.containsSensitiveContent(content)) {
137
+ result.securityIssues.push("Resource content contains sensitive data patterns (credentials, keys, etc.)");
138
+ result.sensitiveDataExposed = true;
139
+ }
140
+ }
141
+ catch (error) {
142
+ result.error = this.extractErrorMessage(error);
143
+ result.accessible = false;
144
+ }
145
+ }
146
+ else {
147
+ result.tested = false;
148
+ result.error = "readResource function not provided - skipping read test";
149
+ }
150
+ return result;
151
+ }
152
+ async testResourceTemplate(template, context) {
153
+ const results = [];
154
+ // Test the template itself
155
+ const templateResult = {
156
+ resourceUri: template.uriTemplate,
157
+ resourceName: template.name,
158
+ mimeType: template.mimeType,
159
+ tested: true,
160
+ accessible: false,
161
+ securityIssues: [],
162
+ pathTraversalVulnerable: false,
163
+ sensitiveDataExposed: false,
164
+ validUri: this.isValidUriTemplate(template.uriTemplate),
165
+ };
166
+ // Check template for sensitive patterns
167
+ if (this.isSensitiveUri(template.uriTemplate)) {
168
+ templateResult.securityIssues.push(`Resource template matches sensitive file pattern: ${template.uriTemplate}`);
169
+ templateResult.sensitiveDataExposed = true;
170
+ }
171
+ results.push(templateResult);
172
+ // Test path traversal vulnerabilities if readResource is available
173
+ if (context.readResource) {
174
+ for (const payload of PATH_TRAVERSAL_PAYLOADS) {
175
+ this.testCount++;
176
+ const testUri = this.injectPayloadIntoTemplate(template.uriTemplate, payload);
177
+ const traversalResult = {
178
+ resourceUri: testUri,
179
+ resourceName: `${template.name} (path traversal test)`,
180
+ tested: true,
181
+ accessible: false,
182
+ securityIssues: [],
183
+ pathTraversalVulnerable: false,
184
+ sensitiveDataExposed: false,
185
+ validUri: false,
186
+ };
187
+ try {
188
+ const content = await this.executeWithTimeout(context.readResource(testUri), 3000);
189
+ // If we got content with a path traversal payload, it's vulnerable
190
+ if (content &&
191
+ (content.includes("root:") || content.includes("[fonts]"))) {
192
+ traversalResult.pathTraversalVulnerable = true;
193
+ traversalResult.accessible = true;
194
+ traversalResult.securityIssues.push(`Path traversal vulnerability: successfully accessed ${testUri}`);
195
+ }
196
+ }
197
+ catch {
198
+ // Expected - path traversal should be rejected
199
+ traversalResult.accessible = false;
200
+ }
201
+ results.push(traversalResult);
202
+ }
203
+ }
204
+ return results;
205
+ }
206
+ isValidUri(uri) {
207
+ try {
208
+ // Check for common URI schemes
209
+ if (uri.startsWith("file://") ||
210
+ uri.startsWith("http://") ||
211
+ uri.startsWith("https://") ||
212
+ uri.startsWith("resource://") ||
213
+ uri.match(/^[a-z][a-z0-9+.-]*:/i)) {
214
+ return true;
215
+ }
216
+ // Allow relative paths
217
+ return !uri.includes("..") || uri.startsWith("/");
218
+ }
219
+ catch {
220
+ return false;
221
+ }
222
+ }
223
+ isValidUriTemplate(template) {
224
+ // URI templates can contain {variable} placeholders
225
+ const withoutPlaceholders = template.replace(/\{[^}]+\}/g, "placeholder");
226
+ return this.isValidUri(withoutPlaceholders);
227
+ }
228
+ isSensitiveUri(uri) {
229
+ return SENSITIVE_PATTERNS.some((pattern) => pattern.test(uri));
230
+ }
231
+ containsSensitiveContent(content) {
232
+ return SENSITIVE_CONTENT_PATTERNS.some((pattern) => pattern.test(content));
233
+ }
234
+ injectPayloadIntoTemplate(template, payload) {
235
+ // Replace template variables with payload
236
+ const result = template.replace(/\{[^}]+\}/g, payload);
237
+ // If no variables, append payload
238
+ if (result === template) {
239
+ return template + "/" + payload;
240
+ }
241
+ return result;
242
+ }
243
+ determineResourceStatus(pathTraversalVulnerabilities, sensitiveDataExposures, securityIssuesFound, totalResources) {
244
+ // Critical failures
245
+ if (pathTraversalVulnerabilities > 0)
246
+ return "FAIL";
247
+ if (sensitiveDataExposures > 0)
248
+ return "FAIL";
249
+ // Moderate issues
250
+ if (securityIssuesFound > 0)
251
+ return "NEED_MORE_INFO";
252
+ // No resources tested
253
+ if (totalResources === 0)
254
+ return "PASS";
255
+ return "PASS";
256
+ }
257
+ generateExplanation(results, pathTraversalVulnerabilities, sensitiveDataExposures) {
258
+ const parts = [];
259
+ parts.push(`Tested ${results.length} resource(s).`);
260
+ if (pathTraversalVulnerabilities > 0) {
261
+ parts.push(`CRITICAL: ${pathTraversalVulnerabilities} path traversal vulnerability(ies) detected.`);
262
+ }
263
+ if (sensitiveDataExposures > 0) {
264
+ parts.push(`WARNING: ${sensitiveDataExposures} resource(s) may expose sensitive data.`);
265
+ }
266
+ const accessibleCount = results.filter((r) => r.accessible).length;
267
+ if (accessibleCount > 0) {
268
+ parts.push(`${accessibleCount} resource(s) are accessible.`);
269
+ }
270
+ return parts.join(" ");
271
+ }
272
+ generateRecommendations(results) {
273
+ const recommendations = [];
274
+ // Path traversal recommendations
275
+ const pathTraversalResults = results.filter((r) => r.pathTraversalVulnerable);
276
+ if (pathTraversalResults.length > 0) {
277
+ recommendations.push("CRITICAL: Implement path validation to prevent path traversal attacks. Normalize paths and validate against allowed directories.");
278
+ }
279
+ // Sensitive data recommendations
280
+ const sensitiveResults = results.filter((r) => r.sensitiveDataExposed);
281
+ if (sensitiveResults.length > 0) {
282
+ recommendations.push("Review resources for sensitive data exposure. Remove or restrict access to resources containing credentials, keys, or sensitive configuration.");
283
+ }
284
+ // Invalid URI recommendations
285
+ const invalidUriResults = results.filter((r) => !r.validUri);
286
+ if (invalidUriResults.length > 0) {
287
+ recommendations.push("Fix invalid resource URIs to ensure proper URI format compliance.");
288
+ }
289
+ // Inaccessible resource recommendations
290
+ const inaccessibleResults = results.filter((r) => r.tested && !r.accessible && !r.pathTraversalVulnerable);
291
+ if (inaccessibleResults.length > 0) {
292
+ recommendations.push(`${inaccessibleResults.length} declared resource(s) are not accessible. Verify resource paths and permissions.`);
293
+ }
294
+ return recommendations;
295
+ }
296
+ }
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Security Assessor Module
3
- * Tests for backend API security vulnerabilities using 8 focused patterns
4
- * - Critical Injection (3): Command, SQL, Path Traversal
3
+ * Tests for backend API security vulnerabilities using 18 focused patterns
4
+ * - Critical Injection (6): Command, Calculator, SQL, Path Traversal, XXE, NoSQL
5
5
  * - Input Validation (3): Type Safety, Boundary Testing, Required Fields
6
6
  * - Protocol Compliance (2): MCP Error Format, Timeout Handling
7
+ * - Tool-Specific (7): SSRF, Unicode Bypass, Nested Injection, Package Squatting,
8
+ * Data Exfiltration, Configuration Drift, Tool Shadowing
7
9
  */
8
10
  import { SecurityAssessment } from "../../../lib/assessmentTypes.js";
9
11
  import { BaseAssessor } from "./BaseAssessor.js";
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;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;AAU9D,qBAAa,gBAAiB,SAAQ,YAAY;IAC1C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuFrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;OAIG;YACW,yBAAyB;IAuKvC;;;;OAIG;YACW,qBAAqB;IA2JnC;;OAEG;YACW,WAAW;IA2HzB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAmDtC;;OAEG;IACH,OAAO,CAAC,aAAa;IA+BrB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAgClC;;;OAGG;IACH,OAAO,CAAC,eAAe;IA6HvB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAiE7B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAqC5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAsB3B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAkC5B;;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,mBAAmB;IAuI3B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,oBAAoB;IAiM5B;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IAiDhC;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;IAoE5B;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACH,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAmB3B"}
1
+ {"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;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;AAa9D,qBAAa,gBAAiB,SAAQ,YAAY;IAC1C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuFrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;OAIG;YACW,yBAAyB;IAuKvC;;;;OAIG;YACW,qBAAqB;IA2JnC;;OAEG;YACW,WAAW;IA2HzB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAmDtC;;OAEG;IACH,OAAO,CAAC,aAAa;IA+BrB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAgClC;;;OAGG;IACH,OAAO,CAAC,eAAe;IA6HvB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAiE7B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAqC5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAsB3B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAkC5B;;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,mBAAmB;IAuI3B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,oBAAoB;IAgK5B;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;IAoE5B;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACH,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAmB3B"}
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Security Assessor Module
3
- * Tests for backend API security vulnerabilities using 8 focused patterns
4
- * - Critical Injection (3): Command, SQL, Path Traversal
3
+ * Tests for backend API security vulnerabilities using 18 focused patterns
4
+ * - Critical Injection (6): Command, Calculator, SQL, Path Traversal, XXE, NoSQL
5
5
  * - Input Validation (3): Type Safety, Boundary Testing, Required Fields
6
6
  * - Protocol Compliance (2): MCP Error Format, Timeout Handling
7
+ * - Tool-Specific (7): SSRF, Unicode Bypass, Nested Injection, Package Squatting,
8
+ * Data Exfiltration, Configuration Drift, Tool Shadowing
7
9
  */
8
10
  import { BaseAssessor } from "./BaseAssessor.js";
9
11
  import { getAllAttackPatterns, getPayloadsForAttack, } from "../../../lib/securityPatterns.js";
@@ -1071,8 +1073,6 @@ export class SecurityAssessor extends BaseAssessor {
1071
1073
  * Layer 2: Verify NO execution evidence (defense-in-depth)
1072
1074
  */
1073
1075
  isReflectionResponse(responseText) {
1074
- console.log("[DIAG] isReflectionResponse called");
1075
- console.log("[DIAG] Response preview:", responseText.substring(0, 200));
1076
1076
  // Status message patterns (NEW)
1077
1077
  const statusPatterns = [
1078
1078
  // "Action executed successfully: <anything>" (generic status message)
@@ -1152,18 +1152,7 @@ export class SecurityAssessor extends BaseAssessor {
1152
1152
  /error:.*too (long|short|large)/i,
1153
1153
  ];
1154
1154
  // LAYER 1: Check for reflection/status patterns
1155
- const matchedPatterns = [];
1156
- const hasReflection = reflectionPatterns.some((pattern) => {
1157
- const matches = pattern.test(responseText);
1158
- if (matches) {
1159
- matchedPatterns.push(pattern.source.substring(0, 50));
1160
- }
1161
- return matches;
1162
- });
1163
- console.log("[DIAG] Has reflection:", hasReflection);
1164
- if (matchedPatterns.length > 0) {
1165
- console.log("[DIAG] Matched reflection patterns:", matchedPatterns.join(", "));
1166
- }
1155
+ const hasReflection = reflectionPatterns.some((pattern) => pattern.test(responseText));
1167
1156
  if (hasReflection) {
1168
1157
  // LAYER 2: Defense-in-depth - verify NO execution evidence
1169
1158
  // For JSON responses, check execution artifacts only in result/output fields
@@ -1187,25 +1176,18 @@ export class SecurityAssessor extends BaseAssessor {
1187
1176
  ? this.detectExecutionArtifacts(outputFields) // Skip result, check only output fields
1188
1177
  : this.detectExecutionArtifacts(resultText) ||
1189
1178
  this.detectExecutionArtifacts(outputFields);
1190
- console.log("[DIAG] JSON mode - checking execution in result/output fields only");
1191
- console.log("[DIAG] Has execution in output:", hasExecutionInOutput);
1192
1179
  if (hasExecutionInOutput) {
1193
- console.log("[DIAG] RESULT: Reflection + Execution in output = VULNERABLE (false)");
1194
- return false;
1180
+ return false; // Reflection + Execution in output = VULNERABLE
1195
1181
  }
1196
- console.log("[DIAG] RESULT: Reflection + clean output = SAFE (true)");
1197
- return true;
1182
+ return true; // Reflection + clean output = SAFE
1198
1183
  }
1199
1184
  catch {
1200
1185
  // Not JSON, check entire response for execution
1201
1186
  const hasExecution = this.detectExecutionArtifacts(responseText);
1202
- console.log("[DIAG] Text mode - Has execution artifacts:", hasExecution);
1203
1187
  if (hasExecution) {
1204
- console.log("[DIAG] RESULT: Reflection + Execution = VULNERABLE (false)");
1205
- return false;
1188
+ return false; // Reflection + Execution = VULNERABLE
1206
1189
  }
1207
- console.log("[DIAG] RESULT: Reflection only = SAFE (true)");
1208
- return true;
1190
+ return true; // Reflection only = SAFE
1209
1191
  }
1210
1192
  }
1211
1193
  // JSON Structural Analysis with execution verification
@@ -1238,7 +1220,6 @@ export class SecurityAssessor extends BaseAssessor {
1238
1220
  * MEDIUM confidence: Contextual patterns (root alone, paths)
1239
1221
  */
1240
1222
  detectExecutionArtifacts(responseText) {
1241
- console.log("[DIAG] detectExecutionArtifacts called");
1242
1223
  const executionIndicators = [
1243
1224
  // HIGH CONFIDENCE - System files (requires format)
1244
1225
  /[a-z]+:x:\d+:\d+:/i, // passwd: "root:x:0:0:"
@@ -1259,19 +1240,7 @@ export class SecurityAssessor extends BaseAssessor {
1259
1240
  // MEDIUM CONFIDENCE - Process info
1260
1241
  /PID:\s*\d{3,}/i, // Process ID
1261
1242
  ];
1262
- const matchedExecutionPatterns = [];
1263
- const found = executionIndicators.some((pattern) => {
1264
- const matches = pattern.test(responseText);
1265
- if (matches) {
1266
- matchedExecutionPatterns.push(pattern.source.substring(0, 50));
1267
- }
1268
- return matches;
1269
- });
1270
- if (matchedExecutionPatterns.length > 0) {
1271
- console.log("[DIAG] Matched execution patterns:", matchedExecutionPatterns.join(", "));
1272
- }
1273
- console.log("[DIAG] Execution artifacts found:", found);
1274
- return found;
1243
+ return executionIndicators.some((pattern) => pattern.test(responseText));
1275
1244
  }
1276
1245
  /**
1277
1246
  * Analyze injection response (existing logic)
@@ -12,6 +12,7 @@ import { BaseAssessor } from "./BaseAssessor.js";
12
12
  export declare class TemporalAssessor extends BaseAssessor {
13
13
  private invocationsPerTool;
14
14
  private readonly DESTRUCTIVE_PATTERNS;
15
+ private readonly PER_INVOCATION_TIMEOUT;
15
16
  constructor(config: AssessmentConfiguration);
16
17
  assess(context: AssessmentContext): Promise<TemporalAssessment>;
17
18
  private assessTool;
@@ -1 +1 @@
1
- {"version":3,"file":"TemporalAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/TemporalAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,uBAAuB,EAEvB,kBAAkB,EAEnB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAS9C,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,kBAAkB,CAAS;IAGnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAanC;gBAEU,MAAM,EAAE,uBAAuB;IAKrC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA8CvD,UAAU;IA8CxB,OAAO,CAAC,gBAAgB;IA0DxB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA0CzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,uBAAuB;CA8BhC"}
1
+ {"version":3,"file":"TemporalAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/TemporalAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,uBAAuB,EAEvB,kBAAkB,EAEnB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAY9C,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,kBAAkB,CAAS;IAGnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAoBnC;IAGF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;gBAErC,MAAM,EAAE,uBAAuB;IAKrC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA8CvD,UAAU;IAkExB,OAAO,CAAC,gBAAgB;IA0DxB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAoDzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,uBAAuB;CA8BhC"}
@@ -7,6 +7,8 @@
7
7
  * payloads but never call the same tool repeatedly with identical payloads.
8
8
  */
9
9
  import { BaseAssessor } from "./BaseAssessor.js";
10
+ // Security: Maximum response size to prevent memory exhaustion attacks
11
+ const MAX_RESPONSE_SIZE = 1_000_000; // 1MB
10
12
  export class TemporalAssessor extends BaseAssessor {
11
13
  invocationsPerTool;
12
14
  // Patterns that suggest a tool may have side effects
@@ -23,7 +25,16 @@ export class TemporalAssessor extends BaseAssessor {
23
25
  "submit",
24
26
  "execute",
25
27
  "run",
28
+ // P2-3: Additional destructive patterns
29
+ "drop",
30
+ "truncate",
31
+ "clear",
32
+ "purge",
33
+ "destroy",
34
+ "reset",
26
35
  ];
36
+ // P2-2: Per-invocation timeout to prevent long-running tools from blocking
37
+ PER_INVOCATION_TIMEOUT = 10_000; // 10 seconds
27
38
  constructor(config) {
28
39
  super(config);
29
40
  this.invocationsPerTool = config.temporalInvocations ?? 25;
@@ -72,7 +83,19 @@ export class TemporalAssessor extends BaseAssessor {
72
83
  for (let i = 1; i <= invocations; i++) {
73
84
  this.testCount++;
74
85
  try {
75
- const response = await this.executeWithTimeout(context.callTool(tool.name, payload));
86
+ // P2-2: Use shorter per-invocation timeout (10s vs default 30s)
87
+ const response = await this.executeWithTimeout(context.callTool(tool.name, payload), this.PER_INVOCATION_TIMEOUT);
88
+ // Security: Prevent memory exhaustion from large responses
89
+ const responseSize = JSON.stringify(response).length;
90
+ if (responseSize > MAX_RESPONSE_SIZE) {
91
+ responses.push({
92
+ invocation: i,
93
+ response: null,
94
+ error: `Response exceeded size limit (${responseSize} > ${MAX_RESPONSE_SIZE} bytes)`,
95
+ timestamp: Date.now(),
96
+ });
97
+ continue;
98
+ }
76
99
  responses.push({
77
100
  invocation: i,
78
101
  response,
@@ -88,6 +111,10 @@ export class TemporalAssessor extends BaseAssessor {
88
111
  timestamp: Date.now(),
89
112
  });
90
113
  }
114
+ // P2-4: Small delay between invocations to prevent rate limiting false positives
115
+ if (i < invocations) {
116
+ await this.sleep(50);
117
+ }
91
118
  }
92
119
  const result = this.analyzeResponses(tool, responses);
93
120
  return {
@@ -191,8 +218,8 @@ export class TemporalAssessor extends BaseAssessor {
191
218
  normalizeResponse(response) {
192
219
  const str = JSON.stringify(response);
193
220
  return (str
194
- // ISO timestamps
195
- .replace(/"\d{4}-\d{2}-\d{2}T[\d:.]+Z?"/g, '"<TIMESTAMP>"')
221
+ // ISO timestamps (bounded quantifier to prevent ReDoS)
222
+ .replace(/"\d{4}-\d{2}-\d{2}T[\d:.]{1,30}Z?"/g, '"<TIMESTAMP>"')
196
223
  // Unix timestamps (13 digits)
197
224
  .replace(/"\d{13}"/g, '"<TIMESTAMP>"')
198
225
  // UUIDs
@@ -218,7 +245,11 @@ export class TemporalAssessor extends BaseAssessor {
218
245
  .replace(/"index":\s*\d+/g, '"index": <NUMBER>')
219
246
  .replace(/\\"index\\":\s*\d+/g, '\\"index\\": <NUMBER>')
220
247
  // String IDs
221
- .replace(/"id":\s*"[^"]+"/g, '"id": "<ID>"'));
248
+ .replace(/"id":\s*"[^"]+"/g, '"id": "<ID>"')
249
+ // P2-1: Additional timestamp fields that vary between calls
250
+ .replace(/"(updated_at|created_at|modified_at)":\s*"[^"]+"/g, '"$1": "<TIMESTAMP>"')
251
+ // P2-1: Dynamic tokens/hashes that change per request
252
+ .replace(/"(nonce|token|hash|etag|session_id|correlation_id)":\s*"[^"]+"/g, '"$1": "<DYNAMIC>"'));
222
253
  }
223
254
  /**
224
255
  * Detect if a tool may have side effects based on naming patterns.
@@ -0,0 +1,68 @@
1
+ export type JsonValue = string | number | boolean | null | undefined | JsonValue[] | {
2
+ [key: string]: JsonValue;
3
+ };
4
+ export type JsonSchemaConst = {
5
+ const: JsonValue;
6
+ title?: string;
7
+ description?: string;
8
+ };
9
+ export type JsonSchemaType = {
10
+ type?: "string" | "number" | "integer" | "boolean" | "array" | "object" | "null" | ("string" | "number" | "integer" | "boolean" | "array" | "object" | "null")[];
11
+ title?: string;
12
+ description?: string;
13
+ required?: string[];
14
+ default?: JsonValue;
15
+ properties?: Record<string, JsonSchemaType>;
16
+ items?: JsonSchemaType;
17
+ minItems?: number;
18
+ maxItems?: number;
19
+ minimum?: number;
20
+ maximum?: number;
21
+ minLength?: number;
22
+ maxLength?: number;
23
+ nullable?: boolean;
24
+ pattern?: string;
25
+ format?: string;
26
+ enum?: string[];
27
+ enumNames?: string[];
28
+ const?: JsonValue;
29
+ oneOf?: (JsonSchemaType | JsonSchemaConst)[];
30
+ anyOf?: (JsonSchemaType | JsonSchemaConst)[];
31
+ $ref?: string;
32
+ };
33
+ export type JsonObject = {
34
+ [key: string]: JsonValue;
35
+ };
36
+ export type DataType = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "array" | "null";
37
+ /**
38
+ * Determines the specific data type of a JSON value
39
+ * @param value The JSON value to analyze
40
+ * @returns The specific data type including "array" and "null" as distinct types
41
+ */
42
+ export declare function getDataType(value: JsonValue): DataType;
43
+ /**
44
+ * Attempts to parse a string as JSON, only for objects and arrays
45
+ * @param str The string to parse
46
+ * @returns Object with success boolean and either parsed data or original string
47
+ */
48
+ export declare function tryParseJson(str: string): {
49
+ success: boolean;
50
+ data: JsonValue;
51
+ };
52
+ /**
53
+ * Updates a value at a specific path in a nested JSON structure
54
+ * @param obj The original JSON value
55
+ * @param path Array of keys/indices representing the path to the value
56
+ * @param value The new value to set
57
+ * @returns A new JSON value with the updated path
58
+ */
59
+ export declare function updateValueAtPath(obj: JsonValue, path: string[], value: JsonValue): JsonValue;
60
+ /**
61
+ * Gets a value at a specific path in a nested JSON structure
62
+ * @param obj The JSON value to traverse
63
+ * @param path Array of keys/indices representing the path to the value
64
+ * @param defaultValue Value to return if path doesn't exist
65
+ * @returns The value at the path, or defaultValue if not found
66
+ */
67
+ export declare function getValueAtPath(obj: JsonValue, path: string[], defaultValue?: JsonValue): JsonValue;
68
+ //# sourceMappingURL=jsonUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonUtils.d.ts","sourceRoot":"","sources":["../../src/utils/jsonUtils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EACD,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,SAAS,GACT,OAAO,GACP,QAAQ,GACR,MAAM,GACN,CACI,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,SAAS,GACT,OAAO,GACP,QAAQ,GACR,MAAM,CACT,EAAE,CAAC;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,cAAc,CAAC;IAEvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,cAAc,GAAG,eAAe,CAAC,EAAE,CAAC;IAC7C,KAAK,CAAC,EAAE,CAAC,cAAc,GAAG,eAAe,CAAC,EAAE,CAAC;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,QAAQ,GAChB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,UAAU,GACV,OAAO,GACP,MAAM,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAItD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;CACjB,CAcA;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,SAAS,GACf,SAAS,CAkBX;AA+ED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EAAE,EACd,YAAY,GAAE,SAAgB,GAC7B,SAAS,CAyBX"}