framework-mcp 1.3.5 → 1.3.6
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/COPILOT_INTEGRATION.md +1 -1
- package/DEPLOYMENT_GUIDE.md +1 -1
- package/dist/index.d.ts +0 -53
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1245
- package/dist/index.js.map +1 -1
- package/dist/interfaces/http/http-server.js +2 -2
- package/dist/interfaces/mcp/mcp-server.js +1 -1
- package/package.json +1 -1
- package/scripts/validate-documentation.sh +4 -4
- package/src/index.ts +0 -1598
- package/src/interfaces/http/http-server.ts +2 -2
- package/src/interfaces/mcp/mcp-server.ts +1 -1
- package/swagger.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,1249 +11,4 @@ export { FrameworkHttpServer } from './interfaces/http/http-server.js';
|
|
|
11
11
|
export { CapabilityAnalyzer } from './core/capability-analyzer.js';
|
|
12
12
|
export { SafeguardManager } from './core/safeguard-manager.js';
|
|
13
13
|
// Clean architecture - all safeguards data moved to SafeguardManager
|
|
14
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
15
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
16
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
17
|
-
import { SafeguardManager } from './core/safeguard-manager.js';
|
|
18
|
-
// Domain-specific validation mapping
|
|
19
|
-
const SAFEGUARD_DOMAIN_REQUIREMENTS = {
|
|
20
|
-
"1.1": {
|
|
21
|
-
domain: "Asset Inventory",
|
|
22
|
-
required_tool_types: ["inventory", "asset_management", "cmdb", "discovery"],
|
|
23
|
-
description: "Only asset inventory and discovery tools can provide FULL/PARTIAL implementation capability"
|
|
24
|
-
},
|
|
25
|
-
"1.2": {
|
|
26
|
-
domain: "Asset Management",
|
|
27
|
-
required_tool_types: ["inventory", "asset_management", "network_security", "nac"],
|
|
28
|
-
description: "Asset management or network access control tools required for FULL/PARTIAL implementation capability"
|
|
29
|
-
},
|
|
30
|
-
"5.1": {
|
|
31
|
-
domain: "Account Management",
|
|
32
|
-
required_tool_types: ["identity_management", "iam", "directory", "account_management"],
|
|
33
|
-
description: "Identity and account management tools required for FULL/PARTIAL implementation capability"
|
|
34
|
-
},
|
|
35
|
-
"6.3": {
|
|
36
|
-
domain: "Authentication",
|
|
37
|
-
required_tool_types: ["mfa", "authentication", "identity_management", "iam"],
|
|
38
|
-
description: "Multi-factor authentication and identity tools required for FULL/PARTIAL implementation capability"
|
|
39
|
-
},
|
|
40
|
-
"7.1": {
|
|
41
|
-
domain: "Vulnerability Management",
|
|
42
|
-
required_tool_types: ["vulnerability_management", "vulnerability_scanner", "patch_management"],
|
|
43
|
-
description: "Vulnerability management and scanning tools required for FULL/PARTIAL implementation capability"
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
// CIS_SAFEGUARDS removed - data now managed by SafeguardManager class
|
|
47
|
-
// Legacy data removed to eliminate duplication and ensure single source of truth
|
|
48
|
-
export class GRCAnalysisServer {
|
|
49
|
-
constructor() {
|
|
50
|
-
// Performance monitoring and caching
|
|
51
|
-
this.performanceMetrics = {
|
|
52
|
-
toolExecutionTimes: new Map(),
|
|
53
|
-
startTime: Date.now(),
|
|
54
|
-
totalRequests: 0,
|
|
55
|
-
errorCount: 0
|
|
56
|
-
};
|
|
57
|
-
// Simple cache for safeguard details (most commonly requested data)
|
|
58
|
-
this.cache = {
|
|
59
|
-
safeguardDetails: new Map(),
|
|
60
|
-
safeguardList: null,
|
|
61
|
-
lastCacheCleanup: Date.now()
|
|
62
|
-
};
|
|
63
|
-
this.server = new Server({
|
|
64
|
-
name: 'framework-mcp',
|
|
65
|
-
version: '1.0.0',
|
|
66
|
-
});
|
|
67
|
-
this.safeguardManager = new SafeguardManager();
|
|
68
|
-
this.setupToolHandlers();
|
|
69
|
-
}
|
|
70
|
-
setupToolHandlers() {
|
|
71
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
72
|
-
tools: [
|
|
73
|
-
{
|
|
74
|
-
name: 'analyze_vendor_response',
|
|
75
|
-
description: 'Analyze a vendor response to determine their tool capability role (Full Implementation, Partial Implementation, Facilitates, Governance, or Validates) for a specific CIS Control safeguard',
|
|
76
|
-
inputSchema: {
|
|
77
|
-
type: 'object',
|
|
78
|
-
properties: {
|
|
79
|
-
vendor_name: {
|
|
80
|
-
type: 'string',
|
|
81
|
-
description: 'Name of the vendor'
|
|
82
|
-
},
|
|
83
|
-
safeguard_id: {
|
|
84
|
-
type: 'string',
|
|
85
|
-
description: 'CIS Control safeguard ID (e.g., "5.1", "1.1", "6.3")',
|
|
86
|
-
pattern: '^[0-9]+\\.[0-9]+$'
|
|
87
|
-
},
|
|
88
|
-
response_text: {
|
|
89
|
-
type: 'string',
|
|
90
|
-
description: 'Vendor response text describing their tool capabilities for the safeguard'
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
required: ['vendor_name', 'safeguard_id', 'response_text']
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: 'get_safeguard_details',
|
|
98
|
-
description: 'Get detailed information about a CIS Control safeguard including governance requirements, core elements, and sub-taxonomical breakdown',
|
|
99
|
-
inputSchema: {
|
|
100
|
-
type: 'object',
|
|
101
|
-
properties: {
|
|
102
|
-
safeguard_id: {
|
|
103
|
-
type: 'string',
|
|
104
|
-
description: 'CIS Control safeguard ID (e.g., "5.1", "1.1")',
|
|
105
|
-
pattern: '^[0-9]+\\.[0-9]+$'
|
|
106
|
-
},
|
|
107
|
-
include_examples: {
|
|
108
|
-
type: 'boolean',
|
|
109
|
-
description: 'Include implementation examples and suggestions',
|
|
110
|
-
default: true
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
required: ['safeguard_id']
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: 'list_available_safeguards',
|
|
118
|
-
description: 'List all available CIS Control safeguards with their categorization',
|
|
119
|
-
inputSchema: {
|
|
120
|
-
type: 'object',
|
|
121
|
-
properties: {
|
|
122
|
-
implementation_group: {
|
|
123
|
-
type: 'string',
|
|
124
|
-
enum: ['IG1', 'IG2', 'IG3'],
|
|
125
|
-
description: 'Filter by implementation group (optional)'
|
|
126
|
-
},
|
|
127
|
-
security_function: {
|
|
128
|
-
type: 'string',
|
|
129
|
-
enum: ['Identify', 'Protect', 'Detect', 'Respond', 'Recover', 'Govern'],
|
|
130
|
-
description: 'Filter by security function (optional)'
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: 'validate_vendor_mapping',
|
|
137
|
-
description: 'Validate whether a vendor\'s claimed capability role (Full Implementation/Partial Implementation/Facilitates/Governance/Validates) is actually supported by their supporting evidence for a specific CIS safeguard',
|
|
138
|
-
inputSchema: {
|
|
139
|
-
type: 'object',
|
|
140
|
-
properties: {
|
|
141
|
-
vendor_name: {
|
|
142
|
-
type: 'string',
|
|
143
|
-
description: 'Name of the vendor (optional)',
|
|
144
|
-
default: 'Unknown Vendor'
|
|
145
|
-
},
|
|
146
|
-
safeguard_id: {
|
|
147
|
-
type: 'string',
|
|
148
|
-
description: 'CIS Control safeguard ID (e.g., "5.1", "1.1", "6.3")',
|
|
149
|
-
pattern: '^[0-9]+\\.[0-9]+$'
|
|
150
|
-
},
|
|
151
|
-
claimed_capability: {
|
|
152
|
-
type: 'string',
|
|
153
|
-
enum: ['full', 'partial', 'facilitates', 'governance', 'validates'],
|
|
154
|
-
description: 'Vendor\'s claimed capability role: full (complete implementation), partial (limited implementation), facilitates (enables/enhances), governance (policies/processes), validates (evidence/reporting)'
|
|
155
|
-
},
|
|
156
|
-
supporting_text: {
|
|
157
|
-
type: 'string',
|
|
158
|
-
description: 'Vendor\'s supporting evidence explaining how their tool fulfills the claimed capability role'
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
required: ['safeguard_id', 'claimed_capability', 'supporting_text']
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
],
|
|
165
|
-
}));
|
|
166
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
167
|
-
const { name, arguments: args } = request.params;
|
|
168
|
-
const startTime = Date.now();
|
|
169
|
-
try {
|
|
170
|
-
let result;
|
|
171
|
-
switch (name) {
|
|
172
|
-
case 'analyze_vendor_response':
|
|
173
|
-
result = await this.analyzeVendorResponse(args);
|
|
174
|
-
break;
|
|
175
|
-
case 'get_safeguard_details':
|
|
176
|
-
result = await this.getSafeguardDetails(args);
|
|
177
|
-
break;
|
|
178
|
-
case 'list_available_safeguards':
|
|
179
|
-
result = await this.listAvailableSafeguards(args);
|
|
180
|
-
break;
|
|
181
|
-
case 'validate_vendor_mapping':
|
|
182
|
-
result = await this.validateVendorMapping(args);
|
|
183
|
-
break;
|
|
184
|
-
default:
|
|
185
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
186
|
-
}
|
|
187
|
-
// Record successful execution time
|
|
188
|
-
const executionTime = Date.now() - startTime;
|
|
189
|
-
this.recordToolExecution(name, executionTime);
|
|
190
|
-
return result;
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
// Record error and performance metrics
|
|
194
|
-
this.performanceMetrics.errorCount++;
|
|
195
|
-
const executionTime = Date.now() - startTime;
|
|
196
|
-
this.recordToolExecution(`${name}_error`, executionTime);
|
|
197
|
-
const errorMessage = this.formatErrorMessage(error, name);
|
|
198
|
-
console.error(`[FrameworkMCP] Tool execution error: ${name}`, error);
|
|
199
|
-
return {
|
|
200
|
-
content: [
|
|
201
|
-
{
|
|
202
|
-
type: 'text',
|
|
203
|
-
text: errorMessage,
|
|
204
|
-
},
|
|
205
|
-
],
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
async getSafeguardDetails(args) {
|
|
211
|
-
const { safeguard_id, include_examples = true } = args;
|
|
212
|
-
// Input validation
|
|
213
|
-
this.validateSafeguardId(safeguard_id);
|
|
214
|
-
// Check cache first
|
|
215
|
-
const cached = this.getCachedSafeguardDetails(safeguard_id, include_examples);
|
|
216
|
-
if (cached) {
|
|
217
|
-
return cached;
|
|
218
|
-
}
|
|
219
|
-
const safeguard = this.safeguardManager.getSafeguardDetails(safeguard_id);
|
|
220
|
-
if (!safeguard) {
|
|
221
|
-
throw new Error(`Safeguard ${safeguard_id} not found`);
|
|
222
|
-
}
|
|
223
|
-
const result = {
|
|
224
|
-
...safeguard,
|
|
225
|
-
elementBreakdown: {
|
|
226
|
-
governanceElements: {
|
|
227
|
-
count: safeguard.governanceElements.length,
|
|
228
|
-
description: "Orange elements - MUST be met for compliance",
|
|
229
|
-
elements: safeguard.governanceElements
|
|
230
|
-
},
|
|
231
|
-
coreRequirements: {
|
|
232
|
-
count: safeguard.coreRequirements.length,
|
|
233
|
-
description: "Green elements - The 'what' of the safeguard",
|
|
234
|
-
elements: safeguard.coreRequirements
|
|
235
|
-
},
|
|
236
|
-
subTaxonomicalElements: {
|
|
237
|
-
count: safeguard.subTaxonomicalElements.length,
|
|
238
|
-
description: "Yellow elements - Detailed sub-taxonomical components",
|
|
239
|
-
elements: safeguard.subTaxonomicalElements
|
|
240
|
-
},
|
|
241
|
-
...(include_examples && {
|
|
242
|
-
implementationSuggestions: {
|
|
243
|
-
count: safeguard.implementationSuggestions.length,
|
|
244
|
-
description: "Gray elements - Implementation suggestions and methods",
|
|
245
|
-
elements: safeguard.implementationSuggestions
|
|
246
|
-
}
|
|
247
|
-
})
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
const response = {
|
|
251
|
-
content: [
|
|
252
|
-
{
|
|
253
|
-
type: 'text',
|
|
254
|
-
text: JSON.stringify(result, null, 2),
|
|
255
|
-
},
|
|
256
|
-
],
|
|
257
|
-
};
|
|
258
|
-
// Cache the result for future requests
|
|
259
|
-
this.setCachedSafeguardDetails(safeguard_id, include_examples, response);
|
|
260
|
-
return response;
|
|
261
|
-
}
|
|
262
|
-
async listAvailableSafeguards(args) {
|
|
263
|
-
const { implementation_group, security_function } = args;
|
|
264
|
-
// Check cache for complete safeguard list (only if no filters applied)
|
|
265
|
-
if (!implementation_group && !security_function && this.cache.safeguardList) {
|
|
266
|
-
const cacheAge = Date.now() - this.cache.safeguardList.timestamp;
|
|
267
|
-
if (cacheAge < 10 * 60 * 1000) { // 10 minute cache for safeguard list
|
|
268
|
-
return this.cache.safeguardList.data;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
let safeguards = Object.values(this.safeguardManager.getAllSafeguards());
|
|
272
|
-
if (implementation_group) {
|
|
273
|
-
safeguards = safeguards.filter(s => s.implementationGroup === implementation_group);
|
|
274
|
-
}
|
|
275
|
-
if (security_function) {
|
|
276
|
-
safeguards = safeguards.filter(s => s.securityFunction.includes(security_function));
|
|
277
|
-
}
|
|
278
|
-
const summary = {
|
|
279
|
-
totalSafeguards: safeguards.length,
|
|
280
|
-
byImplementationGroup: {
|
|
281
|
-
IG1: safeguards.filter(s => s.implementationGroup === 'IG1').length,
|
|
282
|
-
IG2: safeguards.filter(s => s.implementationGroup === 'IG2').length,
|
|
283
|
-
IG3: safeguards.filter(s => s.implementationGroup === 'IG3').length,
|
|
284
|
-
},
|
|
285
|
-
safeguards: safeguards.map(s => ({
|
|
286
|
-
id: s.id,
|
|
287
|
-
title: s.title,
|
|
288
|
-
implementationGroup: s.implementationGroup,
|
|
289
|
-
assetType: s.assetType,
|
|
290
|
-
securityFunction: s.securityFunction
|
|
291
|
-
}))
|
|
292
|
-
};
|
|
293
|
-
const response = {
|
|
294
|
-
content: [
|
|
295
|
-
{
|
|
296
|
-
type: 'text',
|
|
297
|
-
text: JSON.stringify(summary, null, 2),
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
};
|
|
301
|
-
// Cache the complete list if no filters were applied
|
|
302
|
-
if (!implementation_group && !security_function) {
|
|
303
|
-
this.cache.safeguardList = {
|
|
304
|
-
data: response,
|
|
305
|
-
timestamp: Date.now()
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
return response;
|
|
309
|
-
}
|
|
310
|
-
async analyzeVendorResponse(args) {
|
|
311
|
-
const { vendor_name, safeguard_id, response_text } = args;
|
|
312
|
-
// Input validation
|
|
313
|
-
this.validateSafeguardId(safeguard_id);
|
|
314
|
-
this.validateTextInput(response_text, 'Response text');
|
|
315
|
-
if (!vendor_name || typeof vendor_name !== 'string' || vendor_name.trim().length === 0) {
|
|
316
|
-
throw new Error('Vendor name is required');
|
|
317
|
-
}
|
|
318
|
-
const safeguard = this.safeguardManager.getSafeguardDetails(safeguard_id);
|
|
319
|
-
if (!safeguard) {
|
|
320
|
-
throw new Error(`Safeguard ${safeguard_id} not found`);
|
|
321
|
-
}
|
|
322
|
-
const analysis = this.performCapabilityAnalysis(vendor_name, safeguard, response_text);
|
|
323
|
-
return {
|
|
324
|
-
content: [
|
|
325
|
-
{
|
|
326
|
-
type: 'text',
|
|
327
|
-
text: JSON.stringify(analysis, null, 2),
|
|
328
|
-
},
|
|
329
|
-
],
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
performCapabilityAnalysis(vendorName, safeguard, responseText) {
|
|
333
|
-
const text = responseText.toLowerCase();
|
|
334
|
-
// Step 1: Determine what type of capability this tool is claiming
|
|
335
|
-
const claimedCapability = this.determineClaimedCapability(text, safeguard);
|
|
336
|
-
// Step 2: Assess how well the tool executes that specific capability type
|
|
337
|
-
const qualityAssessment = this.assessCapabilityQuality(text, safeguard, claimedCapability);
|
|
338
|
-
// Step 3: Generate capability-focused analysis
|
|
339
|
-
return this.generateCapabilityAnalysis(vendorName, safeguard, responseText, claimedCapability, qualityAssessment);
|
|
340
|
-
}
|
|
341
|
-
determineClaimedCapability(text, safeguard) {
|
|
342
|
-
// Capability detection keywords - focused on what the tool DOES, not what elements it covers
|
|
343
|
-
const governanceIndicators = [
|
|
344
|
-
'policy', 'policies', 'manage', 'process', 'workflow', 'governance', 'grc',
|
|
345
|
-
'compliance management', 'documented', 'establish', 'maintain', 'procedure',
|
|
346
|
-
'control', 'controls', 'framework', 'standard', 'enterprise risk management',
|
|
347
|
-
'centralized management', 'oversight'
|
|
348
|
-
];
|
|
349
|
-
const facilitatesIndicators = [
|
|
350
|
-
'improve', 'enhance', 'optimize', 'faster', 'better', 'stronger', 'automate',
|
|
351
|
-
'streamline', 'efficiency', 'facilitate', 'support', 'enable', 'accelerate',
|
|
352
|
-
'api', 'integration', 'data', 'export', 'import', 'sync', 'feed',
|
|
353
|
-
'provides data', 'data source', 'data feeds', 'enrichment', 'data enrichment',
|
|
354
|
-
'supplemental data', 'additional data', 'contextual data', 'threat data',
|
|
355
|
-
'intelligence feeds', 'data aggregation', 'data collection', 'data gathering',
|
|
356
|
-
'feeds data', 'populates', 'informs', 'enriches', 'supplements',
|
|
357
|
-
'enables compliance', 'facilitates implementation', 'supports compliance',
|
|
358
|
-
'creates framework', 'enables organizations', 'infrastructure', 'foundation',
|
|
359
|
-
'template', 'templates', 'workflow automation', 'orchestration'
|
|
360
|
-
];
|
|
361
|
-
const validatesIndicators = [
|
|
362
|
-
'audit', 'report', 'evidence', 'verify', 'validate', 'check', 'monitor',
|
|
363
|
-
'compliance', 'compliance report', 'assessment', 'logging', 'tracking', 'review', 'attest',
|
|
364
|
-
'dashboard', 'metrics', 'analytics', 'visibility', 'alert', 'attestation',
|
|
365
|
-
'compliance tracking', 'audit trail', 'reporting capabilities', 'audit capabilities'
|
|
366
|
-
];
|
|
367
|
-
// Direct implementation indicators (tools that actually perform the safeguard)
|
|
368
|
-
const implementationIndicators = this.getImplementationIndicators(safeguard.id);
|
|
369
|
-
// Calculate keyword presence scores
|
|
370
|
-
const governanceScore = this.calculateKeywordScore(text, governanceIndicators);
|
|
371
|
-
const facilitatesScore = this.calculateKeywordScore(text, facilitatesIndicators);
|
|
372
|
-
const validatesScore = this.calculateKeywordScore(text, validatesIndicators);
|
|
373
|
-
const implementationScore = this.calculateKeywordScore(text, implementationIndicators);
|
|
374
|
-
// Determine claimed capability based on strongest indicators and patterns
|
|
375
|
-
if (implementationScore > 0.2 && implementationScore >= Math.max(facilitatesScore, governanceScore, validatesScore)) {
|
|
376
|
-
// Tool claims to directly implement the safeguard
|
|
377
|
-
return implementationScore > 0.5 ? 'full' : 'partial';
|
|
378
|
-
}
|
|
379
|
-
else if (governanceScore > 0.2 && governanceScore >= Math.max(facilitatesScore, validatesScore)) {
|
|
380
|
-
return 'governance';
|
|
381
|
-
}
|
|
382
|
-
else if (validatesScore > 0.2 && validatesScore >= facilitatesScore) {
|
|
383
|
-
return 'validates';
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
return 'facilitates';
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
getImplementationIndicators(safeguardId) {
|
|
390
|
-
// Return specific indicators for what constitutes direct implementation for each safeguard
|
|
391
|
-
const implementationMap = {
|
|
392
|
-
"1.1": ['inventory', 'discovery', 'asset management', 'catalog', 'enumerate', 'scan devices', 'device database'],
|
|
393
|
-
"1.2": ['unauthorized', 'rogue', 'detect', 'identify unauthorized', 'quarantine', 'block unauthorized'],
|
|
394
|
-
"5.1": ['account inventory', 'user management', 'identity management', 'account lifecycle', 'user provisioning'],
|
|
395
|
-
"6.3": ['multi-factor', 'mfa', '2fa', 'authentication', 'two-factor', 'multi-factor authentication'],
|
|
396
|
-
"7.1": ['vulnerability', 'vuln', 'scanning', 'vulnerability management', 'security testing', 'penetration testing']
|
|
397
|
-
};
|
|
398
|
-
return implementationMap[safeguardId] || [];
|
|
399
|
-
}
|
|
400
|
-
assessCapabilityQuality(text, safeguard, claimedCapability) {
|
|
401
|
-
// Assess how well the tool executes the claimed capability type
|
|
402
|
-
const evidence = [];
|
|
403
|
-
const gaps = [];
|
|
404
|
-
// Quality assessment based on capability type
|
|
405
|
-
let qualityScore = 0;
|
|
406
|
-
switch (claimedCapability) {
|
|
407
|
-
case 'full':
|
|
408
|
-
case 'partial':
|
|
409
|
-
qualityScore = this.assessImplementationQuality(text, safeguard, evidence, gaps);
|
|
410
|
-
break;
|
|
411
|
-
case 'governance':
|
|
412
|
-
qualityScore = this.assessGovernanceQuality(text, safeguard, evidence, gaps);
|
|
413
|
-
break;
|
|
414
|
-
case 'facilitates':
|
|
415
|
-
qualityScore = this.assessFacilitationQuality(text, safeguard, evidence, gaps);
|
|
416
|
-
break;
|
|
417
|
-
case 'validates':
|
|
418
|
-
qualityScore = this.assessValidationQuality(text, safeguard, evidence, gaps);
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
// Convert score to quality rating
|
|
422
|
-
let quality;
|
|
423
|
-
if (qualityScore >= 0.8)
|
|
424
|
-
quality = 'excellent';
|
|
425
|
-
else if (qualityScore >= 0.6)
|
|
426
|
-
quality = 'good';
|
|
427
|
-
else if (qualityScore >= 0.4)
|
|
428
|
-
quality = 'fair';
|
|
429
|
-
else
|
|
430
|
-
quality = 'poor';
|
|
431
|
-
return {
|
|
432
|
-
quality,
|
|
433
|
-
confidence: Math.round(qualityScore * 100),
|
|
434
|
-
evidence: evidence.slice(0, 5), // Top 5 pieces of evidence
|
|
435
|
-
gaps: gaps.slice(0, 3) // Top 3 gaps identified
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
assessImplementationQuality(text, safeguard, evidence, gaps) {
|
|
439
|
-
// Assess quality of direct implementation capability
|
|
440
|
-
const implementationIndicators = this.getImplementationIndicators(safeguard.id);
|
|
441
|
-
let score = 0;
|
|
442
|
-
// Check for strong implementation language
|
|
443
|
-
const strongIndicators = implementationIndicators.filter(indicator => text.includes(indicator.toLowerCase()));
|
|
444
|
-
if (strongIndicators.length > 0) {
|
|
445
|
-
score += 0.4;
|
|
446
|
-
evidence.push(`Strong implementation indicators: ${strongIndicators.join(', ')}`);
|
|
447
|
-
}
|
|
448
|
-
// Check for comprehensive functionality description
|
|
449
|
-
if (text.includes('comprehensive') || text.includes('complete') || text.includes('full')) {
|
|
450
|
-
score += 0.2;
|
|
451
|
-
evidence.push('Claims comprehensive functionality');
|
|
452
|
-
}
|
|
453
|
-
// Check for automated vs manual implementation
|
|
454
|
-
if (text.includes('automat') || text.includes('real-time') || text.includes('continuous')) {
|
|
455
|
-
score += 0.2;
|
|
456
|
-
evidence.push('Automated implementation capability');
|
|
457
|
-
}
|
|
458
|
-
// Identify potential gaps
|
|
459
|
-
if (strongIndicators.length === 0) {
|
|
460
|
-
gaps.push('No clear implementation indicators found');
|
|
461
|
-
}
|
|
462
|
-
return Math.min(score, 1.0);
|
|
463
|
-
}
|
|
464
|
-
assessGovernanceQuality(text, safeguard, evidence, gaps) {
|
|
465
|
-
const governanceIndicators = ['policy', 'process', 'governance', 'compliance', 'documentation', 'oversight'];
|
|
466
|
-
let score = 0;
|
|
467
|
-
const foundIndicators = governanceIndicators.filter(indicator => text.includes(indicator));
|
|
468
|
-
if (foundIndicators.length >= 3) {
|
|
469
|
-
score += 0.5;
|
|
470
|
-
evidence.push(`Strong governance capability: ${foundIndicators.join(', ')}`);
|
|
471
|
-
}
|
|
472
|
-
if (text.includes('documented') && text.includes('process')) {
|
|
473
|
-
score += 0.3;
|
|
474
|
-
evidence.push('Documented process management');
|
|
475
|
-
}
|
|
476
|
-
return Math.min(score, 1.0);
|
|
477
|
-
}
|
|
478
|
-
assessFacilitationQuality(text, safeguard, evidence, gaps) {
|
|
479
|
-
const facilitationIndicators = ['enhance', 'enable', 'support', 'facilitate', 'improve', 'optimize'];
|
|
480
|
-
let score = 0;
|
|
481
|
-
const foundIndicators = facilitationIndicators.filter(indicator => text.includes(indicator));
|
|
482
|
-
if (foundIndicators.length >= 2) {
|
|
483
|
-
score += 0.4;
|
|
484
|
-
evidence.push(`Clear facilitation capability: ${foundIndicators.join(', ')}`);
|
|
485
|
-
}
|
|
486
|
-
return Math.min(score, 1.0);
|
|
487
|
-
}
|
|
488
|
-
assessValidationQuality(text, safeguard, evidence, gaps) {
|
|
489
|
-
const validationIndicators = ['audit', 'report', 'monitor', 'track', 'verify', 'validate'];
|
|
490
|
-
let score = 0;
|
|
491
|
-
const foundIndicators = validationIndicators.filter(indicator => text.includes(indicator));
|
|
492
|
-
if (foundIndicators.length >= 2) {
|
|
493
|
-
score += 0.4;
|
|
494
|
-
evidence.push(`Strong validation capability: ${foundIndicators.join(', ')}`);
|
|
495
|
-
}
|
|
496
|
-
return Math.min(score, 1.0);
|
|
497
|
-
}
|
|
498
|
-
generateCapabilityAnalysis(vendorName, safeguard, responseText, claimedCapability, qualityAssessment) {
|
|
499
|
-
// Generate the new capability-focused analysis result
|
|
500
|
-
return {
|
|
501
|
-
vendor: vendorName,
|
|
502
|
-
safeguardId: safeguard.id,
|
|
503
|
-
safeguardTitle: safeguard.title,
|
|
504
|
-
capability: claimedCapability,
|
|
505
|
-
capabilities: {
|
|
506
|
-
full: claimedCapability === 'full',
|
|
507
|
-
partial: claimedCapability === 'partial',
|
|
508
|
-
facilitates: claimedCapability === 'facilitates',
|
|
509
|
-
governance: claimedCapability === 'governance',
|
|
510
|
-
validates: claimedCapability === 'validates'
|
|
511
|
-
},
|
|
512
|
-
confidence: qualityAssessment.confidence,
|
|
513
|
-
reasoning: this.generateCapabilityReasoning(claimedCapability, qualityAssessment, safeguard),
|
|
514
|
-
evidence: qualityAssessment.evidence,
|
|
515
|
-
// Note: elementsCovered is now less relevant in capability-focused analysis
|
|
516
|
-
// We focus on tool capability rather than element coverage
|
|
517
|
-
toolCapabilityDescription: this.generateToolCapabilityDescription(claimedCapability, qualityAssessment),
|
|
518
|
-
recommendedUse: this.generateRecommendedUse(claimedCapability, safeguard)
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
generateCapabilityReasoning(claimedCapability, qualityAssessment, safeguard) {
|
|
522
|
-
const capabilityDescriptions = {
|
|
523
|
-
full: "directly implements the core functionality of this safeguard",
|
|
524
|
-
partial: "implements limited aspects of this safeguard with clear scope boundaries",
|
|
525
|
-
facilitates: "enhances or enables the implementation of this safeguard by others",
|
|
526
|
-
governance: "provides governance, policy, and oversight capabilities for this safeguard",
|
|
527
|
-
validates: "provides evidence, audit, and compliance validation for this safeguard"
|
|
528
|
-
};
|
|
529
|
-
const qualityDescriptor = qualityAssessment.quality;
|
|
530
|
-
const description = capabilityDescriptions[claimedCapability];
|
|
531
|
-
return `This tool ${description} with ${qualityDescriptor} capability quality. ${qualityAssessment.evidence.length > 0 ? 'Evidence: ' + qualityAssessment.evidence.join('; ') : ''}`;
|
|
532
|
-
}
|
|
533
|
-
generateToolCapabilityDescription(claimedCapability, qualityAssessment) {
|
|
534
|
-
const roleDescriptions = {
|
|
535
|
-
full: "Core Implementation Tool - Directly performs the safeguard functionality",
|
|
536
|
-
partial: "Focused Implementation Tool - Performs specific aspects of the safeguard",
|
|
537
|
-
facilitates: "Enablement Tool - Helps organizations implement the safeguard more effectively",
|
|
538
|
-
governance: "Governance Tool - Manages policies, processes, and oversight for the safeguard",
|
|
539
|
-
validates: "Compliance Tool - Provides audit, evidence, and validation capabilities"
|
|
540
|
-
};
|
|
541
|
-
return roleDescriptions[claimedCapability] || "Support Tool";
|
|
542
|
-
}
|
|
543
|
-
generateRecommendedUse(claimedCapability, safeguard) {
|
|
544
|
-
const recommendations = {
|
|
545
|
-
full: `Use as primary implementation tool for ${safeguard.title}. Ensure proper configuration and integration.`,
|
|
546
|
-
partial: `Use as part of a multi-tool strategy for ${safeguard.title}. Supplement with additional capabilities as needed.`,
|
|
547
|
-
facilitates: `Use to enhance existing safeguard implementation. Combine with direct implementation tools.`,
|
|
548
|
-
governance: `Use for policy management and governance oversight. Pair with implementation and validation tools.`,
|
|
549
|
-
validates: `Use for compliance monitoring and evidence collection. Combine with implementation tools for complete coverage.`
|
|
550
|
-
};
|
|
551
|
-
return recommendations[claimedCapability] || "Evaluate specific use case alignment.";
|
|
552
|
-
}
|
|
553
|
-
calculateKeywordScore(text, keywords) {
|
|
554
|
-
let matchedKeywords = 0;
|
|
555
|
-
let totalMatches = 0;
|
|
556
|
-
keywords.forEach(keyword => {
|
|
557
|
-
const regex = new RegExp(keyword.replace(/\s+/g, '\\s+'), 'gi');
|
|
558
|
-
const keywordMatches = (text.match(regex) || []).length;
|
|
559
|
-
if (keywordMatches > 0) {
|
|
560
|
-
matchedKeywords++;
|
|
561
|
-
totalMatches += keywordMatches;
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
// Return percentage of keywords that were found, with bonus for multiple matches
|
|
565
|
-
const baseScore = matchedKeywords / keywords.length;
|
|
566
|
-
const matchBonus = Math.min(totalMatches * 0.1, 0.5); // Up to 50% bonus for multiple matches
|
|
567
|
-
return Math.min(baseScore + matchBonus, 1.0);
|
|
568
|
-
}
|
|
569
|
-
analyzeElementCoverage(text, elements) {
|
|
570
|
-
const coveredElements = [];
|
|
571
|
-
const uncoveredElements = [];
|
|
572
|
-
elements.forEach(element => {
|
|
573
|
-
const elementKeywords = element.toLowerCase().split(/[\s\-\(\)\/]+/).filter(w => w.length > 2);
|
|
574
|
-
let elementFound = false;
|
|
575
|
-
// Check for keyword matches
|
|
576
|
-
for (const keyword of elementKeywords) {
|
|
577
|
-
if (text.includes(keyword)) {
|
|
578
|
-
elementFound = true;
|
|
579
|
-
break;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
// Check for semantic matches
|
|
583
|
-
if (!elementFound) {
|
|
584
|
-
const regex = new RegExp(element.replace(/[()]/g, '').replace(/\s+/g, '\\s*'), 'gi');
|
|
585
|
-
if (regex.test(text)) {
|
|
586
|
-
elementFound = true;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
if (elementFound) {
|
|
590
|
-
coveredElements.push(element);
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
uncoveredElements.push(element);
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
return {
|
|
597
|
-
percentage: elements.length > 0 ? (coveredElements.length / elements.length) * 100 : 0,
|
|
598
|
-
coveredElements,
|
|
599
|
-
uncoveredElements
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
analyzeBinaryElementCoverage(text, elements) {
|
|
603
|
-
const coveredElements = [];
|
|
604
|
-
const uncoveredElements = [];
|
|
605
|
-
elements.forEach(element => {
|
|
606
|
-
const elementKeywords = element.toLowerCase().split(/[\s\-\(\)\/]+/).filter(w => w.length > 2);
|
|
607
|
-
let elementFound = false;
|
|
608
|
-
// Check for keyword matches
|
|
609
|
-
for (const keyword of elementKeywords) {
|
|
610
|
-
if (text.includes(keyword)) {
|
|
611
|
-
elementFound = true;
|
|
612
|
-
break;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
// Check for semantic matches
|
|
616
|
-
if (!elementFound) {
|
|
617
|
-
const regex = new RegExp(element.replace(/[()]/g, '').replace(/\s+/g, '\\s*'), 'gi');
|
|
618
|
-
if (regex.test(text)) {
|
|
619
|
-
elementFound = true;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
if (elementFound) {
|
|
623
|
-
coveredElements.push(element);
|
|
624
|
-
}
|
|
625
|
-
else {
|
|
626
|
-
uncoveredElements.push(element);
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
return {
|
|
630
|
-
coveredElements,
|
|
631
|
-
uncoveredElements
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
extractEnhancedEvidence(text, safeguard) {
|
|
635
|
-
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 15);
|
|
636
|
-
const evidence = [];
|
|
637
|
-
// Collect all keywords from all categories
|
|
638
|
-
const allKeywords = [
|
|
639
|
-
...safeguard.keywords,
|
|
640
|
-
...safeguard.governanceElements,
|
|
641
|
-
...safeguard.coreRequirements,
|
|
642
|
-
...safeguard.subTaxonomicalElements
|
|
643
|
-
];
|
|
644
|
-
// Extract relevant sentences
|
|
645
|
-
allKeywords.forEach(keyword => {
|
|
646
|
-
sentences.forEach(sentence => {
|
|
647
|
-
const lowerSentence = sentence.toLowerCase();
|
|
648
|
-
const lowerKeyword = keyword.toLowerCase();
|
|
649
|
-
if (lowerSentence.includes(lowerKeyword) &&
|
|
650
|
-
!evidence.includes(sentence.trim()) &&
|
|
651
|
-
evidence.length < 8) {
|
|
652
|
-
evidence.push(sentence.trim());
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
});
|
|
656
|
-
return evidence;
|
|
657
|
-
}
|
|
658
|
-
generateEnhancedReasoning(governance, facilitates, validates, coverageBreakdown, safeguard) {
|
|
659
|
-
const reasons = [];
|
|
660
|
-
// GRC attribute analysis
|
|
661
|
-
if (governance > 0.3)
|
|
662
|
-
reasons.push(`Strong governance capabilities for ${safeguard.title}`);
|
|
663
|
-
if (facilitates > 0.3)
|
|
664
|
-
reasons.push(`Enhancement features that facilitate ${safeguard.id} implementation`);
|
|
665
|
-
if (validates > 0.3)
|
|
666
|
-
reasons.push(`Validation capabilities for ${safeguard.id} compliance verification`);
|
|
667
|
-
// Coverage breakdown analysis
|
|
668
|
-
reasons.push(`Coverage breakdown: ${coverageBreakdown.governance}% governance, ${coverageBreakdown.core}% core requirements, ${coverageBreakdown.subElements}% sub-elements`);
|
|
669
|
-
if (coverageBreakdown.governance < 50) {
|
|
670
|
-
reasons.push(`Limited governance element coverage may impact compliance`);
|
|
671
|
-
}
|
|
672
|
-
if (coverageBreakdown.core < 50) {
|
|
673
|
-
reasons.push(`Core requirements coverage needs improvement`);
|
|
674
|
-
}
|
|
675
|
-
return reasons.join('. ');
|
|
676
|
-
}
|
|
677
|
-
validateClaim(claim, analysis, capabilities, safeguard) {
|
|
678
|
-
const validation = {
|
|
679
|
-
coverageClaimValid: false,
|
|
680
|
-
capabilityClaimsValid: {},
|
|
681
|
-
gaps: [],
|
|
682
|
-
recommendations: []
|
|
683
|
-
};
|
|
684
|
-
// Validate coverage claims
|
|
685
|
-
if (claim === 'FULL') {
|
|
686
|
-
const meetsFullCriteria = analysis.capabilities.full;
|
|
687
|
-
validation.coverageClaimValid = meetsFullCriteria;
|
|
688
|
-
if (!meetsFullCriteria) {
|
|
689
|
-
validation.gaps.push(`FULL implementation capability claim not supported: Tool does not demonstrate direct implementation of core safeguard functionality`);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
else if (claim === 'PARTIAL') {
|
|
693
|
-
const meetsPartialCriteria = analysis.capabilities.partial || analysis.capabilities.full;
|
|
694
|
-
validation.coverageClaimValid = meetsPartialCriteria;
|
|
695
|
-
if (!meetsPartialCriteria) {
|
|
696
|
-
validation.gaps.push(`PARTIAL implementation capability claim questionable: No core or sub-taxonomical elements clearly addressed`);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
// Validate capability claims
|
|
700
|
-
capabilities.forEach(capability => {
|
|
701
|
-
switch (capability.toLowerCase()) {
|
|
702
|
-
case 'governance':
|
|
703
|
-
validation.capabilityClaimsValid[capability] = analysis.capabilities.governance;
|
|
704
|
-
if (!analysis.capabilities.governance) {
|
|
705
|
-
validation.gaps.push(`Governance capability not evidenced in response`);
|
|
706
|
-
}
|
|
707
|
-
break;
|
|
708
|
-
case 'facilitates':
|
|
709
|
-
validation.capabilityClaimsValid[capability] = analysis.capabilities.facilitates;
|
|
710
|
-
if (!analysis.capabilities.facilitates) {
|
|
711
|
-
validation.gaps.push(`Facilitates capability not evidenced in response`);
|
|
712
|
-
}
|
|
713
|
-
break;
|
|
714
|
-
case 'validates':
|
|
715
|
-
validation.capabilityClaimsValid[capability] = analysis.capabilities.validates;
|
|
716
|
-
if (!analysis.capabilities.validates) {
|
|
717
|
-
validation.gaps.push(`Validates capability not evidenced in response`);
|
|
718
|
-
}
|
|
719
|
-
break;
|
|
720
|
-
case 'full':
|
|
721
|
-
validation.capabilityClaimsValid[capability] = analysis.capabilities.full;
|
|
722
|
-
if (!analysis.capabilities.full) {
|
|
723
|
-
validation.gaps.push(`Full coverage capability not evidenced in response`);
|
|
724
|
-
}
|
|
725
|
-
break;
|
|
726
|
-
case 'partial':
|
|
727
|
-
validation.capabilityClaimsValid[capability] = analysis.capabilities.partial;
|
|
728
|
-
if (!analysis.capabilities.partial) {
|
|
729
|
-
validation.gaps.push(`Partial coverage capability not evidenced in response`);
|
|
730
|
-
}
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
});
|
|
734
|
-
// Generate recommendations
|
|
735
|
-
if (validation.gaps.length === 0) {
|
|
736
|
-
validation.recommendations.push(`Claims appear to be well-supported by the vendor response`);
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
validation.recommendations.push(`Consider requesting additional information about: ${validation.gaps.join(', ')}`);
|
|
740
|
-
// Capability-based recommendations
|
|
741
|
-
if (analysis.capability === 'facilitates') {
|
|
742
|
-
validation.recommendations.push('Consider pairing with direct implementation tools for complete safeguard coverage');
|
|
743
|
-
}
|
|
744
|
-
else if (analysis.capability === 'partial') {
|
|
745
|
-
validation.recommendations.push('Evaluate scope limitations and identify complementary tools for full coverage');
|
|
746
|
-
}
|
|
747
|
-
else if (analysis.capability === 'governance') {
|
|
748
|
-
validation.recommendations.push('Combine with technical implementation tools for complete safeguard execution');
|
|
749
|
-
}
|
|
750
|
-
else if (analysis.capability === 'validates') {
|
|
751
|
-
validation.recommendations.push('Pair with implementation tools to ensure both execution and validation coverage');
|
|
752
|
-
}
|
|
753
|
-
if (!analysis.capabilities.validates && capabilities.includes('Validates')) {
|
|
754
|
-
validation.recommendations.push(`Request examples of audit trails, reporting capabilities, and compliance evidence`);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
return validation;
|
|
758
|
-
}
|
|
759
|
-
async validateVendorMapping(args) {
|
|
760
|
-
const { vendor_name = 'Unknown Vendor', safeguard_id, claimed_capability, supporting_text } = args;
|
|
761
|
-
// Input validation
|
|
762
|
-
this.validateSafeguardId(safeguard_id);
|
|
763
|
-
this.validateTextInput(supporting_text, 'Supporting text');
|
|
764
|
-
this.validateCapability(claimed_capability);
|
|
765
|
-
const safeguard = this.safeguardManager.getSafeguardDetails(safeguard_id);
|
|
766
|
-
if (!safeguard) {
|
|
767
|
-
throw new Error(`Safeguard ${safeguard_id} not found`);
|
|
768
|
-
}
|
|
769
|
-
const validation = this.validateCapabilityClaim(vendor_name, safeguard, claimed_capability, supporting_text);
|
|
770
|
-
return {
|
|
771
|
-
content: [
|
|
772
|
-
{
|
|
773
|
-
type: 'text',
|
|
774
|
-
text: JSON.stringify(validation, null, 2),
|
|
775
|
-
},
|
|
776
|
-
],
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
validateCapabilityClaim(vendorName, safeguard, claimedCapability, supportingText) {
|
|
780
|
-
const text = supportingText.toLowerCase();
|
|
781
|
-
// Detect tool type and validate domain match
|
|
782
|
-
const detectedToolType = this.detectToolType(supportingText, safeguard.id);
|
|
783
|
-
const domainValidation = this.validateDomainMatch(safeguard.id, claimedCapability, detectedToolType);
|
|
784
|
-
// Adjust capability if domain mismatch detected
|
|
785
|
-
let effectiveCapability = claimedCapability;
|
|
786
|
-
let originalClaim;
|
|
787
|
-
if (domainValidation.should_adjust_capability) {
|
|
788
|
-
originalClaim = claimedCapability;
|
|
789
|
-
effectiveCapability = domainValidation.adjusted_capability;
|
|
790
|
-
}
|
|
791
|
-
// Perform capability-focused analysis of the supporting text
|
|
792
|
-
const actualAnalysis = this.performCapabilityAnalysis(vendorName, safeguard, supportingText);
|
|
793
|
-
// Validate claimed capability against actual capability analysis and domain requirements
|
|
794
|
-
const validation = this.assessCapabilityClaimAlignment(claimedCapability, effectiveCapability, actualAnalysis, domainValidation, text);
|
|
795
|
-
// Add domain validation gaps if capability was adjusted
|
|
796
|
-
if (domainValidation.should_adjust_capability) {
|
|
797
|
-
validation.gaps.unshift(`Domain mismatch: ${domainValidation.reasoning}`);
|
|
798
|
-
validation.recommendations.unshift(`Correct capability mapping should be '${effectiveCapability.toUpperCase()}' for ${detectedToolType} tools`);
|
|
799
|
-
// Reduce confidence for domain mismatches
|
|
800
|
-
validation.confidence = Math.max(validation.confidence - 20, 0);
|
|
801
|
-
}
|
|
802
|
-
return {
|
|
803
|
-
vendor: vendorName,
|
|
804
|
-
safeguard_id: safeguard.id,
|
|
805
|
-
safeguard_title: safeguard.title,
|
|
806
|
-
claimed_capability: claimedCapability,
|
|
807
|
-
validation_status: validation.status,
|
|
808
|
-
confidence_score: validation.confidence,
|
|
809
|
-
actual_capability_detected: actualAnalysis.capability,
|
|
810
|
-
capability_confidence: actualAnalysis.confidence,
|
|
811
|
-
tool_capability_description: actualAnalysis.toolCapabilityDescription,
|
|
812
|
-
recommended_use: actualAnalysis.recommendedUse,
|
|
813
|
-
domain_validation: {
|
|
814
|
-
required_tool_type: domainValidation.required_tool_types.join('/'),
|
|
815
|
-
detected_tool_type: detectedToolType,
|
|
816
|
-
domain_match: domainValidation.domain_match,
|
|
817
|
-
capability_adjusted: domainValidation.should_adjust_capability,
|
|
818
|
-
original_claim: originalClaim
|
|
819
|
-
},
|
|
820
|
-
gaps_identified: validation.gaps,
|
|
821
|
-
strengths_identified: validation.strengths,
|
|
822
|
-
recommendations: validation.recommendations,
|
|
823
|
-
detailed_feedback: validation.feedback
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
assessCapabilityClaimAlignment(claimedCapability, effectiveCapability, actualAnalysis, domainValidation, text) {
|
|
827
|
-
const gaps = [];
|
|
828
|
-
const strengths = [];
|
|
829
|
-
const recommendations = [];
|
|
830
|
-
// Compare claimed capability with what we actually detected
|
|
831
|
-
const claimedLower = claimedCapability.toLowerCase();
|
|
832
|
-
const effectiveLower = effectiveCapability.toLowerCase();
|
|
833
|
-
const actualCapability = actualAnalysis.capability;
|
|
834
|
-
let alignmentScore = 0;
|
|
835
|
-
// Check if claim aligns with actual analysis
|
|
836
|
-
if (claimedLower === actualCapability) {
|
|
837
|
-
alignmentScore = actualAnalysis.confidence;
|
|
838
|
-
strengths.push(`Claimed capability '${claimedCapability}' aligns with detected capability`);
|
|
839
|
-
strengths.push(`Tool demonstrates appropriate ${actualAnalysis.toolCapabilityDescription.toLowerCase()}`);
|
|
840
|
-
}
|
|
841
|
-
else if (effectiveLower === actualCapability) {
|
|
842
|
-
// Domain validation adjusted the capability and it matches the analysis
|
|
843
|
-
alignmentScore = Math.max(actualAnalysis.confidence - 20, 30);
|
|
844
|
-
gaps.push(`Original claim '${claimedCapability}' doesn't match tool type, adjusted to '${effectiveCapability}'`);
|
|
845
|
-
strengths.push(`Adjusted capability '${effectiveCapability}' aligns with tool analysis`);
|
|
846
|
-
}
|
|
847
|
-
else {
|
|
848
|
-
// Neither claimed nor effective capability matches the analysis
|
|
849
|
-
alignmentScore = Math.max(actualAnalysis.confidence - 40, 10);
|
|
850
|
-
gaps.push(`Claimed capability '${claimedCapability}' doesn't align with detected capability '${actualCapability}'`);
|
|
851
|
-
gaps.push(`Tool appears to be: ${actualAnalysis.toolCapabilityDescription}`);
|
|
852
|
-
}
|
|
853
|
-
// Add evidence from the capability analysis
|
|
854
|
-
if (actualAnalysis.evidence && actualAnalysis.evidence.length > 0) {
|
|
855
|
-
strengths.push(`Supporting evidence: ${actualAnalysis.evidence.slice(0, 2).join('; ')}`);
|
|
856
|
-
}
|
|
857
|
-
// Domain validation feedback
|
|
858
|
-
if (!domainValidation.domain_match) {
|
|
859
|
-
gaps.push(`Tool type '${domainValidation.detected_tool_type}' not appropriate for this safeguard domain`);
|
|
860
|
-
}
|
|
861
|
-
// Determine overall status based on alignment score
|
|
862
|
-
let status;
|
|
863
|
-
if (alignmentScore >= 70) {
|
|
864
|
-
status = 'SUPPORTED';
|
|
865
|
-
}
|
|
866
|
-
else if (alignmentScore >= 40) {
|
|
867
|
-
status = 'QUESTIONABLE';
|
|
868
|
-
}
|
|
869
|
-
else {
|
|
870
|
-
status = 'UNSUPPORTED';
|
|
871
|
-
}
|
|
872
|
-
// Generate capability-focused recommendations
|
|
873
|
-
recommendations.push(actualAnalysis.recommendedUse);
|
|
874
|
-
if (status === 'QUESTIONABLE') {
|
|
875
|
-
recommendations.push('Consider clarifying tool capabilities or adjusting capability claim');
|
|
876
|
-
}
|
|
877
|
-
if (!domainValidation.domain_match) {
|
|
878
|
-
recommendations.push(`For ${actualCapability.toUpperCase()} capability, focus on how the tool ${actualCapability === 'facilitates' ? 'enables and enhances' : actualCapability === 'governance' ? 'manages policies and processes for' : actualCapability === 'validates' ? 'provides evidence and reporting on' : 'directly implements'} this safeguard`);
|
|
879
|
-
}
|
|
880
|
-
const feedback = this.generateCapabilityValidationFeedback(claimedCapability, effectiveCapability, actualCapability, status, alignmentScore, domainValidation);
|
|
881
|
-
return {
|
|
882
|
-
status,
|
|
883
|
-
confidence: Math.round(alignmentScore),
|
|
884
|
-
gaps,
|
|
885
|
-
strengths,
|
|
886
|
-
recommendations,
|
|
887
|
-
feedback
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
hasFacilitationLanguage(text) {
|
|
891
|
-
const facilitationPatterns = [
|
|
892
|
-
'enables', 'facilitates', 'supports', 'helps', 'provides framework',
|
|
893
|
-
'creates infrastructure', 'enables organizations', 'supports compliance',
|
|
894
|
-
'provides data', 'enriches', 'supplements', 'feeds data'
|
|
895
|
-
];
|
|
896
|
-
return facilitationPatterns.some(pattern => text.includes(pattern));
|
|
897
|
-
}
|
|
898
|
-
hasScopeBoundaries(text) {
|
|
899
|
-
const boundaryPatterns = [
|
|
900
|
-
'covers', 'but not', 'limited to', 'specifically', 'only', 'excludes',
|
|
901
|
-
'scope includes', 'scope excludes', 'within', 'applies to'
|
|
902
|
-
];
|
|
903
|
-
return boundaryPatterns.some(pattern => text.includes(pattern));
|
|
904
|
-
}
|
|
905
|
-
hasDirectImplementationLanguage(text) {
|
|
906
|
-
const implementationPatterns = [
|
|
907
|
-
'directly implements', 'performs', 'executes', 'scans', 'catalogs',
|
|
908
|
-
'inventories', 'manages assets', 'controls access', 'blocks threats'
|
|
909
|
-
];
|
|
910
|
-
return implementationPatterns.some(pattern => text.includes(pattern));
|
|
911
|
-
}
|
|
912
|
-
assessImplementationLanguage(text) {
|
|
913
|
-
const implementationKeywords = [
|
|
914
|
-
'implements', 'performs', 'executes', 'manages', 'controls', 'maintains',
|
|
915
|
-
'establishes', 'configures', 'monitors', 'tracks', 'inventories'
|
|
916
|
-
];
|
|
917
|
-
return this.calculateKeywordScore(text, implementationKeywords) * 100;
|
|
918
|
-
}
|
|
919
|
-
assessFacilitationLanguage(text) {
|
|
920
|
-
const facilitationKeywords = [
|
|
921
|
-
'enables', 'facilitates', 'supports', 'helps', 'enhances', 'improves',
|
|
922
|
-
'streamlines', 'automates', 'optimizes', 'provides framework'
|
|
923
|
-
];
|
|
924
|
-
return this.calculateKeywordScore(text, facilitationKeywords) * 100;
|
|
925
|
-
}
|
|
926
|
-
assessGovernanceLanguage(text) {
|
|
927
|
-
const governanceKeywords = [
|
|
928
|
-
'policy', 'governance', 'compliance', 'oversight', 'management',
|
|
929
|
-
'framework', 'procedures', 'processes', 'controls', 'standards'
|
|
930
|
-
];
|
|
931
|
-
return this.calculateKeywordScore(text, governanceKeywords) * 100;
|
|
932
|
-
}
|
|
933
|
-
assessValidationLanguage(text) {
|
|
934
|
-
const validationKeywords = [
|
|
935
|
-
'audit', 'validate', 'verify', 'report', 'evidence', 'monitor',
|
|
936
|
-
'assess', 'check', 'review', 'attest', 'compliance', 'compliance tracking',
|
|
937
|
-
'compliance reports', 'audit capabilities', 'audit trail', 'reporting capabilities'
|
|
938
|
-
];
|
|
939
|
-
return this.calculateKeywordScore(text, validationKeywords) * 100;
|
|
940
|
-
}
|
|
941
|
-
detectToolType(text, safeguardId) {
|
|
942
|
-
const lowerText = text.toLowerCase();
|
|
943
|
-
// Enhanced keyword definitions with scoring weights and context
|
|
944
|
-
const toolTypePatterns = {
|
|
945
|
-
'inventory': {
|
|
946
|
-
primary: [
|
|
947
|
-
'asset management', 'inventory management', 'cmdb', 'configuration management database',
|
|
948
|
-
'asset discovery', 'hardware inventory', 'software inventory', 'device inventory',
|
|
949
|
-
'asset tracking', 'it asset management', 'endpoint discovery', 'asset lifecycle'
|
|
950
|
-
],
|
|
951
|
-
secondary: [
|
|
952
|
-
'inventory', 'discovery', 'device management', 'endpoint management',
|
|
953
|
-
'configuration management', 'asset database', 'equipment tracking'
|
|
954
|
-
],
|
|
955
|
-
weight: { primary: 3, secondary: 1 }
|
|
956
|
-
},
|
|
957
|
-
'identity_management': {
|
|
958
|
-
primary: [
|
|
959
|
-
'identity management', 'iam', 'active directory', 'identity provider',
|
|
960
|
-
'single sign-on', 'sso', 'multi-factor authentication', 'mfa',
|
|
961
|
-
'user management', 'account management', 'directory service'
|
|
962
|
-
],
|
|
963
|
-
secondary: [
|
|
964
|
-
'ldap', 'authentication', 'access management', 'identity access management',
|
|
965
|
-
'user directory', 'account lifecycle', 'privileged access'
|
|
966
|
-
],
|
|
967
|
-
weight: { primary: 3, secondary: 1 }
|
|
968
|
-
},
|
|
969
|
-
'vulnerability_management': {
|
|
970
|
-
primary: [
|
|
971
|
-
'vulnerability management', 'vulnerability scanner', 'patch management',
|
|
972
|
-
'security scanning', 'vulnerability assessment', 'penetration testing'
|
|
973
|
-
],
|
|
974
|
-
secondary: [
|
|
975
|
-
'vuln scan', 'security scanner', 'network scanner', 'scanning capabilities',
|
|
976
|
-
'security assessment', 'vulnerability scanning', 'patch deployment'
|
|
977
|
-
],
|
|
978
|
-
weight: { primary: 3, secondary: 1 }
|
|
979
|
-
},
|
|
980
|
-
'threat_intelligence': {
|
|
981
|
-
primary: [
|
|
982
|
-
'threat intelligence', 'cyber threat intelligence', 'threat intel',
|
|
983
|
-
'threat feed', 'ioc feed', 'security intelligence', 'threat data'
|
|
984
|
-
],
|
|
985
|
-
secondary: [
|
|
986
|
-
'enrichment', 'data feed', 'contextual data', 'risk intelligence',
|
|
987
|
-
'threat indicators', 'intelligence platform', 'threat correlation'
|
|
988
|
-
],
|
|
989
|
-
weight: { primary: 3, secondary: 1 }
|
|
990
|
-
},
|
|
991
|
-
'network_security': {
|
|
992
|
-
primary: [
|
|
993
|
-
'firewall', 'network access control', 'nac', 'intrusion detection',
|
|
994
|
-
'network security', 'network segmentation'
|
|
995
|
-
],
|
|
996
|
-
secondary: [
|
|
997
|
-
'network monitoring', 'traffic analysis', 'perimeter security',
|
|
998
|
-
'network protection', 'network filtering'
|
|
999
|
-
],
|
|
1000
|
-
weight: { primary: 3, secondary: 1 }
|
|
1001
|
-
},
|
|
1002
|
-
'governance': {
|
|
1003
|
-
primary: [
|
|
1004
|
-
'grc', 'governance risk compliance', 'compliance management',
|
|
1005
|
-
'policy management', 'risk management', 'audit management'
|
|
1006
|
-
],
|
|
1007
|
-
secondary: [
|
|
1008
|
-
'governance', 'compliance platform', 'policy enforcement',
|
|
1009
|
-
'regulatory compliance', 'framework management'
|
|
1010
|
-
],
|
|
1011
|
-
weight: { primary: 3, secondary: 1 }
|
|
1012
|
-
},
|
|
1013
|
-
'security_analytics': {
|
|
1014
|
-
primary: [
|
|
1015
|
-
'siem', 'security information event management', 'security analytics',
|
|
1016
|
-
'soar', 'security orchestration', 'log management'
|
|
1017
|
-
],
|
|
1018
|
-
secondary: [
|
|
1019
|
-
'security monitoring', 'event correlation', 'log analysis',
|
|
1020
|
-
'security intelligence', 'incident response platform'
|
|
1021
|
-
],
|
|
1022
|
-
weight: { primary: 3, secondary: 1 }
|
|
1023
|
-
}
|
|
1024
|
-
};
|
|
1025
|
-
// Calculate scores for each tool type
|
|
1026
|
-
const toolScores = {};
|
|
1027
|
-
for (const [toolType, patterns] of Object.entries(toolTypePatterns)) {
|
|
1028
|
-
let score = 0;
|
|
1029
|
-
// Check primary keywords
|
|
1030
|
-
for (const keyword of patterns.primary) {
|
|
1031
|
-
if (lowerText.includes(keyword)) {
|
|
1032
|
-
score += patterns.weight.primary;
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
// Check secondary keywords
|
|
1036
|
-
for (const keyword of patterns.secondary) {
|
|
1037
|
-
if (lowerText.includes(keyword)) {
|
|
1038
|
-
score += patterns.weight.secondary;
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
toolScores[toolType] = score;
|
|
1042
|
-
}
|
|
1043
|
-
// Apply safeguard-specific context weighting if provided
|
|
1044
|
-
if (safeguardId && toolScores) {
|
|
1045
|
-
const contextBonus = 1; // Small bonus for domain alignment
|
|
1046
|
-
// Asset inventory safeguards (1.1, 1.2) favor inventory tools
|
|
1047
|
-
if (['1.1', '1.2'].includes(safeguardId) && toolScores['inventory'] > 0) {
|
|
1048
|
-
toolScores['inventory'] += contextBonus;
|
|
1049
|
-
}
|
|
1050
|
-
// Account inventory safeguards (5.1, 5.2, 5.3) favor identity tools
|
|
1051
|
-
if (['5.1', '5.2', '5.3'].includes(safeguardId) && toolScores['identity_management'] > 0) {
|
|
1052
|
-
toolScores['identity_management'] += contextBonus;
|
|
1053
|
-
}
|
|
1054
|
-
// Authentication safeguards (6.1, 6.2, 6.3) favor identity tools
|
|
1055
|
-
if (['6.1', '6.2', '6.3'].includes(safeguardId) && toolScores['identity_management'] > 0) {
|
|
1056
|
-
toolScores['identity_management'] += contextBonus;
|
|
1057
|
-
}
|
|
1058
|
-
// Vulnerability safeguards (7.1-7.7) favor vulnerability management tools
|
|
1059
|
-
if (['7.1', '7.2', '7.3', '7.4', '7.5', '7.6', '7.7'].includes(safeguardId) && toolScores['vulnerability_management'] > 0) {
|
|
1060
|
-
toolScores['vulnerability_management'] += contextBonus;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
// Find the tool type with highest score
|
|
1064
|
-
let maxScore = 0;
|
|
1065
|
-
let detectedType = 'unknown';
|
|
1066
|
-
for (const [toolType, score] of Object.entries(toolScores)) {
|
|
1067
|
-
if (score > maxScore) {
|
|
1068
|
-
maxScore = score;
|
|
1069
|
-
detectedType = toolType;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
// Require minimum score threshold to avoid false positives
|
|
1073
|
-
return maxScore >= 2 ? detectedType : 'unknown';
|
|
1074
|
-
}
|
|
1075
|
-
validateDomainMatch(safeguardId, claimedCapability, detectedToolType) {
|
|
1076
|
-
const domainReq = SAFEGUARD_DOMAIN_REQUIREMENTS[safeguardId];
|
|
1077
|
-
if (!domainReq) {
|
|
1078
|
-
return {
|
|
1079
|
-
domain_match: true,
|
|
1080
|
-
required_tool_types: [],
|
|
1081
|
-
should_adjust_capability: false,
|
|
1082
|
-
reasoning: 'No domain restrictions defined for this safeguard'
|
|
1083
|
-
};
|
|
1084
|
-
}
|
|
1085
|
-
const isFullOrPartial = ['full', 'partial'].includes(claimedCapability.toLowerCase());
|
|
1086
|
-
const toolTypeMatches = domainReq.required_tool_types.includes(detectedToolType);
|
|
1087
|
-
if (isFullOrPartial && !toolTypeMatches) {
|
|
1088
|
-
return {
|
|
1089
|
-
domain_match: false,
|
|
1090
|
-
required_tool_types: domainReq.required_tool_types,
|
|
1091
|
-
should_adjust_capability: true,
|
|
1092
|
-
adjusted_capability: 'facilitates',
|
|
1093
|
-
reasoning: `${domainReq.domain} safeguard requires ${domainReq.required_tool_types.join('/')} tool types for FULL/PARTIAL implementation capability. Detected tool type '${detectedToolType}' can only facilitate implementation.`
|
|
1094
|
-
};
|
|
1095
|
-
}
|
|
1096
|
-
return {
|
|
1097
|
-
domain_match: true,
|
|
1098
|
-
required_tool_types: domainReq.required_tool_types,
|
|
1099
|
-
should_adjust_capability: false,
|
|
1100
|
-
reasoning: toolTypeMatches ?
|
|
1101
|
-
`Tool type '${detectedToolType}' is appropriate for ${domainReq.domain} safeguard` :
|
|
1102
|
-
'No domain validation required for this capability type'
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
|
-
generateCapabilityValidationFeedback(claimedCapability, effectiveCapability, actualCapability, status, score, domainValidation) {
|
|
1106
|
-
let feedback = `Capability Validation: ${claimedCapability.toUpperCase()} claim is ${status} (${Math.round(score)}% confidence)\n\n`;
|
|
1107
|
-
// Analysis summary
|
|
1108
|
-
feedback += `ANALYSIS:\n`;
|
|
1109
|
-
feedback += `• Claimed: ${claimedCapability.toUpperCase()}\n`;
|
|
1110
|
-
if (effectiveCapability !== claimedCapability) {
|
|
1111
|
-
feedback += `• Domain Adjusted: ${effectiveCapability.toUpperCase()} (${domainValidation.reasoning})\n`;
|
|
1112
|
-
}
|
|
1113
|
-
feedback += `• Detected: ${actualCapability.toUpperCase()}\n`;
|
|
1114
|
-
feedback += `• Tool Type: ${domainValidation.detected_tool_type}\n\n`;
|
|
1115
|
-
// Assessment
|
|
1116
|
-
feedback += `ASSESSMENT: `;
|
|
1117
|
-
if (claimedCapability.toLowerCase() === actualCapability) {
|
|
1118
|
-
feedback += 'Claimed capability aligns perfectly with tool analysis.';
|
|
1119
|
-
}
|
|
1120
|
-
else if (effectiveCapability.toLowerCase() === actualCapability) {
|
|
1121
|
-
feedback += 'After domain validation adjustment, capability aligns with tool analysis.';
|
|
1122
|
-
}
|
|
1123
|
-
else {
|
|
1124
|
-
feedback += `Capability mismatch detected. Tool functions as ${actualCapability.toUpperCase()} rather than claimed ${claimedCapability.toUpperCase()}.`;
|
|
1125
|
-
}
|
|
1126
|
-
return feedback;
|
|
1127
|
-
}
|
|
1128
|
-
generateValidationFeedback(claimedCapability, status, gaps, strengths, score) {
|
|
1129
|
-
let feedback = `Validation of ${claimedCapability.toUpperCase()} capability role claim: ${status} (${Math.round(score)}% alignment)\n\n`;
|
|
1130
|
-
if (strengths.length > 0) {
|
|
1131
|
-
feedback += `STRENGTHS:\n${strengths.map(s => `• ${s}`).join('\n')}\n\n`;
|
|
1132
|
-
}
|
|
1133
|
-
if (gaps.length > 0) {
|
|
1134
|
-
feedback += `GAPS IDENTIFIED:\n${gaps.map(g => `• ${g}`).join('\n')}\n\n`;
|
|
1135
|
-
}
|
|
1136
|
-
feedback += `ASSESSMENT: `;
|
|
1137
|
-
switch (status) {
|
|
1138
|
-
case 'SUPPORTED':
|
|
1139
|
-
feedback += 'The vendor\'s supporting evidence strongly aligns with their claimed capability role.';
|
|
1140
|
-
break;
|
|
1141
|
-
case 'QUESTIONABLE':
|
|
1142
|
-
feedback += 'The vendor\'s evidence partially supports their claim but has notable gaps or inconsistencies.';
|
|
1143
|
-
break;
|
|
1144
|
-
case 'UNSUPPORTED':
|
|
1145
|
-
feedback += 'The vendor\'s evidence does not adequately support their claimed capability role.';
|
|
1146
|
-
break;
|
|
1147
|
-
}
|
|
1148
|
-
return feedback;
|
|
1149
|
-
}
|
|
1150
|
-
// Enhanced error handling and formatting
|
|
1151
|
-
formatErrorMessage(error, toolName) {
|
|
1152
|
-
if (error instanceof Error) {
|
|
1153
|
-
// Production-friendly error messages
|
|
1154
|
-
switch (error.message) {
|
|
1155
|
-
case /^Safeguard .+ not found/.test(error.message) ? error.message : '':
|
|
1156
|
-
return `❌ Invalid safeguard ID. Use 'list_available_safeguards' to see available CIS Control safeguards.`;
|
|
1157
|
-
case /^Unknown tool/.test(error.message) ? error.message : '':
|
|
1158
|
-
return `❌ Tool '${toolName}' is not available. Available tools: analyze_vendor_response, validate_vendor_mapping, get_safeguard_details, list_available_safeguards.`;
|
|
1159
|
-
default:
|
|
1160
|
-
return `❌ Error in ${toolName}: ${error.message}`;
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
return `❌ Unexpected error in ${toolName}: ${String(error)}`;
|
|
1164
|
-
}
|
|
1165
|
-
getCachedSafeguardDetails(safeguardId, includeExamples) {
|
|
1166
|
-
const cacheKey = `${safeguardId}_${includeExamples}`;
|
|
1167
|
-
const cached = this.cache.safeguardDetails.get(cacheKey);
|
|
1168
|
-
if (cached && (Date.now() - cached.timestamp < 5 * 60 * 1000)) { // 5 minute cache
|
|
1169
|
-
return cached.data;
|
|
1170
|
-
}
|
|
1171
|
-
return null;
|
|
1172
|
-
}
|
|
1173
|
-
setCachedSafeguardDetails(safeguardId, includeExamples, data) {
|
|
1174
|
-
const cacheKey = `${safeguardId}_${includeExamples}`;
|
|
1175
|
-
this.cache.safeguardDetails.set(cacheKey, {
|
|
1176
|
-
data,
|
|
1177
|
-
timestamp: Date.now()
|
|
1178
|
-
});
|
|
1179
|
-
// Periodic cache cleanup to prevent memory leaks
|
|
1180
|
-
if (Date.now() - this.cache.lastCacheCleanup > 10 * 60 * 1000) { // 10 minutes
|
|
1181
|
-
this.cleanupCache();
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
cleanupCache() {
|
|
1185
|
-
const now = Date.now();
|
|
1186
|
-
const maxAge = 10 * 60 * 1000; // 10 minutes
|
|
1187
|
-
// Clean up safeguard details cache
|
|
1188
|
-
for (const [key, value] of this.cache.safeguardDetails.entries()) {
|
|
1189
|
-
if (now - value.timestamp > maxAge) {
|
|
1190
|
-
this.cache.safeguardDetails.delete(key);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
this.cache.lastCacheCleanup = now;
|
|
1194
|
-
}
|
|
1195
|
-
recordToolExecution(toolName, executionTime) {
|
|
1196
|
-
this.performanceMetrics.totalRequests++;
|
|
1197
|
-
if (!this.performanceMetrics.toolExecutionTimes.has(toolName)) {
|
|
1198
|
-
this.performanceMetrics.toolExecutionTimes.set(toolName, []);
|
|
1199
|
-
}
|
|
1200
|
-
const times = this.performanceMetrics.toolExecutionTimes.get(toolName);
|
|
1201
|
-
times.push(executionTime);
|
|
1202
|
-
// Keep only last 100 measurements for memory efficiency
|
|
1203
|
-
if (times.length > 100) {
|
|
1204
|
-
times.shift();
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
getPerformanceStats() {
|
|
1208
|
-
const uptime = Date.now() - this.performanceMetrics.startTime;
|
|
1209
|
-
const avgTimes = Array.from(this.performanceMetrics.toolExecutionTimes.entries())
|
|
1210
|
-
.map(([tool, times]) => {
|
|
1211
|
-
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
|
1212
|
-
return `${tool}: ${avg.toFixed(2)}ms`;
|
|
1213
|
-
});
|
|
1214
|
-
return `Performance Stats (Uptime: ${(uptime / 1000).toFixed(1)}s, Requests: ${this.performanceMetrics.totalRequests}, Errors: ${this.performanceMetrics.errorCount})\n${avgTimes.join(', ')}`;
|
|
1215
|
-
}
|
|
1216
|
-
// Input validation and sanitization
|
|
1217
|
-
validateSafeguardId(safeguardId) {
|
|
1218
|
-
if (!safeguardId || typeof safeguardId !== 'string') {
|
|
1219
|
-
throw new Error('Safeguard ID is required and must be a string');
|
|
1220
|
-
}
|
|
1221
|
-
if (!/^[0-9]+\.[0-9]+$/.test(safeguardId)) {
|
|
1222
|
-
throw new Error('Safeguard ID must be in format "X.Y" (e.g., "1.1", "5.1")');
|
|
1223
|
-
}
|
|
1224
|
-
if (!this.safeguardManager.getSafeguardDetails(safeguardId)) {
|
|
1225
|
-
throw new Error(`Safeguard ${safeguardId} not found`);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
validateTextInput(text, fieldName) {
|
|
1229
|
-
if (!text || typeof text !== 'string') {
|
|
1230
|
-
throw new Error(`${fieldName} is required and must be a string`);
|
|
1231
|
-
}
|
|
1232
|
-
if (text.length > 10000) {
|
|
1233
|
-
throw new Error(`${fieldName} must be less than 10,000 characters`);
|
|
1234
|
-
}
|
|
1235
|
-
if (text.trim().length < 10) {
|
|
1236
|
-
throw new Error(`${fieldName} must contain at least 10 meaningful characters`);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
validateCapability(capability) {
|
|
1240
|
-
const validCapabilities = ['full', 'partial', 'facilitates', 'governance', 'validates'];
|
|
1241
|
-
if (!validCapabilities.includes(capability.toLowerCase())) {
|
|
1242
|
-
throw new Error(`Invalid capability. Must be one of: ${validCapabilities.join(', ')}`);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
async run() {
|
|
1246
|
-
const transport = new StdioServerTransport();
|
|
1247
|
-
await this.server.connect(transport);
|
|
1248
|
-
console.error('FrameworkMCP server running on stdio');
|
|
1249
|
-
// Log performance stats every 5 minutes in production
|
|
1250
|
-
if (process.env.NODE_ENV === 'production') {
|
|
1251
|
-
setInterval(() => {
|
|
1252
|
-
console.error(`[FrameworkMCP] ${this.getPerformanceStats()}`);
|
|
1253
|
-
}, 5 * 60 * 1000);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
const server = new GRCAnalysisServer();
|
|
1258
|
-
server.run().catch(console.error);
|
|
1259
14
|
//# sourceMappingURL=index.js.map
|