@bryan-thompson/inspector-assessment-client 1.13.0 → 1.14.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.
Files changed (32) hide show
  1. package/dist/assets/{OAuthCallback-D8KW6pFf.js → OAuthCallback-DaKwjxdn.js} +1 -1
  2. package/dist/assets/{OAuthDebugCallback-D15nNAOl.js → OAuthDebugCallback-HiI2IPgB.js} +1 -1
  3. package/dist/assets/{index-cVkEgqCc.js → index-BAJG90Yd.js} +20 -8
  4. package/dist/assets/{index-Cuc9GxjD.css → index-BdXNC65t.css} +3 -0
  5. package/dist/index.html +2 -2
  6. package/lib/lib/assessmentDiffer.d.ts +79 -0
  7. package/lib/lib/assessmentDiffer.d.ts.map +1 -0
  8. package/lib/lib/assessmentDiffer.js +289 -0
  9. package/lib/lib/assessmentTypes.d.ts +69 -0
  10. package/lib/lib/assessmentTypes.d.ts.map +1 -1
  11. package/lib/lib/assessmentTypes.js +10 -0
  12. package/lib/lib/reportFormatters/DiffReportFormatter.d.ts +10 -0
  13. package/lib/lib/reportFormatters/DiffReportFormatter.d.ts.map +1 -0
  14. package/lib/lib/reportFormatters/DiffReportFormatter.js +177 -0
  15. package/lib/services/assessment/AssessmentOrchestrator.d.ts +1 -0
  16. package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
  17. package/lib/services/assessment/AssessmentOrchestrator.js +22 -1
  18. package/lib/services/assessment/modules/AuthenticationAssessor.d.ts +48 -0
  19. package/lib/services/assessment/modules/AuthenticationAssessor.d.ts.map +1 -0
  20. package/lib/services/assessment/modules/AuthenticationAssessor.js +270 -0
  21. package/lib/services/assessment/modules/ExternalAPIScannerAssessor.d.ts +58 -0
  22. package/lib/services/assessment/modules/ExternalAPIScannerAssessor.d.ts.map +1 -0
  23. package/lib/services/assessment/modules/ExternalAPIScannerAssessor.js +248 -0
  24. package/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
  25. package/lib/services/assessment/modules/FunctionalityAssessor.js +7 -0
  26. package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +4 -0
  27. package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -1
  28. package/lib/services/assessment/modules/ManifestValidationAssessor.js +118 -4
  29. package/lib/services/assessment/modules/index.d.ts +1 -0
  30. package/lib/services/assessment/modules/index.d.ts.map +1 -1
  31. package/lib/services/assessment/modules/index.js +1 -0
  32. package/package.json +1 -1
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Authentication Assessor
3
+ * Evaluates if OAuth is appropriate for the deployment model (local vs remote).
4
+ *
5
+ * Detection Logic:
6
+ * 1. Check if server uses OAuth (serverInfo/manifest)
7
+ * 2. Analyze if tools access local resources (files, apps, OS features)
8
+ * 3. If OAuth + no local deps = recommend cloud deployment
9
+ * 4. If OAuth + local deps = warn about mixed model
10
+ */
11
+ import { BaseAssessor } from "./BaseAssessor.js";
12
+ // Patterns that indicate OAuth usage
13
+ const OAUTH_PATTERNS = [
14
+ /oauth/i,
15
+ /authorization\s*code/i,
16
+ /access_token/i,
17
+ /refresh_token/i,
18
+ /client_id/i,
19
+ /client_secret/i,
20
+ /pkce/i,
21
+ /code_verifier/i,
22
+ /code_challenge/i,
23
+ /authorize\s*url/i,
24
+ /token\s*endpoint/i,
25
+ ];
26
+ // Patterns that indicate API key usage
27
+ const API_KEY_PATTERNS = [
28
+ /api[_-]?key/i,
29
+ /x-api-key/i,
30
+ /bearer\s+token/i,
31
+ /authorization:\s*bearer/i,
32
+ /secret[_-]?key/i,
33
+ ];
34
+ // Patterns that indicate local resource dependencies
35
+ const LOCAL_RESOURCE_PATTERNS = [
36
+ /process\.cwd/i,
37
+ /fs\.(read|write|exists|mkdir|rmdir)/i,
38
+ /child_process/i,
39
+ /execSync|spawnSync/i,
40
+ /os\.(homedir|tmpdir|platform)/i,
41
+ /path\.(join|resolve|dirname)/i,
42
+ /__dirname|__filename/i,
43
+ /\.local|\.config|\.cache/i,
44
+ /localhost|127\.0\.0\.1/i,
45
+ /file:\/\//i,
46
+ ];
47
+ export class AuthenticationAssessor extends BaseAssessor {
48
+ /**
49
+ * Run authentication assessment
50
+ */
51
+ async assess(context) {
52
+ this.log("Starting authentication assessment");
53
+ this.testCount = 0;
54
+ const oauthIndicators = [];
55
+ const localResourceIndicators = [];
56
+ const apiKeyIndicators = [];
57
+ // Analyze source code for patterns
58
+ if (context.sourceCodeFiles) {
59
+ for (const [filePath, content] of context.sourceCodeFiles) {
60
+ this.testCount++;
61
+ // Check for OAuth patterns
62
+ for (const pattern of OAUTH_PATTERNS) {
63
+ if (pattern.test(content)) {
64
+ const indicator = `${filePath}: ${pattern.source}`;
65
+ if (!oauthIndicators.includes(indicator)) {
66
+ oauthIndicators.push(indicator);
67
+ }
68
+ }
69
+ }
70
+ // Check for API key patterns
71
+ for (const pattern of API_KEY_PATTERNS) {
72
+ if (pattern.test(content)) {
73
+ const indicator = `${filePath}: ${pattern.source}`;
74
+ if (!apiKeyIndicators.includes(indicator)) {
75
+ apiKeyIndicators.push(indicator);
76
+ }
77
+ }
78
+ }
79
+ // Check for local resource patterns
80
+ for (const pattern of LOCAL_RESOURCE_PATTERNS) {
81
+ if (pattern.test(content)) {
82
+ const indicator = `${filePath}: ${pattern.source}`;
83
+ if (!localResourceIndicators.includes(indicator)) {
84
+ localResourceIndicators.push(indicator);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ // Determine auth method
91
+ const authMethod = this.detectAuthMethod(oauthIndicators, apiKeyIndicators, context);
92
+ // Determine if there are local dependencies
93
+ const hasLocalDependencies = localResourceIndicators.length > 0;
94
+ // Get transport type
95
+ const transportType = this.detectTransportType(context);
96
+ // Evaluate appropriateness
97
+ const appropriateness = this.evaluateAppropriateness({
98
+ authMethod,
99
+ hasLocalDependencies,
100
+ transportType,
101
+ oauthIndicators,
102
+ });
103
+ const recommendation = this.generateRecommendation(authMethod, hasLocalDependencies, transportType, appropriateness);
104
+ const status = this.evaluateStatus(appropriateness);
105
+ const explanation = this.generateExplanation(authMethod, hasLocalDependencies, transportType, appropriateness);
106
+ const recommendations = this.generateRecommendations(authMethod, hasLocalDependencies, appropriateness);
107
+ this.log(`Assessment complete: auth=${authMethod}, localDeps=${hasLocalDependencies}`);
108
+ return {
109
+ authMethod,
110
+ hasLocalDependencies,
111
+ transportType,
112
+ appropriateness,
113
+ recommendation,
114
+ detectedPatterns: {
115
+ oauthIndicators,
116
+ localResourceIndicators,
117
+ apiKeyIndicators,
118
+ },
119
+ status,
120
+ explanation,
121
+ recommendations,
122
+ };
123
+ }
124
+ /**
125
+ * Detect authentication method
126
+ */
127
+ detectAuthMethod(oauthIndicators, apiKeyIndicators, context) {
128
+ // Check manifest for OAuth configuration
129
+ if (context.manifestJson) {
130
+ const manifestStr = JSON.stringify(context.manifestJson);
131
+ if (/oauth/i.test(manifestStr)) {
132
+ return "oauth";
133
+ }
134
+ }
135
+ // Check for OAuth patterns in code
136
+ if (oauthIndicators.length >= 3) {
137
+ return "oauth";
138
+ }
139
+ // Check for API key patterns
140
+ if (apiKeyIndicators.length >= 2) {
141
+ return "api_key";
142
+ }
143
+ // If we found some indicators but not enough to be confident
144
+ if (oauthIndicators.length > 0 || apiKeyIndicators.length > 0) {
145
+ return "unknown";
146
+ }
147
+ return "none";
148
+ }
149
+ /**
150
+ * Detect transport type from context
151
+ */
152
+ detectTransportType(context) {
153
+ // Check config for transport
154
+ if (context.config) {
155
+ const config = context.config;
156
+ if (config.transport) {
157
+ return String(config.transport);
158
+ }
159
+ }
160
+ // Default to stdio for local servers
161
+ return "stdio";
162
+ }
163
+ /**
164
+ * Evaluate if authentication setup is appropriate
165
+ */
166
+ evaluateAppropriateness(params) {
167
+ const { authMethod, hasLocalDependencies, transportType } = params;
168
+ const concerns = [];
169
+ let isAppropriate = true;
170
+ // OAuth on local stdio server with local dependencies is concerning
171
+ if (authMethod === "oauth" &&
172
+ hasLocalDependencies &&
173
+ transportType === "stdio") {
174
+ concerns.push("OAuth authentication detected on local stdio server with file system dependencies");
175
+ concerns.push("Consider: Is OAuth necessary for a local-only server that accesses local resources?");
176
+ isAppropriate = false;
177
+ }
178
+ // OAuth on stdio without local deps might suggest it should be remote
179
+ if (authMethod === "oauth" &&
180
+ !hasLocalDependencies &&
181
+ transportType === "stdio") {
182
+ concerns.push("OAuth detected on stdio transport but no local resource dependencies found");
183
+ concerns.push("This server might be better suited for remote deployment");
184
+ }
185
+ // API key on remote server without proper security
186
+ if (authMethod === "api_key" && transportType !== "stdio") {
187
+ concerns.push("API key authentication on remote transport - ensure HTTPS is enforced");
188
+ }
189
+ // No auth on remote server is a concern
190
+ if (authMethod === "none" && transportType !== "stdio") {
191
+ concerns.push("No authentication detected on remote transport");
192
+ concerns.push("Consider adding authentication for production use");
193
+ isAppropriate = false;
194
+ }
195
+ const explanation = concerns.length > 0
196
+ ? `Found ${concerns.length} concern(s) with authentication setup`
197
+ : "Authentication configuration appears appropriate for deployment model";
198
+ return { isAppropriate, concerns, explanation };
199
+ }
200
+ /**
201
+ * Generate recommendation based on analysis
202
+ */
203
+ generateRecommendation(authMethod, hasLocalDependencies, transportType, appropriateness) {
204
+ if (appropriateness.isAppropriate) {
205
+ return "Authentication configuration is appropriate for the deployment model";
206
+ }
207
+ if (authMethod === "oauth" && !hasLocalDependencies) {
208
+ return "Consider deploying as a remote server if OAuth is required - no local dependencies detected";
209
+ }
210
+ if (authMethod === "oauth" && hasLocalDependencies) {
211
+ return "Review if OAuth is necessary - server has local dependencies suggesting local-only usage";
212
+ }
213
+ if (authMethod === "none" && transportType !== "stdio") {
214
+ return "Add authentication for remote deployment to prevent unauthorized access";
215
+ }
216
+ return "Review authentication configuration for production deployment";
217
+ }
218
+ /**
219
+ * Evaluate overall status based on appropriateness
220
+ */
221
+ evaluateStatus(appropriateness) {
222
+ if (!appropriateness.isAppropriate) {
223
+ return "NEED_MORE_INFO";
224
+ }
225
+ if (appropriateness.concerns.length > 0) {
226
+ return "NEED_MORE_INFO";
227
+ }
228
+ return "PASS";
229
+ }
230
+ /**
231
+ * Generate explanation
232
+ */
233
+ generateExplanation(authMethod, hasLocalDependencies, transportType, appropriateness) {
234
+ const parts = [];
235
+ parts.push(`Detected authentication: ${authMethod}`);
236
+ parts.push(`Transport: ${transportType}`);
237
+ parts.push(`Local dependencies: ${hasLocalDependencies ? "Yes" : "No"}`);
238
+ if (appropriateness.concerns.length > 0) {
239
+ parts.push(`Concerns: ${appropriateness.concerns.length}`);
240
+ }
241
+ return parts.join(". ");
242
+ }
243
+ /**
244
+ * Generate recommendations
245
+ */
246
+ generateRecommendations(authMethod, hasLocalDependencies, appropriateness) {
247
+ const recommendations = [];
248
+ if (!appropriateness.isAppropriate) {
249
+ recommendations.push("REVIEW - Authentication configuration:");
250
+ for (const concern of appropriateness.concerns.slice(0, 3)) {
251
+ recommendations.push(` - ${concern}`);
252
+ }
253
+ }
254
+ if (authMethod === "oauth") {
255
+ recommendations.push("Ensure OAuth configuration follows RFC 8707");
256
+ recommendations.push("Verify PKCE is implemented for public clients");
257
+ }
258
+ if (authMethod === "api_key") {
259
+ recommendations.push("Ensure API keys are not hardcoded");
260
+ recommendations.push("Use environment variables for secrets");
261
+ }
262
+ if (authMethod === "none" && !hasLocalDependencies) {
263
+ recommendations.push("Consider adding authentication if server will be remotely accessible");
264
+ }
265
+ if (recommendations.length === 0) {
266
+ recommendations.push("Authentication configuration appears appropriate");
267
+ }
268
+ return recommendations;
269
+ }
270
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * External API Scanner Assessor
3
+ *
4
+ * Scans source code for external API dependencies and checks for affiliation.
5
+ * Helps identify:
6
+ * - External services used (GitHub, Slack, AWS, etc.)
7
+ * - Hardcoded URLs that may need privacy policy disclosure
8
+ * - Server names that suggest affiliation without verification
9
+ *
10
+ * Part of Priority 2 gap-closing features.
11
+ */
12
+ import { BaseAssessor } from "./BaseAssessor.js";
13
+ import type { AssessmentContext } from "../AssessmentOrchestrator.js";
14
+ import type { ExternalAPIScannerAssessment } from "../../../lib/assessmentTypes.js";
15
+ export declare class ExternalAPIScannerAssessor extends BaseAssessor {
16
+ assess(context: AssessmentContext): Promise<ExternalAPIScannerAssessment>;
17
+ /**
18
+ * Check if file should be skipped
19
+ */
20
+ private shouldSkipFile;
21
+ /**
22
+ * Scan a file for external API URLs
23
+ */
24
+ private scanFileForAPIs;
25
+ /**
26
+ * Check if URL should be skipped
27
+ */
28
+ private shouldSkipUrl;
29
+ /**
30
+ * Clean URL by removing trailing punctuation
31
+ */
32
+ private cleanUrl;
33
+ /**
34
+ * Identify which service a URL belongs to
35
+ */
36
+ private identifyService;
37
+ /**
38
+ * Check if server name suggests affiliation that needs verification
39
+ */
40
+ private checkAffiliation;
41
+ /**
42
+ * Compute assessment status based on scan results
43
+ */
44
+ private computeStatus;
45
+ /**
46
+ * Generate explanation text
47
+ */
48
+ private generateExplanation;
49
+ /**
50
+ * Generate recommendations
51
+ */
52
+ private generateRecommendations;
53
+ /**
54
+ * Create result when source code analysis is not available
55
+ */
56
+ private createNoSourceResult;
57
+ }
58
+ //# sourceMappingURL=ExternalAPIScannerAssessor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExternalAPIScannerAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ExternalAPIScannerAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAEV,4BAA4B,EAE7B,MAAM,uBAAuB,CAAC;AAmE/B,qBAAa,0BAA2B,SAAQ,YAAY;IACpD,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,4BAA4B,CAAC;IA6DxC;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,eAAe;IA0BvB;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;OAEG;IACH,OAAO,CAAC,aAAa;IAcrB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA2B/B;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAa7B"}
@@ -0,0 +1,248 @@
1
+ /**
2
+ * External API Scanner Assessor
3
+ *
4
+ * Scans source code for external API dependencies and checks for affiliation.
5
+ * Helps identify:
6
+ * - External services used (GitHub, Slack, AWS, etc.)
7
+ * - Hardcoded URLs that may need privacy policy disclosure
8
+ * - Server names that suggest affiliation without verification
9
+ *
10
+ * Part of Priority 2 gap-closing features.
11
+ */
12
+ import { BaseAssessor } from "./BaseAssessor.js";
13
+ /**
14
+ * Known external services and their URL patterns
15
+ */
16
+ const KNOWN_SERVICES = {
17
+ github: [/api\.github\.com/i, /github\.com\/[^/]+\/[^/]+/i],
18
+ gitlab: [/gitlab\.com/i, /api\.gitlab\.com/i],
19
+ slack: [/slack\.com\/api/i, /api\.slack\.com/i, /hooks\.slack\.com/i],
20
+ discord: [/discord\.com\/api/i, /discordapp\.com/i],
21
+ aws: [/\.amazonaws\.com/i, /\.aws\.amazon\.com/i],
22
+ azure: [/\.azure\.com/i, /\.microsoft\.com/i],
23
+ gcp: [/\.googleapis\.com/i, /\.google\.com\/.*api/i],
24
+ openai: [/api\.openai\.com/i],
25
+ anthropic: [/api\.anthropic\.com/i],
26
+ huggingface: [/huggingface\.co/i, /api\.huggingface\.co/i],
27
+ stripe: [/api\.stripe\.com/i],
28
+ twilio: [/api\.twilio\.com/i],
29
+ sendgrid: [/api\.sendgrid\.com/i],
30
+ firebase: [/firebaseio\.com/i, /firebase\.google\.com/i],
31
+ supabase: [/supabase\.co/i],
32
+ notion: [/api\.notion\.com/i],
33
+ airtable: [/api\.airtable\.com/i],
34
+ dropbox: [/api\.dropbox\.com/i, /dropboxapi\.com/i],
35
+ jira: [/atlassian\.net/i, /jira\.com/i],
36
+ linear: [/api\.linear\.app/i],
37
+ asana: [/api\.asana\.com/i],
38
+ };
39
+ /**
40
+ * URL patterns to skip (not external APIs)
41
+ */
42
+ const SKIP_URL_PATTERNS = [
43
+ /localhost/i,
44
+ /127\.0\.0\.1/i,
45
+ /0\.0\.0\.0/i,
46
+ /example\.com/i,
47
+ /test\.com/i,
48
+ /\.local\//i,
49
+ /schema\.org/i,
50
+ /w3\.org/i,
51
+ /json-schema\.org/i,
52
+ /npmjs\.com/i,
53
+ /unpkg\.com/i,
54
+ /cdn\./i,
55
+ /fonts\.googleapis\.com/i,
56
+ ];
57
+ /**
58
+ * File patterns to skip during scanning
59
+ */
60
+ const SKIP_FILE_PATTERNS = [
61
+ /node_modules/i,
62
+ /\.test\.(ts|js|tsx|jsx)$/i,
63
+ /\.spec\.(ts|js|tsx|jsx)$/i,
64
+ /\.d\.ts$/i,
65
+ /package-lock\.json$/i,
66
+ /yarn\.lock$/i,
67
+ /\.map$/i,
68
+ /README\.md$/i,
69
+ /CHANGELOG\.md$/i,
70
+ /LICENSE/i,
71
+ /\.git\//i,
72
+ /dist\//i,
73
+ /build\//i,
74
+ ];
75
+ export class ExternalAPIScannerAssessor extends BaseAssessor {
76
+ async assess(context) {
77
+ this.log("Starting external API scanner assessment");
78
+ this.resetTestCount();
79
+ const detectedAPIs = [];
80
+ let scannedFiles = 0;
81
+ // Check if source code analysis is enabled
82
+ if (!context.sourceCodeFiles || !context.config.enableSourceCodeAnalysis) {
83
+ this.log("Source code analysis not enabled, skipping external API scan");
84
+ return this.createNoSourceResult();
85
+ }
86
+ // Scan each source file
87
+ for (const [filePath, content] of context.sourceCodeFiles) {
88
+ if (this.shouldSkipFile(filePath))
89
+ continue;
90
+ this.testCount++;
91
+ scannedFiles++;
92
+ const fileAPIs = this.scanFileForAPIs(filePath, content);
93
+ detectedAPIs.push(...fileAPIs);
94
+ }
95
+ // Extract unique services
96
+ const uniqueServices = [...new Set(detectedAPIs.map((api) => api.service))];
97
+ // Check for affiliation concerns
98
+ const affiliationWarning = this.checkAffiliation(context.serverName, uniqueServices);
99
+ // Determine status
100
+ const status = this.computeStatus(detectedAPIs, affiliationWarning);
101
+ const explanation = this.generateExplanation(detectedAPIs, uniqueServices, affiliationWarning, scannedFiles);
102
+ const recommendations = this.generateRecommendations(uniqueServices, affiliationWarning);
103
+ this.log(`External API scan complete: ${detectedAPIs.length} APIs found in ${scannedFiles} files`);
104
+ return {
105
+ detectedAPIs,
106
+ uniqueServices,
107
+ affiliationWarning,
108
+ scannedFiles,
109
+ status,
110
+ explanation,
111
+ recommendations,
112
+ };
113
+ }
114
+ /**
115
+ * Check if file should be skipped
116
+ */
117
+ shouldSkipFile(filePath) {
118
+ return SKIP_FILE_PATTERNS.some((pattern) => pattern.test(filePath));
119
+ }
120
+ /**
121
+ * Scan a file for external API URLs
122
+ */
123
+ scanFileForAPIs(filePath, content) {
124
+ const apis = [];
125
+ const urlPattern = /https?:\/\/[^\s'"`)}\]><]+/g;
126
+ const matches = content.match(urlPattern) || [];
127
+ for (const url of matches) {
128
+ // Skip non-API URLs
129
+ if (this.shouldSkipUrl(url))
130
+ continue;
131
+ // Identify the service
132
+ const service = this.identifyService(url);
133
+ // Avoid duplicates in the same file
134
+ if (!apis.some((api) => api.url === url && api.filePath === filePath)) {
135
+ apis.push({
136
+ url: this.cleanUrl(url),
137
+ service,
138
+ filePath,
139
+ });
140
+ }
141
+ }
142
+ return apis;
143
+ }
144
+ /**
145
+ * Check if URL should be skipped
146
+ */
147
+ shouldSkipUrl(url) {
148
+ return SKIP_URL_PATTERNS.some((pattern) => pattern.test(url));
149
+ }
150
+ /**
151
+ * Clean URL by removing trailing punctuation
152
+ */
153
+ cleanUrl(url) {
154
+ return url.replace(/[.,;:!?'")\]}>]+$/, "");
155
+ }
156
+ /**
157
+ * Identify which service a URL belongs to
158
+ */
159
+ identifyService(url) {
160
+ for (const [service, patterns] of Object.entries(KNOWN_SERVICES)) {
161
+ if (patterns.some((pattern) => pattern.test(url))) {
162
+ return service;
163
+ }
164
+ }
165
+ return "unknown";
166
+ }
167
+ /**
168
+ * Check if server name suggests affiliation that needs verification
169
+ */
170
+ checkAffiliation(serverName, detectedServices) {
171
+ const nameLower = serverName.toLowerCase();
172
+ const nameParts = nameLower.split(/[-_\s]/);
173
+ // Check if server name contains a known service name
174
+ for (const service of Object.keys(KNOWN_SERVICES)) {
175
+ if (nameParts.includes(service) || nameLower.includes(service)) {
176
+ // Server claims this service - check if actually uses it
177
+ if (detectedServices.includes(service)) {
178
+ return `Server name "${serverName}" suggests affiliation with ${service}. Verify the author is officially affiliated before approving.`;
179
+ }
180
+ else {
181
+ return `Server name "${serverName}" suggests affiliation with ${service}, but no ${service} API calls detected. This may be misleading.`;
182
+ }
183
+ }
184
+ }
185
+ return undefined;
186
+ }
187
+ /**
188
+ * Compute assessment status based on scan results
189
+ */
190
+ computeStatus(apis, affiliationWarning) {
191
+ if (affiliationWarning) {
192
+ return "NEED_MORE_INFO";
193
+ }
194
+ if (apis.length === 0) {
195
+ return "PASS";
196
+ }
197
+ // Having external APIs isn't a failure, just informational
198
+ return "PASS";
199
+ }
200
+ /**
201
+ * Generate explanation text
202
+ */
203
+ generateExplanation(apis, services, affiliationWarning, scannedFiles) {
204
+ const parts = [];
205
+ parts.push(`Scanned ${scannedFiles} source files.`);
206
+ if (apis.length === 0) {
207
+ parts.push("No external API dependencies detected.");
208
+ }
209
+ else {
210
+ parts.push(`Found ${apis.length} external API URL(s) across ${services.length} service(s): ${services.join(", ")}.`);
211
+ }
212
+ if (affiliationWarning) {
213
+ parts.push(`ATTENTION: ${affiliationWarning}`);
214
+ }
215
+ return parts.join(" ");
216
+ }
217
+ /**
218
+ * Generate recommendations
219
+ */
220
+ generateRecommendations(services, affiliationWarning) {
221
+ const recommendations = [];
222
+ if (affiliationWarning) {
223
+ recommendations.push("Verify author affiliation with claimed service before directory approval");
224
+ }
225
+ if (services.length > 0 && !services.every((s) => s === "unknown")) {
226
+ recommendations.push(`Ensure privacy policies are declared for external services: ${services.filter((s) => s !== "unknown").join(", ")}`);
227
+ }
228
+ if (services.includes("unknown")) {
229
+ recommendations.push("Review unrecognized external URLs for security and privacy implications");
230
+ }
231
+ return recommendations;
232
+ }
233
+ /**
234
+ * Create result when source code analysis is not available
235
+ */
236
+ createNoSourceResult() {
237
+ return {
238
+ detectedAPIs: [],
239
+ uniqueServices: [],
240
+ scannedFiles: 0,
241
+ status: "NEED_MORE_INFO",
242
+ explanation: "External API scanning requires source code analysis. Enable with --source flag.",
243
+ recommendations: [
244
+ "Re-run assessment with --source <path> to enable external API scanning",
245
+ ],
246
+ };
247
+ }
248
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"FunctionalityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/FunctionalityAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAGxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAM9D,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,OAAO,CAAC,cAAc,CAAwB;IAE9C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoCvB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;YA0H5D,QAAQ;IAiFtB,OAAO,CAAC,qBAAqB;IA0D7B,OAAO,CAAC,kBAAkB;IAwF1B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAe7C;IAEF;;;OAGG;IACH,OAAO,CAAC,mCAAmC;IAsF3C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAWlB,iBAAiB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO;IAI9C,OAAO,CAAC,mBAAmB;CA+B5B"}
1
+ {"version":3,"file":"FunctionalityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/FunctionalityAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAGxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAM9D,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,OAAO,CAAC,cAAc,CAAwB;IAE9C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoCvB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;YAkI5D,QAAQ;IAiFtB,OAAO,CAAC,qBAAqB;IA0D7B,OAAO,CAAC,kBAAkB;IAwF1B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAe7C;IAEF;;;OAGG;IACH,OAAO,CAAC,mCAAmC;IAsF3C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAWlB,iBAAiB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO;IAI9C,OAAO,CAAC,mBAAmB;CA+B5B"}
@@ -108,6 +108,12 @@ export class FunctionalityAssessor extends BaseAssessor {
108
108
  const coveragePercentage = testedTools > 0 ? (workingTools / testedTools) * 100 : 0;
109
109
  const status = this.determineStatus(workingTools, testedTools);
110
110
  const explanation = this.generateExplanation(totalTools, testedTools, workingTools, brokenTools);
111
+ // Map tools to include only schema-relevant fields for downstream consumers
112
+ const tools = context.tools.map((t) => ({
113
+ name: t.name,
114
+ description: t.description,
115
+ inputSchema: t.inputSchema,
116
+ }));
111
117
  return {
112
118
  totalTools,
113
119
  testedTools,
@@ -117,6 +123,7 @@ export class FunctionalityAssessor extends BaseAssessor {
117
123
  status,
118
124
  explanation,
119
125
  toolResults,
126
+ tools,
120
127
  };
121
128
  }
122
129
  async testTool(tool, callTool) {
@@ -54,6 +54,10 @@ export declare class ManifestValidationAssessor extends BaseAssessor {
54
54
  * Validate version format (semver)
55
55
  */
56
56
  private validateVersionFormat;
57
+ /**
58
+ * Validate privacy policy URLs are accessible
59
+ */
60
+ private validatePrivacyPolicyUrls;
57
61
  /**
58
62
  * Determine overall status
59
63
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ManifestValidationAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ManifestValidationAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,4BAA4B,EAI7B,MAAM,uBAAuB,CAAC;AAM/B,qBAAa,0BAA2B,SAAQ,YAAY;IAC1D;;OAEG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,4BAA4B,CAAC;IA0GxC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAyB9B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAmB/B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAgC/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAiC7B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAiChC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+CzB;;OAEG;IACH,OAAO,CAAC,YAAY;IAqCpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA+B1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA8B7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CA+BhC"}
1
+ {"version":3,"file":"ManifestValidationAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ManifestValidationAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,4BAA4B,EAK7B,MAAM,uBAAuB,CAAC;AAM/B,qBAAa,0BAA2B,SAAQ,YAAY;IAC1D;;OAEG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,4BAA4B,CAAC;IA6JxC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAyB9B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAmB/B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAgC/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAiC7B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAiChC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+CzB;;OAEG;IACH,OAAO,CAAC,YAAY;IAqCpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA+B1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA8B7B;;OAEG;YACW,yBAAyB;IA2EvC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA0C3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CA+ChC"}