@bryan-thompson/inspector-assessment 1.5.0 → 1.7.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 +528 -0
- package/cli/build/assess-security.js +342 -0
- package/cli/build/cli.js +10 -1
- package/client/dist/assets/{OAuthCallback-TeTvKfWE.js → OAuthCallback-Xo9zS7pv.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-DwA2sKy9.js → OAuthDebugCallback-CaIey8K_.js} +1 -1
- package/client/dist/assets/{index-BwAoxcvr.js → index-nCPw6E-c.js} +4 -4
- package/client/dist/index.html +1 -1
- package/client/lib/lib/assessmentTypes.d.ts +670 -0
- package/client/lib/lib/assessmentTypes.d.ts.map +1 -0
- package/client/lib/lib/assessmentTypes.js +220 -0
- package/client/lib/lib/aupPatterns.d.ts +63 -0
- package/client/lib/lib/aupPatterns.d.ts.map +1 -0
- package/client/lib/lib/aupPatterns.js +344 -0
- package/client/lib/lib/prohibitedLibraries.d.ts +76 -0
- package/client/lib/lib/prohibitedLibraries.d.ts.map +1 -0
- package/client/lib/lib/prohibitedLibraries.js +364 -0
- package/client/lib/lib/securityPatterns.d.ts +64 -0
- package/client/lib/lib/securityPatterns.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns.js +453 -0
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +88 -0
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -0
- package/client/lib/services/assessment/AssessmentOrchestrator.js +418 -0
- package/client/lib/services/assessment/ResponseValidator.d.ts +69 -0
- package/client/lib/services/assessment/ResponseValidator.d.ts.map +1 -0
- package/client/lib/services/assessment/ResponseValidator.js +1038 -0
- package/client/lib/services/assessment/TestDataGenerator.d.ts +86 -0
- package/client/lib/services/assessment/TestDataGenerator.d.ts.map +1 -0
- package/client/lib/services/assessment/TestDataGenerator.js +669 -0
- package/client/lib/services/assessment/TestScenarioEngine.d.ts +91 -0
- package/client/lib/services/assessment/TestScenarioEngine.d.ts.map +1 -0
- package/client/lib/services/assessment/TestScenarioEngine.js +505 -0
- package/client/lib/services/assessment/ToolClassifier.d.ts +61 -0
- package/client/lib/services/assessment/ToolClassifier.d.ts.map +1 -0
- package/client/lib/services/assessment/ToolClassifier.js +349 -0
- package/client/lib/services/assessment/lib/claudeCodeBridge.d.ts +160 -0
- package/client/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -0
- package/client/lib/services/assessment/lib/claudeCodeBridge.js +357 -0
- package/client/lib/services/assessment/modules/AUPComplianceAssessor.d.ts +100 -0
- package/client/lib/services/assessment/modules/AUPComplianceAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/AUPComplianceAssessor.js +474 -0
- package/client/lib/services/assessment/modules/BaseAssessor.d.ts +71 -0
- package/client/lib/services/assessment/modules/BaseAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/BaseAssessor.js +171 -0
- package/client/lib/services/assessment/modules/DocumentationAssessor.d.ts +45 -0
- package/client/lib/services/assessment/modules/DocumentationAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/DocumentationAssessor.js +355 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +25 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +564 -0
- package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts +20 -0
- package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/FunctionalityAssessor.js +253 -0
- package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +70 -0
- package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +508 -0
- package/client/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +70 -0
- package/client/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/ManifestValidationAssessor.js +430 -0
- package/client/lib/services/assessment/modules/PortabilityAssessor.d.ts +43 -0
- package/client/lib/services/assessment/modules/PortabilityAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/PortabilityAssessor.js +347 -0
- package/client/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts +41 -0
- package/client/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/ProhibitedLibrariesAssessor.js +256 -0
- package/client/lib/services/assessment/modules/SecurityAssessor.d.ts +176 -0
- package/client/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/SecurityAssessor.js +1333 -0
- package/client/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +96 -0
- package/client/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/ToolAnnotationAssessor.js +593 -0
- package/client/lib/services/assessment/modules/UsabilityAssessor.d.ts +21 -0
- package/client/lib/services/assessment/modules/UsabilityAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/UsabilityAssessor.js +241 -0
- package/client/lib/services/assessment/modules/index.d.ts +33 -0
- package/client/lib/services/assessment/modules/index.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/index.js +35 -0
- package/package.json +7 -2
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portability Assessor
|
|
3
|
+
* Detects hardcoded paths and platform-specific code
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* - Hardcoded absolute paths
|
|
7
|
+
* - User home directory references
|
|
8
|
+
* - ${BUNDLE_ROOT} anti-pattern (should use ${__dirname})
|
|
9
|
+
* - Platform-specific code without fallbacks
|
|
10
|
+
* - ${__dirname} usage (correct pattern)
|
|
11
|
+
*
|
|
12
|
+
* Reference: MCPB Bundle Portability Requirements
|
|
13
|
+
*/
|
|
14
|
+
import { BaseAssessor } from "./BaseAssessor.js";
|
|
15
|
+
/**
|
|
16
|
+
* Patterns for detecting portability issues
|
|
17
|
+
*/
|
|
18
|
+
const ISSUE_PATTERNS = {
|
|
19
|
+
// Absolute Unix paths (not ${__dirname})
|
|
20
|
+
absoluteUnixPath: /(?<!\$\{__dirname\}|['"])\/(?:usr|home|var|etc|opt|tmp|Users|Applications)\/[^\s'"]+/g,
|
|
21
|
+
// Absolute Windows paths
|
|
22
|
+
absoluteWindowsPath: /[A-Z]:\\[^\s'"]+/gi,
|
|
23
|
+
// User home directory references
|
|
24
|
+
userHomePath: /(?:~\/|\/Users\/|\/home\/)[^\s'"]+/g,
|
|
25
|
+
// ${BUNDLE_ROOT} anti-pattern
|
|
26
|
+
bundleRootAntipattern: /\$\{BUNDLE_ROOT\}/g,
|
|
27
|
+
// Platform-specific checks without fallbacks
|
|
28
|
+
platformSpecificDarwin: /process\.platform\s*===?\s*['"]darwin['"]/g,
|
|
29
|
+
platformSpecificWin32: /process\.platform\s*===?\s*['"]win32['"]/g,
|
|
30
|
+
platformSpecificLinux: /process\.platform\s*===?\s*['"]linux['"]/g,
|
|
31
|
+
// Hardcoded config paths
|
|
32
|
+
hardcodedConfigPaths: /['"](?:\/etc\/|~\/\.|\.config\/)[^'"]+['"]/g,
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Patterns for correct usage (positive signals)
|
|
36
|
+
*/
|
|
37
|
+
const GOOD_PATTERNS = {
|
|
38
|
+
// Correct ${__dirname} usage
|
|
39
|
+
dirname: /\$\{__dirname\}/g,
|
|
40
|
+
// process.cwd() (usually acceptable)
|
|
41
|
+
processCwd: /process\.cwd\(\)/g,
|
|
42
|
+
// Cross-platform path handling
|
|
43
|
+
pathJoin: /path\.join\(/g,
|
|
44
|
+
pathResolve: /path\.resolve\(/g,
|
|
45
|
+
};
|
|
46
|
+
export class PortabilityAssessor extends BaseAssessor {
|
|
47
|
+
/**
|
|
48
|
+
* Run portability assessment
|
|
49
|
+
*/
|
|
50
|
+
async assess(context) {
|
|
51
|
+
this.log("Starting portability assessment");
|
|
52
|
+
this.testCount = 0;
|
|
53
|
+
const issues = [];
|
|
54
|
+
let scannedFiles = 0;
|
|
55
|
+
let platformSpecificCount = 0;
|
|
56
|
+
let hardcodedPathCount = 0;
|
|
57
|
+
let usesDirname = false;
|
|
58
|
+
let usesBundleRoot = false;
|
|
59
|
+
// Check manifest if available
|
|
60
|
+
if (context.manifestRaw) {
|
|
61
|
+
this.testCount++;
|
|
62
|
+
scannedFiles++;
|
|
63
|
+
const manifestIssues = this.scanFile("manifest.json", context.manifestRaw);
|
|
64
|
+
issues.push(...manifestIssues);
|
|
65
|
+
// Check for ${__dirname} and ${BUNDLE_ROOT} in manifest
|
|
66
|
+
if (GOOD_PATTERNS.dirname.test(context.manifestRaw)) {
|
|
67
|
+
usesDirname = true;
|
|
68
|
+
}
|
|
69
|
+
if (ISSUE_PATTERNS.bundleRootAntipattern.test(context.manifestRaw)) {
|
|
70
|
+
usesBundleRoot = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Check package.json scripts
|
|
74
|
+
if (context.packageJson) {
|
|
75
|
+
this.testCount++;
|
|
76
|
+
scannedFiles++;
|
|
77
|
+
const packageJson = context.packageJson;
|
|
78
|
+
if (packageJson.scripts) {
|
|
79
|
+
const scriptsStr = JSON.stringify(packageJson.scripts);
|
|
80
|
+
const scriptIssues = this.scanFile("package.json (scripts)", scriptsStr);
|
|
81
|
+
issues.push(...scriptIssues);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Check source code files if available
|
|
85
|
+
if (context.sourceCodeFiles && context.config.enableSourceCodeAnalysis) {
|
|
86
|
+
this.log("Scanning source code files for portability issues...");
|
|
87
|
+
for (const [filePath, content] of context.sourceCodeFiles) {
|
|
88
|
+
// Skip irrelevant files
|
|
89
|
+
if (this.shouldSkipFile(filePath))
|
|
90
|
+
continue;
|
|
91
|
+
this.testCount++;
|
|
92
|
+
scannedFiles++;
|
|
93
|
+
const fileIssues = this.scanFile(filePath, content);
|
|
94
|
+
issues.push(...fileIssues);
|
|
95
|
+
// Check for good patterns
|
|
96
|
+
if (GOOD_PATTERNS.dirname.test(content)) {
|
|
97
|
+
usesDirname = true;
|
|
98
|
+
}
|
|
99
|
+
if (ISSUE_PATTERNS.bundleRootAntipattern.test(content)) {
|
|
100
|
+
usesBundleRoot = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Count issue types
|
|
105
|
+
hardcodedPathCount = issues.filter((i) => i.type === "hardcoded_path" ||
|
|
106
|
+
i.type === "absolute_path" ||
|
|
107
|
+
i.type === "user_home_path").length;
|
|
108
|
+
platformSpecificCount = issues.filter((i) => i.type === "platform_specific").length;
|
|
109
|
+
const status = this.determinePortabilityStatus(issues, usesDirname, usesBundleRoot);
|
|
110
|
+
const explanation = this.generateExplanation(issues, usesDirname, usesBundleRoot, scannedFiles);
|
|
111
|
+
const recommendations = this.generateRecommendations(issues, usesDirname, usesBundleRoot);
|
|
112
|
+
this.log(`Assessment complete: ${issues.length} portability issues found`);
|
|
113
|
+
return {
|
|
114
|
+
issues,
|
|
115
|
+
scannedFiles,
|
|
116
|
+
platformSpecificCount,
|
|
117
|
+
hardcodedPathCount,
|
|
118
|
+
usesDirname,
|
|
119
|
+
usesBundleRoot,
|
|
120
|
+
status,
|
|
121
|
+
explanation,
|
|
122
|
+
recommendations,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Scan a file for portability issues
|
|
127
|
+
*/
|
|
128
|
+
scanFile(filePath, content) {
|
|
129
|
+
const issues = [];
|
|
130
|
+
const lines = content.split("\n");
|
|
131
|
+
for (let i = 0; i < lines.length; i++) {
|
|
132
|
+
const line = lines[i];
|
|
133
|
+
const lineNumber = i + 1;
|
|
134
|
+
// Check for ${BUNDLE_ROOT} anti-pattern
|
|
135
|
+
const bundleRootMatches = line.match(ISSUE_PATTERNS.bundleRootAntipattern);
|
|
136
|
+
if (bundleRootMatches) {
|
|
137
|
+
for (const match of bundleRootMatches) {
|
|
138
|
+
issues.push({
|
|
139
|
+
type: "bundle_root_antipattern",
|
|
140
|
+
filePath,
|
|
141
|
+
lineNumber,
|
|
142
|
+
matchedText: match,
|
|
143
|
+
severity: "HIGH",
|
|
144
|
+
recommendation: "Replace ${BUNDLE_ROOT} with ${__dirname} - BUNDLE_ROOT is not supported",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Check for absolute Unix paths (excluding __dirname prefixed)
|
|
149
|
+
const cleanLine = line.replace(/\$\{__dirname\}/g, ""); // Remove __dirname to avoid false positives
|
|
150
|
+
const unixPathMatches = cleanLine.match(ISSUE_PATTERNS.absoluteUnixPath);
|
|
151
|
+
if (unixPathMatches) {
|
|
152
|
+
for (const match of unixPathMatches) {
|
|
153
|
+
// Skip if it looks like a URL or comment
|
|
154
|
+
if (match.includes("://") ||
|
|
155
|
+
line.trim().startsWith("//") ||
|
|
156
|
+
line.trim().startsWith("*")) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
issues.push({
|
|
160
|
+
type: "absolute_path",
|
|
161
|
+
filePath,
|
|
162
|
+
lineNumber,
|
|
163
|
+
matchedText: match,
|
|
164
|
+
severity: "HIGH",
|
|
165
|
+
recommendation: "Use relative paths or ${__dirname} for bundle portability",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Check for absolute Windows paths
|
|
170
|
+
const windowsPathMatches = line.match(ISSUE_PATTERNS.absoluteWindowsPath);
|
|
171
|
+
if (windowsPathMatches) {
|
|
172
|
+
for (const match of windowsPathMatches) {
|
|
173
|
+
issues.push({
|
|
174
|
+
type: "absolute_path",
|
|
175
|
+
filePath,
|
|
176
|
+
lineNumber,
|
|
177
|
+
matchedText: match,
|
|
178
|
+
severity: "HIGH",
|
|
179
|
+
recommendation: "Use relative paths or path.join() for cross-platform support",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Check for user home paths
|
|
184
|
+
const homePathMatches = line.match(ISSUE_PATTERNS.userHomePath);
|
|
185
|
+
if (homePathMatches) {
|
|
186
|
+
for (const match of homePathMatches) {
|
|
187
|
+
// Skip if in a comment
|
|
188
|
+
if (line.trim().startsWith("//") ||
|
|
189
|
+
line.trim().startsWith("*") ||
|
|
190
|
+
line.trim().startsWith("#")) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
issues.push({
|
|
194
|
+
type: "user_home_path",
|
|
195
|
+
filePath,
|
|
196
|
+
lineNumber,
|
|
197
|
+
matchedText: match,
|
|
198
|
+
severity: "MEDIUM",
|
|
199
|
+
recommendation: "Use os.homedir() or environment variable for user home paths",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Check for platform-specific code without apparent fallback
|
|
204
|
+
const platformChecks = [
|
|
205
|
+
ISSUE_PATTERNS.platformSpecificDarwin,
|
|
206
|
+
ISSUE_PATTERNS.platformSpecificWin32,
|
|
207
|
+
ISSUE_PATTERNS.platformSpecificLinux,
|
|
208
|
+
];
|
|
209
|
+
for (const pattern of platformChecks) {
|
|
210
|
+
const matches = line.match(pattern);
|
|
211
|
+
if (matches) {
|
|
212
|
+
// Check if there's a fallback (else clause or default case)
|
|
213
|
+
const hasElse = content
|
|
214
|
+
.substring(content.indexOf(line))
|
|
215
|
+
.includes("else");
|
|
216
|
+
const hasDefault = content
|
|
217
|
+
.substring(content.indexOf(line))
|
|
218
|
+
.includes("default:");
|
|
219
|
+
if (!hasElse && !hasDefault) {
|
|
220
|
+
for (const match of matches) {
|
|
221
|
+
issues.push({
|
|
222
|
+
type: "platform_specific",
|
|
223
|
+
filePath,
|
|
224
|
+
lineNumber,
|
|
225
|
+
matchedText: match,
|
|
226
|
+
severity: "LOW",
|
|
227
|
+
recommendation: "Consider adding fallback for other platforms",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return issues;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Check if file should be skipped
|
|
238
|
+
*/
|
|
239
|
+
shouldSkipFile(filePath) {
|
|
240
|
+
const skipPatterns = [
|
|
241
|
+
/node_modules/,
|
|
242
|
+
/\.test\.(ts|js|tsx|jsx)$/,
|
|
243
|
+
/\.spec\.(ts|js|tsx|jsx)$/,
|
|
244
|
+
/\.d\.ts$/,
|
|
245
|
+
/package-lock\.json$/,
|
|
246
|
+
/yarn\.lock$/,
|
|
247
|
+
/\.map$/,
|
|
248
|
+
/\.min\.(js|css)$/,
|
|
249
|
+
/README\.md$/i,
|
|
250
|
+
/CHANGELOG\.md$/i,
|
|
251
|
+
/LICENSE/i,
|
|
252
|
+
];
|
|
253
|
+
return skipPatterns.some((pattern) => pattern.test(filePath));
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Determine overall status
|
|
257
|
+
*/
|
|
258
|
+
determinePortabilityStatus(issues, usesDirname, usesBundleRoot) {
|
|
259
|
+
// ${BUNDLE_ROOT} usage = automatic FAIL
|
|
260
|
+
if (usesBundleRoot) {
|
|
261
|
+
return "FAIL";
|
|
262
|
+
}
|
|
263
|
+
// HIGH severity issues = FAIL
|
|
264
|
+
const highIssues = issues.filter((i) => i.severity === "HIGH");
|
|
265
|
+
if (highIssues.length > 0) {
|
|
266
|
+
return "FAIL";
|
|
267
|
+
}
|
|
268
|
+
// MEDIUM severity issues = NEED_MORE_INFO
|
|
269
|
+
const mediumIssues = issues.filter((i) => i.severity === "MEDIUM");
|
|
270
|
+
if (mediumIssues.length > 0) {
|
|
271
|
+
return "NEED_MORE_INFO";
|
|
272
|
+
}
|
|
273
|
+
// Uses ${__dirname} is a positive signal
|
|
274
|
+
if (usesDirname && issues.length === 0) {
|
|
275
|
+
return "PASS";
|
|
276
|
+
}
|
|
277
|
+
// LOW severity issues only
|
|
278
|
+
if (issues.length > 0) {
|
|
279
|
+
return "NEED_MORE_INFO";
|
|
280
|
+
}
|
|
281
|
+
return "PASS";
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Generate explanation
|
|
285
|
+
*/
|
|
286
|
+
generateExplanation(issues, usesDirname, usesBundleRoot, scannedFiles) {
|
|
287
|
+
const parts = [];
|
|
288
|
+
if (usesBundleRoot) {
|
|
289
|
+
parts.push("CRITICAL: Uses ${BUNDLE_ROOT} which is not supported in MCPB bundles.");
|
|
290
|
+
}
|
|
291
|
+
if (issues.length === 0) {
|
|
292
|
+
parts.push("No portability issues detected.");
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
const highCount = issues.filter((i) => i.severity === "HIGH").length;
|
|
296
|
+
const mediumCount = issues.filter((i) => i.severity === "MEDIUM").length;
|
|
297
|
+
if (highCount > 0) {
|
|
298
|
+
parts.push(`${highCount} high-severity portability issue(s) found.`);
|
|
299
|
+
}
|
|
300
|
+
if (mediumCount > 0) {
|
|
301
|
+
parts.push(`${mediumCount} medium-severity issue(s) found.`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (usesDirname) {
|
|
305
|
+
parts.push("Uses ${__dirname} correctly for relative paths.");
|
|
306
|
+
}
|
|
307
|
+
parts.push(`Scanned ${scannedFiles} file(s).`);
|
|
308
|
+
return parts.join(" ");
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Generate recommendations
|
|
312
|
+
*/
|
|
313
|
+
generateRecommendations(issues, usesDirname, usesBundleRoot) {
|
|
314
|
+
const recommendations = [];
|
|
315
|
+
if (usesBundleRoot) {
|
|
316
|
+
recommendations.push("CRITICAL: Replace all ${BUNDLE_ROOT} with ${__dirname} - BUNDLE_ROOT variable is not supported in MCPB bundles.");
|
|
317
|
+
}
|
|
318
|
+
// Group issues by type
|
|
319
|
+
const byType = new Map();
|
|
320
|
+
for (const issue of issues) {
|
|
321
|
+
const existing = byType.get(issue.type) || [];
|
|
322
|
+
existing.push(issue);
|
|
323
|
+
byType.set(issue.type, existing);
|
|
324
|
+
}
|
|
325
|
+
// Add recommendations by type
|
|
326
|
+
for (const [type, typeIssues] of byType) {
|
|
327
|
+
if (type === "bundle_root_antipattern")
|
|
328
|
+
continue; // Already handled
|
|
329
|
+
const first = typeIssues[0];
|
|
330
|
+
if (typeIssues.length === 1) {
|
|
331
|
+
recommendations.push(`${first.filePath}:${first.lineNumber}: ${first.recommendation}`);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
recommendations.push(`${typeIssues.length} ${type.replace(/_/g, " ")} issues: ${first.recommendation}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (recommendations.length === 0) {
|
|
338
|
+
if (usesDirname) {
|
|
339
|
+
recommendations.push("Server uses proper relative paths with ${__dirname}. Good portability.");
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
recommendations.push("Consider using ${__dirname} for paths in manifest.json for better portability.");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return recommendations;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prohibited Libraries Assessor
|
|
3
|
+
* Detects financial and media processing libraries per Policy #28-30
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* - package.json dependencies
|
|
7
|
+
* - requirements.txt (Python)
|
|
8
|
+
* - Source code imports (if sourceCodePath provided)
|
|
9
|
+
*
|
|
10
|
+
* Reference: Anthropic MCP Directory Policy #28-30
|
|
11
|
+
*/
|
|
12
|
+
import { BaseAssessor } from "./BaseAssessor.js";
|
|
13
|
+
import { AssessmentContext } from "../AssessmentOrchestrator.js";
|
|
14
|
+
import type { ProhibitedLibrariesAssessment } from "../../../lib/assessmentTypes.js";
|
|
15
|
+
export declare class ProhibitedLibrariesAssessor extends BaseAssessor {
|
|
16
|
+
/**
|
|
17
|
+
* Run prohibited libraries assessment
|
|
18
|
+
*/
|
|
19
|
+
assess(context: AssessmentContext): Promise<ProhibitedLibrariesAssessment>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if file is a source file worth scanning
|
|
22
|
+
*/
|
|
23
|
+
private isSourceFile;
|
|
24
|
+
/**
|
|
25
|
+
* De-duplicate matches, keeping the most severe
|
|
26
|
+
*/
|
|
27
|
+
private deduplicateMatches;
|
|
28
|
+
/**
|
|
29
|
+
* Calculate overall status from matches
|
|
30
|
+
*/
|
|
31
|
+
private calculateStatusFromMatches;
|
|
32
|
+
/**
|
|
33
|
+
* Generate explanation
|
|
34
|
+
*/
|
|
35
|
+
private generateExplanation;
|
|
36
|
+
/**
|
|
37
|
+
* Generate recommendations
|
|
38
|
+
*/
|
|
39
|
+
private generateRecommendations;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=ProhibitedLibrariesAssessor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProhibitedLibrariesAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ProhibitedLibrariesAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,6BAA6B,EAG9B,MAAM,uBAAuB,CAAC;AAO/B,qBAAa,2BAA4B,SAAQ,YAAY;IAC3D;;OAEG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,6BAA6B,CAAC;IA4IzC;;OAEG;IACH,OAAO,CAAC,YAAY;IA0BpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAqB1B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAuBlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAoD3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CAqDhC"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prohibited Libraries Assessor
|
|
3
|
+
* Detects financial and media processing libraries per Policy #28-30
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* - package.json dependencies
|
|
7
|
+
* - requirements.txt (Python)
|
|
8
|
+
* - Source code imports (if sourceCodePath provided)
|
|
9
|
+
*
|
|
10
|
+
* Reference: Anthropic MCP Directory Policy #28-30
|
|
11
|
+
*/
|
|
12
|
+
import { BaseAssessor } from "./BaseAssessor.js";
|
|
13
|
+
import { checkPackageJsonDependencies, checkRequirementsTxt, checkSourceImports, } from "../../../lib/prohibitedLibraries.js";
|
|
14
|
+
export class ProhibitedLibrariesAssessor extends BaseAssessor {
|
|
15
|
+
/**
|
|
16
|
+
* Run prohibited libraries assessment
|
|
17
|
+
*/
|
|
18
|
+
async assess(context) {
|
|
19
|
+
this.log("Starting prohibited libraries assessment");
|
|
20
|
+
this.testCount = 0;
|
|
21
|
+
const matches = [];
|
|
22
|
+
const scannedFiles = [];
|
|
23
|
+
let hasFinancialLibraries = false;
|
|
24
|
+
let hasMediaLibraries = false;
|
|
25
|
+
// Check package.json dependencies
|
|
26
|
+
if (context.packageJson) {
|
|
27
|
+
this.log("Scanning package.json dependencies...");
|
|
28
|
+
this.testCount++;
|
|
29
|
+
scannedFiles.push("package.json");
|
|
30
|
+
const packageJson = context.packageJson;
|
|
31
|
+
const depMatches = checkPackageJsonDependencies(packageJson);
|
|
32
|
+
for (const match of depMatches) {
|
|
33
|
+
matches.push({
|
|
34
|
+
name: match.library.name,
|
|
35
|
+
category: match.library.category,
|
|
36
|
+
location: "package.json",
|
|
37
|
+
severity: match.library.severity,
|
|
38
|
+
reason: match.library.reason,
|
|
39
|
+
policyReference: match.library.policyReference,
|
|
40
|
+
});
|
|
41
|
+
if (match.library.category === "financial" ||
|
|
42
|
+
match.library.category === "payments" ||
|
|
43
|
+
match.library.category === "banking") {
|
|
44
|
+
hasFinancialLibraries = true;
|
|
45
|
+
}
|
|
46
|
+
if (match.library.category === "media") {
|
|
47
|
+
hasMediaLibraries = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Check source code files if available
|
|
52
|
+
if (context.sourceCodeFiles && context.config.enableSourceCodeAnalysis) {
|
|
53
|
+
this.log("Scanning source code files...");
|
|
54
|
+
for (const [filePath, content] of context.sourceCodeFiles) {
|
|
55
|
+
// Check Python requirements files
|
|
56
|
+
if (filePath.endsWith("requirements.txt") ||
|
|
57
|
+
filePath.endsWith("requirements-dev.txt")) {
|
|
58
|
+
this.testCount++;
|
|
59
|
+
scannedFiles.push(filePath);
|
|
60
|
+
const reqMatches = checkRequirementsTxt(content);
|
|
61
|
+
for (const match of reqMatches) {
|
|
62
|
+
matches.push({
|
|
63
|
+
name: match.library.name,
|
|
64
|
+
category: match.library.category,
|
|
65
|
+
location: "requirements.txt",
|
|
66
|
+
filePath,
|
|
67
|
+
lineNumber: match.lineNumber,
|
|
68
|
+
severity: match.library.severity,
|
|
69
|
+
reason: match.library.reason,
|
|
70
|
+
policyReference: match.library.policyReference,
|
|
71
|
+
});
|
|
72
|
+
if (match.library.category === "financial" ||
|
|
73
|
+
match.library.category === "payments" ||
|
|
74
|
+
match.library.category === "banking") {
|
|
75
|
+
hasFinancialLibraries = true;
|
|
76
|
+
}
|
|
77
|
+
if (match.library.category === "media") {
|
|
78
|
+
hasMediaLibraries = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Check source code imports
|
|
83
|
+
if (this.isSourceFile(filePath)) {
|
|
84
|
+
this.testCount++;
|
|
85
|
+
scannedFiles.push(filePath);
|
|
86
|
+
const importMatches = checkSourceImports(content);
|
|
87
|
+
for (const match of importMatches) {
|
|
88
|
+
matches.push({
|
|
89
|
+
name: match.library.name,
|
|
90
|
+
category: match.library.category,
|
|
91
|
+
location: "source_import",
|
|
92
|
+
filePath,
|
|
93
|
+
lineNumber: match.lineNumber,
|
|
94
|
+
severity: match.library.severity,
|
|
95
|
+
reason: match.library.reason,
|
|
96
|
+
policyReference: match.library.policyReference,
|
|
97
|
+
});
|
|
98
|
+
if (match.library.category === "financial" ||
|
|
99
|
+
match.library.category === "payments" ||
|
|
100
|
+
match.library.category === "banking") {
|
|
101
|
+
hasFinancialLibraries = true;
|
|
102
|
+
}
|
|
103
|
+
if (match.library.category === "media") {
|
|
104
|
+
hasMediaLibraries = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// De-duplicate matches by library name
|
|
111
|
+
const uniqueMatches = this.deduplicateMatches(matches);
|
|
112
|
+
const status = this.calculateStatusFromMatches(uniqueMatches);
|
|
113
|
+
const explanation = this.generateExplanation(uniqueMatches, hasFinancialLibraries, hasMediaLibraries, scannedFiles);
|
|
114
|
+
const recommendations = this.generateRecommendations(uniqueMatches);
|
|
115
|
+
this.log(`Assessment complete: ${uniqueMatches.length} prohibited libraries found`);
|
|
116
|
+
return {
|
|
117
|
+
matches: uniqueMatches,
|
|
118
|
+
scannedFiles,
|
|
119
|
+
hasFinancialLibraries,
|
|
120
|
+
hasMediaLibraries,
|
|
121
|
+
status,
|
|
122
|
+
explanation,
|
|
123
|
+
recommendations,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if file is a source file worth scanning
|
|
128
|
+
*/
|
|
129
|
+
isSourceFile(filePath) {
|
|
130
|
+
const sourceExtensions = [
|
|
131
|
+
".ts",
|
|
132
|
+
".tsx",
|
|
133
|
+
".js",
|
|
134
|
+
".jsx",
|
|
135
|
+
".mjs",
|
|
136
|
+
".cjs",
|
|
137
|
+
".py",
|
|
138
|
+
".rs",
|
|
139
|
+
".go",
|
|
140
|
+
];
|
|
141
|
+
// Skip test files and node_modules
|
|
142
|
+
if (filePath.includes("node_modules") ||
|
|
143
|
+
filePath.includes(".test.") ||
|
|
144
|
+
filePath.includes(".spec.") ||
|
|
145
|
+
filePath.includes("__tests__")) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return sourceExtensions.some((ext) => filePath.endsWith(ext));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* De-duplicate matches, keeping the most severe
|
|
152
|
+
*/
|
|
153
|
+
deduplicateMatches(matches) {
|
|
154
|
+
const byName = new Map();
|
|
155
|
+
for (const match of matches) {
|
|
156
|
+
const existing = byName.get(match.name);
|
|
157
|
+
if (!existing) {
|
|
158
|
+
byName.set(match.name, match);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Keep the more severe match
|
|
162
|
+
const severityOrder = { BLOCKING: 3, HIGH: 2, MEDIUM: 1 };
|
|
163
|
+
if (severityOrder[match.severity] > severityOrder[existing.severity]) {
|
|
164
|
+
byName.set(match.name, match);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return Array.from(byName.values());
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Calculate overall status from matches
|
|
172
|
+
*/
|
|
173
|
+
calculateStatusFromMatches(matches) {
|
|
174
|
+
// Any BLOCKING library = FAIL
|
|
175
|
+
const blockingMatches = matches.filter((m) => m.severity === "BLOCKING");
|
|
176
|
+
if (blockingMatches.length > 0) {
|
|
177
|
+
return "FAIL";
|
|
178
|
+
}
|
|
179
|
+
// HIGH severity = NEED_MORE_INFO (requires justification)
|
|
180
|
+
const highMatches = matches.filter((m) => m.severity === "HIGH");
|
|
181
|
+
if (highMatches.length > 0) {
|
|
182
|
+
return "NEED_MORE_INFO";
|
|
183
|
+
}
|
|
184
|
+
// MEDIUM severity = PASS with notes
|
|
185
|
+
if (matches.length > 0) {
|
|
186
|
+
return "NEED_MORE_INFO";
|
|
187
|
+
}
|
|
188
|
+
return "PASS";
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Generate explanation
|
|
192
|
+
*/
|
|
193
|
+
generateExplanation(matches, hasFinancial, hasMedia, scannedFiles) {
|
|
194
|
+
const parts = [];
|
|
195
|
+
if (matches.length === 0) {
|
|
196
|
+
parts.push("No prohibited libraries detected. Server appears compliant with Policy #28-30.");
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const blockingCount = matches.filter((m) => m.severity === "BLOCKING").length;
|
|
200
|
+
const highCount = matches.filter((m) => m.severity === "HIGH").length;
|
|
201
|
+
const mediumCount = matches.filter((m) => m.severity === "MEDIUM").length;
|
|
202
|
+
if (blockingCount > 0) {
|
|
203
|
+
parts.push(`BLOCKING: ${blockingCount} prohibited library/libraries detected that violate MCP Directory policy.`);
|
|
204
|
+
}
|
|
205
|
+
if (highCount > 0) {
|
|
206
|
+
parts.push(`HIGH: ${highCount} library/libraries detected that require justification for MCP server use.`);
|
|
207
|
+
}
|
|
208
|
+
if (mediumCount > 0) {
|
|
209
|
+
parts.push(`MEDIUM: ${mediumCount} library/libraries flagged for review.`);
|
|
210
|
+
}
|
|
211
|
+
if (hasFinancial) {
|
|
212
|
+
parts.push("Financial/payment processing libraries detected - violates Policy #28-29.");
|
|
213
|
+
}
|
|
214
|
+
if (hasMedia) {
|
|
215
|
+
parts.push("Media processing libraries detected - may require justification per Policy #30.");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
parts.push(`Scanned ${scannedFiles.length} file(s).`);
|
|
219
|
+
return parts.join(" ");
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Generate recommendations
|
|
223
|
+
*/
|
|
224
|
+
generateRecommendations(matches) {
|
|
225
|
+
const recommendations = [];
|
|
226
|
+
// Group by severity
|
|
227
|
+
const blocking = matches.filter((m) => m.severity === "BLOCKING");
|
|
228
|
+
const high = matches.filter((m) => m.severity === "HIGH");
|
|
229
|
+
const medium = matches.filter((m) => m.severity === "MEDIUM");
|
|
230
|
+
if (blocking.length > 0) {
|
|
231
|
+
recommendations.push("BLOCKING - The following libraries must be removed for MCP Directory approval:");
|
|
232
|
+
for (const match of blocking) {
|
|
233
|
+
recommendations.push(`- ${match.name} (${match.policyReference}): ${match.reason}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (high.length > 0) {
|
|
237
|
+
recommendations.push("HIGH - The following libraries require strong justification:");
|
|
238
|
+
for (const match of high) {
|
|
239
|
+
recommendations.push(`- ${match.name} (${match.policyReference}): ${match.reason}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (medium.length > 0) {
|
|
243
|
+
recommendations.push("MEDIUM - Review the following libraries for necessity:");
|
|
244
|
+
for (const match of medium.slice(0, 3)) {
|
|
245
|
+
recommendations.push(`- ${match.name} (${match.policyReference}): ${match.reason}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (matches.length === 0) {
|
|
249
|
+
recommendations.push("No prohibited libraries detected. Server is compliant with library restrictions.");
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
recommendations.push("Reference: MCP Directory Policy #28-30 restricts financial transaction and media processing libraries.");
|
|
253
|
+
}
|
|
254
|
+
return recommendations;
|
|
255
|
+
}
|
|
256
|
+
}
|