@bryan-thompson/inspector-assessment-client 1.34.2 → 1.35.2

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 (39) hide show
  1. package/dist/assets/{OAuthCallback-CBcYNwyM.js → OAuthCallback-jfmizOMH.js} +1 -1
  2. package/dist/assets/{OAuthDebugCallback-B0zFGlM8.js → OAuthDebugCallback-bU5kKvnt.js} +1 -1
  3. package/dist/assets/{index-Djm_oTDV.js → index-Ce63ds7G.js} +5 -4
  4. package/dist/index.html +1 -1
  5. package/lib/lib/assessment/extendedTypes.d.ts +19 -5
  6. package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
  7. package/lib/lib/assessment/resultTypes.d.ts +42 -0
  8. package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
  9. package/lib/lib/assessment/sharedSchemas.d.ts +13 -0
  10. package/lib/lib/assessment/sharedSchemas.d.ts.map +1 -1
  11. package/lib/lib/assessment/sharedSchemas.js +9 -0
  12. package/lib/lib/assessment/summarizer/AssessmentSummarizer.d.ts +112 -0
  13. package/lib/lib/assessment/summarizer/AssessmentSummarizer.d.ts.map +1 -0
  14. package/lib/lib/assessment/summarizer/AssessmentSummarizer.js +452 -0
  15. package/lib/lib/assessment/summarizer/index.d.ts +19 -0
  16. package/lib/lib/assessment/summarizer/index.d.ts.map +1 -0
  17. package/lib/lib/assessment/summarizer/index.js +19 -0
  18. package/lib/lib/assessment/summarizer/stageBEnrichmentBuilder.d.ts +36 -0
  19. package/lib/lib/assessment/summarizer/stageBEnrichmentBuilder.d.ts.map +1 -0
  20. package/lib/lib/assessment/summarizer/stageBEnrichmentBuilder.js +282 -0
  21. package/lib/lib/assessment/summarizer/stageBTypes.d.ts +154 -0
  22. package/lib/lib/assessment/summarizer/stageBTypes.d.ts.map +1 -0
  23. package/lib/lib/assessment/summarizer/stageBTypes.js +24 -0
  24. package/lib/lib/assessment/summarizer/tokenEstimator.d.ts +103 -0
  25. package/lib/lib/assessment/summarizer/tokenEstimator.d.ts.map +1 -0
  26. package/lib/lib/assessment/summarizer/tokenEstimator.js +225 -0
  27. package/lib/lib/assessment/summarizer/types.d.ts +187 -0
  28. package/lib/lib/assessment/summarizer/types.d.ts.map +1 -0
  29. package/lib/lib/assessment/summarizer/types.js +20 -0
  30. package/lib/lib/moduleScoring.d.ts +2 -1
  31. package/lib/lib/moduleScoring.d.ts.map +1 -1
  32. package/lib/lib/moduleScoring.js +2 -1
  33. package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +8 -0
  34. package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -1
  35. package/lib/services/assessment/modules/ManifestValidationAssessor.js +51 -8
  36. package/lib/services/assessment/modules/securityTests/TestValidityAnalyzer.d.ts +28 -0
  37. package/lib/services/assessment/modules/securityTests/TestValidityAnalyzer.d.ts.map +1 -1
  38. package/lib/services/assessment/modules/securityTests/TestValidityAnalyzer.js +180 -0
  39. package/package.json +1 -1
@@ -11,6 +11,9 @@ const DEFAULT_CONFIG = {
11
11
  confidenceReduceThresholdPercent: 90,
12
12
  minimumTestsForAnalysis: 10,
13
13
  maxResponseCompareLength: 1000,
14
+ // Issue #135: Enhanced data defaults
15
+ maxSamplePairs: 10,
16
+ maxDistributionEntries: 5,
14
17
  };
