@bryan-thompson/inspector-assessment-cli 1.43.1 → 1.43.3
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/build/__tests__/assessment-runner/index.test.js +11 -3
- package/build/__tests__/assessment-runner/native-module-detector.test.js +199 -0
- package/build/__tests__/assessment-runner/server-connection.test.js +184 -1
- package/build/__tests__/assessment-runner-facade.test.js +10 -2
- package/build/__tests__/jsonl-events.test.js +83 -1
- package/build/__tests__/static-mode-integration.test.js +387 -0
- package/build/__tests__/static-only-mode.test.js +439 -0
- package/build/__tests__/transport.test.js +141 -0
- package/build/assess-full.js +532 -106
- package/build/assess-security.js +54 -90
- package/build/lib/assessment-runner/assessment-executor.js +194 -0
- package/build/lib/assessment-runner/config-builder.js +46 -1
- package/build/lib/assessment-runner/index.js +2 -0
- package/build/lib/assessment-runner/native-module-detector.js +107 -0
- package/build/lib/assessment-runner/server-connection.js +56 -9
- package/build/lib/cli-parser.js +67 -0
- package/build/lib/cli-parserSchemas.js +12 -0
- package/build/lib/jsonl-events.js +29 -0
- package/build/lib/static-modules.js +103 -0
- package/build/transport.js +32 -7
- package/build/validate-testbed.js +0 -0
- package/package.json +1 -1
- package/build/lib/__tests__/zodErrorFormatter.test.js +0 -282
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static-Only Mode Unit Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for Issue #213: Static/manifest-only validation mode.
|
|
5
|
+
*
|
|
6
|
+
* Tests cover:
|
|
7
|
+
* - CLI flag parsing for --static-only and --fallback-static
|
|
8
|
+
* - Mutual exclusivity validation
|
|
9
|
+
* - Static module configuration
|
|
10
|
+
* - Config builder static mode handling
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, jest, beforeEach, afterEach, } from "@jest/globals";
|
|
13
|
+
import { parseArgs } from "../lib/cli-parser.js";
|
|
14
|
+
import { STATIC_MODULES, RUNTIME_MODULES, isStaticModule, isRuntimeModule, filterToStaticModules, filterToRuntimeModules, } from "../lib/static-modules.js";
|
|
15
|
+
import { buildConfig } from "../lib/assessment-runner/config-builder.js";
|
|
16
|
+
describe("Static-Only Mode CLI Parsing", () => {
|
|
17
|
+
// Suppress console output during tests
|
|
18
|
+
let consoleErrorSpy;
|
|
19
|
+
let consoleWarnSpy;
|
|
20
|
+
let consoleLogSpy;
|
|
21
|
+
let processExitSpy;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
24
|
+
consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => { });
|
|
25
|
+
consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
26
|
+
processExitSpy = jest
|
|
27
|
+
.spyOn(process, "exit")
|
|
28
|
+
.mockImplementation(() => undefined);
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
jest.restoreAllMocks();
|
|
32
|
+
});
|
|
33
|
+
describe("--static-only flag", () => {
|
|
34
|
+
it("should parse --static-only flag", () => {
|
|
35
|
+
const result = parseArgs([
|
|
36
|
+
"--server",
|
|
37
|
+
"test-server",
|
|
38
|
+
"--source",
|
|
39
|
+
"/path/to/source",
|
|
40
|
+
"--static-only",
|
|
41
|
+
]);
|
|
42
|
+
expect(result.staticOnly).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it("should set staticOnly to undefined when not provided", () => {
|
|
45
|
+
const result = parseArgs([
|
|
46
|
+
"--server",
|
|
47
|
+
"test-server",
|
|
48
|
+
"--http",
|
|
49
|
+
"http://localhost:8080",
|
|
50
|
+
]);
|
|
51
|
+
expect(result.staticOnly).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("--fallback-static flag", () => {
|
|
55
|
+
it("should parse --fallback-static flag", () => {
|
|
56
|
+
const result = parseArgs([
|
|
57
|
+
"--server",
|
|
58
|
+
"test-server",
|
|
59
|
+
"--http",
|
|
60
|
+
"http://localhost:8080",
|
|
61
|
+
"--source",
|
|
62
|
+
"/path/to/source",
|
|
63
|
+
"--fallback-static",
|
|
64
|
+
]);
|
|
65
|
+
expect(result.fallbackStatic).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it("should set fallbackStatic to undefined when not provided", () => {
|
|
68
|
+
const result = parseArgs([
|
|
69
|
+
"--server",
|
|
70
|
+
"test-server",
|
|
71
|
+
"--http",
|
|
72
|
+
"http://localhost:8080",
|
|
73
|
+
]);
|
|
74
|
+
expect(result.fallbackStatic).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe("Mutual Exclusivity", () => {
|
|
78
|
+
it("should reject --static-only with --fallback-static", () => {
|
|
79
|
+
const result = parseArgs([
|
|
80
|
+
"--server",
|
|
81
|
+
"test-server",
|
|
82
|
+
"--source",
|
|
83
|
+
"/path/to/source",
|
|
84
|
+
"--static-only",
|
|
85
|
+
"--fallback-static",
|
|
86
|
+
]);
|
|
87
|
+
expect(result.helpRequested).toBe(true);
|
|
88
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("mutually exclusive"));
|
|
89
|
+
});
|
|
90
|
+
it("should reject --static-only without --source", () => {
|
|
91
|
+
const result = parseArgs(["--server", "test-server", "--static-only"]);
|
|
92
|
+
expect(result.helpRequested).toBe(true);
|
|
93
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("requires --source"));
|
|
94
|
+
});
|
|
95
|
+
it("should reject --static-only with --http", () => {
|
|
96
|
+
const result = parseArgs([
|
|
97
|
+
"--server",
|
|
98
|
+
"test-server",
|
|
99
|
+
"--source",
|
|
100
|
+
"/path/to/source",
|
|
101
|
+
"--static-only",
|
|
102
|
+
"--http",
|
|
103
|
+
"http://localhost:8080",
|
|
104
|
+
]);
|
|
105
|
+
expect(result.helpRequested).toBe(true);
|
|
106
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("cannot be used with --http"));
|
|
107
|
+
});
|
|
108
|
+
it("should reject --static-only with --sse", () => {
|
|
109
|
+
const result = parseArgs([
|
|
110
|
+
"--server",
|
|
111
|
+
"test-server",
|
|
112
|
+
"--source",
|
|
113
|
+
"/path/to/source",
|
|
114
|
+
"--static-only",
|
|
115
|
+
"--sse",
|
|
116
|
+
"http://localhost:8080/sse",
|
|
117
|
+
]);
|
|
118
|
+
expect(result.helpRequested).toBe(true);
|
|
119
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("cannot be used with --http"));
|
|
120
|
+
});
|
|
121
|
+
it("should reject --static-only with --config", () => {
|
|
122
|
+
const result = parseArgs([
|
|
123
|
+
"--server",
|
|
124
|
+
"test-server",
|
|
125
|
+
"--source",
|
|
126
|
+
"/path/to/source",
|
|
127
|
+
"--static-only",
|
|
128
|
+
"--config",
|
|
129
|
+
"/path/to/config.json",
|
|
130
|
+
]);
|
|
131
|
+
expect(result.helpRequested).toBe(true);
|
|
132
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("cannot be used with"));
|
|
133
|
+
});
|
|
134
|
+
it("should reject --static-only with --module", () => {
|
|
135
|
+
const result = parseArgs([
|
|
136
|
+
"--server",
|
|
137
|
+
"test-server",
|
|
138
|
+
"--source",
|
|
139
|
+
"/path/to/source",
|
|
140
|
+
"--static-only",
|
|
141
|
+
"--module",
|
|
142
|
+
"security",
|
|
143
|
+
]);
|
|
144
|
+
expect(result.helpRequested).toBe(true);
|
|
145
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("cannot be used with --module"));
|
|
146
|
+
});
|
|
147
|
+
it("should allow --static-only with --only-modules", () => {
|
|
148
|
+
const result = parseArgs([
|
|
149
|
+
"--server",
|
|
150
|
+
"test-server",
|
|
151
|
+
"--source",
|
|
152
|
+
"/path/to/source",
|
|
153
|
+
"--static-only",
|
|
154
|
+
"--only-modules",
|
|
155
|
+
"manifestValidation,prohibitedLibraries",
|
|
156
|
+
]);
|
|
157
|
+
// Should not error - this is allowed for filtering static modules
|
|
158
|
+
expect(result.staticOnly).toBe(true);
|
|
159
|
+
expect(result.onlyModules).toEqual([
|
|
160
|
+
"manifestValidation",
|
|
161
|
+
"prohibitedLibraries",
|
|
162
|
+
]);
|
|
163
|
+
});
|
|
164
|
+
it("should allow --static-only with --skip-modules", () => {
|
|
165
|
+
const result = parseArgs([
|
|
166
|
+
"--server",
|
|
167
|
+
"test-server",
|
|
168
|
+
"--source",
|
|
169
|
+
"/path/to/source",
|
|
170
|
+
"--static-only",
|
|
171
|
+
"--skip-modules",
|
|
172
|
+
"portability",
|
|
173
|
+
]);
|
|
174
|
+
// Should not error - this is allowed for filtering static modules
|
|
175
|
+
expect(result.staticOnly).toBe(true);
|
|
176
|
+
expect(result.skipModules).toEqual(["portability"]);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe("Static Modules Definition", () => {
|
|
181
|
+
describe("STATIC_MODULES constant", () => {
|
|
182
|
+
it("should contain expected static-capable modules", () => {
|
|
183
|
+
expect(STATIC_MODULES).toContain("manifestValidation");
|
|
184
|
+
expect(STATIC_MODULES).toContain("documentation"); // Legacy name for DeveloperExperience
|
|
185
|
+
expect(STATIC_MODULES).toContain("usability"); // Legacy name for DeveloperExperience
|
|
186
|
+
expect(STATIC_MODULES).toContain("prohibitedLibraries");
|
|
187
|
+
expect(STATIC_MODULES).toContain("portability");
|
|
188
|
+
expect(STATIC_MODULES).toContain("externalAPIScanner");
|
|
189
|
+
expect(STATIC_MODULES).toContain("fileModularization");
|
|
190
|
+
expect(STATIC_MODULES).toContain("conformance");
|
|
191
|
+
expect(STATIC_MODULES).toContain("toolAnnotations");
|
|
192
|
+
expect(STATIC_MODULES).toContain("authentication");
|
|
193
|
+
expect(STATIC_MODULES).toContain("aupCompliance");
|
|
194
|
+
});
|
|
195
|
+
it("should not contain runtime-only modules", () => {
|
|
196
|
+
expect(STATIC_MODULES).not.toContain("functionality");
|
|
197
|
+
expect(STATIC_MODULES).not.toContain("security");
|
|
198
|
+
expect(STATIC_MODULES).not.toContain("temporal");
|
|
199
|
+
expect(STATIC_MODULES).not.toContain("protocolCompliance");
|
|
200
|
+
});
|
|
201
|
+
it("should have exactly 11 static modules", () => {
|
|
202
|
+
// documentation + usability (both legacy names for DeveloperExperience)
|
|
203
|
+
expect(STATIC_MODULES.length).toBe(11);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
describe("RUNTIME_MODULES constant", () => {
|
|
207
|
+
it("should contain expected runtime-only modules", () => {
|
|
208
|
+
expect(RUNTIME_MODULES).toContain("functionality");
|
|
209
|
+
expect(RUNTIME_MODULES).toContain("security");
|
|
210
|
+
expect(RUNTIME_MODULES).toContain("temporal");
|
|
211
|
+
expect(RUNTIME_MODULES).toContain("protocolCompliance");
|
|
212
|
+
expect(RUNTIME_MODULES).toContain("resources");
|
|
213
|
+
expect(RUNTIME_MODULES).toContain("prompts");
|
|
214
|
+
expect(RUNTIME_MODULES).toContain("crossCapability");
|
|
215
|
+
expect(RUNTIME_MODULES).toContain("errorHandling");
|
|
216
|
+
expect(RUNTIME_MODULES).toContain("dependencyVulnerability");
|
|
217
|
+
});
|
|
218
|
+
it("should not contain static-capable modules", () => {
|
|
219
|
+
expect(RUNTIME_MODULES).not.toContain("manifestValidation");
|
|
220
|
+
expect(RUNTIME_MODULES).not.toContain("prohibitedLibraries");
|
|
221
|
+
expect(RUNTIME_MODULES).not.toContain("portability");
|
|
222
|
+
expect(RUNTIME_MODULES).not.toContain("documentation");
|
|
223
|
+
});
|
|
224
|
+
it("should have exactly 9 runtime modules", () => {
|
|
225
|
+
// Includes dependencyVulnerability (requires live server for npm/yarn audit)
|
|
226
|
+
expect(RUNTIME_MODULES.length).toBe(9);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
describe("isStaticModule helper", () => {
|
|
230
|
+
it("should return true for static-capable modules", () => {
|
|
231
|
+
expect(isStaticModule("manifestValidation")).toBe(true);
|
|
232
|
+
expect(isStaticModule("prohibitedLibraries")).toBe(true);
|
|
233
|
+
expect(isStaticModule("toolAnnotations")).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
it("should return false for runtime-only modules", () => {
|
|
236
|
+
expect(isStaticModule("functionality")).toBe(false);
|
|
237
|
+
expect(isStaticModule("security")).toBe(false);
|
|
238
|
+
expect(isStaticModule("temporal")).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
it("should return false for unknown modules", () => {
|
|
241
|
+
expect(isStaticModule("unknownModule")).toBe(false);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
describe("isRuntimeModule helper", () => {
|
|
245
|
+
it("should return true for runtime-only modules", () => {
|
|
246
|
+
expect(isRuntimeModule("functionality")).toBe(true);
|
|
247
|
+
expect(isRuntimeModule("security")).toBe(true);
|
|
248
|
+
expect(isRuntimeModule("temporal")).toBe(true);
|
|
249
|
+
});
|
|
250
|
+
it("should return false for static-capable modules", () => {
|
|
251
|
+
expect(isRuntimeModule("manifestValidation")).toBe(false);
|
|
252
|
+
expect(isRuntimeModule("prohibitedLibraries")).toBe(false);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
describe("filterToStaticModules helper", () => {
|
|
256
|
+
it("should filter to only static-capable modules", () => {
|
|
257
|
+
const input = [
|
|
258
|
+
"manifestValidation",
|
|
259
|
+
"functionality",
|
|
260
|
+
"security",
|
|
261
|
+
"prohibitedLibraries",
|
|
262
|
+
];
|
|
263
|
+
const result = filterToStaticModules(input);
|
|
264
|
+
expect(result).toEqual(["manifestValidation", "prohibitedLibraries"]);
|
|
265
|
+
});
|
|
266
|
+
it("should return empty array when no static modules present", () => {
|
|
267
|
+
const input = ["functionality", "security", "temporal"];
|
|
268
|
+
const result = filterToStaticModules(input);
|
|
269
|
+
expect(result).toEqual([]);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
describe("filterToRuntimeModules helper", () => {
|
|
273
|
+
it("should filter to only runtime-only modules", () => {
|
|
274
|
+
const input = [
|
|
275
|
+
"manifestValidation",
|
|
276
|
+
"functionality",
|
|
277
|
+
"security",
|
|
278
|
+
"prohibitedLibraries",
|
|
279
|
+
];
|
|
280
|
+
const result = filterToRuntimeModules(input);
|
|
281
|
+
expect(result).toEqual(["functionality", "security"]);
|
|
282
|
+
});
|
|
283
|
+
it("should return empty array when no runtime modules present", () => {
|
|
284
|
+
const input = [
|
|
285
|
+
"manifestValidation",
|
|
286
|
+
"prohibitedLibraries",
|
|
287
|
+
"portability",
|
|
288
|
+
];
|
|
289
|
+
const result = filterToRuntimeModules(input);
|
|
290
|
+
expect(result).toEqual([]);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
describe("Config Builder Static Mode", () => {
|
|
295
|
+
// Suppress console output during tests
|
|
296
|
+
let consoleWarnSpy;
|
|
297
|
+
let consoleLogSpy;
|
|
298
|
+
beforeEach(() => {
|
|
299
|
+
consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => { });
|
|
300
|
+
consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
301
|
+
});
|
|
302
|
+
afterEach(() => {
|
|
303
|
+
jest.restoreAllMocks();
|
|
304
|
+
});
|
|
305
|
+
describe("buildConfig with staticOnly option", () => {
|
|
306
|
+
it("should enable only static-capable modules", () => {
|
|
307
|
+
const config = buildConfig({
|
|
308
|
+
serverName: "test-server",
|
|
309
|
+
sourceCodePath: "/path/to/source",
|
|
310
|
+
staticOnly: true,
|
|
311
|
+
});
|
|
312
|
+
const categories = config.assessmentCategories;
|
|
313
|
+
// Static modules should be enabled (using legacy property names)
|
|
314
|
+
expect(categories.manifestValidation).toBe(true);
|
|
315
|
+
expect(categories.documentation).toBe(true); // Legacy name for DeveloperExperience
|
|
316
|
+
expect(categories.usability).toBe(true); // Legacy name for DeveloperExperience
|
|
317
|
+
expect(categories.prohibitedLibraries).toBe(true);
|
|
318
|
+
expect(categories.portability).toBe(true);
|
|
319
|
+
expect(categories.externalAPIScanner).toBe(true);
|
|
320
|
+
expect(categories.toolAnnotations).toBe(true);
|
|
321
|
+
expect(categories.authentication).toBe(true);
|
|
322
|
+
expect(categories.aupCompliance).toBe(true);
|
|
323
|
+
// Runtime modules should be disabled
|
|
324
|
+
expect(categories.functionality).toBe(false);
|
|
325
|
+
expect(categories.security).toBe(false);
|
|
326
|
+
expect(categories.temporal).toBe(false);
|
|
327
|
+
expect(categories.protocolCompliance).toBe(false);
|
|
328
|
+
});
|
|
329
|
+
it("should apply --only-modules filter to static modules", () => {
|
|
330
|
+
const config = buildConfig({
|
|
331
|
+
serverName: "test-server",
|
|
332
|
+
sourceCodePath: "/path/to/source",
|
|
333
|
+
staticOnly: true,
|
|
334
|
+
onlyModules: ["manifestValidation", "prohibitedLibraries"],
|
|
335
|
+
});
|
|
336
|
+
const categories = config.assessmentCategories;
|
|
337
|
+
// Only whitelisted static modules should be enabled
|
|
338
|
+
expect(categories.manifestValidation).toBe(true);
|
|
339
|
+
expect(categories.prohibitedLibraries).toBe(true);
|
|
340
|
+
// Other static modules should be disabled
|
|
341
|
+
expect(categories.portability).toBe(false);
|
|
342
|
+
expect(categories.toolAnnotations).toBe(false);
|
|
343
|
+
// Runtime modules still disabled
|
|
344
|
+
expect(categories.functionality).toBe(false);
|
|
345
|
+
expect(categories.security).toBe(false);
|
|
346
|
+
});
|
|
347
|
+
it("should apply --skip-modules filter to static modules", () => {
|
|
348
|
+
const config = buildConfig({
|
|
349
|
+
serverName: "test-server",
|
|
350
|
+
sourceCodePath: "/path/to/source",
|
|
351
|
+
staticOnly: true,
|
|
352
|
+
skipModules: ["portability", "externalAPIScanner"],
|
|
353
|
+
});
|
|
354
|
+
const categories = config.assessmentCategories;
|
|
355
|
+
// Skipped modules should be disabled
|
|
356
|
+
expect(categories.portability).toBe(false);
|
|
357
|
+
expect(categories.externalAPIScanner).toBe(false);
|
|
358
|
+
// Other static modules should still be enabled
|
|
359
|
+
expect(categories.manifestValidation).toBe(true);
|
|
360
|
+
expect(categories.prohibitedLibraries).toBe(true);
|
|
361
|
+
});
|
|
362
|
+
it("should ignore runtime modules in --only-modules with static mode", () => {
|
|
363
|
+
const config = buildConfig({
|
|
364
|
+
serverName: "test-server",
|
|
365
|
+
sourceCodePath: "/path/to/source",
|
|
366
|
+
staticOnly: true,
|
|
367
|
+
onlyModules: ["manifestValidation", "security", "functionality"],
|
|
368
|
+
});
|
|
369
|
+
const categories = config.assessmentCategories;
|
|
370
|
+
// Only static module from whitelist should be enabled
|
|
371
|
+
expect(categories.manifestValidation).toBe(true);
|
|
372
|
+
// Runtime modules from whitelist should still be disabled
|
|
373
|
+
expect(categories.security).toBe(false);
|
|
374
|
+
expect(categories.functionality).toBe(false);
|
|
375
|
+
});
|
|
376
|
+
it("should result in zero enabled modules if only runtime modules in --only-modules", () => {
|
|
377
|
+
const config = buildConfig({
|
|
378
|
+
serverName: "test-server",
|
|
379
|
+
sourceCodePath: "/path/to/source",
|
|
380
|
+
staticOnly: true,
|
|
381
|
+
onlyModules: ["functionality", "security", "temporal"],
|
|
382
|
+
});
|
|
383
|
+
const categories = config.assessmentCategories;
|
|
384
|
+
// All runtime modules should be disabled
|
|
385
|
+
expect(categories.functionality).toBe(false);
|
|
386
|
+
expect(categories.security).toBe(false);
|
|
387
|
+
expect(categories.temporal).toBe(false);
|
|
388
|
+
// Static modules should also be disabled (not in onlyModules)
|
|
389
|
+
expect(categories.manifestValidation).toBe(false);
|
|
390
|
+
expect(categories.prohibitedLibraries).toBe(false);
|
|
391
|
+
});
|
|
392
|
+
it("should result in zero enabled modules if all static modules skipped", () => {
|
|
393
|
+
const config = buildConfig({
|
|
394
|
+
serverName: "test-server",
|
|
395
|
+
sourceCodePath: "/path/to/source",
|
|
396
|
+
staticOnly: true,
|
|
397
|
+
skipModules: [
|
|
398
|
+
"manifestValidation",
|
|
399
|
+
"documentation",
|
|
400
|
+
"usability",
|
|
401
|
+
"prohibitedLibraries",
|
|
402
|
+
"portability",
|
|
403
|
+
"externalAPIScanner",
|
|
404
|
+
"fileModularization",
|
|
405
|
+
"conformance",
|
|
406
|
+
"toolAnnotations",
|
|
407
|
+
"authentication",
|
|
408
|
+
"aupCompliance",
|
|
409
|
+
],
|
|
410
|
+
});
|
|
411
|
+
const categories = config.assessmentCategories;
|
|
412
|
+
// All static modules should be disabled
|
|
413
|
+
expect(categories.manifestValidation).toBe(false);
|
|
414
|
+
expect(categories.prohibitedLibraries).toBe(false);
|
|
415
|
+
expect(categories.portability).toBe(false);
|
|
416
|
+
expect(categories.toolAnnotations).toBe(false);
|
|
417
|
+
});
|
|
418
|
+
it("should log static mode info", () => {
|
|
419
|
+
buildConfig({
|
|
420
|
+
serverName: "test-server",
|
|
421
|
+
sourceCodePath: "/path/to/source",
|
|
422
|
+
staticOnly: true,
|
|
423
|
+
});
|
|
424
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining("Static-only mode"));
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
describe("buildConfig without staticOnly option", () => {
|
|
428
|
+
it("should enable runtime modules by default", () => {
|
|
429
|
+
const config = buildConfig({
|
|
430
|
+
serverName: "test-server",
|
|
431
|
+
sourceCodePath: "/path/to/source",
|
|
432
|
+
});
|
|
433
|
+
const categories = config.assessmentCategories;
|
|
434
|
+
// Runtime modules should be enabled by default
|
|
435
|
+
expect(categories.functionality).toBe(true);
|
|
436
|
+
expect(categories.security).toBe(true);
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
});
|
|
@@ -68,6 +68,147 @@ describe("Transport Creation", () => {
|
|
|
68
68
|
expect(transport).toBeDefined();
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
|
+
describe("Minimal Environment Variables (Issue #211)", () => {
|
|
72
|
+
let originalEnv;
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
// Restore original environment after each test
|
|
75
|
+
process.env = originalEnv;
|
|
76
|
+
});
|
|
77
|
+
it("should pass minimal env vars (PATH, HOME) to spawned servers", () => {
|
|
78
|
+
originalEnv = process.env;
|
|
79
|
+
process.env = {
|
|
80
|
+
PATH: "/usr/bin:/usr/local/bin",
|
|
81
|
+
HOME: "/home/testuser",
|
|
82
|
+
};
|
|
83
|
+
const options = {
|
|
84
|
+
transportType: "stdio",
|
|
85
|
+
command: "node",
|
|
86
|
+
args: ["server.js"],
|
|
87
|
+
};
|
|
88
|
+
// Creating the transport should not throw
|
|
89
|
+
const transport = createTransport(options);
|
|
90
|
+
expect(transport).toBeDefined();
|
|
91
|
+
// The transport is created with minimal env (PATH, HOME, NODE_ENV)
|
|
92
|
+
// We can't directly inspect StdioClientTransport's env without mocking,
|
|
93
|
+
// but we verify the transport was created successfully
|
|
94
|
+
});
|
|
95
|
+
it("should default NODE_ENV to production when not set", () => {
|
|
96
|
+
originalEnv = process.env;
|
|
97
|
+
process.env = {
|
|
98
|
+
PATH: "/usr/bin",
|
|
99
|
+
HOME: "/home/user",
|
|
100
|
+
// NODE_ENV intentionally not set
|
|
101
|
+
};
|
|
102
|
+
const options = {
|
|
103
|
+
transportType: "stdio",
|
|
104
|
+
command: "python",
|
|
105
|
+
args: ["server.py"],
|
|
106
|
+
};
|
|
107
|
+
const transport = createTransport(options);
|
|
108
|
+
expect(transport).toBeDefined();
|
|
109
|
+
});
|
|
110
|
+
it("should preserve NODE_ENV when set", () => {
|
|
111
|
+
originalEnv = process.env;
|
|
112
|
+
process.env = {
|
|
113
|
+
PATH: "/usr/bin",
|
|
114
|
+
HOME: "/home/user",
|
|
115
|
+
NODE_ENV: "development",
|
|
116
|
+
};
|
|
117
|
+
const options = {
|
|
118
|
+
transportType: "stdio",
|
|
119
|
+
command: "node",
|
|
120
|
+
args: ["server.js"],
|
|
121
|
+
};
|
|
122
|
+
const transport = createTransport(options);
|
|
123
|
+
expect(transport).toBeDefined();
|
|
124
|
+
});
|
|
125
|
+
it("should NOT pass arbitrary process.env vars (Issue #211)", () => {
|
|
126
|
+
originalEnv = process.env;
|
|
127
|
+
process.env = {
|
|
128
|
+
PATH: "/usr/bin",
|
|
129
|
+
HOME: "/home/user",
|
|
130
|
+
SOME_RANDOM_VAR: "should-not-pass",
|
|
131
|
+
ENABLE_DYNAMIC_MAPS: "true", // This caused TomTom MCP issues
|
|
132
|
+
AWS_ACCESS_KEY_ID: "secret", // Sensitive vars should not leak
|
|
133
|
+
CUSTOM_CONFIG: "value",
|
|
134
|
+
};
|
|
135
|
+
const options = {
|
|
136
|
+
transportType: "stdio",
|
|
137
|
+
command: "node",
|
|
138
|
+
args: ["server.js"],
|
|
139
|
+
};
|
|
140
|
+
// Creating transport should succeed
|
|
141
|
+
const transport = createTransport(options);
|
|
142
|
+
expect(transport).toBeDefined();
|
|
143
|
+
// The arbitrary environment variables are filtered out by getMinimalEnv()
|
|
144
|
+
// We verify the transport was created without throwing errors
|
|
145
|
+
});
|
|
146
|
+
it("should pass platform-specific essential vars (USER, SHELL, LANG)", () => {
|
|
147
|
+
originalEnv = process.env;
|
|
148
|
+
process.env = {
|
|
149
|
+
PATH: "/usr/bin",
|
|
150
|
+
HOME: "/home/user",
|
|
151
|
+
USER: "testuser",
|
|
152
|
+
SHELL: "/bin/bash",
|
|
153
|
+
LANG: "en_US.UTF-8",
|
|
154
|
+
};
|
|
155
|
+
const options = {
|
|
156
|
+
transportType: "stdio",
|
|
157
|
+
command: "node",
|
|
158
|
+
args: ["server.js"],
|
|
159
|
+
};
|
|
160
|
+
const transport = createTransport(options);
|
|
161
|
+
expect(transport).toBeDefined();
|
|
162
|
+
});
|
|
163
|
+
it("should pass temp directory vars (TMPDIR, TMP, TEMP)", () => {
|
|
164
|
+
originalEnv = process.env;
|
|
165
|
+
process.env = {
|
|
166
|
+
PATH: "/usr/bin",
|
|
167
|
+
HOME: "/home/user",
|
|
168
|
+
TMPDIR: "/tmp",
|
|
169
|
+
TMP: "/tmp",
|
|
170
|
+
TEMP: "/tmp",
|
|
171
|
+
};
|
|
172
|
+
const options = {
|
|
173
|
+
transportType: "stdio",
|
|
174
|
+
command: "python",
|
|
175
|
+
args: ["-m", "server"],
|
|
176
|
+
};
|
|
177
|
+
const transport = createTransport(options);
|
|
178
|
+
expect(transport).toBeDefined();
|
|
179
|
+
});
|
|
180
|
+
it("should handle minimal env when optional vars are missing", () => {
|
|
181
|
+
originalEnv = process.env;
|
|
182
|
+
process.env = {
|
|
183
|
+
PATH: "/usr/bin",
|
|
184
|
+
// HOME, USER, SHELL, etc. intentionally not set
|
|
185
|
+
};
|
|
186
|
+
const options = {
|
|
187
|
+
transportType: "stdio",
|
|
188
|
+
command: "node",
|
|
189
|
+
args: ["server.js"],
|
|
190
|
+
};
|
|
191
|
+
// Should still work with just PATH
|
|
192
|
+
const transport = createTransport(options);
|
|
193
|
+
expect(transport).toBeDefined();
|
|
194
|
+
});
|
|
195
|
+
it("should combine SDK defaults with minimal env vars", () => {
|
|
196
|
+
originalEnv = process.env;
|
|
197
|
+
process.env = {
|
|
198
|
+
PATH: "/custom/path",
|
|
199
|
+
HOME: "/home/custom",
|
|
200
|
+
NODE_ENV: "test",
|
|
201
|
+
};
|
|
202
|
+
const options = {
|
|
203
|
+
transportType: "stdio",
|
|
204
|
+
command: "node",
|
|
205
|
+
args: ["server.js"],
|
|
206
|
+
};
|
|
207
|
+
// getDefaultEnvironment() from SDK is merged with getMinimalEnv()
|
|
208
|
+
const transport = createTransport(options);
|
|
209
|
+
expect(transport).toBeDefined();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
71
212
|
describe("HTTP Transport", () => {
|
|
72
213
|
it("should create transport for valid HTTP options", () => {
|
|
73
214
|
const options = {
|