@bryan-thompson/inspector-assessment-client 1.34.1 → 1.35.1
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-C27_rGpA.js → OAuthCallback-DC1cIXHT.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-DpgDVJTf.js → OAuthDebugCallback-C3gqJjgQ.js} +1 -1
- package/dist/assets/{index-BX8lZxC_.js → index-Dn2w887x.js} +5 -4
- package/dist/index.html +1 -1
- package/lib/lib/assessment/configSchemas.d.ts +12 -12
- package/lib/lib/assessment/jsonlEventSchemas.d.ts +79 -1
- package/lib/lib/assessment/jsonlEventSchemas.d.ts.map +1 -1
- package/lib/lib/assessment/jsonlEventSchemas.js +31 -1
- package/lib/lib/assessment/progressTypes.d.ts +17 -1
- package/lib/lib/assessment/progressTypes.d.ts.map +1 -1
- package/lib/lib/assessment/resultTypes.d.ts +64 -0
- package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/lib/lib/assessment/sharedSchemas.d.ts +13 -0
- package/lib/lib/assessment/sharedSchemas.d.ts.map +1 -1
- package/lib/lib/assessment/sharedSchemas.js +9 -0
- package/lib/lib/assessment/summarizer/AssessmentSummarizer.d.ts +112 -0
- package/lib/lib/assessment/summarizer/AssessmentSummarizer.d.ts.map +1 -0
- package/lib/lib/assessment/summarizer/AssessmentSummarizer.js +452 -0
- package/lib/lib/assessment/summarizer/index.d.ts +19 -0
- package/lib/lib/assessment/summarizer/index.d.ts.map +1 -0
- package/lib/lib/assessment/summarizer/index.js +19 -0
- package/lib/lib/assessment/summarizer/stageBEnrichmentBuilder.d.ts +36 -0
- package/lib/lib/assessment/summarizer/stageBEnrichmentBuilder.d.ts.map +1 -0
- package/lib/lib/assessment/summarizer/stageBEnrichmentBuilder.js +282 -0
- package/lib/lib/assessment/summarizer/stageBTypes.d.ts +154 -0
- package/lib/lib/assessment/summarizer/stageBTypes.d.ts.map +1 -0
- package/lib/lib/assessment/summarizer/stageBTypes.js +24 -0
- package/lib/lib/assessment/summarizer/tokenEstimator.d.ts +103 -0
- package/lib/lib/assessment/summarizer/tokenEstimator.d.ts.map +1 -0
- package/lib/lib/assessment/summarizer/tokenEstimator.js +225 -0
- package/lib/lib/assessment/summarizer/types.d.ts +187 -0
- package/lib/lib/assessment/summarizer/types.d.ts.map +1 -0
- package/lib/lib/assessment/summarizer/types.js +20 -0
- package/lib/lib/moduleScoring.d.ts +6 -1
- package/lib/lib/moduleScoring.d.ts.map +1 -1
- package/lib/lib/moduleScoring.js +6 -1
- package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/SecurityAssessor.js +37 -3
- package/lib/services/assessment/modules/securityTests/TestValidityAnalyzer.d.ts +118 -0
- package/lib/services/assessment/modules/securityTests/TestValidityAnalyzer.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/TestValidityAnalyzer.js +403 -0
- package/lib/services/assessment/modules/securityTests/index.d.ts +1 -0
- package/lib/services/assessment/modules/securityTests/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Validity Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Detects when security test responses are suspiciously uniform,
|
|
5
|
+
* indicating tests may not have reached security-relevant code paths.
|
|
6
|
+
*
|
|
7
|
+
* @see Issue #134: Detect identical security test responses (test validity masking)
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
warningThresholdPercent: 80,
|
|
11
|
+
confidenceReduceThresholdPercent: 90,
|
|
12
|
+
minimumTestsForAnalysis: 10,
|
|
13
|
+
maxResponseCompareLength: 1000,
|
|
14
|
+
// Issue #135: Enhanced data defaults
|
|
15
|
+
maxSamplePairs: 10,
|
|
16
|
+
maxDistributionEntries: 5,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Analyzes security test results for response uniformity.
|
|
20
|
+
*
|
|
21
|
+
* When a high percentage of test responses are identical, it indicates
|
|
22
|
+
* that tests may be hitting a configuration error, connection issue,
|
|
23
|
+
* or other problem that prevents them from reaching security-relevant code.
|
|
24
|
+
*/
|
|
25
|
+
export class TestValidityAnalyzer {
|
|
26
|
+
config;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Analyze test results for response uniformity
|
|
32
|
+
*
|
|
33
|
+
* @param testResults - Array of security test results with responses
|
|
34
|
+
* @returns Analysis result with warning details if uniformity detected
|
|
35
|
+
*/
|
|
36
|
+
analyze(testResults) {
|
|
37
|
+
// Filter to tests with responses
|
|
38
|
+
const testsWithResponses = testResults.filter((t) => t.response !== undefined && t.response !== null);
|
|
39
|
+
// Need minimum number of tests for meaningful analysis
|
|
40
|
+
if (testsWithResponses.length < this.config.minimumTestsForAnalysis) {
|
|
41
|
+
return {
|
|
42
|
+
isCompromised: false,
|
|
43
|
+
warningLevel: "none",
|
|
44
|
+
recommendedConfidence: "high",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Count response frequency with normalization
|
|
48
|
+
const responseCounts = this.countNormalizedResponses(testsWithResponses);
|
|
49
|
+
// Find most common response
|
|
50
|
+
const [mostCommonResponse, mostCommonCount] = this.findMostCommon(responseCounts);
|
|
51
|
+
const percentageIdentical = (mostCommonCount / testsWithResponses.length) * 100;
|
|
52
|
+
// Detect pattern category
|
|
53
|
+
const detectedPattern = this.detectPatternCategory(mostCommonResponse);
|
|
54
|
+
// Per-tool analysis
|
|
55
|
+
const toolUniformity = this.analyzePerTool(testResults);
|
|
56
|
+
// Determine warning level
|
|
57
|
+
let warningLevel = "none";
|
|
58
|
+
let recommendedConfidence = "high";
|
|
59
|
+
if (percentageIdentical >= this.config.confidenceReduceThresholdPercent) {
|
|
60
|
+
warningLevel = "critical";
|
|
61
|
+
recommendedConfidence = "low";
|
|
62
|
+
}
|
|
63
|
+
else if (percentageIdentical >= this.config.warningThresholdPercent) {
|
|
64
|
+
warningLevel = "warning";
|
|
65
|
+
recommendedConfidence = "medium";
|
|
66
|
+
}
|
|
67
|
+
const isCompromised = warningLevel !== "none";
|
|
68
|
+
// Find the original (non-normalized) sample response for display
|
|
69
|
+
const sampleResponse = this.findOriginalSample(testsWithResponses, mostCommonResponse);
|
|
70
|
+
return {
|
|
71
|
+
isCompromised,
|
|
72
|
+
warningLevel,
|
|
73
|
+
recommendedConfidence,
|
|
74
|
+
warning: isCompromised
|
|
75
|
+
? {
|
|
76
|
+
// Existing fields
|
|
77
|
+
identicalResponseCount: mostCommonCount,
|
|
78
|
+
totalResponses: testsWithResponses.length,
|
|
79
|
+
percentageIdentical: Math.round(percentageIdentical * 10) / 10,
|
|
80
|
+
sampleResponse: sampleResponse.substring(0, 500),
|
|
81
|
+
detectedPattern,
|
|
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),
|
|
93
|
+
}
|
|
94
|
+
: undefined,
|
|
95
|
+
toolUniformity,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Normalize response for comparison.
|
|
100
|
+
* Removes timestamps, UUIDs, request IDs, and other variable content.
|
|
101
|
+
*/
|
|
102
|
+
normalizeResponse(response) {
|
|
103
|
+
let normalized = response.substring(0, this.config.maxResponseCompareLength);
|
|
104
|
+
// Remove common variable patterns
|
|
105
|
+
normalized = normalized
|
|
106
|
+
// ISO timestamps
|
|
107
|
+
.replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[.\d]*Z?/g, "<TIMESTAMP>")
|
|
108
|
+
// Unix timestamps (10-13 digits)
|
|
109
|
+
.replace(/\b\d{10,13}\b/g, "<TIMESTAMP>")
|
|
110
|
+
// UUIDs
|
|
111
|
+
.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<UUID>")
|
|
112
|
+
// Request/correlation IDs (common patterns)
|
|
113
|
+
.replace(/"(?:request_?id|correlation_?id|trace_?id)"\s*:\s*"[^"]+"/gi, '"<ID>": "<VALUE>"')
|
|
114
|
+
// Generic hex IDs (16+ chars)
|
|
115
|
+
.replace(/\b[0-9a-f]{16,}\b/gi, "<HEX_ID>")
|
|
116
|
+
// Normalize whitespace
|
|
117
|
+
.replace(/\s+/g, " ")
|
|
118
|
+
.trim()
|
|
119
|
+
.toLowerCase();
|
|
120
|
+
return normalized;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Count occurrences of normalized responses
|
|
124
|
+
*/
|
|
125
|
+
countNormalizedResponses(tests) {
|
|
126
|
+
const counts = new Map();
|
|
127
|
+
for (const test of tests) {
|
|
128
|
+
if (test.response) {
|
|
129
|
+
const normalized = this.normalizeResponse(test.response);
|
|
130
|
+
counts.set(normalized, (counts.get(normalized) ?? 0) + 1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return counts;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Find the most common response
|
|
137
|
+
*/
|
|
138
|
+
findMostCommon(counts) {
|
|
139
|
+
let maxCount = 0;
|
|
140
|
+
let mostCommon = "";
|
|
141
|
+
for (const [response, count] of counts) {
|
|
142
|
+
if (count > maxCount) {
|
|
143
|
+
maxCount = count;
|
|
144
|
+
mostCommon = response;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return [mostCommon, maxCount];
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Find original (non-normalized) sample that matches the normalized pattern
|
|
151
|
+
*/
|
|
152
|
+
findOriginalSample(tests, normalizedPattern) {
|
|
153
|
+
for (const test of tests) {
|
|
154
|
+
if (test.response) {
|
|
155
|
+
const normalized = this.normalizeResponse(test.response);
|
|
156
|
+
if (normalized === normalizedPattern) {
|
|
157
|
+
return test.response;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return normalizedPattern;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Detect the category of the response pattern
|
|
165
|
+
*/
|
|
166
|
+
detectPatternCategory(response) {
|
|
167
|
+
const lower = response.toLowerCase();
|
|
168
|
+
// Configuration errors
|
|
169
|
+
if (/api[_\s]?key|credentials?|auth|token|secret|config/i.test(lower) &&
|
|
170
|
+
/missing|invalid|required|not found|error/i.test(lower)) {
|
|
171
|
+
return "configuration_error";
|
|
172
|
+
}
|
|
173
|
+
// Connection errors
|
|
174
|
+
if (/connection|refused|econnrefused|timeout|unreachable|network|socket/i.test(lower)) {
|
|
175
|
+
return "connection_error";
|
|
176
|
+
}
|
|
177
|
+
// Timeout
|
|
178
|
+
if (/timeout|timed?\s*out|exceeded/i.test(lower)) {
|
|
179
|
+
return "timeout";
|
|
180
|
+
}
|
|
181
|
+
// Empty responses
|
|
182
|
+
if (response.length < 50 ||
|
|
183
|
+
/^(\s*\{\s*\}|\s*\[\s*\]|\s*null\s*|)$/i.test(response.trim())) {
|
|
184
|
+
return "empty_response";
|
|
185
|
+
}
|
|
186
|
+
// Generic error
|
|
187
|
+
if (/error|exception|fail|invalid/i.test(lower)) {
|
|
188
|
+
return "generic_error";
|
|
189
|
+
}
|
|
190
|
+
return "unknown";
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Analyze uniformity per tool
|
|
194
|
+
*/
|
|
195
|
+
analyzePerTool(tests) {
|
|
196
|
+
const result = new Map();
|
|
197
|
+
const toolTests = new Map();
|
|
198
|
+
// Group by tool
|
|
199
|
+
for (const test of tests) {
|
|
200
|
+
const toolName = test.toolName ?? "unknown";
|
|
201
|
+
if (!toolTests.has(toolName)) {
|
|
202
|
+
toolTests.set(toolName, []);
|
|
203
|
+
}
|
|
204
|
+
toolTests.get(toolName).push(test);
|
|
205
|
+
}
|
|
206
|
+
// Analyze each tool
|
|
207
|
+
for (const [toolName, toolTestResults] of toolTests) {
|
|
208
|
+
const testsWithResponse = toolTestResults.filter((t) => t.response);
|
|
209
|
+
const counts = this.countNormalizedResponses(testsWithResponse);
|
|
210
|
+
const [, maxCount] = this.findMostCommon(counts);
|
|
211
|
+
const total = testsWithResponse.length;
|
|
212
|
+
if (total > 0) {
|
|
213
|
+
result.set(toolName, {
|
|
214
|
+
identicalCount: maxCount,
|
|
215
|
+
totalCount: total,
|
|
216
|
+
percentageIdentical: Math.round((maxCount / total) * 100 * 10) / 10,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Generate human-readable explanation
|
|
224
|
+
*/
|
|
225
|
+
generateExplanation(percentage, pattern, identicalCount, totalCount) {
|
|
226
|
+
const patternDescriptions = {
|
|
227
|
+
configuration_error: "a configuration error (e.g., missing API key)",
|
|
228
|
+
connection_error: "a connection failure",
|
|
229
|
+
timeout: "request timeouts",
|
|
230
|
+
empty_response: "empty responses",
|
|
231
|
+
generic_error: "a generic error",
|
|
232
|
+
unknown: "an unknown pattern",
|
|
233
|
+
};
|
|
234
|
+
const patternDesc = patternDescriptions[pattern] ?? patternDescriptions.unknown;
|
|
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.`;
|
|
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
|
+
}
|
|
403
|
+
}
|
|
@@ -12,4 +12,5 @@ export { SecurityPayloadTester, type TestProgressCallback, type PayloadTestConfi
|
|
|
12
12
|
export { SecurityPayloadGenerator } from "./SecurityPayloadGenerator.js";
|
|
13
13
|
export { CrossToolStateTester, type CrossToolTestResult, type ToolPair, type CallToolFunction, type CrossToolTestConfig, } from "./CrossToolStateTester.js";
|
|
14
14
|
export { ChainExecutionTester, type ChainExecutionTestResult, type ChainExploitationSummary, type ChainExecutionTesterConfig, type ChainTestReason, } from "./ChainExecutionTester.js";
|
|
15
|
+
export { TestValidityAnalyzer, type TestValidityConfig, type TestValidityResult, } from "./TestValidityAnalyzer.js";
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"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;AAEhC,OAAO,EACL,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC"}
|
|
@@ -12,3 +12,4 @@ export { SecurityPayloadTester, } from "./SecurityPayloadTester.js";
|
|
|
12
12
|
export { SecurityPayloadGenerator } from "./SecurityPayloadGenerator.js";
|
|
13
13
|
export { CrossToolStateTester, } from "./CrossToolStateTester.js";
|
|
14
14
|
export { ChainExecutionTester, } from "./ChainExecutionTester.js";
|
|
15
|
+
export { TestValidityAnalyzer, } from "./TestValidityAnalyzer.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.1",
|
|
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>",
|