@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.
- package/dist/assets/{OAuthCallback-DC1cIXHT.js → OAuthCallback-jfmizOMH.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-C3gqJjgQ.js → OAuthDebugCallback-bU5kKvnt.js} +1 -1
- package/dist/assets/{index-Dn2w887x.js → index-Ce63ds7G.js} +4 -4
- package/dist/index.html +1 -1
- package/lib/lib/assessment/coreTypes.d.ts +23 -0
- package/lib/lib/assessment/coreTypes.d.ts.map +1 -1
- package/lib/lib/assessment/extendedTypes.d.ts +64 -7
- package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
- package/lib/lib/assessment/jsonlEventSchemas.d.ts +4 -4
- package/lib/lib/assessment/resultTypes.d.ts +12 -1
- package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/lib/lib/aupPatterns.d.ts +50 -0
- package/lib/lib/aupPatterns.d.ts.map +1 -1
- package/lib/lib/aupPatterns.js +140 -0
- package/lib/lib/securityPatterns.d.ts.map +1 -1
- package/lib/lib/securityPatterns.js +92 -0
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts +26 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.js +160 -1
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +40 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ManifestValidationAssessor.js +269 -28
- package/lib/services/assessment/modules/securityTests/ConfidenceScorer.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/ConfidenceScorer.js +28 -0
- package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts +95 -0
- package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.js +174 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +15 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +40 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +143 -131
- package/package.json +1 -1
package/lib/lib/aupPatterns.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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 =
|
|
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;
|
|
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"}
|