@bryan-thompson/inspector-assessment-client 1.35.1 → 1.35.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.
Files changed (33) hide show
  1. package/dist/assets/{OAuthCallback-DC1cIXHT.js → OAuthCallback-jfmizOMH.js} +1 -1
  2. package/dist/assets/{OAuthDebugCallback-C3gqJjgQ.js → OAuthDebugCallback-bU5kKvnt.js} +1 -1
  3. package/dist/assets/{index-Dn2w887x.js → index-Ce63ds7G.js} +4 -4
  4. package/dist/index.html +1 -1
  5. package/lib/lib/assessment/coreTypes.d.ts +23 -0
  6. package/lib/lib/assessment/coreTypes.d.ts.map +1 -1
  7. package/lib/lib/assessment/extendedTypes.d.ts +64 -7
  8. package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
  9. package/lib/lib/assessment/jsonlEventSchemas.d.ts +4 -4
  10. package/lib/lib/assessment/resultTypes.d.ts +12 -1
  11. package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
  12. package/lib/lib/aupPatterns.d.ts +50 -0
  13. package/lib/lib/aupPatterns.d.ts.map +1 -1
  14. package/lib/lib/aupPatterns.js +140 -0
  15. package/lib/lib/securityPatterns.d.ts.map +1 -1
  16. package/lib/lib/securityPatterns.js +92 -0
  17. package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts +26 -1
  18. package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts.map +1 -1
  19. package/lib/services/assessment/modules/DeveloperExperienceAssessor.js +160 -1
  20. package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +40 -0
  21. package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -1
  22. package/lib/services/assessment/modules/ManifestValidationAssessor.js +269 -28
  23. package/lib/services/assessment/modules/securityTests/ConfidenceScorer.d.ts.map +1 -1
  24. package/lib/services/assessment/modules/securityTests/ConfidenceScorer.js +28 -0
  25. package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts +95 -0
  26. package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map +1 -1
  27. package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.js +174 -0
  28. package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
  29. package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +15 -0
  30. package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +40 -0
  31. package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
  32. package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +143 -131
  33. package/package.json +1 -1
@@ -294,6 +294,91 @@ export const HIGH_RISK_DOMAINS = [
294
294
  reason: "May involve insurance decisions or claims processing",
295
295
  },
296
296
  ];
