@bryan-thompson/inspector-assessment 1.17.0 → 1.17.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/client/dist/assets/{OAuthCallback-DKHgeaa0.js → OAuthCallback-DRaMXbvu.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-5vclGNC-.js → OAuthDebugCallback-Dj_-SG3N.js} +1 -1
- package/client/dist/assets/{index-B3FHZqtg.js → index-sOgf80Op.js} +4 -4
- package/client/dist/index.html +1 -1
- package/client/lib/services/assessment/modules/TemporalAssessor.d.ts +6 -1
- package/client/lib/services/assessment/modules/TemporalAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/TemporalAssessor.js +29 -5
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-
|
|
1
|
+
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-sOgf80Op.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
package/client/dist/assets/{OAuthDebugCallback-5vclGNC-.js → OAuthDebugCallback-Dj_-SG3N.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-
|
|
1
|
+
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-sOgf80Op.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|
|
@@ -16320,7 +16320,7 @@ object({
|
|
|
16320
16320
|
token_type_hint: string().optional()
|
|
16321
16321
|
}).strip();
|
|
16322
16322
|
const name = "@bryan-thompson/inspector-assessment-client";
|
|
16323
|
-
const version$1 = "1.
|
|
16323
|
+
const version$1 = "1.17.1";
|
|
16324
16324
|
const packageJson = {
|
|
16325
16325
|
name,
|
|
16326
16326
|
version: version$1
|
|
@@ -45337,7 +45337,7 @@ const useTheme = () => {
|
|
|
45337
45337
|
[theme, setThemeWithSideEffect]
|
|
45338
45338
|
);
|
|
45339
45339
|
};
|
|
45340
|
-
const version = "1.17.
|
|
45340
|
+
const version = "1.17.1";
|
|
45341
45341
|
var [createTooltipContext] = createContextScope("Tooltip", [
|
|
45342
45342
|
createPopperScope
|
|
45343
45343
|
]);
|
|
@@ -58267,13 +58267,13 @@ const App = () => {
|
|
|
58267
58267
|
) });
|
|
58268
58268
|
if (window.location.pathname === "/oauth/callback") {
|
|
58269
58269
|
const OAuthCallback = React.lazy(
|
|
58270
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
58270
|
+
() => __vitePreload(() => import("./OAuthCallback-DRaMXbvu.js"), true ? [] : void 0)
|
|
58271
58271
|
);
|
|
58272
58272
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
58273
58273
|
}
|
|
58274
58274
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
58275
58275
|
const OAuthDebugCallback = React.lazy(
|
|
58276
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
58276
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-Dj_-SG3N.js"), true ? [] : void 0)
|
|
58277
58277
|
);
|
|
58278
58278
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
58279
58279
|
}
|
package/client/dist/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/mcp.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Inspector</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-sOgf80Op.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-CzoGuYPy.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -18,6 +18,11 @@ export declare class TemporalAssessor extends BaseAssessor {
|
|
|
18
18
|
* These tools legitimately return different results based on data state,
|
|
19
19
|
* which is NOT a rug pull vulnerability (e.g., search returning more results
|
|
20
20
|
* after other tools have stored data).
|
|
21
|
+
*
|
|
22
|
+
* NOTE: Uses substring matching, so "get" matches "get_user", "forget",
|
|
23
|
+
* "target", etc. This favors recall over precision - we prefer lenient
|
|
24
|
+
* schema comparison for edge cases over false positives on legitimate tools.
|
|
25
|
+
* Consider word-boundary regex if false positives become problematic.
|
|
21
26
|
*/
|
|
22
27
|
private readonly STATEFUL_TOOL_PATTERNS;
|
|
23
28
|
constructor(config: AssessmentConfiguration);
|
|
@@ -55,7 +60,7 @@ export declare class TemporalAssessor extends BaseAssessor {
|
|
|
55
60
|
private compareSchemas;
|
|
56
61
|
/**
|
|
57
62
|
* Extract all field names from an object recursively.
|
|
58
|
-
* Handles arrays by sampling
|
|
63
|
+
* Handles arrays by sampling multiple elements to detect heterogeneous schemas.
|
|
59
64
|
*/
|
|
60
65
|
private extractFieldNames;
|
|
61
66
|
private determineTemporalStatus;
|
|
@@ -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;AAY9C,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,kBAAkB,CAAS;IAGnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAoBnC;IAGF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;IAEjD
|
|
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;IAEjD;;;;;;;;;;OAUG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CASrC;gBAEU,MAAM,EAAE,uBAAuB;IAKrC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA8CvD,UAAU;IAkExB,OAAO,CAAC,gBAAgB;IAmFxB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAoDzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAKzB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAYtB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAuBtB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,uBAAuB;CA8BhC"}
|
|
@@ -40,6 +40,11 @@ export class TemporalAssessor extends BaseAssessor {
|
|
|
40
40
|
* These tools legitimately return different results based on data state,
|
|
41
41
|
* which is NOT a rug pull vulnerability (e.g., search returning more results
|
|
42
42
|
* after other tools have stored data).
|
|
43
|
+
*
|
|
44
|
+
* NOTE: Uses substring matching, so "get" matches "get_user", "forget",
|
|
45
|
+
* "target", etc. This favors recall over precision - we prefer lenient
|
|
46
|
+
* schema comparison for edge cases over false positives on legitimate tools.
|
|
47
|
+
* Consider word-boundary regex if false positives become problematic.
|
|
43
48
|
*/
|
|
44
49
|
STATEFUL_TOOL_PATTERNS = [
|
|
45
50
|
"search",
|
|
@@ -157,6 +162,9 @@ export class TemporalAssessor extends BaseAssessor {
|
|
|
157
162
|
// For stateful tools (search, list, etc.), use schema comparison instead of exact match
|
|
158
163
|
// These tools legitimately return different content based on data state
|
|
159
164
|
const isStateful = this.isStatefulTool(tool);
|
|
165
|
+
if (isStateful) {
|
|
166
|
+
this.log(`${tool.name} classified as stateful - using schema comparison`);
|
|
167
|
+
}
|
|
160
168
|
for (let i = 1; i < responses.length; i++) {
|
|
161
169
|
if (responses[i].error) {
|
|
162
170
|
errors.push(i + 1); // Track errors as potential indicators
|
|
@@ -298,6 +306,11 @@ export class TemporalAssessor extends BaseAssessor {
|
|
|
298
306
|
*/
|
|
299
307
|
isStatefulTool(tool) {
|
|
300
308
|
const toolName = tool.name.toLowerCase();
|
|
309
|
+
// Exclude tools that are ALSO destructive - they should get strict exact comparison
|
|
310
|
+
// e.g., "get_and_delete" matches both "get" (stateful) and "delete" (destructive)
|
|
311
|
+
if (this.isDestructiveTool(tool)) {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
301
314
|
return this.STATEFUL_TOOL_PATTERNS.some((pattern) => toolName.includes(pattern));
|
|
302
315
|
}
|
|
303
316
|
/**
|
|
@@ -310,6 +323,11 @@ export class TemporalAssessor extends BaseAssessor {
|
|
|
310
323
|
compareSchemas(response1, response2) {
|
|
311
324
|
const fields1 = this.extractFieldNames(response1).sort();
|
|
312
325
|
const fields2 = this.extractFieldNames(response2).sort();
|
|
326
|
+
// Edge case: empty baseline with populated later response is suspicious
|
|
327
|
+
// An attacker could start with {} then switch to content with malicious fields
|
|
328
|
+
if (fields1.length === 0 && fields2.length > 0) {
|
|
329
|
+
return false; // Flag as schema mismatch
|
|
330
|
+
}
|
|
313
331
|
// Check for exact match (handles non-array cases)
|
|
314
332
|
const exactMatch = fields1.join(",") === fields2.join(",");
|
|
315
333
|
if (exactMatch)
|
|
@@ -323,18 +341,24 @@ export class TemporalAssessor extends BaseAssessor {
|
|
|
323
341
|
}
|
|
324
342
|
/**
|
|
325
343
|
* Extract all field names from an object recursively.
|
|
326
|
-
* Handles arrays by sampling
|
|
344
|
+
* Handles arrays by sampling multiple elements to detect heterogeneous schemas.
|
|
327
345
|
*/
|
|
328
346
|
extractFieldNames(obj, prefix = "") {
|
|
329
347
|
if (obj === null || obj === undefined || typeof obj !== "object")
|
|
330
348
|
return [];
|
|
331
349
|
const fields = [];
|
|
332
|
-
// Handle arrays: sample
|
|
350
|
+
// Handle arrays: sample multiple elements to detect heterogeneous schemas
|
|
351
|
+
// An attacker could hide malicious fields in non-first array elements
|
|
333
352
|
if (Array.isArray(obj)) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
353
|
+
const samplesToCheck = Math.min(obj.length, 3); // Check up to 3 elements
|
|
354
|
+
const seenFields = new Set();
|
|
355
|
+
for (let i = 0; i < samplesToCheck; i++) {
|
|
356
|
+
if (typeof obj[i] === "object" && obj[i] !== null) {
|
|
357
|
+
const itemFields = this.extractFieldNames(obj[i], `${prefix}[]`);
|
|
358
|
+
itemFields.forEach((f) => seenFields.add(f));
|
|
359
|
+
}
|
|
337
360
|
}
|
|
361
|
+
fields.push(...seenFields);
|
|
338
362
|
return fields;
|
|
339
363
|
}
|
|
340
364
|
// Handle objects
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.1",
|
|
4
4
|
"description": "Enhanced MCP Inspector with comprehensive assessment capabilities for server validation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|