@bryan-thompson/inspector-assessment 1.15.2 → 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.
- package/cli/build/assess-full.js +91 -0
- package/cli/build/assess-security.js +111 -27
- package/cli/build/cli.js +119 -3
- package/client/dist/assets/{OAuthCallback-B2W3bBou.js → OAuthCallback-KwMiy-L3.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-BL3_Hknj.js → OAuthDebugCallback-hckdJlo3.js} +1 -1
- package/client/dist/assets/{index-Css2Fvxh.js → index-C89umkGV.js} +512 -665
- package/client/dist/index.html +1 -1
- package/client/lib/lib/assessmentTypes.d.ts +123 -0
- package/client/lib/lib/assessmentTypes.d.ts.map +1 -1
- package/client/lib/lib/assessmentTypes.js +20 -0
- package/client/lib/lib/securityPatterns.d.ts +2 -2
- package/client/lib/lib/securityPatterns.d.ts.map +1 -1
- package/client/lib/lib/securityPatterns.js +215 -16
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +67 -0
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/client/lib/services/assessment/AssessmentOrchestrator.js +91 -1
- package/client/lib/services/assessment/ResponseValidator.d.ts +7 -34
- package/client/lib/services/assessment/ResponseValidator.d.ts.map +1 -1
- package/client/lib/services/assessment/ResponseValidator.js +100 -704
- package/client/lib/services/assessment/config/annotationPatterns.js +1 -1
- package/client/lib/services/assessment/lib/RequestHistoryAnalyzer.d.ts +67 -0
- package/client/lib/services/assessment/lib/RequestHistoryAnalyzer.d.ts.map +1 -0
- package/client/lib/services/assessment/lib/RequestHistoryAnalyzer.js +191 -0
- package/client/lib/services/assessment/lib/claudeCodeBridge.d.ts +1 -0
- package/client/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -1
- package/client/lib/services/assessment/lib/claudeCodeBridge.js +5 -4
- package/client/lib/services/assessment/modules/AuthenticationAssessor.d.ts +4 -0
- package/client/lib/services/assessment/modules/AuthenticationAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/AuthenticationAssessor.js +97 -1
- package/client/lib/services/assessment/modules/CrossCapabilitySecurityAssessor.d.ts +39 -0
- package/client/lib/services/assessment/modules/CrossCapabilitySecurityAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/CrossCapabilitySecurityAssessor.js +330 -0
- package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/FunctionalityAssessor.js +46 -13
- package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +5 -0
- package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +81 -0
- package/client/lib/services/assessment/modules/ManifestValidationAssessor.js +1 -1
- package/client/lib/services/assessment/modules/PromptAssessor.d.ts +30 -0
- package/client/lib/services/assessment/modules/PromptAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/PromptAssessor.js +367 -0
- package/client/lib/services/assessment/modules/ResourceAssessor.d.ts +28 -0
- package/client/lib/services/assessment/modules/ResourceAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/ResourceAssessor.js +296 -0
- package/client/lib/services/assessment/modules/SecurityAssessor.d.ts +4 -2
- package/client/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/SecurityAssessor.js +10 -41
- package/client/lib/utils/jsonUtils.d.ts +68 -0
- package/client/lib/utils/jsonUtils.d.ts.map +1 -0
- package/client/lib/utils/jsonUtils.js +141 -0
- package/client/lib/utils/paramUtils.d.ts +11 -0
- package/client/lib/utils/paramUtils.d.ts.map +1 -0
- package/client/lib/utils/paramUtils.js +37 -0
- package/client/lib/utils/schemaUtils.d.ts +74 -0
- package/client/lib/utils/schemaUtils.d.ts.map +1 -0
- package/client/lib/utils/schemaUtils.js +268 -0
- package/package.json +4 -4
package/cli/build/assess-full.js
CHANGED
|
@@ -252,6 +252,10 @@ function buildConfig(options) {
|
|
|
252
252
|
portability: true,
|
|
253
253
|
externalAPIScanner: !!options.sourceCodePath,
|
|
254
254
|
temporal: !options.skipTemporal, // Enable by default with --full, skip with --skip-temporal
|
|
255
|
+
// New capability assessors - always enabled in full mode
|
|
256
|
+
resources: true,
|
|
257
|
+
prompts: true,
|
|
258
|
+
crossCapability: true,
|
|
255
259
|
};
|
|
256
260
|
}
|
|
257
261
|
// Temporal/rug pull detection configuration
|
|
@@ -304,6 +308,61 @@ async function runFullAssessment(options) {
|
|
|
304
308
|
if (!options.jsonOnly) {
|
|
305
309
|
console.log(`🔧 Found ${tools.length} tool${tools.length !== 1 ? "s" : ""}`);
|
|
306
310
|
}
|
|
311
|
+
// Fetch resources for new capability assessments
|
|
312
|
+
let resources = [];
|
|
313
|
+
let resourceTemplates = [];
|
|
314
|
+
try {
|
|
315
|
+
const resourcesResponse = await client.listResources();
|
|
316
|
+
resources = (resourcesResponse.resources || []).map((r) => ({
|
|
317
|
+
uri: r.uri,
|
|
318
|
+
name: r.name,
|
|
319
|
+
description: r.description,
|
|
320
|
+
mimeType: r.mimeType,
|
|
321
|
+
}));
|
|
322
|
+
// resourceTemplates may be typed as unknown in some SDK versions
|
|
323
|
+
const templates = resourcesResponse.resourceTemplates;
|
|
324
|
+
if (templates) {
|
|
325
|
+
resourceTemplates = templates.map((rt) => ({
|
|
326
|
+
uriTemplate: rt.uriTemplate,
|
|
327
|
+
name: rt.name,
|
|
328
|
+
description: rt.description,
|
|
329
|
+
mimeType: rt.mimeType,
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
if (!options.jsonOnly &&
|
|
333
|
+
(resources.length > 0 || resourceTemplates.length > 0)) {
|
|
334
|
+
console.log(`📦 Found ${resources.length} resource(s) and ${resourceTemplates.length} resource template(s)`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
// Server may not support resources - that's okay
|
|
339
|
+
if (!options.jsonOnly) {
|
|
340
|
+
console.log("📦 Resources not supported by server");
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Fetch prompts for new capability assessments
|
|
344
|
+
let prompts = [];
|
|
345
|
+
try {
|
|
346
|
+
const promptsResponse = await client.listPrompts();
|
|
347
|
+
prompts = (promptsResponse.prompts || []).map((p) => ({
|
|
348
|
+
name: p.name,
|
|
349
|
+
description: p.description,
|
|
350
|
+
arguments: p.arguments?.map((a) => ({
|
|
351
|
+
name: a.name,
|
|
352
|
+
description: a.description,
|
|
353
|
+
required: a.required,
|
|
354
|
+
})),
|
|
355
|
+
}));
|
|
356
|
+
if (!options.jsonOnly && prompts.length > 0) {
|
|
357
|
+
console.log(`💬 Found ${prompts.length} prompt(s)`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// Server may not support prompts - that's okay
|
|
362
|
+
if (!options.jsonOnly) {
|
|
363
|
+
console.log("💬 Prompts not supported by server");
|
|
364
|
+
}
|
|
365
|
+
}
|
|
307
366
|
// State management for resumable assessments
|
|
308
367
|
const stateManager = new AssessmentStateManager(options.serverName);
|
|
309
368
|
if (stateManager.exists() && !options.noResume) {
|
|
@@ -398,6 +457,32 @@ async function runFullAssessment(options) {
|
|
|
398
457
|
console.log(`📁 Loaded source files from: ${options.sourceCodePath}`);
|
|
399
458
|
}
|
|
400
459
|
}
|
|
460
|
+
// Create readResource wrapper for ResourceAssessor
|
|
461
|
+
const readResource = async (uri) => {
|
|
462
|
+
const response = await client.readResource({ uri });
|
|
463
|
+
// Extract text content from response
|
|
464
|
+
if (response.contents && response.contents.length > 0) {
|
|
465
|
+
const content = response.contents[0];
|
|
466
|
+
if ("text" in content && content.text) {
|
|
467
|
+
return content.text;
|
|
468
|
+
}
|
|
469
|
+
if ("blob" in content && content.blob) {
|
|
470
|
+
// Return base64 blob as string
|
|
471
|
+
return content.blob;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return "";
|
|
475
|
+
};
|
|
476
|
+
// Create getPrompt wrapper for PromptAssessor
|
|
477
|
+
const getPrompt = async (name, args) => {
|
|
478
|
+
const response = await client.getPrompt({ name, arguments: args });
|
|
479
|
+
return {
|
|
480
|
+
messages: (response.messages || []).map((m) => ({
|
|
481
|
+
role: m.role,
|
|
482
|
+
content: typeof m.content === "string" ? m.content : JSON.stringify(m.content),
|
|
483
|
+
})),
|
|
484
|
+
};
|
|
485
|
+
};
|
|
401
486
|
const context = {
|
|
402
487
|
serverName: options.serverName,
|
|
403
488
|
tools,
|
|
@@ -405,6 +490,12 @@ async function runFullAssessment(options) {
|
|
|
405
490
|
config,
|
|
406
491
|
sourceCodePath: options.sourceCodePath,
|
|
407
492
|
...sourceFiles,
|
|
493
|
+
// New capability assessment data
|
|
494
|
+
resources,
|
|
495
|
+
resourceTemplates,
|
|
496
|
+
prompts,
|
|
497
|
+
readResource,
|
|
498
|
+
getPrompt,
|
|
408
499
|
};
|
|
409
500
|
if (!options.jsonOnly) {
|
|
410
501
|
console.log(`\n🏃 Running assessment with ${Object.keys(config.assessmentCategories || {}).length} modules...`);
|
|
@@ -12,6 +12,74 @@
|
|
|
12
12
|
import * as fs from "fs";
|
|
13
13
|
import * as path from "path";
|
|
14
14
|
import * as os from "os";
|
|
15
|
+
import { execSync } from "child_process";
|
|
16
|
+
/**
|
|
17
|
+
* Validate that a command is safe to execute
|
|
18
|
+
* - Must be an absolute path or resolvable via PATH
|
|
19
|
+
* - Must not contain shell metacharacters
|
|
20
|
+
*/
|
|
21
|
+
function validateCommand(command) {
|
|
22
|
+
// Check for shell metacharacters that could indicate injection
|
|
23
|
+
const dangerousChars = /[;&|`$(){}[\]<>!\\]/;
|
|
24
|
+
if (dangerousChars.test(command)) {
|
|
25
|
+
throw new Error(`Invalid command: contains shell metacharacters: ${command}`);
|
|
26
|
+
}
|
|
27
|
+
// Verify the command exists and is executable
|
|
28
|
+
try {
|
|
29
|
+
// Use 'which' on Unix-like systems, 'where' on Windows
|
|
30
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
31
|
+
execSync(`${whichCmd} "${command}"`, { stdio: "pipe" });
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Check if it's an absolute path that exists
|
|
35
|
+
if (path.isAbsolute(command) && fs.existsSync(command)) {
|
|
36
|
+
try {
|
|
37
|
+
fs.accessSync(command, fs.constants.X_OK);
|
|
38
|
+
return; // Command exists and is executable
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error(`Command not executable: ${command}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Command not found: ${command}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validate environment variables from config
|
|
49
|
+
* - Keys must be valid env var names (alphanumeric + underscore)
|
|
50
|
+
* - Values should not contain null bytes
|
|
51
|
+
*/
|
|
52
|
+
function validateEnvVars(env) {
|
|
53
|
+
if (!env)
|
|
54
|
+
return {};
|
|
55
|
+
const validatedEnv = {};
|
|
56
|
+
const validKeyPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
57
|
+
for (const [key, value] of Object.entries(env)) {
|
|
58
|
+
// Validate key format
|
|
59
|
+
if (!validKeyPattern.test(key)) {
|
|
60
|
+
console.warn(`Skipping invalid environment variable name: ${key} (must match [a-zA-Z_][a-zA-Z0-9_]*)`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// Check for null bytes in value (could truncate strings)
|
|
64
|
+
if (typeof value === "string" && value.includes("\0")) {
|
|
65
|
+
console.warn(`Skipping environment variable with null byte: ${key}`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
validatedEnv[key] = String(value);
|
|
69
|
+
}
|
|
70
|
+
return validatedEnv;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Safely parse JSON with error handling
|
|
74
|
+
*/
|
|
75
|
+
function safeJsonParse(content, filePath) {
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(content);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new Error(`Failed to parse JSON from ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
15
83
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
16
84
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
17
85
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
@@ -31,34 +99,46 @@ function loadServerConfig(serverName, configPath) {
|
|
|
31
99
|
for (const tryPath of possiblePaths) {
|
|
32
100
|
if (!fs.existsSync(tryPath))
|
|
33
101
|
continue;
|
|
34
|
-
const
|
|
35
|
-
if
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
throw new Error(`Invalid server config: transport is '${config.transport}' but 'url' is missing`);
|
|
102
|
+
const rawConfig = safeJsonParse(fs.readFileSync(tryPath, "utf-8"), tryPath);
|
|
103
|
+
// Type guard: check if it's a Claude Desktop config with mcpServers
|
|
104
|
+
if (rawConfig &&
|
|
105
|
+
typeof rawConfig === "object" &&
|
|
106
|
+
"mcpServers" in rawConfig) {
|
|
107
|
+
const desktopConfig = rawConfig;
|
|
108
|
+
const serverConfig = desktopConfig.mcpServers?.[serverName];
|
|
109
|
+
if (serverConfig) {
|
|
110
|
+
return {
|
|
111
|
+
transport: "stdio",
|
|
112
|
+
command: serverConfig.command,
|
|
113
|
+
args: serverConfig.args || [],
|
|
114
|
+
env: serverConfig.env || {},
|
|
115
|
+
};
|
|
49
116
|
}
|
|
50
|
-
return {
|
|
51
|
-
transport: config.transport || "http",
|
|
52
|
-
url: config.url,
|
|
53
|
-
};
|
|
54
117
|
}
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
118
|
+
// Type guard: check if it's a direct config file
|
|
119
|
+
if (rawConfig && typeof rawConfig === "object") {
|
|
120
|
+
const directConfig = rawConfig;
|
|
121
|
+
// Check for HTTP/SSE transport
|
|
122
|
+
if (directConfig.url ||
|
|
123
|
+
directConfig.transport === "http" ||
|
|
124
|
+
directConfig.transport === "sse") {
|
|
125
|
+
if (!directConfig.url) {
|
|
126
|
+
throw new Error(`Invalid server config: transport is '${directConfig.transport}' but 'url' is missing`);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
transport: directConfig.transport || "http",
|
|
130
|
+
url: directConfig.url,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// Check for stdio transport
|
|
134
|
+
if (directConfig.command) {
|
|
135
|
+
return {
|
|
136
|
+
transport: "stdio",
|
|
137
|
+
command: directConfig.command,
|
|
138
|
+
args: directConfig.args || [],
|
|
139
|
+
env: directConfig.env || {},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
62
142
|
}
|
|
63
143
|
}
|
|
64
144
|
throw new Error(`Server config not found for: ${serverName}\nTried: ${possiblePaths.join(", ")}`);
|
|
@@ -83,12 +163,16 @@ async function connectToServer(config) {
|
|
|
83
163
|
default:
|
|
84
164
|
if (!config.command)
|
|
85
165
|
throw new Error("Command required for stdio transport");
|
|
166
|
+
// Validate command before execution to prevent injection attacks
|
|
167
|
+
validateCommand(config.command);
|
|
168
|
+
// Validate and sanitize environment variables from config
|
|
169
|
+
const validatedEnv = validateEnvVars(config.env);
|
|
86
170
|
transport = new StdioClientTransport({
|
|
87
171
|
command: config.command,
|
|
88
172
|
args: config.args,
|
|
89
173
|
env: {
|
|
90
174
|
...Object.fromEntries(Object.entries(process.env).filter(([, v]) => v !== undefined)),
|
|
91
|
-
...
|
|
175
|
+
...validatedEnv,
|
|
92
176
|
},
|
|
93
177
|
stderr: "pipe",
|
|
94
178
|
});
|
package/cli/build/cli.js
CHANGED
|
@@ -5,7 +5,103 @@ import path from "node:path";
|
|
|
5
5
|
import { dirname, resolve } from "path";
|
|
6
6
|
import { spawnPromise } from "spawn-rx";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
/**
|
|
11
|
+
* Validate environment variable names
|
|
12
|
+
* - Must start with letter or underscore
|
|
13
|
+
* - Can contain letters, numbers, underscores
|
|
14
|
+
*/
|
|
15
|
+
function isValidEnvVarName(name) {
|
|
16
|
+
return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate environment variable values
|
|
20
|
+
* - No null bytes (could truncate strings)
|
|
21
|
+
*/
|
|
22
|
+
function isValidEnvVarValue(value) {
|
|
23
|
+
return !value.includes("\0");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validate and sanitize environment variables
|
|
27
|
+
* Returns filtered environment variables with invalid entries removed
|
|
28
|
+
*/
|
|
29
|
+
function validateEnvVars(env) {
|
|
30
|
+
const validated = {};
|
|
31
|
+
for (const [key, value] of Object.entries(env)) {
|
|
32
|
+
if (!isValidEnvVarName(key)) {
|
|
33
|
+
console.warn(`Warning: Skipping invalid environment variable name: ${key}`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (!isValidEnvVarValue(value)) {
|
|
37
|
+
console.warn(`Warning: Skipping environment variable with invalid value: ${key}`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
validated[key] = value;
|
|
41
|
+
}
|
|
42
|
+
return validated;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate that a URL is safe for connection
|
|
46
|
+
* - Must be http or https
|
|
47
|
+
* - Blocks private/internal IPs to prevent SSRF
|
|
48
|
+
*/
|
|
49
|
+
function validateServerUrl(url) {
|
|
50
|
+
try {
|
|
51
|
+
const parsed = new URL(url);
|
|
52
|
+
// Must be http or https
|
|
53
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
54
|
+
throw new Error(`Invalid URL protocol: ${parsed.protocol}. Must be http or https.`);
|
|
55
|
+
}
|
|
56
|
+
// Block private IPs to prevent SSRF attacks
|
|
57
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
58
|
+
const privatePatterns = [
|
|
59
|
+
/^localhost$/,
|
|
60
|
+
/^127\./,
|
|
61
|
+
/^10\./,
|
|
62
|
+
/^172\.(1[6-9]|2[0-9]|3[01])\./,
|
|
63
|
+
/^192\.168\./,
|
|
64
|
+
/^169\.254\./, // Link-local
|
|
65
|
+
/^\[::1\]$/, // IPv6 localhost
|
|
66
|
+
/^\[fe80:/i, // IPv6 link-local
|
|
67
|
+
];
|
|
68
|
+
// Only warn for private IPs (don't block - may be intentional for local testing)
|
|
69
|
+
if (privatePatterns.some((pattern) => pattern.test(hostname))) {
|
|
70
|
+
console.warn(`Warning: Connecting to private/internal address: ${hostname}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error instanceof Error && error.message.startsWith("Invalid URL")) {
|
|
75
|
+
throw new Error(`Invalid server URL: ${url}`);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Validate a command exists and is safe to execute
|
|
82
|
+
*/
|
|
83
|
+
function validateCommand(command) {
|
|
84
|
+
// Check for shell metacharacters
|
|
85
|
+
const dangerousChars = /[;&|`$(){}[\]<>!]/;
|
|
86
|
+
if (dangerousChars.test(command)) {
|
|
87
|
+
throw new Error(`Invalid command: contains shell metacharacters: ${command}`);
|
|
88
|
+
}
|
|
89
|
+
// For absolute paths, verify the file exists
|
|
90
|
+
if (path.isAbsolute(command)) {
|
|
91
|
+
if (!fs.existsSync(command)) {
|
|
92
|
+
throw new Error(`Command not found: ${command}`);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// For relative commands, verify they exist in PATH
|
|
97
|
+
try {
|
|
98
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
99
|
+
execSync(`${whichCmd} "${command}"`, { stdio: "pipe" });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw new Error(`Command not found in PATH: ${command}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
9
105
|
function handleError(error) {
|
|
10
106
|
let message;
|
|
11
107
|
if (error instanceof Error) {
|
|
@@ -24,6 +120,14 @@ function delay(ms) {
|
|
|
24
120
|
return new Promise((resolve) => setTimeout(resolve, ms, true));
|
|
25
121
|
}
|
|
26
122
|
async function runWebClient(args) {
|
|
123
|
+
// Validate inputs before proceeding
|
|
124
|
+
const validatedEnvArgs = validateEnvVars(args.envArgs);
|
|
125
|
+
if (args.serverUrl) {
|
|
126
|
+
validateServerUrl(args.serverUrl);
|
|
127
|
+
}
|
|
128
|
+
if (args.command) {
|
|
129
|
+
validateCommand(args.command);
|
|
130
|
+
}
|
|
27
131
|
// Path to the client entry point
|
|
28
132
|
const inspectorClientPath = resolve(__dirname, "../../", "client", "bin", "start.js");
|
|
29
133
|
const abort = new AbortController();
|
|
@@ -34,8 +138,8 @@ async function runWebClient(args) {
|
|
|
34
138
|
});
|
|
35
139
|
// Build arguments to pass to start.js
|
|
36
140
|
const startArgs = [];
|
|
37
|
-
// Pass environment variables
|
|
38
|
-
for (const [key, value] of Object.entries(
|
|
141
|
+
// Pass validated environment variables
|
|
142
|
+
for (const [key, value] of Object.entries(validatedEnvArgs)) {
|
|
39
143
|
startArgs.push("-e", `${key}=${value}`);
|
|
40
144
|
}
|
|
41
145
|
// Pass transport type if specified
|
|
@@ -70,6 +174,18 @@ async function runWebClient(args) {
|
|
|
70
174
|
}
|
|
71
175
|
}
|
|
72
176
|
async function runCli(args) {
|
|
177
|
+
// Validate inputs before proceeding
|
|
178
|
+
const validatedEnvArgs = validateEnvVars(args.envArgs);
|
|
179
|
+
if (args.command) {
|
|
180
|
+
// For CLI mode, command might be a URL - validate appropriately
|
|
181
|
+
if (args.command.startsWith("http://") ||
|
|
182
|
+
args.command.startsWith("https://")) {
|
|
183
|
+
validateServerUrl(args.command);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
validateCommand(args.command);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
73
189
|
const projectRoot = resolve(__dirname, "..");
|
|
74
190
|
const cliPath = resolve(projectRoot, "build", "index.js");
|
|
75
191
|
const abort = new AbortController();
|
|
@@ -96,7 +212,7 @@ async function runCli(args) {
|
|
|
96
212
|
}
|
|
97
213
|
}
|
|
98
214
|
await spawnPromise("node", cliArgs, {
|
|
99
|
-
env: { ...process.env, ...
|
|
215
|
+
env: { ...process.env, ...validatedEnvArgs },
|
|
100
216
|
signal: abort.signal,
|
|
101
217
|
echoOutput: true,
|
|
102
218
|
// pipe the stdout through here, prevents issues with buffering and
|
|
@@ -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-C89umkGV.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
package/client/dist/assets/{OAuthDebugCallback-BL3_Hknj.js → OAuthDebugCallback-hckdJlo3.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-C89umkGV.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|