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
package/dist/config.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Amir Mironi
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.DEFAULT_CONFIG = void 0;
|
|
10
|
+
exports.mergeConfig = mergeConfig;
|
|
11
|
+
exports.validateConfig = validateConfig;
|
|
12
|
+
/**
|
|
13
|
+
* Default security configuration values
|
|
14
|
+
*/
|
|
15
|
+
exports.DEFAULT_CONFIG = {
|
|
16
|
+
maxToolCallsPerMinute: 30,
|
|
17
|
+
blockedPatterns: [],
|
|
18
|
+
allowedFilePaths: [],
|
|
19
|
+
alertThreshold: 5,
|
|
20
|
+
enablePromptInjectionDetection: true,
|
|
21
|
+
enableSensitiveDataDetection: true,
|
|
22
|
+
logPath: "mcp_security.log",
|
|
23
|
+
enableProFeatures: false,
|
|
24
|
+
licenseFilePath: ".contextguard-license",
|
|
25
|
+
enableDatabaseReporting: false,
|
|
26
|
+
database: {
|
|
27
|
+
type: "json",
|
|
28
|
+
path: "contextguard_events.json",
|
|
29
|
+
batchSize: 100,
|
|
30
|
+
flushIntervalMs: 5000,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Merges user configuration with defaults
|
|
35
|
+
* @param userConfig - User-provided configuration
|
|
36
|
+
* @returns Merged configuration with defaults
|
|
37
|
+
*/
|
|
38
|
+
function mergeConfig(userConfig = {}) {
|
|
39
|
+
return {
|
|
40
|
+
...exports.DEFAULT_CONFIG,
|
|
41
|
+
...userConfig,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validates configuration values
|
|
46
|
+
* @param config - Configuration to validate
|
|
47
|
+
* @throws Error if configuration is invalid
|
|
48
|
+
*/
|
|
49
|
+
function validateConfig(config) {
|
|
50
|
+
if (config.maxToolCallsPerMinute !== undefined && config.maxToolCallsPerMinute < 1) {
|
|
51
|
+
throw new Error("maxToolCallsPerMinute must be at least 1");
|
|
52
|
+
}
|
|
53
|
+
if (config.alertThreshold !== undefined && config.alertThreshold < 1) {
|
|
54
|
+
throw new Error("alertThreshold must be at least 1");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Amir Mironi
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { SecurityEvent } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* Database configuration options
|
|
10
|
+
*/
|
|
11
|
+
export interface DatabaseConfig {
|
|
12
|
+
/** Database type */
|
|
13
|
+
type: "sqlite" | "json" | "postgresql" | "mysql";
|
|
14
|
+
/** Connection string or file path */
|
|
15
|
+
connectionString?: string;
|
|
16
|
+
/** Database file path (for SQLite/JSON) */
|
|
17
|
+
dbPath?: string;
|
|
18
|
+
/** Enable database reporting */
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
/** Batch size for bulk inserts */
|
|
21
|
+
batchSize?: number;
|
|
22
|
+
/** Flush interval in milliseconds */
|
|
23
|
+
flushIntervalMs?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Event record for database storage
|
|
27
|
+
*/
|
|
28
|
+
export interface EventRecord {
|
|
29
|
+
id?: number;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
sessionId: string;
|
|
32
|
+
eventType: string;
|
|
33
|
+
severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
|
|
34
|
+
method?: string;
|
|
35
|
+
toolName?: string;
|
|
36
|
+
userId?: string;
|
|
37
|
+
violations?: string[];
|
|
38
|
+
blocked: boolean;
|
|
39
|
+
details: string;
|
|
40
|
+
createdAt?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Database reporter interface
|
|
44
|
+
*/
|
|
45
|
+
export interface IDatabaseReporter {
|
|
46
|
+
reportEvent(event: SecurityEvent, additionalData?: Record<string, unknown>): Promise<void>;
|
|
47
|
+
reportToolCall(sessionId: string, toolName: string, params: Record<string, unknown>, violations: string[], blocked: boolean): Promise<void>;
|
|
48
|
+
flush(): Promise<void>;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
getRecentEvents(limit?: number): Promise<EventRecord[]>;
|
|
51
|
+
getEventsBySession(sessionId: string): Promise<EventRecord[]>;
|
|
52
|
+
getEventsBySeverity(severity: string): Promise<EventRecord[]>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* JSON file-based database reporter
|
|
56
|
+
* Simple implementation for development and small deployments
|
|
57
|
+
*/
|
|
58
|
+
export declare class JSONDatabaseReporter implements IDatabaseReporter {
|
|
59
|
+
private dbPath;
|
|
60
|
+
private eventBuffer;
|
|
61
|
+
private batchSize;
|
|
62
|
+
private flushInterval;
|
|
63
|
+
private eventIdCounter;
|
|
64
|
+
constructor(config: DatabaseConfig);
|
|
65
|
+
private initializeDatabase;
|
|
66
|
+
private loadEventCounter;
|
|
67
|
+
reportEvent(event: SecurityEvent, additionalData?: Record<string, unknown>): Promise<void>;
|
|
68
|
+
reportToolCall(sessionId: string, toolName: string, params: Record<string, unknown>, violations: string[], blocked: boolean): Promise<void>;
|
|
69
|
+
flush(): Promise<void>;
|
|
70
|
+
close(): Promise<void>;
|
|
71
|
+
getRecentEvents(limit?: number): Promise<EventRecord[]>;
|
|
72
|
+
getEventsBySession(sessionId: string): Promise<EventRecord[]>;
|
|
73
|
+
getEventsBySeverity(severity: string): Promise<EventRecord[]>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* SQLite database reporter
|
|
77
|
+
* For production deployments with better performance
|
|
78
|
+
*/
|
|
79
|
+
export declare class SQLiteDatabaseReporter implements IDatabaseReporter {
|
|
80
|
+
private dbPath;
|
|
81
|
+
private eventBuffer;
|
|
82
|
+
private batchSize;
|
|
83
|
+
private flushInterval;
|
|
84
|
+
constructor(config: DatabaseConfig);
|
|
85
|
+
reportEvent(_event: SecurityEvent, _additionalData?: Record<string, unknown>): Promise<void>;
|
|
86
|
+
reportToolCall(_sessionId: string, _toolName: string, _params: Record<string, unknown>, _violations: string[], _blocked: boolean): Promise<void>;
|
|
87
|
+
flush(): Promise<void>;
|
|
88
|
+
close(): Promise<void>;
|
|
89
|
+
getRecentEvents(_limit?: number): Promise<EventRecord[]>;
|
|
90
|
+
getEventsBySession(_sessionId: string): Promise<EventRecord[]>;
|
|
91
|
+
getEventsBySeverity(_severity: string): Promise<EventRecord[]>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* PostgreSQL database reporter
|
|
95
|
+
* For enterprise deployments with high volume
|
|
96
|
+
*/
|
|
97
|
+
export declare class PostgreSQLDatabaseReporter implements IDatabaseReporter {
|
|
98
|
+
private connectionString;
|
|
99
|
+
private eventBuffer;
|
|
100
|
+
private batchSize;
|
|
101
|
+
constructor(config: DatabaseConfig);
|
|
102
|
+
reportEvent(_event: SecurityEvent, _additionalData?: Record<string, unknown>): Promise<void>;
|
|
103
|
+
reportToolCall(_sessionId: string, _toolName: string, _params: Record<string, unknown>, _violations: string[], _blocked: boolean): Promise<void>;
|
|
104
|
+
flush(): Promise<void>;
|
|
105
|
+
close(): Promise<void>;
|
|
106
|
+
getRecentEvents(_limit?: number): Promise<EventRecord[]>;
|
|
107
|
+
getEventsBySession(_sessionId: string): Promise<EventRecord[]>;
|
|
108
|
+
getEventsBySeverity(_severity: string): Promise<EventRecord[]>;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Database reporter factory
|
|
112
|
+
* Creates appropriate reporter based on configuration
|
|
113
|
+
*/
|
|
114
|
+
export declare class DatabaseReporterFactory {
|
|
115
|
+
static create(config: DatabaseConfig): IDatabaseReporter;
|
|
116
|
+
}
|
package/dist/database.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Amir Mironi
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
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.DatabaseReporterFactory = exports.PostgreSQLDatabaseReporter = exports.SQLiteDatabaseReporter = exports.JSONDatabaseReporter = void 0;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
/**
|
|
45
|
+
* JSON file-based database reporter
|
|
46
|
+
* Simple implementation for development and small deployments
|
|
47
|
+
*/
|
|
48
|
+
class JSONDatabaseReporter {
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.eventBuffer = [];
|
|
51
|
+
this.flushInterval = null;
|
|
52
|
+
this.eventIdCounter = 0;
|
|
53
|
+
this.dbPath = config.dbPath || "contextguard_events.json";
|
|
54
|
+
this.batchSize = config.batchSize || 100;
|
|
55
|
+
// Initialize database file if it doesn't exist
|
|
56
|
+
this.initializeDatabase();
|
|
57
|
+
// Load existing event count for ID generation
|
|
58
|
+
this.loadEventCounter();
|
|
59
|
+
// Set up auto-flush
|
|
60
|
+
if (config.flushIntervalMs) {
|
|
61
|
+
this.flushInterval = setInterval(() => {
|
|
62
|
+
this.flush().catch(err => console.error("Auto-flush error:", err));
|
|
63
|
+
}, config.flushIntervalMs);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
initializeDatabase() {
|
|
67
|
+
if (!fs.existsSync(this.dbPath)) {
|
|
68
|
+
fs.writeFileSync(this.dbPath, JSON.stringify({ events: [] }, null, 2));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
loadEventCounter() {
|
|
72
|
+
try {
|
|
73
|
+
const data = JSON.parse(fs.readFileSync(this.dbPath, "utf-8"));
|
|
74
|
+
this.eventIdCounter = data.events?.length || 0;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error("Failed to load event counter:", error);
|
|
78
|
+
this.eventIdCounter = 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async reportEvent(event, additionalData) {
|
|
82
|
+
const record = {
|
|
83
|
+
id: ++this.eventIdCounter,
|
|
84
|
+
timestamp: event.timestamp,
|
|
85
|
+
sessionId: event.sessionId,
|
|
86
|
+
eventType: event.eventType,
|
|
87
|
+
severity: event.severity,
|
|
88
|
+
blocked: false,
|
|
89
|
+
details: JSON.stringify({ ...event.details, ...additionalData }),
|
|
90
|
+
createdAt: new Date().toISOString(),
|
|
91
|
+
};
|
|
92
|
+
this.eventBuffer.push(record);
|
|
93
|
+
// Auto-flush if buffer is full
|
|
94
|
+
if (this.eventBuffer.length >= this.batchSize) {
|
|
95
|
+
await this.flush();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async reportToolCall(sessionId, toolName, params, violations, blocked) {
|
|
99
|
+
const record = {
|
|
100
|
+
id: ++this.eventIdCounter,
|
|
101
|
+
timestamp: new Date().toISOString(),
|
|
102
|
+
sessionId,
|
|
103
|
+
eventType: "TOOL_CALL",
|
|
104
|
+
severity: violations.length > 0 ? "HIGH" : "LOW",
|
|
105
|
+
toolName,
|
|
106
|
+
violations,
|
|
107
|
+
blocked,
|
|
108
|
+
details: JSON.stringify({ params, violations }),
|
|
109
|
+
createdAt: new Date().toISOString(),
|
|
110
|
+
};
|
|
111
|
+
this.eventBuffer.push(record);
|
|
112
|
+
if (this.eventBuffer.length >= this.batchSize) {
|
|
113
|
+
await this.flush();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async flush() {
|
|
117
|
+
if (this.eventBuffer.length === 0)
|
|
118
|
+
return;
|
|
119
|
+
try {
|
|
120
|
+
// Read existing data
|
|
121
|
+
const data = JSON.parse(fs.readFileSync(this.dbPath, "utf-8"));
|
|
122
|
+
// Append new events
|
|
123
|
+
data.events = data.events || [];
|
|
124
|
+
data.events.push(...this.eventBuffer);
|
|
125
|
+
// Write back to file
|
|
126
|
+
fs.writeFileSync(this.dbPath, JSON.stringify(data, null, 2));
|
|
127
|
+
// Clear buffer
|
|
128
|
+
this.eventBuffer = [];
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error("Failed to flush events to database:", error);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async close() {
|
|
136
|
+
if (this.flushInterval) {
|
|
137
|
+
clearInterval(this.flushInterval);
|
|
138
|
+
this.flushInterval = null;
|
|
139
|
+
}
|
|
140
|
+
await this.flush();
|
|
141
|
+
}
|
|
142
|
+
async getRecentEvents(limit = 100) {
|
|
143
|
+
try {
|
|
144
|
+
const data = JSON.parse(fs.readFileSync(this.dbPath, "utf-8"));
|
|
145
|
+
const events = data.events || [];
|
|
146
|
+
return events.slice(-limit).reverse();
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error("Failed to read events:", error);
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async getEventsBySession(sessionId) {
|
|
154
|
+
try {
|
|
155
|
+
const data = JSON.parse(fs.readFileSync(this.dbPath, "utf-8"));
|
|
156
|
+
const events = data.events || [];
|
|
157
|
+
return events.filter((e) => e.sessionId === sessionId);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error("Failed to read events:", error);
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async getEventsBySeverity(severity) {
|
|
165
|
+
try {
|
|
166
|
+
const data = JSON.parse(fs.readFileSync(this.dbPath, "utf-8"));
|
|
167
|
+
const events = data.events || [];
|
|
168
|
+
return events.filter((e) => e.severity === severity);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error("Failed to read events:", error);
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.JSONDatabaseReporter = JSONDatabaseReporter;
|
|
177
|
+
/**
|
|
178
|
+
* SQLite database reporter
|
|
179
|
+
* For production deployments with better performance
|
|
180
|
+
*/
|
|
181
|
+
class SQLiteDatabaseReporter {
|
|
182
|
+
constructor(config) {
|
|
183
|
+
this.eventBuffer = [];
|
|
184
|
+
this.flushInterval = null;
|
|
185
|
+
this.dbPath = config.dbPath || "contextguard_events.db";
|
|
186
|
+
this.batchSize = config.batchSize || 100;
|
|
187
|
+
console.warn("SQLite reporter requires 'better-sqlite3' package. " +
|
|
188
|
+
"Install with: npm install better-sqlite3");
|
|
189
|
+
// Note: Actual SQLite implementation would require better-sqlite3
|
|
190
|
+
// This is a placeholder that falls back to JSON
|
|
191
|
+
console.warn("Falling back to JSON reporter. Install better-sqlite3 for SQLite support.");
|
|
192
|
+
}
|
|
193
|
+
async reportEvent(_event, _additionalData) {
|
|
194
|
+
// Placeholder - would use SQLite in production
|
|
195
|
+
console.log("SQLite reporter not implemented, use JSONDatabaseReporter");
|
|
196
|
+
}
|
|
197
|
+
async reportToolCall(_sessionId, _toolName, _params, _violations, _blocked) {
|
|
198
|
+
// Placeholder
|
|
199
|
+
console.log("SQLite reporter not implemented, use JSONDatabaseReporter");
|
|
200
|
+
}
|
|
201
|
+
async flush() {
|
|
202
|
+
// Placeholder
|
|
203
|
+
}
|
|
204
|
+
async close() {
|
|
205
|
+
if (this.flushInterval) {
|
|
206
|
+
clearInterval(this.flushInterval);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async getRecentEvents(_limit) {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
async getEventsBySession(_sessionId) {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
async getEventsBySeverity(_severity) {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.SQLiteDatabaseReporter = SQLiteDatabaseReporter;
|
|
220
|
+
/**
|
|
221
|
+
* PostgreSQL database reporter
|
|
222
|
+
* For enterprise deployments with high volume
|
|
223
|
+
*/
|
|
224
|
+
class PostgreSQLDatabaseReporter {
|
|
225
|
+
constructor(config) {
|
|
226
|
+
this.eventBuffer = [];
|
|
227
|
+
this.connectionString = config.connectionString || "";
|
|
228
|
+
this.batchSize = config.batchSize || 1000;
|
|
229
|
+
console.warn("PostgreSQL reporter requires 'pg' package. " +
|
|
230
|
+
"Install with: npm install pg");
|
|
231
|
+
}
|
|
232
|
+
async reportEvent(_event, _additionalData) {
|
|
233
|
+
// Placeholder - would use pg library in production
|
|
234
|
+
console.log("PostgreSQL reporter not implemented, use JSONDatabaseReporter");
|
|
235
|
+
}
|
|
236
|
+
async reportToolCall(_sessionId, _toolName, _params, _violations, _blocked) {
|
|
237
|
+
// Placeholder
|
|
238
|
+
console.log("PostgreSQL reporter not implemented, use JSONDatabaseReporter");
|
|
239
|
+
}
|
|
240
|
+
async flush() {
|
|
241
|
+
// Placeholder
|
|
242
|
+
}
|
|
243
|
+
async close() {
|
|
244
|
+
// Placeholder
|
|
245
|
+
}
|
|
246
|
+
async getRecentEvents(_limit) {
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
async getEventsBySession(_sessionId) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
async getEventsBySeverity(_severity) {
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
exports.PostgreSQLDatabaseReporter = PostgreSQLDatabaseReporter;
|
|
257
|
+
/**
|
|
258
|
+
* Database reporter factory
|
|
259
|
+
* Creates appropriate reporter based on configuration
|
|
260
|
+
*/
|
|
261
|
+
class DatabaseReporterFactory {
|
|
262
|
+
static create(config) {
|
|
263
|
+
if (!config.enabled) {
|
|
264
|
+
return new NullDatabaseReporter();
|
|
265
|
+
}
|
|
266
|
+
switch (config.type) {
|
|
267
|
+
case "json":
|
|
268
|
+
return new JSONDatabaseReporter(config);
|
|
269
|
+
case "sqlite":
|
|
270
|
+
return new SQLiteDatabaseReporter(config);
|
|
271
|
+
case "postgresql":
|
|
272
|
+
return new PostgreSQLDatabaseReporter(config);
|
|
273
|
+
default:
|
|
274
|
+
console.warn(`Unknown database type: ${config.type}, using JSON`);
|
|
275
|
+
return new JSONDatabaseReporter(config);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
exports.DatabaseReporterFactory = DatabaseReporterFactory;
|
|
280
|
+
/**
|
|
281
|
+
* Null reporter for when database is disabled
|
|
282
|
+
*/
|
|
283
|
+
class NullDatabaseReporter {
|
|
284
|
+
async reportEvent() { }
|
|
285
|
+
async reportToolCall() { }
|
|
286
|
+
async flush() { }
|
|
287
|
+
async close() { }
|
|
288
|
+
async getRecentEvents() { return []; }
|
|
289
|
+
async getEventsBySession() { return []; }
|
|
290
|
+
async getEventsBySeverity() { return []; }
|
|
291
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Amir Mironi
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
export { createAgent } from "./agent";
|
|
8
|
+
export type { Agent } from "./agent";
|
|
9
|
+
export type { MCPMessage } from "./types/mcp";
|
|
10
|
+
export { createPolicyChecker, DEFAULT_POLICY } from "./policy";
|
|
11
|
+
export type { PolicyChecker } from "./policy";
|
|
12
|
+
export { createLogger } from "./logger";
|
|
13
|
+
export type { Logger, SecurityStatistics } from "./logger";
|
|
14
|
+
export { createSupabaseClient } from "./lib/supabase-client";
|
|
15
|
+
export type { SupabaseConfig } from "./lib/supabase-client";
|
|
16
|
+
export type { AgentPolicyRow, AgentPolicyInsert, AgentPolicyUpdate, SecurityEventRow, SecurityEventInsert, SecurityEventUpdate, AgentStatusRow, AgentStatusInsert, AgentStatusUpdate, EventStatisticsRow, SecuritySeverity, AgentStatus, } from "./types/database.types";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Amir Mironi
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.createSupabaseClient = exports.createLogger = exports.DEFAULT_POLICY = exports.createPolicyChecker = exports.createAgent = void 0;
|
|
10
|
+
var agent_1 = require("./agent");
|
|
11
|
+
Object.defineProperty(exports, "createAgent", { enumerable: true, get: function () { return agent_1.createAgent; } });
|
|
12
|
+
var policy_1 = require("./policy");
|
|
13
|
+
Object.defineProperty(exports, "createPolicyChecker", { enumerable: true, get: function () { return policy_1.createPolicyChecker; } });
|
|
14
|
+
Object.defineProperty(exports, "DEFAULT_POLICY", { enumerable: true, get: function () { return policy_1.DEFAULT_POLICY; } });
|
|
15
|
+
var logger_1 = require("./logger");
|
|
16
|
+
Object.defineProperty(exports, "createLogger", { enumerable: true, get: function () { return logger_1.createLogger; } });
|
|
17
|
+
var supabase_client_1 = require("./lib/supabase-client");
|
|
18
|
+
Object.defineProperty(exports, "createSupabaseClient", { enumerable: true, get: function () { return supabase_client_1.createSupabaseClient; } });
|
package/dist/init.d.ts
ADDED
package/dist/init.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Amir Mironi
|
|
4
|
+
*
|
|
5
|
+
* contextguard init — automatically patch claude_desktop_config.json
|
|
6
|
+
* so that every MCP server is wrapped with ContextGuard.
|
|
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.runInit = runInit;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const os = __importStar(require("os"));
|
|
46
|
+
// ── Config file detection ────────────────────────────────────────────────────
|
|
47
|
+
function findConfigPath() {
|
|
48
|
+
const home = os.homedir();
|
|
49
|
+
let candidates = [];
|
|
50
|
+
switch (process.platform) {
|
|
51
|
+
case "darwin":
|
|
52
|
+
candidates = [
|
|
53
|
+
path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
54
|
+
];
|
|
55
|
+
break;
|
|
56
|
+
case "win32":
|
|
57
|
+
candidates = [
|
|
58
|
+
path.join(process.env.APPDATA ?? path.join(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json"),
|
|
59
|
+
];
|
|
60
|
+
break;
|
|
61
|
+
default: // Linux
|
|
62
|
+
candidates = [
|
|
63
|
+
path.join(home, ".config", "Claude", "claude_desktop_config.json"),
|
|
64
|
+
path.join(home, ".config", "claude", "claude_desktop_config.json"),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
return candidates.find((p) => fs.existsSync(p)) ?? null;
|
|
68
|
+
}
|
|
69
|
+
// ── Wrapping helpers ─────────────────────────────────────────────────────────
|
|
70
|
+
function isAlreadyWrapped(server) {
|
|
71
|
+
if (server.command === "contextguard")
|
|
72
|
+
return true;
|
|
73
|
+
return (server.args ?? []).includes("contextguard");
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Wrap an MCP server entry:
|
|
77
|
+
* { command, args } → { command: "npx", args: ["-y", "contextguard", "--", <original command+args>] }
|
|
78
|
+
*/
|
|
79
|
+
function wrapServer(server, apiKey) {
|
|
80
|
+
const originalCmd = [server.command, ...(server.args ?? [])];
|
|
81
|
+
return {
|
|
82
|
+
command: "npx",
|
|
83
|
+
args: ["-y", "contextguard", "--", ...originalCmd],
|
|
84
|
+
env: {
|
|
85
|
+
...server.env,
|
|
86
|
+
CONTEXTGUARD_API_KEY: apiKey,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function parseInitArgs(argv) {
|
|
91
|
+
let apiKey = "";
|
|
92
|
+
let configPath;
|
|
93
|
+
let dryRun = false;
|
|
94
|
+
for (let i = 0; i < argv.length; i++) {
|
|
95
|
+
const arg = argv[i];
|
|
96
|
+
if ((arg === "--api-key" || arg === "-k") && argv[i + 1]) {
|
|
97
|
+
apiKey = argv[++i];
|
|
98
|
+
}
|
|
99
|
+
else if ((arg === "--config" || arg === "-c") && argv[i + 1]) {
|
|
100
|
+
configPath = argv[++i];
|
|
101
|
+
}
|
|
102
|
+
else if (arg === "--dry-run") {
|
|
103
|
+
dryRun = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { apiKey, configPath, dryRun };
|
|
107
|
+
}
|
|
108
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
109
|
+
async function runInit(argv) {
|
|
110
|
+
const { apiKey, configPath: customPath, dryRun } = parseInitArgs(argv);
|
|
111
|
+
if (!apiKey) {
|
|
112
|
+
console.error("\n❌ Missing required argument: --api-key <key>\n" +
|
|
113
|
+
" Get your key from the ContextGuard dashboard → Settings → API Keys\n");
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
// Locate config
|
|
117
|
+
const configPath = customPath ?? findConfigPath();
|
|
118
|
+
if (!configPath) {
|
|
119
|
+
console.error("\n❌ Could not find claude_desktop_config.json.\n" +
|
|
120
|
+
" Is Claude Desktop installed? Or specify the path with --config <path>\n");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
console.log(`\n🔍 Config found: ${configPath}`);
|
|
124
|
+
// Read and parse
|
|
125
|
+
let config;
|
|
126
|
+
try {
|
|
127
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
console.error(`\n❌ Failed to parse config: ${configPath}\n`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const servers = { ...(config.mcpServers ?? {}) };
|
|
134
|
+
const names = Object.keys(servers);
|
|
135
|
+
if (names.length === 0) {
|
|
136
|
+
console.log("\n⚠️ No MCP servers found in config — nothing to wrap.\n");
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}
|
|
139
|
+
console.log(`\n📋 Found ${names.length} MCP server(s):\n`);
|
|
140
|
+
let wrapped = 0;
|
|
141
|
+
let skipped = 0;
|
|
142
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
143
|
+
if (isAlreadyWrapped(server)) {
|
|
144
|
+
console.log(` ⏭ ${name} (already wrapped, skipping)`);
|
|
145
|
+
skipped++;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
servers[name] = wrapServer(server, apiKey);
|
|
149
|
+
console.log(` ✅ ${name} → wrapped`);
|
|
150
|
+
wrapped++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
console.log(`\n📝 ${wrapped} wrapped, ${skipped} already configured.\n`);
|
|
154
|
+
if (wrapped === 0) {
|
|
155
|
+
console.log("✨ Nothing to do — all servers are already protected.\n");
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
const updated = { ...config, mcpServers: servers };
|
|
159
|
+
if (dryRun) {
|
|
160
|
+
console.log("⚠️ --dry-run: no changes written. Preview:\n");
|
|
161
|
+
console.log(JSON.stringify(updated, null, 2));
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
// Backup
|
|
165
|
+
const backupPath = configPath + ".bak";
|
|
166
|
+
fs.copyFileSync(configPath, backupPath);
|
|
167
|
+
console.log(`💾 Backup saved: ${backupPath}`);
|
|
168
|
+
// Write
|
|
169
|
+
fs.writeFileSync(configPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
170
|
+
console.log(`✅ Config updated: ${configPath}`);
|
|
171
|
+
console.log("\n🚀 Done! Restart Claude Desktop to activate ContextGuard.\n" +
|
|
172
|
+
" Dashboard: https://contextguard.dev/dashboard\n");
|
|
173
|
+
}
|