@bryan-thompson/inspector-assessment-client 1.25.1 → 1.25.5
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-CkzX_H4T.js → OAuthCallback-Dl4GYls3.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-jZEkm74B.js → OAuthDebugCallback-BdJ38Z-r.js} +1 -1
- package/dist/assets/{index-Df9Sx1jt.css → index-cHhcEXbr.css} +4 -0
- package/dist/assets/{index-BVx1dGJT.js → index-pfUiTdQb.js} +4 -4
- package/dist/index.html +2 -2
- package/lib/lib/assessment/configTypes.d.ts +3 -0
- package/lib/lib/assessment/configTypes.d.ts.map +1 -1
- package/lib/lib/assessment/configTypes.js +11 -6
- package/lib/lib/assessment/coreTypes.d.ts +65 -0
- package/lib/lib/assessment/coreTypes.d.ts.map +1 -1
- package/lib/lib/assessment/extendedTypes.d.ts +127 -0
- package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
- package/lib/lib/assessment/resultTypes.d.ts +45 -0
- package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +4 -12
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.js +49 -238
- package/lib/services/assessment/TestDataGenerator.d.ts +9 -1
- package/lib/services/assessment/TestDataGenerator.d.ts.map +1 -1
- package/lib/services/assessment/TestDataGenerator.js +32 -6
- package/lib/services/assessment/TestScenarioEngine.d.ts +9 -1
- package/lib/services/assessment/TestScenarioEngine.d.ts.map +1 -1
- package/lib/services/assessment/TestScenarioEngine.js +17 -14
- package/lib/services/assessment/ToolClassifier.d.ts +154 -27
- package/lib/services/assessment/ToolClassifier.d.ts.map +1 -1
- package/lib/services/assessment/ToolClassifier.js +171 -318
- package/lib/services/assessment/config/annotationPatterns.d.ts +3 -1
- package/lib/services/assessment/config/annotationPatterns.d.ts.map +1 -1
- package/lib/services/assessment/config/annotationPatterns.js +5 -2
- package/lib/services/assessment/config/architecturePatterns.d.ts +101 -0
- package/lib/services/assessment/config/architecturePatterns.d.ts.map +1 -0
- package/lib/services/assessment/config/architecturePatterns.js +248 -0
- package/lib/services/assessment/config/performanceConfig.d.ts +122 -0
- package/lib/services/assessment/config/performanceConfig.d.ts.map +1 -0
- package/lib/services/assessment/config/performanceConfig.js +154 -0
- package/lib/services/assessment/config/sanitizationPatterns.d.ts +63 -0
- package/lib/services/assessment/config/sanitizationPatterns.d.ts.map +1 -0
- package/lib/services/assessment/config/sanitizationPatterns.js +223 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts +3 -1
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -1
- package/lib/services/assessment/lib/claudeCodeBridge.js +5 -3
- package/lib/services/assessment/lib/concurrencyLimit.d.ts +6 -2
- package/lib/services/assessment/lib/concurrencyLimit.d.ts.map +1 -1
- package/lib/services/assessment/lib/concurrencyLimit.js +13 -6
- package/lib/services/assessment/lib/errors.d.ts +90 -0
- package/lib/services/assessment/lib/errors.d.ts.map +1 -0
- package/lib/services/assessment/lib/errors.js +136 -0
- package/lib/services/assessment/lib/timeoutUtils.d.ts +69 -0
- package/lib/services/assessment/lib/timeoutUtils.d.ts.map +1 -0
- package/lib/services/assessment/lib/timeoutUtils.js +103 -0
- package/lib/services/assessment/modules/BaseAssessor.d.ts +43 -8
- package/lib/services/assessment/modules/BaseAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/BaseAssessor.js +103 -34
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts +38 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.js +185 -19
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts +5 -0
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/DocumentationAssessor.js +11 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.js +1 -1
- package/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/FunctionalityAssessor.js +6 -3
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +3 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +14 -2
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ManifestValidationAssessor.js +7 -2
- package/lib/services/assessment/modules/PromptAssessor.d.ts +1 -0
- package/lib/services/assessment/modules/PromptAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/PromptAssessor.js +26 -16
- package/lib/services/assessment/modules/ProtocolComplianceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ProtocolComplianceAssessor.js +6 -2
- package/lib/services/assessment/modules/ProtocolConformanceAssessor.d.ts +5 -0
- package/lib/services/assessment/modules/ProtocolConformanceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ProtocolConformanceAssessor.js +15 -0
- package/lib/services/assessment/modules/ResourceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ResourceAssessor.js +8 -2
- package/lib/services/assessment/modules/SecurityAssessor.d.ts +3 -171
- package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/SecurityAssessor.js +25 -1480
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +27 -28
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ToolAnnotationAssessor.js +340 -863
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts +5 -0
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/UsabilityAssessor.js +11 -0
- package/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts +57 -0
- package/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.js +176 -0
- package/lib/services/assessment/modules/annotations/ArchitectureDetector.d.ts +67 -0
- package/lib/services/assessment/modules/annotations/ArchitectureDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/ArchitectureDetector.js +239 -0
- package/lib/services/assessment/modules/annotations/BehaviorInference.d.ts +46 -0
- package/lib/services/assessment/modules/annotations/BehaviorInference.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/BehaviorInference.js +394 -0
- package/lib/services/assessment/modules/annotations/DescriptionAnalyzer.d.ts +64 -0
- package/lib/services/assessment/modules/annotations/DescriptionAnalyzer.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/DescriptionAnalyzer.js +304 -0
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts +43 -0
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.js +276 -0
- package/lib/services/assessment/modules/annotations/SchemaAnalyzer.d.ts +122 -0
- package/lib/services/assessment/modules/annotations/SchemaAnalyzer.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/SchemaAnalyzer.js +388 -0
- package/lib/services/assessment/modules/annotations/index.d.ts +13 -0
- package/lib/services/assessment/modules/annotations/index.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/index.js +15 -0
- package/lib/services/assessment/modules/index.d.ts +10 -0
- package/lib/services/assessment/modules/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/index.js +13 -0
- package/lib/services/assessment/modules/securityTests/SanitizationDetector.d.ts +125 -0
- package/lib/services/assessment/modules/securityTests/SanitizationDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SanitizationDetector.js +345 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.d.ts +33 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.js +128 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +67 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +372 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +178 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +1207 -0
- package/lib/services/assessment/modules/securityTests/index.d.ts +8 -0
- package/lib/services/assessment/modules/securityTests/index.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/index.js +7 -0
- package/lib/services/assessment/orchestratorHelpers.d.ts +83 -0
- package/lib/services/assessment/orchestratorHelpers.d.ts.map +1 -0
- package/lib/services/assessment/orchestratorHelpers.js +212 -0
- package/lib/services/assessment/tool-classifier-patterns.d.ts +85 -0
- package/lib/services/assessment/tool-classifier-patterns.d.ts.map +1 -0
- package/lib/services/assessment/tool-classifier-patterns.js +365 -0
- package/package.json +1 -1
|
@@ -3,312 +3,121 @@
|
|
|
3
3
|
* Categorizes MCP tools based on name/description to select appropriate security test patterns
|
|
4
4
|
*
|
|
5
5
|
* Validated against broken-mcp server with 16 tools (6 HIGH, 4 MEDIUM, 6 SAFE)
|
|
6
|
+
*
|
|
7
|
+
* ## Pattern Matching Design
|
|
8
|
+
*
|
|
9
|
+
* This classifier uses two types of regex patterns intentionally:
|
|
10
|
+
*
|
|
11
|
+
* 1. **Substring patterns** (e.g., `/calculator/i`): Match anywhere in the text.
|
|
12
|
+
* Used for HIGH-risk category keywords that should trigger even when embedded.
|
|
13
|
+
* Example: "recalculator_v2" matches CALCULATOR because any calculator-like
|
|
14
|
+
* tool warrants security scrutiny.
|
|
15
|
+
*
|
|
16
|
+
* 2. **Word boundary patterns** (e.g., `/\bget\b/i`): Match isolated words only.
|
|
17
|
+
* Used for common words that would cause false positives as substrings.
|
|
18
|
+
* Example: "target_selector" should NOT match DATA_ACCESS's `/\bget\b/` pattern.
|
|
19
|
+
*
|
|
20
|
+
* **Underscore vs Hyphen Behavior:**
|
|
21
|
+
* - Word boundaries (`\b`) treat hyphens as boundaries but underscores as word characters
|
|
22
|
+
* - `api-get-data` matches `/\bget\b/` (hyphen is boundary)
|
|
23
|
+
* - `api_get_data` does NOT match `/\bget\b/` (underscore is word char)
|
|
24
|
+
* - This is intentional: underscore-joined names are typically single identifiers
|
|
25
|
+
*
|
|
26
|
+
* See tests in ToolClassifier.test.ts for comprehensive pattern behavior validation.
|
|
27
|
+
*
|
|
28
|
+
* @module ToolClassifier
|
|
6
29
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ToolCategory["SYSTEM_EXEC"] = "system_exec";
|
|
11
|
-
ToolCategory["CODE_EXECUTOR"] = "code_executor";
|
|
12
|
-
ToolCategory["DATA_ACCESS"] = "data_access";
|
|
13
|
-
ToolCategory["TOOL_OVERRIDE"] = "tool_override";
|
|
14
|
-
ToolCategory["CONFIG_MODIFIER"] = "config_modifier";
|
|
15
|
-
ToolCategory["URL_FETCHER"] = "fetcher";
|
|
16
|
-
ToolCategory["UNICODE_PROCESSOR"] = "unicode";
|
|
17
|
-
ToolCategory["JSON_PARSER"] = "parser";
|
|
18
|
-
ToolCategory["PACKAGE_INSTALLER"] = "installer";
|
|
19
|
-
ToolCategory["RUG_PULL"] = "rug_pull";
|
|
20
|
-
ToolCategory["SAFE_STORAGE"] = "safe_storage";
|
|
21
|
-
ToolCategory["API_WRAPPER"] = "api_wrapper";
|
|
22
|
-
ToolCategory["SEARCH_RETRIEVAL"] = "search_retrieval";
|
|
23
|
-
ToolCategory["CRUD_CREATION"] = "crud_creation";
|
|
24
|
-
ToolCategory["READ_ONLY_INFO"] = "read_only_info";
|
|
25
|
-
ToolCategory["GENERIC"] = "generic";
|
|
26
|
-
})(ToolCategory || (ToolCategory = {}));
|
|
30
|
+
import { CATEGORY_PATTERNS, CATEGORY_CHECK_ORDER, GENERIC_CONFIG, ToolCategory, } from "./tool-classifier-patterns.js";
|
|
31
|
+
// Re-export types for backwards compatibility
|
|
32
|
+
export { ToolCategory };
|
|
27
33
|
/**
|
|
28
34
|
* Classifies MCP tools into vulnerability categories based on naming patterns
|
|
29
|
-
* and descriptions. Uses patterns
|
|
35
|
+
* and descriptions. Uses pre-compiled patterns for optimal performance.
|
|
36
|
+
*
|
|
37
|
+
* The classifier is stateless and thread-safe - multiple classifications can
|
|
38
|
+
* run concurrently without interference.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const classifier = new ToolClassifier();
|
|
43
|
+
*
|
|
44
|
+
* // Single classification
|
|
45
|
+
* const result = classifier.classify('vulnerable_calculator_tool');
|
|
46
|
+
* console.log(result.categories); // [ToolCategory.CALCULATOR]
|
|
47
|
+
* console.log(result.confidence); // 90
|
|
48
|
+
*
|
|
49
|
+
* // Batch classification
|
|
50
|
+
* const tools = [
|
|
51
|
+
* { name: 'calculator_tool' },
|
|
52
|
+
* { name: 'search_api', description: 'Search for documents' }
|
|
53
|
+
* ];
|
|
54
|
+
* const results = classifier.classifyBatch(tools);
|
|
55
|
+
* ```
|
|
30
56
|
*/
|
|
31
57
|
export class ToolClassifier {
|
|
58
|
+
/** Maximum input length to prevent ReDoS with pathological inputs */
|
|
59
|
+
static MAX_INPUT_LENGTH = 10000;
|
|
32
60
|
/**
|
|
33
|
-
* Classify a tool into one or more categories
|
|
34
|
-
*
|
|
61
|
+
* Classify a tool into one or more security risk categories.
|
|
62
|
+
*
|
|
63
|
+
* The classifier analyzes both the tool name and optional description,
|
|
64
|
+
* matching against pre-compiled regex patterns for each category.
|
|
65
|
+
* A tool may match multiple categories if it contains multiple patterns.
|
|
66
|
+
*
|
|
67
|
+
* @param toolName - The MCP tool name to classify (e.g., "vulnerable_calculator_tool")
|
|
68
|
+
* @param description - Optional tool description for additional pattern matching
|
|
69
|
+
* @returns Classification result with categories, confidence score (0-100), and reasoning
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const classifier = new ToolClassifier();
|
|
74
|
+
*
|
|
75
|
+
* // Basic classification by name
|
|
76
|
+
* const calc = classifier.classify('calculator_tool');
|
|
77
|
+
* // { toolName: 'calculator_tool', categories: ['calculator'], confidence: 90, ... }
|
|
78
|
+
*
|
|
79
|
+
* // Classification with description
|
|
80
|
+
* const tool = classifier.classify('my_tool', 'Executes shell commands');
|
|
81
|
+
* // { toolName: 'my_tool', categories: ['system_exec'], confidence: 95, ... }
|
|
82
|
+
*
|
|
83
|
+
* // Multi-category match
|
|
84
|
+
* const multi = classifier.classify('calc_exec_command');
|
|
85
|
+
* // { categories: ['calculator', 'system_exec'], confidence: 92, ... }
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @throws Never throws - returns GENERIC category for invalid inputs
|
|
35
89
|
*/
|
|
36
90
|
classify(toolName, description) {
|
|
91
|
+
// Defensive validation for runtime safety (handles JS callers, deserialized data)
|
|
92
|
+
const safeName = typeof toolName === "string" ? toolName : "";
|
|
93
|
+
const safeDesc = typeof description === "string" ? description : "";
|
|
94
|
+
// Handle invalid or empty tool name
|
|
95
|
+
if (!safeName.trim()) {
|
|
96
|
+
return {
|
|
97
|
+
toolName: safeName,
|
|
98
|
+
categories: [ToolCategory.GENERIC],
|
|
99
|
+
confidence: 0,
|
|
100
|
+
reasoning: "Invalid or empty tool name provided",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
37
103
|
const categories = [];
|
|
38
104
|
const confidenceScores = [];
|
|
39
105
|
const reasons = [];
|
|
40
|
-
const toolText = `${
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
/arithmetic/i,
|
|
50
|
-
/expression/i,
|
|
51
|
-
])) {
|
|
52
|
-
categories.push(ToolCategory.CALCULATOR);
|
|
53
|
-
confidenceScores.push(90);
|
|
54
|
-
reasons.push("Calculator pattern detected (arithmetic execution risk)");
|
|
55
|
-
}
|
|
56
|
-
// System execution tools (HIGH RISK)
|
|
57
|
-
// Validated: vulnerable_system_exec_tool
|
|
58
|
-
if (this.matchesPattern(toolText, [
|
|
59
|
-
/system.*exec/i,
|
|
60
|
-
/exec.*tool/i,
|
|
61
|
-
/command/i,
|
|
62
|
-
/shell/i,
|
|
63
|
-
/\brun\b/i,
|
|
64
|
-
/execute/i,
|
|
65
|
-
/process/i,
|
|
66
|
-
])) {
|
|
67
|
-
categories.push(ToolCategory.SYSTEM_EXEC);
|
|
68
|
-
confidenceScores.push(95);
|
|
69
|
-
reasons.push("System execution pattern detected (command injection risk)");
|
|
70
|
-
}
|
|
71
|
-
// Code execution tools (HIGH RISK)
|
|
72
|
-
// Tools that execute arbitrary code in specific languages (Python, JavaScript, etc.)
|
|
73
|
-
// These require language-specific payloads, not shell commands
|
|
74
|
-
if (this.matchesPattern(toolText, [
|
|
75
|
-
/execute.*code/i,
|
|
76
|
-
/run.*code/i,
|
|
77
|
-
/code.*execut/i,
|
|
78
|
-
/run.*script/i,
|
|
79
|
-
/exec.*script/i,
|
|
80
|
-
/\bpython.*code\b/i,
|
|
81
|
-
/\bjavascript.*code\b/i,
|
|
82
|
-
/\bjs.*code\b/i,
|
|
83
|
-
/\beval.*code\b/i,
|
|
84
|
-
/code.*runner/i,
|
|
85
|
-
/script.*runner/i,
|
|
86
|
-
/\bexec\b.*\b(python|js|javascript)\b/i,
|
|
87
|
-
/\b(python|js|javascript)\b.*\bexec\b/i,
|
|
88
|
-
/interpret/i,
|
|
89
|
-
/\brepl\b/i,
|
|
90
|
-
])) {
|
|
91
|
-
categories.push(ToolCategory.CODE_EXECUTOR);
|
|
92
|
-
confidenceScores.push(95);
|
|
93
|
-
reasons.push("Code executor pattern detected (arbitrary code execution risk)");
|
|
94
|
-
}
|
|
95
|
-
// Data access/leak tools (HIGH RISK)
|
|
96
|
-
// Validated: vulnerable_data_leak_tool
|
|
97
|
-
if (this.matchesPattern(toolText, [
|
|
98
|
-
/leak/i,
|
|
99
|
-
/\bdata\b/i,
|
|
100
|
-
/show/i,
|
|
101
|
-
/\bget\b/i,
|
|
102
|
-
/\blist\b/i,
|
|
103
|
-
/display/i,
|
|
104
|
-
/\benv/i,
|
|
105
|
-
/secret/i,
|
|
106
|
-
/\bkey\b/i,
|
|
107
|
-
/credential/i,
|
|
108
|
-
/exfiltrat/i,
|
|
109
|
-
])) {
|
|
110
|
-
categories.push(ToolCategory.DATA_ACCESS);
|
|
111
|
-
confidenceScores.push(85);
|
|
112
|
-
reasons.push("Data access pattern detected (data exfiltration risk)");
|
|
113
|
-
}
|
|
114
|
-
// Tool override/shadowing (HIGH RISK)
|
|
115
|
-
// Validated: vulnerable_tool_override_tool
|
|
116
|
-
if (this.matchesPattern(toolText, [
|
|
117
|
-
/override/i,
|
|
118
|
-
/shadow/i,
|
|
119
|
-
/poison/i,
|
|
120
|
-
/create.*tool/i,
|
|
121
|
-
/register.*tool/i,
|
|
122
|
-
/define.*tool/i,
|
|
123
|
-
/tool.*creator/i,
|
|
124
|
-
/add.*tool/i,
|
|
125
|
-
])) {
|
|
126
|
-
categories.push(ToolCategory.TOOL_OVERRIDE);
|
|
127
|
-
confidenceScores.push(92);
|
|
128
|
-
reasons.push("Tool override pattern detected (shadowing/poisoning risk)");
|
|
129
|
-
}
|
|
130
|
-
// Config modification tools (HIGH RISK)
|
|
131
|
-
// Validated: vulnerable_config_modifier_tool
|
|
132
|
-
if (this.matchesPattern(toolText, [
|
|
133
|
-
/config/i,
|
|
134
|
-
/setting/i,
|
|
135
|
-
/modifier/i,
|
|
136
|
-
/\badmin\b/i,
|
|
137
|
-
/privilege/i,
|
|
138
|
-
/permission/i,
|
|
139
|
-
/configure/i,
|
|
140
|
-
/drift/i,
|
|
141
|
-
])) {
|
|
142
|
-
categories.push(ToolCategory.CONFIG_MODIFIER);
|
|
143
|
-
confidenceScores.push(88);
|
|
144
|
-
reasons.push("Config modification pattern detected (configuration drift risk)");
|
|
145
|
-
}
|
|
146
|
-
// URL fetching tools (HIGH RISK)
|
|
147
|
-
// Validated: vulnerable_fetcher_tool
|
|
148
|
-
if (this.matchesPattern(toolText, [
|
|
149
|
-
/fetch/i,
|
|
150
|
-
/\burl\b/i,
|
|
151
|
-
/http/i,
|
|
152
|
-
/download/i,
|
|
153
|
-
/load/i,
|
|
154
|
-
/retrieve/i,
|
|
155
|
-
/\bget\b.*url/i,
|
|
156
|
-
/external/i,
|
|
157
|
-
])) {
|
|
158
|
-
categories.push(ToolCategory.URL_FETCHER);
|
|
159
|
-
confidenceScores.push(87);
|
|
160
|
-
reasons.push("URL fetcher pattern detected (indirect prompt injection risk)");
|
|
161
|
-
}
|
|
162
|
-
// Unicode processing tools (MEDIUM RISK)
|
|
163
|
-
// Validated: vulnerable_unicode_processor_tool
|
|
164
|
-
if (this.matchesPattern(toolText, [
|
|
165
|
-
/unicode/i,
|
|
166
|
-
/encode/i,
|
|
167
|
-
/decode/i,
|
|
168
|
-
/charset/i,
|
|
169
|
-
/utf/i,
|
|
170
|
-
/hex/i,
|
|
171
|
-
/escape/i,
|
|
172
|
-
])) {
|
|
173
|
-
categories.push(ToolCategory.UNICODE_PROCESSOR);
|
|
174
|
-
confidenceScores.push(75);
|
|
175
|
-
reasons.push("Unicode processor pattern detected (bypass encoding risk)");
|
|
176
|
-
}
|
|
177
|
-
// JSON/nested parsing tools (MEDIUM RISK)
|
|
178
|
-
// Validated: vulnerable_nested_parser_tool
|
|
179
|
-
if (this.matchesPattern(toolText, [
|
|
180
|
-
/parser/i,
|
|
181
|
-
/parse/i,
|
|
182
|
-
/json/i,
|
|
183
|
-
/xml/i,
|
|
184
|
-
/yaml/i,
|
|
185
|
-
/nested/i,
|
|
186
|
-
/deserialize/i,
|
|
187
|
-
/unmarshal/i,
|
|
188
|
-
])) {
|
|
189
|
-
categories.push(ToolCategory.JSON_PARSER);
|
|
190
|
-
confidenceScores.push(78);
|
|
191
|
-
reasons.push("JSON/nested parser pattern detected (nested injection risk)");
|
|
192
|
-
}
|
|
193
|
-
// Package installation tools (MEDIUM RISK)
|
|
194
|
-
// Validated: vulnerable_package_installer_tool
|
|
195
|
-
if (this.matchesPattern(toolText, [
|
|
196
|
-
/install/i,
|
|
197
|
-
/package/i,
|
|
198
|
-
/\bnpm\b/i,
|
|
199
|
-
/\bpip\b/i,
|
|
200
|
-
/dependency/i,
|
|
201
|
-
/module/i,
|
|
202
|
-
/library/i,
|
|
203
|
-
/\bgem\b/i,
|
|
204
|
-
])) {
|
|
205
|
-
categories.push(ToolCategory.PACKAGE_INSTALLER);
|
|
206
|
-
confidenceScores.push(70);
|
|
207
|
-
reasons.push("Package installer pattern detected (typosquatting risk)");
|
|
208
|
-
}
|
|
209
|
-
// Rug pull (behavioral change over time) (MEDIUM RISK)
|
|
210
|
-
// Validated: vulnerable_rug_pull_tool
|
|
211
|
-
if (this.matchesPattern(toolText, [
|
|
212
|
-
/rug.*pull/i,
|
|
213
|
-
/trust/i,
|
|
214
|
-
/behavior.*change/i,
|
|
215
|
-
/malicious.*after/i,
|
|
216
|
-
/invocation.*count/i,
|
|
217
|
-
])) {
|
|
218
|
-
categories.push(ToolCategory.RUG_PULL);
|
|
219
|
-
confidenceScores.push(80);
|
|
220
|
-
reasons.push("Rug pull pattern detected (behavioral change risk)");
|
|
221
|
-
}
|
|
222
|
-
// API wrapper tools (SAFE - data passing, not code execution)
|
|
223
|
-
// These tools call external APIs and return data as text, not execute it as code
|
|
224
|
-
// Examples: Firecrawl (scrape, crawl, search), HTTP clients, REST/GraphQL clients
|
|
225
|
-
if (this.matchesPattern(toolText, [
|
|
226
|
-
/firecrawl/i,
|
|
227
|
-
/\bscrape\b/i,
|
|
228
|
-
/\bcrawl\b/i,
|
|
229
|
-
/web.*scraping/i,
|
|
230
|
-
/api.*wrapper/i,
|
|
231
|
-
/http.*client/i,
|
|
232
|
-
/web.*client/i,
|
|
233
|
-
/rest.*client/i,
|
|
234
|
-
/graphql.*client/i,
|
|
235
|
-
/fetch.*web.*content/i,
|
|
236
|
-
])) {
|
|
237
|
-
categories.push(ToolCategory.API_WRAPPER);
|
|
238
|
-
confidenceScores.push(95);
|
|
239
|
-
reasons.push("API wrapper pattern detected (safe data passing, not code execution)");
|
|
240
|
-
}
|
|
241
|
-
// Search and retrieval tools (SAFE - returns search results/data, not code execution)
|
|
242
|
-
// Examples: notion-search, notion-query-database, search, find, lookup
|
|
243
|
-
if (this.matchesPattern(toolText, [
|
|
244
|
-
/\bsearch\b/i,
|
|
245
|
-
/\bfind\b/i,
|
|
246
|
-
/\blookup\b/i,
|
|
247
|
-
/\bquery\b/i,
|
|
248
|
-
/retrieve/i,
|
|
249
|
-
/\blist\b/i,
|
|
250
|
-
/get.*users/i,
|
|
251
|
-
/get.*pages/i,
|
|
252
|
-
/get.*database/i,
|
|
253
|
-
])) {
|
|
254
|
-
categories.push(ToolCategory.SEARCH_RETRIEVAL);
|
|
255
|
-
confidenceScores.push(93);
|
|
256
|
-
reasons.push("Search/retrieval pattern detected (returns data, not code execution)");
|
|
257
|
-
}
|
|
258
|
-
// CRUD creation/modification tools (SAFE - creates/modifies resources, not code execution)
|
|
259
|
-
// Examples: notion-create-database, notion-create-page, create, add, insert, update
|
|
260
|
-
if (this.matchesPattern(toolText, [
|
|
261
|
-
/\bcreate\b/i,
|
|
262
|
-
/\badd\b/i,
|
|
263
|
-
/\binsert\b/i,
|
|
264
|
-
/\bupdate\b/i,
|
|
265
|
-
/\bmodify\b/i,
|
|
266
|
-
/\bdelete\b/i,
|
|
267
|
-
/\bduplicate\b/i,
|
|
268
|
-
/\bmove\b/i,
|
|
269
|
-
/\bappend\b/i,
|
|
270
|
-
])) {
|
|
271
|
-
categories.push(ToolCategory.CRUD_CREATION);
|
|
272
|
-
confidenceScores.push(92);
|
|
273
|
-
reasons.push("CRUD operation pattern detected (data manipulation, not code execution)");
|
|
274
|
-
}
|
|
275
|
-
// Read-only info tools (SAFE - returns user/workspace info, intended data exposure)
|
|
276
|
-
// Examples: notion-get-self, notion-get-teams, get-self, whoami, get-info, get-status
|
|
277
|
-
if (this.matchesPattern(toolText, [
|
|
278
|
-
/get.*self/i,
|
|
279
|
-
/get.*teams/i,
|
|
280
|
-
/get.*info/i,
|
|
281
|
-
/get.*status/i,
|
|
282
|
-
/\bwhoami\b/i,
|
|
283
|
-
/get.*workspace/i,
|
|
284
|
-
/get.*user/i,
|
|
285
|
-
/current.*user/i,
|
|
286
|
-
])) {
|
|
287
|
-
categories.push(ToolCategory.READ_ONLY_INFO);
|
|
288
|
-
confidenceScores.push(94);
|
|
289
|
-
reasons.push("Read-only info pattern detected (intended data exposure, not vulnerability)");
|
|
290
|
-
}
|
|
291
|
-
// Safe storage tools (CONTROL GROUP - should never show vulnerabilities)
|
|
292
|
-
// Validated: safe_storage_tool_mcp, safe_search_tool_mcp, safe_list_tool_mcp,
|
|
293
|
-
// safe_info_tool_mcp, safe_echo_tool_mcp, safe_validate_tool_mcp
|
|
294
|
-
if (this.matchesPattern(toolText, [
|
|
295
|
-
/safe.*storage/i,
|
|
296
|
-
/safe.*search/i,
|
|
297
|
-
/safe.*list/i,
|
|
298
|
-
/safe.*info/i,
|
|
299
|
-
/safe.*echo/i,
|
|
300
|
-
/safe.*validate/i,
|
|
301
|
-
/safe.*tool/i,
|
|
302
|
-
])) {
|
|
303
|
-
categories.push(ToolCategory.SAFE_STORAGE);
|
|
304
|
-
confidenceScores.push(99);
|
|
305
|
-
reasons.push("Safe tool pattern detected (control group - should be safe)");
|
|
106
|
+
const toolText = `${safeName} ${safeDesc}`.toLowerCase();
|
|
107
|
+
// Check each category in defined order (HIGH -> MEDIUM -> LOW)
|
|
108
|
+
for (const category of CATEGORY_CHECK_ORDER) {
|
|
109
|
+
const config = CATEGORY_PATTERNS[category];
|
|
110
|
+
if (this.matchesPattern(toolText, config.patterns)) {
|
|
111
|
+
categories.push(category);
|
|
112
|
+
confidenceScores.push(config.confidence);
|
|
113
|
+
reasons.push(config.reasoning);
|
|
114
|
+
}
|
|
306
115
|
}
|
|
307
116
|
// Default to generic if no specific matches
|
|
308
117
|
if (categories.length === 0) {
|
|
309
118
|
categories.push(ToolCategory.GENERIC);
|
|
310
|
-
confidenceScores.push(
|
|
311
|
-
reasons.push(
|
|
119
|
+
confidenceScores.push(GENERIC_CONFIG.confidence);
|
|
120
|
+
reasons.push(GENERIC_CONFIG.reasoning);
|
|
312
121
|
}
|
|
313
122
|
// Calculate overall confidence (average of matched pattern confidences)
|
|
314
123
|
const avgConfidence = confidenceScores.reduce((a, b) => a + b, 0) / confidenceScores.length;
|
|
@@ -320,46 +129,90 @@ export class ToolClassifier {
|
|
|
320
129
|
};
|
|
321
130
|
}
|
|
322
131
|
/**
|
|
323
|
-
* Check if text matches any of the provided patterns
|
|
132
|
+
* Check if text matches any of the provided patterns.
|
|
133
|
+
* Limits input length to prevent ReDoS attacks with very long strings.
|
|
134
|
+
*
|
|
135
|
+
* @param text - The text to search in (tool name + description)
|
|
136
|
+
* @param patterns - Pre-compiled regex patterns to match against
|
|
137
|
+
* @returns True if any pattern matches
|
|
324
138
|
*/
|
|
325
139
|
matchesPattern(text, patterns) {
|
|
326
|
-
|
|
140
|
+
// Truncate to prevent ReDoS with pathological inputs
|
|
141
|
+
const safeText = text.length > ToolClassifier.MAX_INPUT_LENGTH
|
|
142
|
+
? text.slice(0, ToolClassifier.MAX_INPUT_LENGTH)
|
|
143
|
+
: text;
|
|
144
|
+
return patterns.some((pattern) => pattern.test(safeText));
|
|
327
145
|
}
|
|
328
146
|
/**
|
|
329
|
-
* Get all tool categories
|
|
147
|
+
* Get all available tool categories.
|
|
148
|
+
*
|
|
149
|
+
* Useful for testing, debugging, or building UI components that need
|
|
150
|
+
* to display all possible categories.
|
|
151
|
+
*
|
|
152
|
+
* @returns Array of all ToolCategory enum values
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* const allCategories = ToolClassifier.getAllCategories();
|
|
157
|
+
* console.log(allCategories.length); // 17
|
|
158
|
+
* ```
|
|
330
159
|
*/
|
|
331
160
|
static getAllCategories() {
|
|
332
161
|
return Object.values(ToolCategory);
|
|
333
162
|
}
|
|
334
163
|
/**
|
|
335
|
-
* Get risk level for a category
|
|
164
|
+
* Get the security risk level for a category.
|
|
165
|
+
*
|
|
166
|
+
* Risk levels help prioritize security testing:
|
|
167
|
+
* - **HIGH**: Requires thorough security testing (code execution, data access)
|
|
168
|
+
* - **MEDIUM**: Requires moderate security testing (encoding bypass, supply chain)
|
|
169
|
+
* - **LOW**: Safe categories that typically don't need security testing
|
|
170
|
+
*
|
|
171
|
+
* @param category - The category to get the risk level for
|
|
172
|
+
* @returns Risk level: "HIGH", "MEDIUM", or "LOW"
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* ToolClassifier.getRiskLevel(ToolCategory.SYSTEM_EXEC); // "HIGH"
|
|
177
|
+
* ToolClassifier.getRiskLevel(ToolCategory.JSON_PARSER); // "MEDIUM"
|
|
178
|
+
* ToolClassifier.getRiskLevel(ToolCategory.SAFE_STORAGE); // "LOW"
|
|
179
|
+
* ```
|
|
336
180
|
*/
|
|
337
181
|
static getRiskLevel(category) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
ToolCategory.URL_FETCHER,
|
|
346
|
-
];
|
|
347
|
-
const mediumRiskCategories = [
|
|
348
|
-
ToolCategory.UNICODE_PROCESSOR,
|
|
349
|
-
ToolCategory.JSON_PARSER,
|
|
350
|
-
ToolCategory.PACKAGE_INSTALLER,
|
|
351
|
-
ToolCategory.RUG_PULL,
|
|
352
|
-
];
|
|
353
|
-
// LOW risk categories (for reference):
|
|
354
|
-
// API_WRAPPER, SEARCH_RETRIEVAL, CRUD_CREATION, READ_ONLY_INFO, SAFE_STORAGE, GENERIC
|
|
355
|
-
if (highRiskCategories.includes(category))
|
|
356
|
-
return "HIGH";
|
|
357
|
-
if (mediumRiskCategories.includes(category))
|
|
358
|
-
return "MEDIUM";
|
|
359
|
-
return "LOW";
|
|
182
|
+
if (category === ToolCategory.GENERIC) {
|
|
183
|
+
return GENERIC_CONFIG.risk;
|
|
184
|
+
}
|
|
185
|
+
// Type assertion needed because TypeScript doesn't narrow the type after the GENERIC check
|
|
186
|
+
const config = CATEGORY_PATTERNS[category];
|
|
187
|
+
// Handle unknown categories gracefully (defensive programming)
|
|
188
|
+
return config?.risk ?? "LOW";
|
|
360
189
|
}
|
|
361
190
|
/**
|
|
362
|
-
* Classify multiple tools at once
|
|
191
|
+
* Classify multiple tools at once.
|
|
192
|
+
*
|
|
193
|
+
* More efficient than calling classify() in a loop when you have
|
|
194
|
+
* many tools to process. The classifier is stateless, so batch
|
|
195
|
+
* processing produces identical results to individual calls.
|
|
196
|
+
*
|
|
197
|
+
* @param tools - Array of tools with name and optional description
|
|
198
|
+
* @returns Array of classification results in the same order as input
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const classifier = new ToolClassifier();
|
|
203
|
+
* const tools = [
|
|
204
|
+
* { name: 'calculator_tool' },
|
|
205
|
+
* { name: 'search_api', description: 'Search documents' },
|
|
206
|
+
* { name: 'unknown_tool' }
|
|
207
|
+
* ];
|
|
208
|
+
*
|
|
209
|
+
* const results = classifier.classifyBatch(tools);
|
|
210
|
+
* // [
|
|
211
|
+
* // { toolName: 'calculator_tool', categories: ['calculator'], ... },
|
|
212
|
+
* // { toolName: 'search_api', categories: ['search_retrieval'], ... },
|
|
213
|
+
* // { toolName: 'unknown_tool', categories: ['generic'], ... }
|
|
214
|
+
* // ]
|
|
215
|
+
* ```
|
|
363
216
|
*/
|
|
364
217
|
classifyBatch(tools) {
|
|
365
218
|
return tools.map((tool) => this.classify(tool.name, tool.description));
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Configurable pattern system for inferring expected tool behavior from names.
|
|
5
5
|
* Supports JSON configuration files for customization.
|
|
6
6
|
*/
|
|
7
|
+
import type { Logger } from "../lib/logger.js";
|
|
7
8
|
/**
|
|
8
9
|
* Pattern configuration for tool behavior inference.
|
|
9
10
|
* Each category contains string patterns that are converted to RegExp at runtime.
|
|
@@ -52,9 +53,10 @@ export declare function compilePatterns(config: AnnotationPatternConfig): Compil
|
|
|
52
53
|
* Partial configs are merged with defaults.
|
|
53
54
|
*
|
|
54
55
|
* @param configPath - Path to JSON configuration file
|
|
56
|
+
* @param logger - Optional logger for diagnostic output
|
|
55
57
|
* @returns Merged configuration with defaults
|
|
56
58
|
*/
|
|
57
|
-
export declare function loadPatternConfig(configPath?: string): AnnotationPatternConfig;
|
|
59
|
+
export declare function loadPatternConfig(configPath?: string, logger?: Logger): AnnotationPatternConfig;
|
|
58
60
|
/**
|
|
59
61
|
* Match a tool name against compiled patterns and return the result.
|
|
60
62
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotationPatterns.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/config/annotationPatterns.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"annotationPatterns.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/config/annotationPatterns.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,8FAA8F;IAC9F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mFAAmF;IACnF,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC;IACzE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,uBAqMzC,CAAC;AAoBF;;GAEG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,uBAAuB,GAC9B,gBAAgB,CAOlB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,uBAAuB,CAyBzB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,gBAAgB,GACzB,kBAAkB,CA0DpB;AAOD;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,gBAAgB,CAK7D;AAMD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,gBAAgB,CAAC;IACxB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACvC;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,EAW3C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,EAU5C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gCAAgC,EAAE,MAAM,EAgCpD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,EAWnD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EAAE,GAClB,wBAAwB,CAuD1B;AAED;;;;;GAKG;AACH,wBAAgB,uCAAuC,CAAC,WAAW,EAAE,MAAM,GAAG;IAC5E,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CA0BA"}
|
|
@@ -239,9 +239,10 @@ export function compilePatterns(config) {
|
|
|
239
239
|
* Partial configs are merged with defaults.
|
|
240
240
|
*
|
|
241
241
|
* @param configPath - Path to JSON configuration file
|
|
242
|
+
* @param logger - Optional logger for diagnostic output
|
|
242
243
|
* @returns Merged configuration with defaults
|
|
243
244
|
*/
|
|
244
|
-
export function loadPatternConfig(configPath) {
|
|
245
|
+
export function loadPatternConfig(configPath, logger) {
|
|
245
246
|
if (!configPath) {
|
|
246
247
|
return DEFAULT_ANNOTATION_PATTERNS;
|
|
247
248
|
}
|
|
@@ -257,7 +258,9 @@ export function loadPatternConfig(configPath) {
|
|
|
257
258
|
};
|
|
258
259
|
}
|
|
259
260
|
catch {
|
|
260
|
-
|
|
261
|
+
logger?.warn("Could not load pattern config, using defaults", {
|
|
262
|
+
configPath,
|
|
263
|
+
});
|
|
261
264
|
return DEFAULT_ANNOTATION_PATTERNS;
|
|
262
265
|
}
|
|
263
266
|
}
|