15
18
  /**
16
19
  * Analyzes security test results for response uniformity.
@@ -70,12 +73,23 @@ export class TestValidityAnalyzer {
70
73
  recommendedConfidence,
71
74
  warning: isCompromised
72
75
  ? {
76
+ // Existing fields
73
77
  identicalResponseCount: mostCommonCount,
74
78
  totalResponses: testsWithResponses.length,
75
79
  percentageIdentical: Math.round(percentageIdentical * 10) / 10,
76
80
  sampleResponse: sampleResponse.substring(0, 500),
77
81
  detectedPattern,
78
82
  explanation: this.generateExplanation(percentageIdentical, detectedPattern, mostCommonCount, testsWithResponses.length),
83
+ // Issue #135: Enhanced fields for Stage B semantic analysis
84
+ responseDiversity: {
85
+ uniqueResponses: responseCounts.size,
86
+ entropyScore: Math.round(this.calculateEntropy(responseCounts) * 100) / 100,
87
+ distribution: this.buildResponseDistribution(responseCounts, testsWithResponses.length),
88
+ },
89
+ toolUniformity: Object.fromEntries(toolUniformity),
90
+ attackPatternCorrelation: this.analyzeAttackPatterns(testsWithResponses),
91
+ samplePairs: this.collectSamplePairs(testsWithResponses),
92
+ responseMetadata: this.collectResponseMetadata(testsWithResponses),
79
93
  }
80
94
  : undefined,
81
95
  toolUniformity,
@@ -220,4 +234,170 @@ export class TestValidityAnalyzer {
220
234
  const patternDesc = patternDescriptions[pattern] ?? patternDescriptions.unknown;
221
235
  return `${Math.round(percentage)}% of security tests (${identicalCount}/${totalCount}) returned identical responses indicating ${patternDesc}. Tests may not have reached security-relevant code paths. Resolve the underlying issue and re-run the assessment for valid security analysis.`;
222
236
  }
237
+ // ==========================================================================
238
+ // Issue #135: Enhanced data methods for Stage B semantic analysis
239
+ // ==========================================================================
240
+ /**
241
+ * Calculate Shannon entropy for response diversity (0=uniform, 1=max diversity)
242
+ */
243
+ calculateEntropy(counts) {
244
+ const total = Array.from(counts.values()).reduce((a, b) => a + b, 0);
245
+ if (total === 0)
246
+ return 0;
247
+ let entropy = 0;
248
+ for (const count of counts.values()) {
249
+ if (count > 0) {
250
+ const p = count / total;
251
+ entropy -= p * Math.log2(p);
252
+ }
253
+ }
254
+ // Normalize to 0-1 scale based on max possible entropy
255
+ const maxEntropy = Math.log2(counts.size);
256
+ return maxEntropy > 0 ? entropy / maxEntropy : 0;
257
+ }
258
+ /**
259
+ * Build response distribution sorted by frequency
260
+ */
261
+ buildResponseDistribution(counts, total) {
262
+ return Array.from(counts.entries())
263
+ .sort((a, b) => b[1] - a[1])
264
+ .slice(0, this.config.maxDistributionEntries)
265
+ .map(([response, count]) => ({
266
+ response: response.substring(0, 200),
267
+ count,
268
+ percentage: Math.round((count / total) * 1000) / 10,
269
+ }));
270
+ }
271
+ /**
272
+ * Extract attack category from test name
273
+ */
274
+ extractAttackCategory(testName) {
275
+ const lower = testName.toLowerCase();
276
+ if (lower.includes("injection") || lower.includes("sqli"))
277
+ return "injection";
278
+ if (lower.includes("xss") || lower.includes("script"))
279
+ return "xss";
280
+ if (lower.includes("path") || lower.includes("traversal"))
281
+ return "path_traversal";
282
+ if (lower.includes("command") || lower.includes("rce"))
283
+ return "command_injection";
284
+ if (lower.includes("ssrf"))
285
+ return "ssrf";
286
+ if (lower.includes("auth"))
287
+ return "authentication";
288
+ return "other";
289
+ }
290
+ /**
291
+ * Analyze attack pattern correlation by category
292
+ */
293
+ analyzeAttackPatterns(tests) {
294
+ const patterns = new Map();
295
+ // Group by attack category
296
+ for (const test of tests) {
297
+ const category = this.extractAttackCategory(test.testName);
298
+ if (!patterns.has(category))
299
+ patterns.set(category, []);
300
+ patterns.get(category).push(test);
301
+ }
302
+ // Build correlation stats
303
+ const result = {};
304
+ for (const [category, categoryTests] of patterns) {
305
+ const withResponse = categoryTests.filter((t) => t.response);
306
+ const counts = this.countNormalizedResponses(withResponse);
307
+ result[category] = {
308
+ testCount: categoryTests.length,
309
+ uniqueResponses: counts.size,
310
+ samplePayload: categoryTests[0]?.payload?.substring(0, 100),
311
+ sampleResponse: categoryTests[0]?.response?.substring(0, 200),
312
+ };
313
+ }
314
+ return result;
315
+ }
316
+ /**
317
+ * Collect sample payload-response pairs with category diversity
318
+ */
319
+ collectSamplePairs(tests) {
320
+ const pairs = [];
321
+ const seenCategories = new Set();
322
+ // Prioritize diverse categories
323
+ for (const test of tests) {
324
+ if (pairs.length >= this.config.maxSamplePairs)
325
+ break;
326
+ if (!test.response || !test.payload)
327
+ continue;
328
+ const category = this.extractAttackCategory(test.testName);
329
+ if (!seenCategories.has(category)) {
330
+ seenCategories.add(category);
331
+ pairs.push({
332
+ attackCategory: category,
333
+ payload: test.payload.substring(0, 100),
334
+ response: test.response.substring(0, 300),
335
+ vulnerable: test.vulnerable,
336
+ });
337
+ }
338
+ }
339
+ // Fill remaining slots with additional samples
340
+ for (const test of tests) {
341
+ if (pairs.length >= this.config.maxSamplePairs)
342
+ break;
343
+ if (!test.response || !test.payload)
344
+ continue;
345
+ const truncatedPayload = test.payload.substring(0, 100);
346
+ if (!pairs.some((p) => p.payload === truncatedPayload)) {
347
+ pairs.push({
348
+ attackCategory: this.extractAttackCategory(test.testName),
349
+ payload: truncatedPayload,
350
+ response: test.response.substring(0, 300),
351
+ vulnerable: test.vulnerable,
352
+ });
353
+ }
354
+ }
355
+ return pairs;
356
+ }
357
+ /**
358
+ * Collect response metadata statistics
359
+ */
360
+ collectResponseMetadata(tests) {
361
+ if (tests.length === 0) {
362
+ return {
363
+ avgLength: 0,
364
+ minLength: 0,
365
+ maxLength: 0,
366
+ emptyCount: 0,
367
+ errorCount: 0,
368
+ };
369
+ }
370
+ let totalLength = 0;
371
+ let minLength = Infinity;
372
+ let maxLength = 0;
373
+ let emptyCount = 0;
374
+ let errorCount = 0;
375
+ let nonEmptyCount = 0;
376
+ for (const test of tests) {
377
+ const response = test.response ?? "";
378
+ const len = response.length;
379
+ // Check for empty/minimal responses
380
+ if (len === 0 ||
381
+ /^(\s*\{\s*\}|\s*\[\s*\]|\s*null\s*|)$/i.test(response.trim())) {
382
+ emptyCount++;
383
+ }
384
+ else {
385
+ totalLength += len;
386
+ minLength = Math.min(minLength, len);
387
+ maxLength = Math.max(maxLength, len);
388
+ nonEmptyCount++;
389
+ }
390
+ // Check for error indicators
391
+ if (/error|exception|fail/i.test(response)) {
392
+ errorCount++;
393
+ }
394
+ }
395
+ return {
396
+ avgLength: nonEmptyCount > 0 ? Math.round(totalLength / nonEmptyCount) : 0,
397
+ minLength: minLength === Infinity ? 0 : minLength,
398
+ maxLength,
399
+ emptyCount,
400
+ errorCount,
401
+ };
402
+ }
223
403
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bryan-thompson/inspector-assessment-client",
3
- "version": "1.34.2",
3
+ "version": "1.35.2",
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>",