297
+ /**
298
+ * Analytics/Reporting Exemption Patterns (Issue #139)
299
+ *
300
+ * These patterns identify legitimate analytics and reporting use cases
301
+ * that should NOT trigger "Financial Services" high-risk domain flag.
302
+ *
303
+ * Key distinction:
304
+ * - Analytics: filter, track, report, analyze, dashboard, metric
305
+ * - Transactions: process, transfer, execute, withdraw, deposit
306
+ */
307
+ export const FINANCIAL_ANALYTICS_EXEMPTION_PATTERNS = [
308
+ // Analytics/BI tool indicators
309
+ /\b(analytics|analytic)\b/i,
310
+ /\bdashboard\b/i,
311
+ /\bmetric(s)?\b/i,
312
+ /\breport(ing|s)?\b/i,
313
+ /\binsight(s)?\b/i,
314
+ /\bvisualization\b/i,
315
+ // Filter/query patterns (read-only operations)
316
+ /\bfilter\s*(by|on|for)\b/i,
317
+ /\bsort\s*(by|on)\b/i,
318
+ /\bgroup\s*(by|on)\b/i,
319
+ /\bquery\b/i,
320
+ /\bsearch\b/i,
321
+ // Tracking/monitoring (read operations)
322
+ /\btrack(ing|er)?\b/i,
323
+ /\bmonitor(ing)?\b/i,
324
+ /\bsession\s*(record(ing)?|replay|data)\b/i,
325
+ /\bheatmaps?\b/i,
326
+ /\bclick\s*(map|track)\b/i,
327
+ // BI/Analytics platforms
328
+ /\bclarity\b/i, // Microsoft Clarity
329
+ /\bgoogle\s*analytics\b/i,
330
+ /\bmixpanel\b/i,
331
+ /\bamplitude\b/i,
332
+ /\bsegment\b/i,
333
+ /\bplausible\b/i,
334
+ /\bmatomo\b/i,
335
+ /\bhotjar\b/i,
336
+ // Read-only financial field indicators
337
+ /\bproduct(Price|Purchases|Revenue|Sales)\b/i,
338
+ /\border(Value|Total|Amount)\b/i,
339
+ /\bconversion(Rate|Value)?\b/i,
340
+ /\brevenue\s*(metric|data|report)/i,
341
+ /\bsales\s*(data|report|metric)/i,
342
+ /\btransaction(Count|Volume)\b/i, // Aggregate metrics, not processing
343
+ ];
344
+ /**
345
+ * Financial Transaction Action Patterns (Issue #139)
346
+ *
347
+ * These patterns identify ACTUAL financial transaction capabilities
348
+ * that SHOULD trigger the "Financial Services" high-risk domain flag.
349
+ *
350
+ * These indicate the server can perform financial operations, not just
351
+ * analyze/report on financial data.
352
+ */
353
+ export const FINANCIAL_ACTION_PATTERNS = [
354
+ // Payment processing
355
+ /\b(process|submit|initiate)\s*(payment|transaction)/i,
356
+ /\bcharge\s*(card|customer|account)/i,
357
+ /\bcreate\s*(payment|invoice|charge)/i,
358
+ // Fund transfers
359
+ /\btransfer\s*(fund|money|balance)/i,
360
+ /\bsend\s*money\b/i,
361
+ /\bwire\s*transfer\b/i,
362
+ // Account operations
363
+ /\bwithdraw(al)?\b/i,
364
+ /\bdeposit\b/i,
365
+ /\bdebit\b/i,
366
+ /\bcredit\s*(account|card|balance)/i,
367
+ // Trading operations
368
+ /\b(execute|place|submit)\s*(trade|order)/i,
369
+ /\bbuy\s*(stock|share|asset|crypto)/i,
370
+ /\bsell\s*(stock|share|asset|crypto)/i,
371
+ /\bmarket\s*order\b/i,
372
+ /\blimit\s*order\b/i,
373
+ // Investment operations
374
+ /\binvest\s*in\b/i,
375
+ /\ballocate\s*(fund|capital)/i,
376
+ /\brebalance\s*portfolio/i,
377
+ // Crypto operations
378
+ /\bmint\s*(token|nft)/i,
379
+ /\bswap\s*(token|crypto)/i,
380
+ /\bstake\b/i,
381
+ ];
297
382
  /**
298
383
  * Get all patterns for a specific severity level
299
384
  */
@@ -330,12 +415,67 @@ export function checkTextForAUPViolations(text) {
330
415
  }
331
416
  return violations;
332
417
  }
418
+ /**
419
+ * Check if text indicates analytics/reporting use case (Issue #139)
420
+ *
421
+ * @param text - The text to analyze (tool name, description, or README)
422
+ * @returns true if analytics/reporting patterns are detected
423
+ */
424
+ export function isAnalyticsContext(text) {
425
+ return FINANCIAL_ANALYTICS_EXEMPTION_PATTERNS.some((pattern) => pattern.test(text));
426
+ }
427
+ /**
428
+ * Check if text indicates actual financial transaction capabilities (Issue #139)
429
+ *
430
+ * @param text - The text to analyze
431
+ * @returns true if financial action patterns are detected
432
+ */
433
+ export function hasFinancialActions(text) {
434
+ return FINANCIAL_ACTION_PATTERNS.some((pattern) => pattern.test(text));
435
+ }
436
+ /**
437
+ * Context-aware check for financial high-risk domain (Issue #139)
438
+ *
439
+ * Returns true ONLY if:
440
+ * 1. Financial keywords are present AND
441
+ * 2. Financial action patterns are present OR analytics exemption patterns are absent
442
+ *
443
+ * This prevents false positives for analytics/reporting servers while
444
+ * maintaining detection of actual financial transaction servers.
445
+ *
446
+ * @param text - The text to analyze
447
+ * @returns true if this is genuinely a financial services context
448
+ */
449
+ export function isFinancialServicesContext(text) {
450
+ const hasFinancialKeyword = /\b(financial|banking|payment|trading|investment)/i.test(text);
451
+ if (!hasFinancialKeyword) {
452
+ return false;
453
+ }
454
+ // If financial action patterns are present, it's genuinely financial services
455
+ if (hasFinancialActions(text)) {
456
+ return true;
457
+ }
458
+ // If analytics context is detected, exempt from financial services flag
459
+ if (isAnalyticsContext(text)) {
460
+ return false;
461
+ }
462
+ // Default: treat as financial services if keywords present but no clear context
463
+ return true;
464
+ }
333
465
  /**
334
466
  * Check text for high-risk domain keywords
467
+ * Now with context-aware detection for Financial Services (Issue #139)
335
468
  */
