contextguard 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -17
- package/README.md +157 -109
- package/dist/agent.d.ts +24 -0
- package/dist/agent.js +369 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +266 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +56 -0
- package/dist/database.d.ts +116 -0
- package/dist/database.js +291 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +18 -0
- package/dist/init.d.ts +7 -0
- package/dist/init.js +173 -0
- package/dist/lib/supabase-client.d.ts +27 -0
- package/dist/lib/supabase-client.js +97 -0
- package/dist/logger.d.ts +36 -0
- package/dist/logger.js +145 -0
- package/dist/mcp-security-wrapper.d.ts +84 -0
- package/dist/mcp-security-wrapper.js +394 -120
- package/dist/mcp-traceability-integration.d.ts +118 -0
- package/dist/mcp-traceability-integration.js +302 -0
- package/dist/policy.d.ts +30 -0
- package/dist/policy.js +273 -0
- package/dist/premium-features.d.ts +364 -0
- package/dist/premium-features.js +950 -0
- package/dist/security-logger.d.ts +45 -0
- package/dist/security-logger.js +125 -0
- package/dist/security-policy.d.ts +55 -0
- package/dist/security-policy.js +140 -0
- package/dist/semantic-detector.d.ts +21 -0
- package/dist/semantic-detector.js +49 -0
- package/dist/sse-proxy.d.ts +21 -0
- package/dist/sse-proxy.js +276 -0
- package/dist/supabase-client.d.ts +27 -0
- package/dist/supabase-client.js +89 -0
- package/dist/types/database.types.d.ts +220 -0
- package/dist/types/database.types.js +8 -0
- package/dist/types/mcp.d.ts +27 -0
- package/dist/types/mcp.js +15 -0
- package/dist/types/types.d.ts +65 -0
- package/dist/types/types.js +8 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.js +8 -0
- package/dist/wrapper.d.ts +115 -0
- package/dist/wrapper.js +417 -0
- package/package.json +35 -10
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -57
- package/CONTRIBUTING.md +0 -532
- package/SECURITY.md +0 -254
- package/assets/demo.mp4 +0 -0
- package/eslint.config.mts +0 -23
- package/examples/config/config.json +0 -19
- package/examples/mcp-server/demo.js +0 -228
- package/examples/mcp-server/package-lock.json +0 -978
- package/examples/mcp-server/package.json +0 -16
- package/examples/mcp-server/pnpm-lock.yaml +0 -745
- package/src/mcp-security-wrapper.ts +0 -529
- package/test/test-server.ts +0 -295
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Amir Mironi
|
|
4
|
+
*
|
|
5
|
+
* Premium/Enterprise Features for ContextGuard
|
|
6
|
+
* These features require a valid license key
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.ContextTracker = exports.MCPTraceabilityManager = exports.SLAMonitor = exports.PrioritySupport = exports.ComplianceReporter = exports.MLDetectionEngine = exports.SSOProvider = exports.CustomRulesEngine = exports.TeamCollaboration = exports.DashboardAnalytics = exports.LicenseManager = exports.LicenseTier = void 0;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const crypto = __importStar(require("crypto"));
|
|
45
|
+
var LicenseTier;
|
|
46
|
+
(function (LicenseTier) {
|
|
47
|
+
LicenseTier["FREE"] = "free";
|
|
48
|
+
LicenseTier["PRO"] = "pro";
|
|
49
|
+
LicenseTier["ENTERPRISE"] = "enterprise";
|
|
50
|
+
})(LicenseTier || (exports.LicenseTier = LicenseTier = {}));
|
|
51
|
+
/**
|
|
52
|
+
* License Manager - Validates and manages license keys
|
|
53
|
+
*/
|
|
54
|
+
class LicenseManager {
|
|
55
|
+
constructor(licenseFilePath = ".contextguard-license") {
|
|
56
|
+
this.licenseInfo = null;
|
|
57
|
+
this.licenseFilePath = licenseFilePath;
|
|
58
|
+
this.loadLicense();
|
|
59
|
+
}
|
|
60
|
+
loadLicense() {
|
|
61
|
+
try {
|
|
62
|
+
if (fs.existsSync(this.licenseFilePath)) {
|
|
63
|
+
const licenseData = JSON.parse(fs.readFileSync(this.licenseFilePath, "utf-8"));
|
|
64
|
+
this.licenseInfo = {
|
|
65
|
+
...licenseData,
|
|
66
|
+
expiresAt: new Date(licenseData.expiresAt),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Default to free tier
|
|
71
|
+
this.licenseInfo = {
|
|
72
|
+
tier: LicenseTier.FREE,
|
|
73
|
+
key: "free",
|
|
74
|
+
expiresAt: new Date("2099-12-31"),
|
|
75
|
+
features: [],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error("Failed to load license:", error);
|
|
81
|
+
this.licenseInfo = {
|
|
82
|
+
tier: LicenseTier.FREE,
|
|
83
|
+
key: "free",
|
|
84
|
+
expiresAt: new Date("2099-12-31"),
|
|
85
|
+
features: [],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
validateLicense() {
|
|
90
|
+
if (!this.licenseInfo)
|
|
91
|
+
return false;
|
|
92
|
+
if (this.licenseInfo.tier === LicenseTier.FREE)
|
|
93
|
+
return true;
|
|
94
|
+
return new Date() < this.licenseInfo.expiresAt;
|
|
95
|
+
}
|
|
96
|
+
getTier() {
|
|
97
|
+
return this.licenseInfo?.tier || LicenseTier.FREE;
|
|
98
|
+
}
|
|
99
|
+
hasFeature(feature) {
|
|
100
|
+
if (!this.licenseInfo)
|
|
101
|
+
return false;
|
|
102
|
+
if (this.licenseInfo.tier === LicenseTier.FREE)
|
|
103
|
+
return false;
|
|
104
|
+
return this.licenseInfo.features.includes(feature);
|
|
105
|
+
}
|
|
106
|
+
getLicenseInfo() {
|
|
107
|
+
return this.licenseInfo;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.LicenseManager = LicenseManager;
|
|
111
|
+
/**
|
|
112
|
+
* Web Dashboard Analytics - Collects and aggregates security metrics
|
|
113
|
+
* PRO FEATURE
|
|
114
|
+
*/
|
|
115
|
+
class DashboardAnalytics {
|
|
116
|
+
constructor(licenseManager) {
|
|
117
|
+
this.analyticsData = [];
|
|
118
|
+
this.maxStoredEvents = 10000;
|
|
119
|
+
this.licenseManager = licenseManager;
|
|
120
|
+
}
|
|
121
|
+
trackEvent(eventType, severity, metadata) {
|
|
122
|
+
if (!this.licenseManager.hasFeature("dashboard")) {
|
|
123
|
+
console.warn("Dashboard analytics requires a Pro license. Upgrade at https://contextguard.dev/pro");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const event = {
|
|
127
|
+
timestamp: new Date(),
|
|
128
|
+
eventType,
|
|
129
|
+
severity,
|
|
130
|
+
metadata,
|
|
131
|
+
};
|
|
132
|
+
this.analyticsData.push(event);
|
|
133
|
+
// Keep only recent events to prevent memory issues
|
|
134
|
+
if (this.analyticsData.length > this.maxStoredEvents) {
|
|
135
|
+
this.analyticsData = this.analyticsData.slice(-this.maxStoredEvents);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
getMetrics(timeRange) {
|
|
139
|
+
if (!this.licenseManager.hasFeature("dashboard")) {
|
|
140
|
+
return { error: "Dashboard analytics requires a Pro license" };
|
|
141
|
+
}
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const ranges = {
|
|
144
|
+
"1h": 60 * 60 * 1000,
|
|
145
|
+
"24h": 24 * 60 * 60 * 1000,
|
|
146
|
+
"7d": 7 * 24 * 60 * 60 * 1000,
|
|
147
|
+
"30d": 30 * 24 * 60 * 60 * 1000,
|
|
148
|
+
};
|
|
149
|
+
const cutoff = now - ranges[timeRange];
|
|
150
|
+
const filteredEvents = this.analyticsData.filter((e) => e.timestamp.getTime() > cutoff);
|
|
151
|
+
return {
|
|
152
|
+
totalEvents: filteredEvents.length,
|
|
153
|
+
eventsByType: this.groupBy(filteredEvents, "eventType"),
|
|
154
|
+
eventsBySeverity: this.groupBy(filteredEvents, "severity"),
|
|
155
|
+
timeline: this.getTimeline(filteredEvents),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
groupBy(events, field) {
|
|
159
|
+
const grouped = {};
|
|
160
|
+
for (const event of events) {
|
|
161
|
+
const key = String(event[field]);
|
|
162
|
+
grouped[key] = (grouped[key] || 0) + 1;
|
|
163
|
+
}
|
|
164
|
+
return grouped;
|
|
165
|
+
}
|
|
166
|
+
getTimeline(events) {
|
|
167
|
+
const timeline = {};
|
|
168
|
+
for (const event of events) {
|
|
169
|
+
const hour = new Date(event.timestamp).toISOString().slice(0, 13);
|
|
170
|
+
timeline[hour] = (timeline[hour] || 0) + 1;
|
|
171
|
+
}
|
|
172
|
+
return Object.entries(timeline).map(([time, count]) => ({ time, count }));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.DashboardAnalytics = DashboardAnalytics;
|
|
176
|
+
/**
|
|
177
|
+
* Team Collaboration Manager
|
|
178
|
+
* PRO FEATURE
|
|
179
|
+
*/
|
|
180
|
+
class TeamCollaboration {
|
|
181
|
+
constructor(licenseManager) {
|
|
182
|
+
this.teamMembers = new Map();
|
|
183
|
+
this.licenseManager = licenseManager;
|
|
184
|
+
}
|
|
185
|
+
addMember(member) {
|
|
186
|
+
if (!this.licenseManager.hasFeature("team-collaboration")) {
|
|
187
|
+
console.warn("Team collaboration requires a Pro license. Upgrade at https://contextguard.dev/pro");
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
const licenseInfo = this.licenseManager.getLicenseInfo();
|
|
191
|
+
if (licenseInfo?.maxUsers && this.teamMembers.size >= licenseInfo.maxUsers) {
|
|
192
|
+
console.warn(`Maximum team size (${licenseInfo.maxUsers}) reached`);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
this.teamMembers.set(member.userId, member);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
removeMember(userId) {
|
|
199
|
+
if (!this.licenseManager.hasFeature("team-collaboration")) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
return this.teamMembers.delete(userId);
|
|
203
|
+
}
|
|
204
|
+
getMember(userId) {
|
|
205
|
+
return this.teamMembers.get(userId);
|
|
206
|
+
}
|
|
207
|
+
listMembers() {
|
|
208
|
+
if (!this.licenseManager.hasFeature("team-collaboration")) {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
return Array.from(this.teamMembers.values());
|
|
212
|
+
}
|
|
213
|
+
hasPermission(userId, permission) {
|
|
214
|
+
const member = this.teamMembers.get(userId);
|
|
215
|
+
if (!member)
|
|
216
|
+
return false;
|
|
217
|
+
return member.permissions.includes(permission) || member.role === "ADMIN";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.TeamCollaboration = TeamCollaboration;
|
|
221
|
+
/**
|
|
222
|
+
* Custom Detection Rules Engine
|
|
223
|
+
* PRO FEATURE
|
|
224
|
+
*/
|
|
225
|
+
class CustomRulesEngine {
|
|
226
|
+
constructor(licenseManager) {
|
|
227
|
+
this.rules = new Map();
|
|
228
|
+
this.licenseManager = licenseManager;
|
|
229
|
+
}
|
|
230
|
+
addRule(rule) {
|
|
231
|
+
if (!this.licenseManager.hasFeature("custom-rules")) {
|
|
232
|
+
console.warn("Custom detection rules require a Pro license. Upgrade at https://contextguard.dev/pro");
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
this.rules.set(rule.id, rule);
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
removeRule(ruleId) {
|
|
239
|
+
if (!this.licenseManager.hasFeature("custom-rules")) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
return this.rules.delete(ruleId);
|
|
243
|
+
}
|
|
244
|
+
evaluateRules(text) {
|
|
245
|
+
if (!this.licenseManager.hasFeature("custom-rules")) {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
const results = [];
|
|
249
|
+
for (const rule of this.rules.values()) {
|
|
250
|
+
if (!rule.enabled)
|
|
251
|
+
continue;
|
|
252
|
+
const matches = text.match(rule.pattern);
|
|
253
|
+
if (matches && matches.length > 0) {
|
|
254
|
+
results.push({ rule, matches: Array.from(matches) });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return results;
|
|
258
|
+
}
|
|
259
|
+
listRules() {
|
|
260
|
+
if (!this.licenseManager.hasFeature("custom-rules")) {
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
return Array.from(this.rules.values());
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
exports.CustomRulesEngine = CustomRulesEngine;
|
|
267
|
+
/**
|
|
268
|
+
* SSO/SAML Authentication Provider
|
|
269
|
+
* ENTERPRISE FEATURE
|
|
270
|
+
*/
|
|
271
|
+
class SSOProvider {
|
|
272
|
+
constructor(licenseManager) {
|
|
273
|
+
this.samlConfig = null;
|
|
274
|
+
this.licenseManager = licenseManager;
|
|
275
|
+
}
|
|
276
|
+
configureSAML(config) {
|
|
277
|
+
if (!this.licenseManager.hasFeature("sso-saml")) {
|
|
278
|
+
console.warn("SSO/SAML requires an Enterprise license. Contact sales@contextguard.dev");
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
this.samlConfig = config;
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
validateSAMLToken(token) {
|
|
285
|
+
if (!this.licenseManager.hasFeature("sso-saml")) {
|
|
286
|
+
return { valid: false };
|
|
287
|
+
}
|
|
288
|
+
// Placeholder implementation
|
|
289
|
+
// In production, this would validate against SAML IdP
|
|
290
|
+
console.log("SAML token validation (placeholder):", token.substring(0, 20));
|
|
291
|
+
return { valid: true, userId: "saml-user-123" };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
exports.SSOProvider = SSOProvider;
|
|
295
|
+
/**
|
|
296
|
+
* Advanced ML-Based Detection
|
|
297
|
+
* PRO FEATURE
|
|
298
|
+
*/
|
|
299
|
+
class MLDetectionEngine {
|
|
300
|
+
constructor(licenseManager) {
|
|
301
|
+
this.anomalyThreshold = 0.75;
|
|
302
|
+
this.licenseManager = licenseManager;
|
|
303
|
+
}
|
|
304
|
+
detectAnomalies(text, _context) {
|
|
305
|
+
if (!this.licenseManager.hasFeature("ml-detection")) {
|
|
306
|
+
console.warn("ML-based detection requires a Pro license. Upgrade at https://contextguard.dev/pro");
|
|
307
|
+
return { isAnomaly: false, confidence: 0 };
|
|
308
|
+
}
|
|
309
|
+
// Placeholder implementation
|
|
310
|
+
// In production, this would use a trained ML model
|
|
311
|
+
const suspiciousPatterns = [
|
|
312
|
+
/eval\(/gi,
|
|
313
|
+
/exec\(/gi,
|
|
314
|
+
/system\(/gi,
|
|
315
|
+
/__import__/gi,
|
|
316
|
+
];
|
|
317
|
+
let suspicionScore = 0;
|
|
318
|
+
for (const pattern of suspiciousPatterns) {
|
|
319
|
+
if (pattern.test(text)) {
|
|
320
|
+
suspicionScore += 0.3;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const isAnomaly = suspicionScore >= this.anomalyThreshold;
|
|
324
|
+
return {
|
|
325
|
+
isAnomaly,
|
|
326
|
+
confidence: Math.min(suspicionScore, 1.0),
|
|
327
|
+
reason: isAnomaly ? "Suspicious code execution patterns detected" : undefined,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
exports.MLDetectionEngine = MLDetectionEngine;
|
|
332
|
+
/**
|
|
333
|
+
* Compliance Reporting Generator
|
|
334
|
+
* PRO FEATURE
|
|
335
|
+
*/
|
|
336
|
+
class ComplianceReporter {
|
|
337
|
+
constructor(licenseManager) {
|
|
338
|
+
this.licenseManager = licenseManager;
|
|
339
|
+
}
|
|
340
|
+
generateReport(standard, _events) {
|
|
341
|
+
if (!this.licenseManager.hasFeature("compliance-reports")) {
|
|
342
|
+
console.warn("Compliance reports require a Pro license. Upgrade at https://contextguard.dev/pro");
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
const reportId = crypto.randomBytes(16).toString("hex");
|
|
346
|
+
const findings = [];
|
|
347
|
+
// Placeholder compliance checks
|
|
348
|
+
switch (standard) {
|
|
349
|
+
case "SOC2":
|
|
350
|
+
findings.push({
|
|
351
|
+
control: "CC6.1 - Logical Access Controls",
|
|
352
|
+
status: "PASS",
|
|
353
|
+
details: "Access controls are properly implemented",
|
|
354
|
+
});
|
|
355
|
+
findings.push({
|
|
356
|
+
control: "CC7.2 - System Monitoring",
|
|
357
|
+
status: "PASS",
|
|
358
|
+
details: "Security monitoring is active",
|
|
359
|
+
});
|
|
360
|
+
break;
|
|
361
|
+
case "GDPR":
|
|
362
|
+
findings.push({
|
|
363
|
+
control: "Article 32 - Security of Processing",
|
|
364
|
+
status: "PASS",
|
|
365
|
+
details: "Appropriate security measures in place",
|
|
366
|
+
});
|
|
367
|
+
break;
|
|
368
|
+
case "HIPAA":
|
|
369
|
+
findings.push({
|
|
370
|
+
control: "164.312(a)(1) - Access Control",
|
|
371
|
+
status: "PASS",
|
|
372
|
+
details: "Access controls implemented",
|
|
373
|
+
});
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
const hasFailures = findings.some((f) => f.status === "FAIL");
|
|
377
|
+
const hasWarnings = findings.some((f) => f.status === "WARNING");
|
|
378
|
+
return {
|
|
379
|
+
reportId,
|
|
380
|
+
generatedAt: new Date(),
|
|
381
|
+
standard,
|
|
382
|
+
findings,
|
|
383
|
+
status: hasFailures ? "FAIL" : hasWarnings ? "WARNING" : "PASS",
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
exportReport(report, format) {
|
|
387
|
+
if (format === "JSON") {
|
|
388
|
+
return JSON.stringify(report, null, 2);
|
|
389
|
+
}
|
|
390
|
+
// PDF generation would require additional libraries
|
|
391
|
+
return `Compliance Report - ${report.standard} (PDF export requires additional setup)`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
exports.ComplianceReporter = ComplianceReporter;
|
|
395
|
+
/**
|
|
396
|
+
* Priority Support Manager
|
|
397
|
+
* PRO/ENTERPRISE FEATURE
|
|
398
|
+
*/
|
|
399
|
+
class PrioritySupport {
|
|
400
|
+
constructor(licenseManager) {
|
|
401
|
+
this.licenseManager = licenseManager;
|
|
402
|
+
}
|
|
403
|
+
createTicket(subject, description, priority) {
|
|
404
|
+
if (!this.licenseManager.hasFeature("priority-support")) {
|
|
405
|
+
console.warn("Priority support requires a Pro license. Upgrade at https://contextguard.dev/pro");
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
const ticketId = `TICKET-${Date.now()}`;
|
|
409
|
+
const tier = this.licenseManager.getTier();
|
|
410
|
+
const slaHours = tier === LicenseTier.ENTERPRISE
|
|
411
|
+
? priority === "CRITICAL"
|
|
412
|
+
? "1 hour"
|
|
413
|
+
: "4 hours"
|
|
414
|
+
: "24 hours";
|
|
415
|
+
console.log(`Support ticket created: ${ticketId} (SLA: ${slaHours})`);
|
|
416
|
+
return { ticketId, sla: slaHours };
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
exports.PrioritySupport = PrioritySupport;
|
|
420
|
+
/**
|
|
421
|
+
* SLA Monitor
|
|
422
|
+
* ENTERPRISE FEATURE
|
|
423
|
+
*/
|
|
424
|
+
class SLAMonitor {
|
|
425
|
+
constructor(licenseManager) {
|
|
426
|
+
this.downtimeEvents = [];
|
|
427
|
+
this.licenseManager = licenseManager;
|
|
428
|
+
this.uptimeStart = new Date();
|
|
429
|
+
}
|
|
430
|
+
recordDowntime() {
|
|
431
|
+
if (!this.licenseManager.hasFeature("sla-guarantees")) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
this.downtimeEvents.push({ start: new Date() });
|
|
435
|
+
}
|
|
436
|
+
recordUptime() {
|
|
437
|
+
if (!this.licenseManager.hasFeature("sla-guarantees")) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const lastEvent = this.downtimeEvents[this.downtimeEvents.length - 1];
|
|
441
|
+
if (lastEvent && !lastEvent.end) {
|
|
442
|
+
lastEvent.end = new Date();
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
getUptimePercentage() {
|
|
446
|
+
if (!this.licenseManager.hasFeature("sla-guarantees")) {
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
const totalTime = Date.now() - this.uptimeStart.getTime();
|
|
450
|
+
let totalDowntime = 0;
|
|
451
|
+
for (const event of this.downtimeEvents) {
|
|
452
|
+
const end = event.end || new Date();
|
|
453
|
+
totalDowntime += end.getTime() - event.start.getTime();
|
|
454
|
+
}
|
|
455
|
+
return ((totalTime - totalDowntime) / totalTime) * 100;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
exports.SLAMonitor = SLAMonitor;
|
|
459
|
+
class MCPTraceabilityManager {
|
|
460
|
+
constructor(licenseManager, persistenceEnabled = true) {
|
|
461
|
+
this.traces = new Map();
|
|
462
|
+
this.maxTraces = 100000; // Keep last 100k traces in memory
|
|
463
|
+
this.persistenceEnabled = true;
|
|
464
|
+
this.licenseManager = licenseManager;
|
|
465
|
+
this.persistenceEnabled = persistenceEnabled;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Record a new trace entry
|
|
469
|
+
*/
|
|
470
|
+
recordTrace(trace) {
|
|
471
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
472
|
+
console.warn("Traceability requires a Pro license. Upgrade at https://contextguard.dev/pro");
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
const traceId = crypto.randomBytes(16).toString("hex");
|
|
476
|
+
const fullTrace = {
|
|
477
|
+
...trace,
|
|
478
|
+
traceId,
|
|
479
|
+
timestamp: new Date(),
|
|
480
|
+
};
|
|
481
|
+
this.traces.set(traceId, fullTrace);
|
|
482
|
+
// Cleanup old traces if limit exceeded
|
|
483
|
+
if (this.traces.size > this.maxTraces) {
|
|
484
|
+
const oldestKeys = Array.from(this.traces.keys()).slice(0, this.traces.size - this.maxTraces);
|
|
485
|
+
for (const key of oldestKeys) {
|
|
486
|
+
this.traces.delete(key);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Persist to storage if enabled
|
|
490
|
+
if (this.persistenceEnabled) {
|
|
491
|
+
this.persistTrace(fullTrace).catch((err) => console.error("Failed to persist trace:", err));
|
|
492
|
+
}
|
|
493
|
+
return traceId;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Query traces with filters
|
|
497
|
+
*/
|
|
498
|
+
queryTraces(query) {
|
|
499
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
500
|
+
return [];
|
|
501
|
+
}
|
|
502
|
+
let results = Array.from(this.traces.values());
|
|
503
|
+
// Apply filters
|
|
504
|
+
if (query.userId) {
|
|
505
|
+
results = results.filter((t) => t.userId === query.userId);
|
|
506
|
+
}
|
|
507
|
+
if (query.userIds && query.userIds.length > 0) {
|
|
508
|
+
results = results.filter((t) => query.userIds.includes(t.userId));
|
|
509
|
+
}
|
|
510
|
+
if (query.mcpServerName) {
|
|
511
|
+
results = results.filter((t) => t.mcpServerName === query.mcpServerName);
|
|
512
|
+
}
|
|
513
|
+
if (query.toolName) {
|
|
514
|
+
results = results.filter((t) => t.toolName === query.toolName);
|
|
515
|
+
}
|
|
516
|
+
if (query.sessionId) {
|
|
517
|
+
results = results.filter((t) => t.sessionId === query.sessionId);
|
|
518
|
+
}
|
|
519
|
+
if (query.status) {
|
|
520
|
+
results = results.filter((t) => t.status === query.status);
|
|
521
|
+
}
|
|
522
|
+
if (query.threatDetected !== undefined) {
|
|
523
|
+
results = results.filter((t) => t.threatDetected === query.threatDetected);
|
|
524
|
+
}
|
|
525
|
+
if (query.startDate) {
|
|
526
|
+
results = results.filter((t) => t.timestamp >= query.startDate);
|
|
527
|
+
}
|
|
528
|
+
if (query.endDate) {
|
|
529
|
+
results = results.filter((t) => t.timestamp <= query.endDate);
|
|
530
|
+
}
|
|
531
|
+
// Sort
|
|
532
|
+
const sortBy = query.sortBy || "timestamp";
|
|
533
|
+
const sortOrder = query.sortOrder || "desc";
|
|
534
|
+
results.sort((a, b) => {
|
|
535
|
+
const aVal = a[sortBy];
|
|
536
|
+
const bVal = b[sortBy];
|
|
537
|
+
const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
538
|
+
return sortOrder === "asc" ? comparison : -comparison;
|
|
539
|
+
});
|
|
540
|
+
// Pagination
|
|
541
|
+
const offset = query.offset || 0;
|
|
542
|
+
const limit = query.limit || 100;
|
|
543
|
+
return results.slice(offset, offset + limit);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Get trace by ID
|
|
547
|
+
*/
|
|
548
|
+
getTrace(traceId) {
|
|
549
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
return this.traces.get(traceId) || null;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Get usage statistics
|
|
556
|
+
*/
|
|
557
|
+
getUsageStatistics(query) {
|
|
558
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
559
|
+
return this.getEmptyStatistics();
|
|
560
|
+
}
|
|
561
|
+
const traces = query ? this.queryTraces(query) : Array.from(this.traces.values());
|
|
562
|
+
const uniqueUsers = new Set(traces.map((t) => t.userId));
|
|
563
|
+
const uniqueMCPServers = new Set(traces.map((t) => t.mcpServerName));
|
|
564
|
+
const uniqueTools = new Set(traces.map((t) => t.toolName));
|
|
565
|
+
// By MCP Server
|
|
566
|
+
const byMCPServer = {};
|
|
567
|
+
for (const trace of traces) {
|
|
568
|
+
if (!byMCPServer[trace.mcpServerName]) {
|
|
569
|
+
byMCPServer[trace.mcpServerName] = {
|
|
570
|
+
count: 0,
|
|
571
|
+
successRate: 0,
|
|
572
|
+
avgExecutionTimeMs: 0,
|
|
573
|
+
totalTokensUsed: 0,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
const stats = byMCPServer[trace.mcpServerName];
|
|
577
|
+
stats.count++;
|
|
578
|
+
stats.avgExecutionTimeMs += trace.executionTimeMs;
|
|
579
|
+
stats.totalTokensUsed += trace.tokensUsed || 0;
|
|
580
|
+
if (trace.status === "success") {
|
|
581
|
+
stats.successRate++;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Calculate averages and percentages
|
|
585
|
+
for (const serverName in byMCPServer) {
|
|
586
|
+
const stats = byMCPServer[serverName];
|
|
587
|
+
stats.avgExecutionTimeMs /= stats.count;
|
|
588
|
+
stats.successRate = (stats.successRate / stats.count) * 100;
|
|
589
|
+
}
|
|
590
|
+
// By Tool
|
|
591
|
+
const byTool = {};
|
|
592
|
+
for (const trace of traces) {
|
|
593
|
+
if (!byTool[trace.toolName]) {
|
|
594
|
+
byTool[trace.toolName] = {
|
|
595
|
+
count: 0,
|
|
596
|
+
successRate: 0,
|
|
597
|
+
avgExecutionTimeMs: 0,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
const stats = byTool[trace.toolName];
|
|
601
|
+
stats.count++;
|
|
602
|
+
stats.avgExecutionTimeMs += trace.executionTimeMs;
|
|
603
|
+
if (trace.status === "success") {
|
|
604
|
+
stats.successRate++;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
for (const toolName in byTool) {
|
|
608
|
+
const stats = byTool[toolName];
|
|
609
|
+
stats.avgExecutionTimeMs /= stats.count;
|
|
610
|
+
stats.successRate = (stats.successRate / stats.count) * 100;
|
|
611
|
+
}
|
|
612
|
+
// By User
|
|
613
|
+
const byUser = {};
|
|
614
|
+
for (const trace of traces) {
|
|
615
|
+
if (!byUser[trace.userId]) {
|
|
616
|
+
byUser[trace.userId] = {
|
|
617
|
+
count: 0,
|
|
618
|
+
mcpServersUsed: [],
|
|
619
|
+
toolsUsed: [],
|
|
620
|
+
totalExecutionTimeMs: 0,
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
const stats = byUser[trace.userId];
|
|
624
|
+
stats.count++;
|
|
625
|
+
stats.totalExecutionTimeMs += trace.executionTimeMs;
|
|
626
|
+
if (!stats.mcpServersUsed.includes(trace.mcpServerName)) {
|
|
627
|
+
stats.mcpServersUsed.push(trace.mcpServerName);
|
|
628
|
+
}
|
|
629
|
+
if (!stats.toolsUsed.includes(trace.toolName)) {
|
|
630
|
+
stats.toolsUsed.push(trace.toolName);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// Security events
|
|
634
|
+
const securityEvents = {
|
|
635
|
+
totalThreats: traces.filter((t) => t.threatDetected).length,
|
|
636
|
+
threatsBySeverity: {},
|
|
637
|
+
blockedRequests: traces.filter((t) => t.status === "blocked").length,
|
|
638
|
+
};
|
|
639
|
+
// Timeline (hourly buckets)
|
|
640
|
+
const timeline = [];
|
|
641
|
+
const timelineBuckets = {};
|
|
642
|
+
for (const trace of traces) {
|
|
643
|
+
const hour = new Date(trace.timestamp).toISOString().slice(0, 13);
|
|
644
|
+
if (!timelineBuckets[hour]) {
|
|
645
|
+
timelineBuckets[hour] = { count: 0, success: 0, failure: 0 };
|
|
646
|
+
}
|
|
647
|
+
timelineBuckets[hour].count++;
|
|
648
|
+
if (trace.status === "success") {
|
|
649
|
+
timelineBuckets[hour].success++;
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
timelineBuckets[hour].failure++;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
for (const [hour, data] of Object.entries(timelineBuckets)) {
|
|
656
|
+
timeline.push({
|
|
657
|
+
timestamp: new Date(hour),
|
|
658
|
+
count: data.count,
|
|
659
|
+
successCount: data.success,
|
|
660
|
+
failureCount: data.failure,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
timeline.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
664
|
+
return {
|
|
665
|
+
totalTraces: traces.length,
|
|
666
|
+
uniqueUsers: uniqueUsers.size,
|
|
667
|
+
uniqueMCPServers: uniqueMCPServers.size,
|
|
668
|
+
uniqueTools: uniqueTools.size,
|
|
669
|
+
byMCPServer,
|
|
670
|
+
byTool,
|
|
671
|
+
byUser,
|
|
672
|
+
securityEvents,
|
|
673
|
+
timeline,
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Export traces for compliance/audit
|
|
678
|
+
*/
|
|
679
|
+
exportTraces(query, format) {
|
|
680
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
681
|
+
return "";
|
|
682
|
+
}
|
|
683
|
+
const traces = this.queryTraces(query);
|
|
684
|
+
if (format === "JSON") {
|
|
685
|
+
return JSON.stringify(traces, null, 2);
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
// CSV format
|
|
689
|
+
const headers = [
|
|
690
|
+
"traceId",
|
|
691
|
+
"timestamp",
|
|
692
|
+
"userId",
|
|
693
|
+
"mcpServerName",
|
|
694
|
+
"toolName",
|
|
695
|
+
"status",
|
|
696
|
+
"executionTimeMs",
|
|
697
|
+
"threatDetected",
|
|
698
|
+
];
|
|
699
|
+
const rows = traces.map((t) => [
|
|
700
|
+
t.traceId,
|
|
701
|
+
t.timestamp.toISOString(),
|
|
702
|
+
t.userId,
|
|
703
|
+
t.mcpServerName,
|
|
704
|
+
t.toolName,
|
|
705
|
+
t.status,
|
|
706
|
+
t.executionTimeMs.toString(),
|
|
707
|
+
t.threatDetected.toString(),
|
|
708
|
+
]);
|
|
709
|
+
return [
|
|
710
|
+
headers.join(","),
|
|
711
|
+
...rows.map((row) => row.join(",")),
|
|
712
|
+
].join("\n");
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Get user activity timeline
|
|
717
|
+
*/
|
|
718
|
+
getUserActivityTimeline(userId, startDate, endDate) {
|
|
719
|
+
return this.queryTraces({
|
|
720
|
+
userId,
|
|
721
|
+
startDate,
|
|
722
|
+
endDate,
|
|
723
|
+
sortBy: "timestamp",
|
|
724
|
+
sortOrder: "asc",
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Detect anomalous usage patterns
|
|
729
|
+
*/
|
|
730
|
+
detectAnomalies(userId) {
|
|
731
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
732
|
+
return [];
|
|
733
|
+
}
|
|
734
|
+
const anomalies = [];
|
|
735
|
+
const traces = userId
|
|
736
|
+
? this.queryTraces({ userId })
|
|
737
|
+
: Array.from(this.traces.values());
|
|
738
|
+
// Detect high failure rate
|
|
739
|
+
const failureRate = traces.filter((t) => t.status === "failure").length / traces.length;
|
|
740
|
+
if (failureRate > 0.3) {
|
|
741
|
+
anomalies.push({
|
|
742
|
+
type: "high_failure_rate",
|
|
743
|
+
severity: "HIGH",
|
|
744
|
+
description: `High failure rate detected: ${(failureRate * 100).toFixed(1)}%`,
|
|
745
|
+
traces: traces.filter((t) => t.status === "failure"),
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
// Detect unusual tool usage
|
|
749
|
+
const toolCounts = {};
|
|
750
|
+
for (const trace of traces) {
|
|
751
|
+
toolCounts[trace.toolName] = (toolCounts[trace.toolName] || 0) + 1;
|
|
752
|
+
}
|
|
753
|
+
const avgToolUsage = Object.values(toolCounts).reduce((a, b) => a + b, 0) /
|
|
754
|
+
Object.keys(toolCounts).length;
|
|
755
|
+
for (const [tool, count] of Object.entries(toolCounts)) {
|
|
756
|
+
if (count > avgToolUsage * 5) {
|
|
757
|
+
anomalies.push({
|
|
758
|
+
type: "unusual_tool_usage",
|
|
759
|
+
severity: "MEDIUM",
|
|
760
|
+
description: `Tool "${tool}" used ${count} times (${(count / avgToolUsage).toFixed(1)}x average)`,
|
|
761
|
+
traces: traces.filter((t) => t.toolName === tool),
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
// Detect security threats
|
|
766
|
+
const threatsDetected = traces.filter((t) => t.threatDetected);
|
|
767
|
+
if (threatsDetected.length > 0) {
|
|
768
|
+
anomalies.push({
|
|
769
|
+
type: "security_threats",
|
|
770
|
+
severity: "HIGH",
|
|
771
|
+
description: `${threatsDetected.length} security threats detected`,
|
|
772
|
+
traces: threatsDetected,
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
return anomalies;
|
|
776
|
+
}
|
|
777
|
+
async persistTrace(trace) {
|
|
778
|
+
// In production, this would write to a database
|
|
779
|
+
// For now, we'll append to a log file
|
|
780
|
+
const logEntry = JSON.stringify(trace) + "\n";
|
|
781
|
+
fs.appendFileSync("mcp_traces.log", logEntry);
|
|
782
|
+
}
|
|
783
|
+
getEmptyStatistics() {
|
|
784
|
+
return {
|
|
785
|
+
totalTraces: 0,
|
|
786
|
+
uniqueUsers: 0,
|
|
787
|
+
uniqueMCPServers: 0,
|
|
788
|
+
uniqueTools: 0,
|
|
789
|
+
byMCPServer: {},
|
|
790
|
+
byTool: {},
|
|
791
|
+
byUser: {},
|
|
792
|
+
securityEvents: {
|
|
793
|
+
totalThreats: 0,
|
|
794
|
+
threatsBySeverity: {},
|
|
795
|
+
blockedRequests: 0,
|
|
796
|
+
},
|
|
797
|
+
timeline: [],
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
exports.MCPTraceabilityManager = MCPTraceabilityManager;
|
|
802
|
+
/**
|
|
803
|
+
* Context Tracker - Track context usage and data flow
|
|
804
|
+
* PRO FEATURE
|
|
805
|
+
*/
|
|
806
|
+
class ContextTracker {
|
|
807
|
+
constructor(licenseManager) {
|
|
808
|
+
this.activeContexts = new Map();
|
|
809
|
+
this.licenseManager = licenseManager;
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Start tracking context for a session
|
|
813
|
+
*/
|
|
814
|
+
startTracking(sessionId) {
|
|
815
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
this.activeContexts.set(sessionId, {
|
|
819
|
+
filesAccessed: [],
|
|
820
|
+
envVarsAccessed: [],
|
|
821
|
+
externalApisCalled: [],
|
|
822
|
+
databaseQueries: [],
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Record file access
|
|
827
|
+
*/
|
|
828
|
+
recordFileAccess(sessionId, path, operation, size) {
|
|
829
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const context = this.activeContexts.get(sessionId);
|
|
833
|
+
if (context) {
|
|
834
|
+
context.filesAccessed.push({ path, operation, size });
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Record environment variable access
|
|
839
|
+
*/
|
|
840
|
+
recordEnvVarAccess(sessionId, varName) {
|
|
841
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
const context = this.activeContexts.get(sessionId);
|
|
845
|
+
if (context && !context.envVarsAccessed.includes(varName)) {
|
|
846
|
+
context.envVarsAccessed.push(varName);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Record external API call
|
|
851
|
+
*/
|
|
852
|
+
recordApiCall(sessionId, url, method, statusCode) {
|
|
853
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const context = this.activeContexts.get(sessionId);
|
|
857
|
+
if (context) {
|
|
858
|
+
context.externalApisCalled.push({ url, method, statusCode });
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Record database query
|
|
863
|
+
*/
|
|
864
|
+
recordDatabaseQuery(sessionId, query, database, rowsAffected) {
|
|
865
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
const context = this.activeContexts.get(sessionId);
|
|
869
|
+
if (context) {
|
|
870
|
+
if (!context.databaseQueries) {
|
|
871
|
+
context.databaseQueries = [];
|
|
872
|
+
}
|
|
873
|
+
context.databaseQueries.push({ query, database, rowsAffected });
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Get context snapshot
|
|
878
|
+
*/
|
|
879
|
+
getContextSnapshot(sessionId) {
|
|
880
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
return this.activeContexts.get(sessionId) || null;
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Stop tracking and return final snapshot
|
|
887
|
+
*/
|
|
888
|
+
stopTracking(sessionId) {
|
|
889
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
const context = this.activeContexts.get(sessionId);
|
|
893
|
+
this.activeContexts.delete(sessionId);
|
|
894
|
+
return context || null;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Analyze context for security risks
|
|
898
|
+
*/
|
|
899
|
+
analyzeContextSecurity(context) {
|
|
900
|
+
if (!this.licenseManager.hasFeature("traceability")) {
|
|
901
|
+
return [];
|
|
902
|
+
}
|
|
903
|
+
const risks = [];
|
|
904
|
+
// Check for sensitive file access
|
|
905
|
+
const sensitiveFiles = context.filesAccessed.filter((f) => f.path.includes(".env") ||
|
|
906
|
+
f.path.includes("secret") ||
|
|
907
|
+
f.path.includes("password") ||
|
|
908
|
+
f.path.includes(".ssh") ||
|
|
909
|
+
f.path.includes("credentials"));
|
|
910
|
+
if (sensitiveFiles.length > 0) {
|
|
911
|
+
risks.push({
|
|
912
|
+
risk: "sensitive_file_access",
|
|
913
|
+
severity: "HIGH",
|
|
914
|
+
details: `Accessed ${sensitiveFiles.length} sensitive files`,
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
// Check for excessive file writes
|
|
918
|
+
const writeOperations = context.filesAccessed.filter((f) => f.operation === "write" || f.operation === "delete");
|
|
919
|
+
if (writeOperations.length > 50) {
|
|
920
|
+
risks.push({
|
|
921
|
+
risk: "excessive_file_writes",
|
|
922
|
+
severity: "MEDIUM",
|
|
923
|
+
details: `Performed ${writeOperations.length} write/delete operations`,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
// Check for external API calls to unknown domains
|
|
927
|
+
const externalCalls = context.externalApisCalled.filter((api) => !api.url.includes("localhost") && !api.url.includes("127.0.0.1"));
|
|
928
|
+
if (externalCalls.length > 0) {
|
|
929
|
+
risks.push({
|
|
930
|
+
risk: "external_api_calls",
|
|
931
|
+
severity: "MEDIUM",
|
|
932
|
+
details: `Made ${externalCalls.length} external API calls`,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
// Check for sensitive environment variables
|
|
936
|
+
const sensitiveEnvVars = context.envVarsAccessed.filter((v) => v.includes("KEY") ||
|
|
937
|
+
v.includes("SECRET") ||
|
|
938
|
+
v.includes("TOKEN") ||
|
|
939
|
+
v.includes("PASSWORD"));
|
|
940
|
+
if (sensitiveEnvVars.length > 0) {
|
|
941
|
+
risks.push({
|
|
942
|
+
risk: "sensitive_env_access",
|
|
943
|
+
severity: "HIGH",
|
|
944
|
+
details: `Accessed ${sensitiveEnvVars.length} sensitive environment variables`,
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
return risks;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
exports.ContextTracker = ContextTracker;
|