336
469
  export function checkTextForHighRiskDomains(text) {
337
470
  const matches = [];
338
471
  for (const domainDef of HIGH_RISK_DOMAINS) {
472
+ // Issue #139: Context-aware Financial Services detection
473
+ // Skip Financial Services if text is analytics context without financial actions
474
+ if (domainDef.domain === "Financial Services") {
475
+ if (!isFinancialServicesContext(text)) {
476
+ continue; // Skip - this is analytics, not financial transactions
477
+ }
478
+ }
339
479
  const match = text.match(domainDef.pattern);
340
480
  if (match) {
341
481
  matches.push({
@@ -1 +1 @@
1
- {"version":3,"file":"securityPatterns.d.ts","sourceRoot":"","sources":["../../src/lib/securityPatterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,aAAa,EAo7DnD,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,eAAe,EAAE,CAQnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,EAAE,CAEtD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;;;;;;;;EA8BnC"}
1
+ {"version":3,"file":"securityPatterns.d.ts","sourceRoot":"","sources":["../../src/lib/securityPatterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,aAAa,EAoiEnD,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,eAAe,EAAE,CAQnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,EAAE,CAEtD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;;;;;;;;EA8BnC"}
@@ -1809,6 +1809,98 @@ export const SECURITY_ATTACK_PATTERNS = [
1809
1809
  },
1810
1810
  ],
1811
1811
  },
1812
+ // ==========================================
1813
+ // EXCESSIVE PERMISSIONS SCOPE TESTS (Issue #144, Challenge #22)
1814
+ // ==========================================
1815
+ // 32. EXCESSIVE PERMISSIONS SCOPE
1816
+ {
1817
+ attackName: "Excessive Permissions Scope",
1818
+ description: "Test if tools with restrictive annotations (readOnlyHint=True) allow privileged operations via specific inputs (CWE-250, CWE-269)",
1819
+ payloads: [
1820
+ // Privileged action payloads for tools that claim readOnlyHint=True
1821
+ {
1822
+ payload: "write_file",
1823
+ evidence: /"scope_violation"\s*:\s*true|"actual_scope"\s*:\s*"write"/i,
1824
+ riskLevel: "HIGH",
1825
+ description: "Write operation bypassing readOnlyHint=True annotation (CWE-250)",
1826
+ payloadType: "scope_violation",
1827
+ parameterTypes: ["action", "operation", "command", "cmd"],
1828
+ },
1829
+ {
1830
+ payload: "delete_data",
1831
+ evidence: /"scope_violation"\s*:\s*true|"actual_scope"\s*:\s*"delete"/i,
1832
+ riskLevel: "HIGH",
1833
+ description: "Delete operation bypassing destructiveHint=False annotation (CWE-250)",
1834
+ payloadType: "scope_violation",
1835
+ parameterTypes: ["action", "operation", "command", "cmd"],
1836
+ },
1837
+ {
1838
+ payload: "execute_command",
1839
+ evidence: /"scope_violation"\s*:\s*true|"actual_scope"\s*:\s*"execute"/i,
1840
+ riskLevel: "HIGH",
1841
+ description: "Execute operation bypassing readOnlyHint=True annotation (CWE-250)",
1842
+ payloadType: "scope_violation",
1843
+ parameterTypes: ["action", "operation", "command", "cmd"],
1844
+ },
1845
+ {
1846
+ payload: "network_request",
1847
+ evidence: /"scope_violation"\s*:\s*true|"actual_scope"\s*:\s*"network"/i,
1848
+ riskLevel: "HIGH",
1849
+ description: "Network operation bypassing openWorldHint=False annotation (CWE-250)",
1850
+ payloadType: "scope_violation",
1851
+ parameterTypes: ["action", "operation", "command", "cmd"],
1852
+ },
1853
+ {
1854
+ payload: "modify_config",
1855
+ evidence: /"scope_violation"\s*:\s*true|"actual_scope"\s*:\s*"modify"/i,
1856
+ riskLevel: "HIGH",
1857
+ description: "Config modification bypassing readOnlyHint=True annotation (CWE-250)",
1858
+ payloadType: "scope_violation",
1859
+ parameterTypes: ["action", "operation", "command", "cmd"],
1860
+ },
1861
+ {
1862
+ payload: "environment_access",
1863
+ evidence: /"scope_violation"\s*:\s*true|"actual_scope"\s*:\s*"environment"/i,
1864
+ riskLevel: "HIGH",
1865
+ description: "Environment access bypassing annotation restrictions (CWE-250)",
1866
+ payloadType: "scope_violation",
1867
+ parameterTypes: ["action", "operation", "command", "cmd"],
1868
+ },
1869
+ // Escalation keyword payloads for tools that claim readOnlyHint=True
1870
+ {
1871
+ payload: "show admin users",
1872
+ evidence: /"scope_escalation"\s*:\s*true|"privileged_data"\s*:|admin[_-]?mode[_-]?activated/i,
1873
+ riskLevel: "HIGH",
1874
+ description: "Keyword-triggered privilege escalation via 'admin' (CWE-269)",
1875
+ payloadType: "scope_escalation",
1876
+ parameterTypes: ["request", "query", "input", "text", "message"],
1877
+ },
1878
+ {
1879
+ payload: "sudo access logs",
1880
+ evidence: /"scope_escalation"\s*:\s*true|"system_secrets"\s*:|"trigger_keyword"\s*:\s*"sudo"/i,
1881
+ riskLevel: "HIGH",
1882
+ description: "Keyword-triggered privilege escalation via 'sudo' (CWE-269)",
1883
+ payloadType: "scope_escalation",
1884
+ parameterTypes: ["request", "query", "input", "text", "message"],
1885
+ },
1886
+ {
1887
+ payload: "elevate to root",
1888
+ evidence: /"scope_escalation"\s*:\s*true|"trigger_keyword"\s*:\s*"(elevate|root)"/i,
1889
+ riskLevel: "HIGH",
1890
+ description: "Keyword-triggered privilege escalation via 'elevate' or 'root' (CWE-269)",
1891
+ payloadType: "scope_escalation",
1892
+ parameterTypes: ["request", "query", "input", "text", "message"],
1893
+ },
1894
+ {
1895
+ payload: "get superuser privileges",
1896
+ evidence: /"scope_escalation"\s*:\s*true|"trigger_keyword"\s*:\s*"(superuser|privilege)"/i,
1897
+ riskLevel: "HIGH",
1898
+ description: "Keyword-triggered privilege escalation via 'superuser' or 'privilege' (CWE-269)",
1899
+ payloadType: "scope_escalation",
1900
+ parameterTypes: ["request", "query", "input", "text", "message"],
1901
+ },
1902
+ ],
1903
+ },
1812
1904
  ];
1813
1905
  /**
1814
1906
  * Get all payloads for an attack type
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * @module assessment/modules/DeveloperExperienceAssessor
14
14
  */
15
- import { DocumentationMetrics, UsabilityMetrics, AssessmentStatus } from "../../../lib/assessmentTypes.js";
15
+ import { DocumentationMetrics, UsabilityMetrics, AssessmentStatus, NamespaceDetectionResult } from "../../../lib/assessmentTypes.js";
16
16
  import { BaseAssessor } from "./BaseAssessor.js";
17
17
  import { AssessmentContext } from "../AssessmentOrchestrator.js";
18
18
  /**
@@ -35,6 +35,8 @@ export interface DeveloperExperienceAssessment {
35
35
  usability: number;
36
36
  overall: number;
37
37
  };
38
+ /** Namespace detection results (Issue #142) */
39
+ namespaceDetection?: NamespaceDetectionResult;
38
40
  }
39
41
  export declare class DeveloperExperienceAssessor extends BaseAssessor<DeveloperExperienceAssessment> {
40
42
  assess(context: AssessmentContext): Promise<DeveloperExperienceAssessment>;
@@ -97,6 +99,29 @@ export declare class DeveloperExperienceAssessor extends BaseAssessor<DeveloperE
97
99
  private isDescriptiveName;
98
100
  private getToolSchema;
99
101
  private calculateUsabilityScore;
102
+ /**
103
+ * Detect namespace/prefix patterns in tool names.
104
+ * This helps identify intentional naming conventions like:
105
+ * - calc_add, calc_subtract -> namespace "calc"
106
+ * - fileRead, fileWrite -> namespace "file"
107
+ * - myserver_tool1, myserver_tool2 -> matches server name
108
+ */
109
+ private detectNamespace;
110
+ /**
111
+ * Find common prefix among tool names.
112
+ * Handles both snake_case (calc_add) and camelCase (calcAdd) conventions.
113
+ */
114
+ private findCommonPrefix;
115
+ /**
116
+ * Check if tool names use the server name as a prefix.
117
+ * Normalizes server name to match common patterns.
118
+ */
119
+ private checkServerNamePrefix;
120
+ /**
121
+ * Normalize server name for prefix matching.
122
+ * Converts "My-Server" -> "myserver", "my_server" -> "myserver"
123
+ */
124
+ private normalizeServerName;
100
125
  private determineOverallStatus;
101
126
  private generateExplanation;
102
127
  private generateRecommendations;
@@ -1 +1 @@
1
- {"version":3,"file":"DeveloperExperienceAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/DeveloperExperienceAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAEhB,gBAAgB,EAKjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,yCAAyC;IACzC,aAAa,EAAE,oBAAoB,CAAC;IACpC,qCAAqC;IACrC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,gDAAgD;IAChD,MAAM,EAAE,gBAAgB,CAAC;IACzB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iDAAiD;IACjD,MAAM,EAAE;QACN,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,qBAAa,2BAA4B,SAAQ,YAAY,CAAC,6BAA6B,CAAC;IACpF,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,6BAA6B,CAAC;IAsEzC,OAAO,CAAC,oBAAoB;IAmH5B,OAAO,CAAC,yBAAyB;IA4CjC,OAAO,CAAC,wBAAwB;IAiBhC,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,mBAAmB;IAwC3B,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,mBAAmB;IA6B3B;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAkBlC;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAgBjC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsEzB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,uBAAuB;IAsC/B,OAAO,CAAC,uBAAuB;IAwC/B,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,kBAAkB;IAsC1B,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,mBAAmB;IAyC3B,OAAO,CAAC,uBAAuB;CAmDhC"}
1
+ {"version":3,"file":"DeveloperExperienceAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/DeveloperExperienceAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAEhB,gBAAgB,EAKhB,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,yCAAyC;IACzC,aAAa,EAAE,oBAAoB,CAAC;IACpC,qCAAqC;IACrC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,gDAAgD;IAChD,MAAM,EAAE,gBAAgB,CAAC;IACzB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iDAAiD;IACjD,MAAM,EAAE;QACN,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;CAC/C;AAED,qBAAa,2BAA4B,SAAQ,YAAY,CAAC,6BAA6B,CAAC;IACpF,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,6BAA6B,CAAC;IA6EzC,OAAO,CAAC,oBAAoB;IAmH5B,OAAO,CAAC,yBAAyB;IA4CjC,OAAO,CAAC,wBAAwB;IAiBhC,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,mBAAmB;IAwC3B,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,mBAAmB;IA6B3B;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAkBlC;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAgBjC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsEzB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,uBAAuB;IAsC/B,OAAO,CAAC,uBAAuB;IAwC/B,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,kBAAkB;IAsC1B,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,uBAAuB;IAgB/B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAsEvB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA0DxB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAsC7B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,mBAAmB;IAyC3B,OAAO,CAAC,uBAAuB;CAmDhC"}
@@ -30,6 +30,8 @@ export class DeveloperExperienceAssessor extends BaseAssessor {
30
30
  // Assess usability
31
31
  const usabilityMetrics = this.analyzeUsability(context.tools);
32
32
  const usabilityScore = this.calculateUsabilityScore(usabilityMetrics);
33
+ // Issue #142: Detect namespace/prefix patterns in tool names
34
+ const namespaceDetection = this.detectNamespace(context.tools, context.serverInfo?.name);
33
35
  // Calculate overall score (weighted average: 60% docs, 40% usability)
34
36
  const overallScore = Math.round(documentationScore * 0.6 + usabilityScore * 0.4);
35
37
  // Determine status using Issue #55 thresholds
@@ -37,7 +39,7 @@ export class DeveloperExperienceAssessor extends BaseAssessor {
37
39
  // Generate explanation and recommendations
38
40
  const explanation = this.generateExplanation(documentationMetrics, usabilityMetrics, context.tools);
39
41
  const recommendations = this.generateRecommendations(documentationMetrics, usabilityMetrics);
40
- this.testCount = 15; // Documentation (5) + Quality (6) + Usability (4) checks
42
+ this.testCount = 16; // Documentation (5) + Quality (6) + Usability (4) + Namespace (1) checks
41
43
  return {
42
44
  documentation: documentationMetrics,
43
45
  usability: usabilityMetrics,
@@ -49,6 +51,7 @@ export class DeveloperExperienceAssessor extends BaseAssessor {
49
51
  usability: usabilityScore,
50
52
  overall: overallScore,
51
53
  },
54
+ namespaceDetection,
52
55
  };
53
56
  }
54
57
  // ============================================================================
@@ -673,6 +676,162 @@ export class DeveloperExperienceAssessor extends BaseAssessor {
673
676
  return Math.round((score / maxScore) * 100);
674
677
  }
675
678
  // ============================================================================
679
+ // Namespace Detection (Issue #142)
680
+ // ============================================================================
681
+ /**
682
+ * Detect namespace/prefix patterns in tool names.
683
+ * This helps identify intentional naming conventions like:
684
+ * - calc_add, calc_subtract -> namespace "calc"
685
+ * - fileRead, fileWrite -> namespace "file"
686
+ * - myserver_tool1, myserver_tool2 -> matches server name
687
+ */
688
+ detectNamespace(tools, serverName) {
689
+ const toolNames = tools.map((t) => t.name);
690
+ const totalTools = toolNames.length;
691
+ // Need at least 2 tools to detect a namespace
692
+ if (totalTools < 2) {
693
+ return {
694
+ detected: false,
695
+ confidence: "low",
696
+ toolsCovered: 0,
697
+ totalTools,
698
+ matchPattern: "none",
699
+ };
700
+ }
701
+ // Try server name prefix first (highest priority)
702
+ if (serverName) {
703
+ const serverNameResult = this.checkServerNamePrefix(toolNames, serverName);
704
+ if (serverNameResult.matches >= 2 &&
705
+ serverNameResult.matches / totalTools >= 0.5) {
706
+ return {
707
+ detected: true,
708
+ namespace: this.normalizeServerName(serverName),
709
+ confidence: "high",
710
+ toolsCovered: serverNameResult.matches,
711
+ totalTools,
712
+ matchPattern: "serverName",
713
+ evidence: serverNameResult.matchedTools.slice(0, 5),
714
+ };
715
+ }
716
+ }
717
+ // Try common prefix detection
718
+ const prefixResult = this.findCommonPrefix(toolNames);
719
+ if (prefixResult) {
720
+ const coverageRatio = prefixResult.count / totalTools;
721
+ const confidence = coverageRatio >= 0.5 ? "high" : coverageRatio >= 0.3 ? "medium" : "low";
722
+ if (prefixResult.count >= 2 && coverageRatio >= 0.3) {
723
+ return {
724
+ detected: true,
725
+ namespace: prefixResult.prefix,
726
+ confidence,
727
+ toolsCovered: prefixResult.count,
728
+ totalTools,
729
+ matchPattern: "prefix",
730
+ evidence: prefixResult.matchedTools.slice(0, 5),
731
+ };
732
+ }
733
+ }
734
+ // No namespace detected
735
+ return {
736
+ detected: false,
737
+ confidence: "low",
738
+ toolsCovered: 0,
739
+ totalTools,
740
+ matchPattern: "none",
741
+ };
742
+ }
743
+ /**
744
+ * Find common prefix among tool names.
745
+ * Handles both snake_case (calc_add) and camelCase (calcAdd) conventions.
746
+ */
747
+ findCommonPrefix(toolNames) {
748
+ if (toolNames.length < 2)
749
+ return null;
750
+ // Build prefix frequency map
751
+ const prefixCounts = new Map();
752
+ for (const name of toolNames) {
753
+ // Extract prefix using snake_case separator
754
+ const snakeMatch = name.match(/^([a-z]+)_/);
755
+ if (snakeMatch && snakeMatch[1].length >= 3) {
756
+ const prefix = snakeMatch[1];
757
+ if (!prefixCounts.has(prefix)) {
758
+ prefixCounts.set(prefix, []);
759
+ }
760
+ prefixCounts.get(prefix).push(name);
761
+ }
762
+ // Extract prefix using camelCase pattern
763
+ const camelMatch = name.match(/^([a-z]+)[A-Z]/);
764
+ if (camelMatch && camelMatch[1].length >= 3) {
765
+ const prefix = camelMatch[1];
766
+ if (!prefixCounts.has(prefix)) {
767
+ prefixCounts.set(prefix, []);
768
+ }
769
+ // Avoid double-counting if snake_case already found this name
770
+ const existing = prefixCounts.get(prefix);
771
+ if (!existing.includes(name)) {
772
+ existing.push(name);
773
+ }
774
+ }
775
+ }
776
+ // Find the prefix with the most matches
777
+ let bestPrefix = null;
778
+ let bestCount = 0;
779
+ let bestTools = [];
780
+ for (const [prefix, tools] of prefixCounts) {
781
+ if (tools.length > bestCount) {
782
+ bestPrefix = prefix;
783
+ bestCount = tools.length;
784
+ bestTools = tools;
785
+ }
786
+ }
787
+ if (bestPrefix && bestCount >= 2) {
788
+ return {
789
+ prefix: bestPrefix,
790
+ count: bestCount,
791
+ matchedTools: bestTools,
792
+ };
793
+ }
794
+ return null;
795
+ }
796
+ /**
797
+ * Check if tool names use the server name as a prefix.
798
+ * Normalizes server name to match common patterns.
799
+ */
800
+ checkServerNamePrefix(toolNames, serverName) {
801
+ const normalizedServerName = this.normalizeServerName(serverName);
802
+ const matchedTools = [];
803
+ for (const name of toolNames) {
804
+ const nameLower = name.toLowerCase();
805
+ // Check snake_case prefix (e.g., myserver_tool)
806
+ if (nameLower.startsWith(normalizedServerName + "_")) {
807
+ matchedTools.push(name);
808
+ continue;
809
+ }
810
+ // Check camelCase prefix (e.g., myserverTool)
811
+ if (nameLower.startsWith(normalizedServerName) &&
812
+ name.length > normalizedServerName.length &&
813
+ /[A-Z]/.test(name[normalizedServerName.length])) {
814
+ matchedTools.push(name);
815
+ continue;
816
+ }
817
+ // Check kebab-case prefix (e.g., myserver-tool)
818
+ if (nameLower.startsWith(normalizedServerName + "-")) {
819
+ matchedTools.push(name);
820
+ }
821
+ }
822
+ return {
823
+ matches: matchedTools.length,
824
+ matchedTools,
825
+ };
826
+ }
827
+ /**
828
+ * Normalize server name for prefix matching.
829
+ * Converts "My-Server" -> "myserver", "my_server" -> "myserver"
830
+ */
831
+ normalizeServerName(serverName) {
832
+ return serverName.toLowerCase().replace(/[-_\s]/g, "");
833
+ }
834
+ // ============================================================================
676
835
  // Combined Status, Explanation, and Recommendations
677
836
  // ============================================================================
678
837
  determineOverallStatus(overallScore) {
@@ -13,7 +13,37 @@
13
13
  import { BaseAssessor } from "./BaseAssessor.js";
14
14
  import { AssessmentContext } from "../AssessmentOrchestrator.js";
15
15
  import type { ManifestValidationAssessment } from "../../../lib/assessmentTypes.js";
16
+ /**
17
+ * Calculate Levenshtein distance between two strings
18
+ * Uses space-optimized two-row algorithm for O(min(n,m)) memory
19
+ * Used for "did you mean?" suggestions on mismatched tool names (Issue #140)
20
+ * Exported for testing (Issue #141 - ISSUE-002)
21
+ */
22
+ export declare function levenshteinDistance(a: string, b: string, maxDist?: number): number;
16
23
  export declare class ManifestValidationAssessor extends BaseAssessor {
24
+ /**
25
+ * Get mcp_config from manifest (supports both root and nested v0.3 format)
26
+ * Issue #138: Manifest v0.3 places mcp_config under server object
27
+ *
28
+ * @param manifest - The parsed manifest JSON
29
+ * @returns The mcp_config object or undefined if not found in either location
30
+ */
31
+ private getMcpConfig;
32
+ /**
33
+ * Extract contact information from manifest (Issue #141 - D4 check)
34
+ * Supports: author object, author string (email parsing), repository fallback
35
+ *
36
+ * @param manifest - The parsed manifest JSON
37
+ * @returns Extracted contact info or undefined if no contact info found
38
+ */
39
+ private extractContactInfo;
40
+ /**
41
+ * Extract version information from manifest (Issue #141 - D5 check)
42
+ *
43
+ * @param manifest - The parsed manifest JSON
44
+ * @returns Extracted version info or undefined if no version found
45
+ */
46
+ private extractVersionInfo;
17
47
  /**
18
48
  * Run manifest validation assessment
19
49
  */
@@ -54,6 +84,16 @@ export declare class ManifestValidationAssessor extends BaseAssessor {
54
84
  * Validate version format (semver)
55
85
  */
56
86
  private validateVersionFormat;
87
+ /**
88
+ * Validate manifest tool declarations against actual server tools (Issue #140)
89
+ * Compares tool names in manifest.tools against context.tools from tools/list
90
+ * Uses Levenshtein distance for "did you mean?" suggestions
91
+ */
92
+ private validateToolNamesMatch;
93
+ /**
94
+ * Fetch with retry logic for transient network failures
95
+ */
96
+ private fetchWithRetry;
57
97
  /**
58
98
  * Validate privacy policy URLs are accessible
59
99
  */
@@ -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,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;IAqFvC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA0C3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CA+ChC"}
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;AAE9D,OAAO,KAAK,EACV,4BAA4B,EAS7B,MAAM,uBAAuB,CAAC;AAO/B;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAwCR;AA+BD,qBAAa,0BAA2B,SAAQ,YAAY;IAC1D;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IAqC1B;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,4BAA4B,CAAC;IAiMxC;;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;IA4B7B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAyE9B;;OAEG;YACW,cAAc;IAuC5B;;OAEG;YACW,yBAAyB;IAmEvC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA0C3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CA+ChC"}