contextguard 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agent.d.ts CHANGED
@@ -4,8 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import { Logger } from "./logger";
8
- import { SupabaseConfig } from "./lib/supabase-client";
7
+ import { Logger, ContextGuardConfig } from "./logger";
9
8
  import { AgentPolicyRow } from "./types/database.types";
10
9
  /**
11
10
  * Agent interface
@@ -17,8 +16,9 @@ export interface Agent {
17
16
  /**
18
17
  * Create an MCP security agent
19
18
  * @param serverCommand - Command to start MCP server
20
- * @param policyConfig - Policy configuration
21
- * @param supabaseConfig - Optional Supabase configuration
19
+ * @param policyConfig - Policy configuration (should be complete with defaults already merged)
20
+ * @param agentId - Agent identifier
21
+ * @param contextGuardConfig - Optional ContextGuard API configuration
22
22
  * @returns Agent functions
23
23
  */
24
- export declare const createAgent: (serverCommand: string[], policyConfig?: AgentPolicyRow, supabaseConfig?: SupabaseConfig) => Agent;
24
+ export declare const createAgent: (serverCommand: string[], policyConfig: Required<AgentPolicyRow>, agentId: string, contextGuardConfig?: ContextGuardConfig) => Agent;
package/dist/agent.js CHANGED
@@ -8,32 +8,20 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.createAgent = void 0;
10
10
  const child_process_1 = require("child_process");
11
- const policy_1 = require("./policy");
11
+ const security_engine_1 = require("./security-engine");
12
12
  const logger_1 = require("./logger");
13
- const supabase_client_1 = require("./lib/supabase-client");
14
13
  const mcp_1 = require("./types/mcp");
15
- /**
16
- * Merge policy with defaults
17
- */
18
- const mergePolicyWithDefaults = (policy) => ({
19
- ...policy_1.DEFAULT_POLICY,
20
- ...policy,
21
- });
22
14
  /**
23
15
  * Create an MCP security agent
24
16
  * @param serverCommand - Command to start MCP server
25
- * @param policyConfig - Policy configuration
26
- * @param supabaseConfig - Optional Supabase configuration
17
+ * @param policyConfig - Policy configuration (should be complete with defaults already merged)
18
+ * @param agentId - Agent identifier
19
+ * @param contextGuardConfig - Optional ContextGuard API configuration
27
20
  * @returns Agent functions
28
21
  */
29
- const createAgent = (serverCommand, policyConfig = {}, supabaseConfig) => {
30
- const config = mergePolicyWithDefaults(policyConfig);
31
- const policy = (0, policy_1.createPolicyChecker)(config);
32
- // Create Supabase client if config provided
33
- const supabaseClient = supabaseConfig
34
- ? (0, supabase_client_1.createSupabaseClient)(supabaseConfig)
35
- : undefined;
36
- const logger = (0, logger_1.createLogger)(config.logPath, supabaseClient, supabaseConfig?.agentId, config.alertWebhook, config.alertOnSeverity);
22
+ const createAgent = (serverCommand, policyConfig, agentId, contextGuardConfig) => {
23
+ const policy = (0, security_engine_1.createPolicyChecker)(policyConfig);
24
+ const logger = (0, logger_1.createLogger)(policyConfig.logPath, agentId, policyConfig.alertWebhook, policyConfig.alertOnSeverity, contextGuardConfig);
37
25
  const sessionId = (0, mcp_1.generateSessionId)();
38
26
  const state = {
39
27
  process: null,
@@ -278,45 +266,13 @@ const createAgent = (serverCommand, policyConfig = {}, supabaseConfig) => {
278
266
  logger.logEvent("SERVER_EXIT", "MEDIUM", { exitCode: code }, sessionId);
279
267
  console.error("\n=== MCP Security Statistics ===");
280
268
  console.error(JSON.stringify(logger.getStatistics(), null, 2));
281
- const finish = () => setImmediate(() => process.exit(code || 0));
282
- if (supabaseClient && supabaseConfig?.agentId) {
283
- supabaseClient
284
- .updateAgentStatus(supabaseConfig.agentId, "offline")
285
- .catch(() => { })
286
- .finally(finish);
287
- }
288
- else {
289
- finish();
290
- }
269
+ setImmediate(() => process.exit(code || 0));
291
270
  };
292
271
  return {
293
272
  /**
294
273
  * Start the MCP server wrapper
295
274
  */
296
275
  start: async () => {
297
- // Fetch policy from Supabase if configured
298
- if (supabaseClient && supabaseConfig?.agentId) {
299
- try {
300
- const remotePolicy = await supabaseClient.fetchPolicy(supabaseConfig.agentId);
301
- if (remotePolicy) {
302
- console.log("✓ Loaded policy from Supabase");
303
- // Merge remote policy with local config
304
- Object.assign(config, remotePolicy);
305
- }
306
- }
307
- catch (error) {
308
- console.warn("⚠ Failed to fetch policy from Supabase:", error);
309
- }
310
- // Update agent status to online
311
- await supabaseClient.updateAgentStatus(supabaseConfig.agentId, "online");
312
- // Send periodic heartbeats every 30 seconds
313
- const heartbeatInterval = setInterval(() => {
314
- supabaseClient
315
- .updateAgentStatus(supabaseConfig.agentId, "online")
316
- .catch(() => { });
317
- }, 30000);
318
- heartbeatInterval.unref();
319
- }
320
276
  state.process = (0, child_process_1.spawn)(serverCommand[0], serverCommand.slice(1), {
321
277
  stdio: ["pipe", "pipe", "pipe"],
322
278
  });
@@ -353,16 +309,7 @@ const createAgent = (serverCommand, policyConfig = {}, supabaseConfig) => {
353
309
  logger.logEvent("AGENT_SHUTDOWN", "MEDIUM", { signal }, sessionId);
354
310
  if (state.process)
355
311
  state.process.kill();
356
- const finish = () => process.exit(0);
357
- if (supabaseClient && supabaseConfig?.agentId) {
358
- supabaseClient
359
- .updateAgentStatus(supabaseConfig.agentId, "offline")
360
- .catch(() => { })
361
- .finally(finish);
362
- }
363
- else {
364
- finish();
365
- }
312
+ process.exit(0);
366
313
  };
367
314
  process.on("SIGTERM", () => shutdown("SIGTERM"));
368
315
  process.on("SIGINT", () => shutdown("SIGINT"));
package/dist/cli.js CHANGED
@@ -41,11 +41,10 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  })();
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
43
  exports.main = main;
44
- const fs = __importStar(require("fs"));
45
44
  const os = __importStar(require("os"));
46
45
  const agent_1 = require("./agent");
47
46
  const sse_proxy_1 = require("./sse-proxy");
48
- const policy_1 = require("./policy");
47
+ const security_engine_1 = require("./security-engine");
49
48
  const init_1 = require("./init");
50
49
  /**
51
50
  * Display help message
@@ -59,10 +58,10 @@ Usage:
59
58
  contextguard init --api-key <key>
60
59
 
61
60
  # stdio transport (Claude Desktop)
62
- contextguard --server "node server.js" --config config.json
61
+ contextguard --server "node server.js"
63
62
 
64
63
  # SSE/HTTP transport proxy
65
- contextguard --transport sse --port 3100 --target http://localhost:3000 --config config.json
64
+ contextguard --transport sse --port 3100 --target http://localhost:3000
66
65
 
67
66
  Commands:
68
67
  init Auto-patch claude_desktop_config.json
@@ -77,28 +76,10 @@ Options:
77
76
  --transport <type> stdio | sse | http (default: stdio)
78
77
  --port <n> Proxy listen port (SSE/HTTP mode, default: 3100)
79
78
  --target <url> Upstream server URL (SSE/HTTP mode)
80
- --config <file> Path to security config JSON (optional)
81
79
  --help Show this help
82
80
 
83
- Configuration File Format (JSON):
84
- {
85
- "mode": "monitor", // "monitor" (log only) | "block" (block + log)
86
- "maxToolCallsPerMinute": 30,
87
- "enablePromptInjectionDetection": true,
88
- "enableSensitiveDataDetection": true,
89
- "enablePathTraversalPrevention": true,
90
- "enableSQLInjectionDetection": true,
91
- "enableXSSDetection": true,
92
- "enableSemanticDetection": false,
93
- "blockedPatterns": ["ignore previous", "system prompt"],
94
- "allowedFilePaths": ["/safe/path"],
95
- "allowedTools": [],
96
- "blockedTools": [],
97
- "customRules": [{ "name": "my-rule", "pattern": "\\bforbidden\\b" }],
98
- "alertWebhook": "https://hooks.example.com/alert",
99
- "alertOnSeverity": ["HIGH", "CRITICAL"],
100
- "logPath": "mcp_security.log"
101
- }
81
+ Note: Security policies are managed via the ContextGuard dashboard.
82
+ Set CONTEXTGUARD_API_KEY to enable remote policy management.
102
83
 
103
84
  For more information, visit: https://contextguard.dev
104
85
  `);
@@ -110,7 +91,6 @@ For more information, visit: https://contextguard.dev
110
91
  function parseArgs() {
111
92
  const args = process.argv.slice(2);
112
93
  let serverCommand = "";
113
- let configFile = "";
114
94
  let transport = "stdio";
115
95
  let port = 3100;
116
96
  let targetUrl = "";
@@ -119,10 +99,6 @@ function parseArgs() {
119
99
  serverCommand = args[i + 1];
120
100
  i++;
121
101
  }
122
- else if (args[i] === "--config" && args[i + 1]) {
123
- configFile = args[i + 1];
124
- i++;
125
- }
126
102
  else if (args[i] === "--transport" && args[i + 1]) {
127
103
  transport = args[i + 1];
128
104
  i++;
@@ -136,7 +112,7 @@ function parseArgs() {
136
112
  i++;
137
113
  }
138
114
  }
139
- return { serverCommand, configFile, transport, port, targetUrl };
115
+ return { serverCommand, transport, port, targetUrl };
140
116
  }
141
117
  /**
142
118
  * Parse a shell command string into an array of arguments,
@@ -170,28 +146,6 @@ function parseCommand(cmd) {
170
146
  tokens.push(current);
171
147
  return tokens;
172
148
  }
173
- /**
174
- * Load configuration from file
175
- * @param configFile - Path to config file
176
- * @returns Security configuration
177
- */
178
- function loadConfig(configFile) {
179
- if (!fs.existsSync(configFile)) {
180
- console.error(`Error: Config file not found: ${configFile}`);
181
- process.exit(1);
182
- }
183
- try {
184
- const config = JSON.parse(fs.readFileSync(configFile, "utf-8"));
185
- // validateConfig(config);
186
- return config;
187
- }
188
- catch (error) {
189
- console.error(`Error: Failed to load config file: ${error}`);
190
- process.exit(1);
191
- }
192
- // TypeScript doesn't know process.exit() never returns
193
- return {};
194
- }
195
149
  /**
196
150
  * Main CLI entry point
197
151
  */
@@ -208,33 +162,23 @@ async function main() {
208
162
  return;
209
163
  }
210
164
  // Parse arguments
211
- const { serverCommand, configFile, transport, port, targetUrl } = parseArgs();
212
- // Load configuration
213
- let config = {};
214
- if (configFile) {
215
- config = loadConfig(configFile);
216
- }
217
- // Merge with defaults
218
- const fullConfig = { ...policy_1.DEFAULT_POLICY, ...config };
219
- // CLI flags override config file
220
- if (transport !== "stdio") {
221
- fullConfig.transport = transport;
222
- }
223
- if (port !== 3100) {
224
- fullConfig.port = port;
225
- }
226
- if (targetUrl) {
227
- fullConfig.targetUrl = targetUrl;
228
- }
229
- const effectiveTransport = fullConfig.transport || "stdio";
165
+ const { serverCommand, transport, port, targetUrl } = parseArgs();
166
+ // Agent identifier (used by both stdio and SSE/HTTP modes)
167
+ const agentId = process.env.AGENT_ID || `${os.hostname()}-${process.pid}`;
230
168
  // SSE/HTTP transport mode
231
- if (effectiveTransport === "sse" || effectiveTransport === "http") {
232
- if (!fullConfig.targetUrl && !targetUrl) {
169
+ if (transport === "sse" || transport === "http") {
170
+ if (!targetUrl) {
233
171
  console.error("Error: --target <url> is required for SSE/HTTP transport");
234
172
  process.exit(1);
235
173
  }
236
- const proxy = (0, sse_proxy_1.createSSEProxy)(fullConfig);
237
- proxy.start(fullConfig.port || 3100, fullConfig.targetUrl || targetUrl);
174
+ const proxyConfig = {
175
+ ...security_engine_1.DEFAULT_POLICY,
176
+ transport: transport,
177
+ port,
178
+ targetUrl,
179
+ };
180
+ const proxy = (0, sse_proxy_1.createSSEProxy)(proxyConfig, agentId);
181
+ proxy.start(port, targetUrl);
238
182
  return;
239
183
  }
240
184
  // stdio transport mode
@@ -243,20 +187,30 @@ async function main() {
243
187
  console.error("Run 'contextguard --help' for usage information");
244
188
  process.exit(1);
245
189
  }
246
- // Load Supabase configuration from environment
247
- const supabaseConfig = process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_KEY
248
- ? {
249
- url: process.env.SUPABASE_URL,
250
- serviceKey: process.env.SUPABASE_SERVICE_KEY,
251
- agentId: process.env.AGENT_ID || "default-agent",
252
- }
253
- : undefined;
254
- // Supabase integration is configured silently to avoid polluting stderr
255
- // Start REST-based heartbeat if CONTEXTGUARD_API_KEY is set
190
+ // ContextGuard API configuration
256
191
  const cgApiKey = process.env.CONTEXTGUARD_API_KEY;
192
+ const baseUrl = process.env.CONTEXTGUARD_BASE_URL || "https://contextguard.dev";
193
+ let remotePolicy = null;
257
194
  if (cgApiKey) {
258
- const baseUrl = process.env.CONTEXTGUARD_BASE_URL || "https://contextguard.dev";
259
- const agentId = process.env.AGENT_ID || `${os.hostname()}-${process.pid}`;
195
+ // Fetch policy from ContextGuard API
196
+ const fetchPolicy = async () => {
197
+ try {
198
+ const response = await fetch(`${baseUrl}/api/agents/policy/${agentId}`, {
199
+ headers: {
200
+ Authorization: `Bearer ${cgApiKey}`,
201
+ },
202
+ });
203
+ if (response.ok) {
204
+ const data = await response.json();
205
+ if (data.policy) {
206
+ remotePolicy = data.policy;
207
+ }
208
+ }
209
+ }
210
+ catch {
211
+ // silently ignore — use local config if API is unreachable
212
+ }
213
+ };
260
214
  const sendHeartbeat = async () => {
261
215
  try {
262
216
  await fetch(`${baseUrl}/api/agents/heartbeat`, {
@@ -278,13 +232,24 @@ async function main() {
278
232
  // silently ignore — don't break the agent if dashboard is unreachable
279
233
  }
280
234
  };
235
+ // Fetch policy first, then start heartbeat
236
+ await fetchPolicy();
281
237
  sendHeartbeat();
282
238
  const interval = setInterval(sendHeartbeat, 30000);
283
239
  interval.unref();
240
+ // Periodically refresh policy (every 5 minutes)
241
+ const policyInterval = setInterval(fetchPolicy, 5 * 60000);
242
+ policyInterval.unref();
284
243
  // Heartbeat configured silently to avoid polluting stderr
285
244
  }
245
+ // Use remote policy if available, otherwise use default policy
246
+ const finalConfig = remotePolicy ?? security_engine_1.DEFAULT_POLICY;
247
+ // Create ContextGuard config if API key is set
248
+ const contextGuardConfig = cgApiKey
249
+ ? { apiKey: cgApiKey, baseUrl }
250
+ : undefined;
286
251
  // Create and start agent
287
- const agent = (0, agent_1.createAgent)(parseCommand(serverCommand), config, supabaseConfig);
252
+ const agent = (0, agent_1.createAgent)(parseCommand(serverCommand), finalConfig, agentId, contextGuardConfig);
288
253
  await agent.start();
289
254
  }
290
255
  // Run CLI if this is the main module
package/dist/index.d.ts CHANGED
@@ -7,10 +7,8 @@
7
7
  export { createAgent } from "./agent";
8
8
  export type { Agent } from "./agent";
9
9
  export type { MCPMessage } from "./types/mcp";
10
- export { createPolicyChecker, DEFAULT_POLICY } from "./policy";
11
- export type { PolicyChecker } from "./policy";
10
+ export { createPolicyChecker, DEFAULT_POLICY } from "./security-engine";
11
+ export type { PolicyChecker } from "./security-engine";
12
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";
13
+ export type { Logger, SecurityStatistics, ContextGuardConfig } from "./logger";
16
14
  export type { AgentPolicyRow, AgentPolicyInsert, AgentPolicyUpdate, SecurityEventRow, SecurityEventInsert, SecurityEventUpdate, AgentStatusRow, AgentStatusInsert, AgentStatusUpdate, EventStatisticsRow, SecuritySeverity, AgentStatus, } from "./types/database.types";
package/dist/index.js CHANGED
@@ -6,13 +6,11 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.createSupabaseClient = exports.createLogger = exports.DEFAULT_POLICY = exports.createPolicyChecker = exports.createAgent = void 0;
9
+ exports.createLogger = exports.DEFAULT_POLICY = exports.createPolicyChecker = exports.createAgent = void 0;
10
10
  var agent_1 = require("./agent");
11
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; } });
12
+ var security_engine_1 = require("./security-engine");
13
+ Object.defineProperty(exports, "createPolicyChecker", { enumerable: true, get: function () { return security_engine_1.createPolicyChecker; } });
14
+ Object.defineProperty(exports, "DEFAULT_POLICY", { enumerable: true, get: function () { return security_engine_1.DEFAULT_POLICY; } });
15
15
  var logger_1 = require("./logger");
16
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/logger.d.ts CHANGED
@@ -4,8 +4,14 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import { SupabaseClient } from "./lib/supabase-client";
8
7
  import { SecurityEventRow, SecuritySeverity } from "./types/database.types";
8
+ /**
9
+ * ContextGuard API configuration
10
+ */
11
+ export interface ContextGuardConfig {
12
+ apiKey: string;
13
+ baseUrl: string;
14
+ }
9
15
  /**
10
16
  * Security statistics interface
11
17
  */
@@ -27,10 +33,10 @@ export interface Logger {
27
33
  /**
28
34
  * Create a security event logger
29
35
  * @param logFile - Path to log file
30
- * @param supabaseClient - Optional Supabase client for remote logging
31
36
  * @param agentId - Agent identifier for event attribution
32
37
  * @param alertWebhook - Optional webhook URL for HIGH/CRITICAL alerts
33
38
  * @param alertOnSeverity - Severity levels that trigger webhook alerts
39
+ * @param contextGuardConfig - Optional ContextGuard API config for remote logging
34
40
  * @returns Logger functions
35
41
  */
36
- export declare const createLogger: (logFile?: string, supabaseClient?: SupabaseClient, agentId?: string, alertWebhook?: string, alertOnSeverity?: string[]) => Logger;
42
+ export declare const createLogger: (logFile?: string, agentId?: string, alertWebhook?: string, alertOnSeverity?: string[], contextGuardConfig?: ContextGuardConfig) => Logger;
package/dist/logger.js CHANGED
@@ -73,13 +73,13 @@ const fireWebhookAlert = (webhookUrl, event) => {
73
73
  /**
74
74
  * Create a security event logger
75
75
  * @param logFile - Path to log file
76
- * @param supabaseClient - Optional Supabase client for remote logging
77
76
  * @param agentId - Agent identifier for event attribution
78
77
  * @param alertWebhook - Optional webhook URL for HIGH/CRITICAL alerts
79
78
  * @param alertOnSeverity - Severity levels that trigger webhook alerts
79
+ * @param contextGuardConfig - Optional ContextGuard API config for remote logging
80
80
  * @returns Logger functions
81
81
  */
82
- const createLogger = (logFile = path.join(os.homedir(), ".contextguard", "mcp_security.log"), supabaseClient, agentId = "", alertWebhook, alertOnSeverity = ["HIGH", "CRITICAL"]) => {
82
+ const createLogger = (logFile = path.join(os.homedir(), ".contextguard", "mcp_security.log"), agentId = "", alertWebhook, alertOnSeverity = ["HIGH", "CRITICAL"], contextGuardConfig) => {
83
83
  let events = [];
84
84
  return {
85
85
  /**
@@ -118,10 +118,17 @@ const createLogger = (logFile = path.join(os.homedir(), ".contextguard", "mcp_se
118
118
  if (alertWebhook && alertOnSeverity.includes(severity)) {
119
119
  fireWebhookAlert(alertWebhook, event);
120
120
  }
121
- // Report to Supabase if client is provided
122
- if (supabaseClient) {
123
- supabaseClient.reportEvent(event).catch((err) => {
124
- console.error("Failed to report event to Supabase:", err);
121
+ // Report to ContextGuard API if configured
122
+ if (contextGuardConfig) {
123
+ fetch(`${contextGuardConfig.baseUrl}/api/events`, {
124
+ method: "POST",
125
+ headers: {
126
+ "Content-Type": "application/json",
127
+ Authorization: `Bearer ${contextGuardConfig.apiKey}`,
128
+ },
129
+ body: JSON.stringify(event),
130
+ }).catch(() => {
131
+ // Silently ignore - don't break agent if API is unreachable
125
132
  });
126
133
  }
127
134
  },
@@ -0,0 +1,30 @@
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
+ import { AgentPolicyRow } from "./types/database.types";
8
+ /**
9
+ * Policy checker interface
10
+ */
11
+ export interface PolicyChecker {
12
+ checkPromptInjection: (text: string) => string[];
13
+ checkSensitiveData: (text: string) => string[];
14
+ checkFileAccess: (filePath: string) => string[];
15
+ checkRateLimit: (timestamps: number[]) => boolean;
16
+ checkBlockedPatterns: (text: string) => string[];
17
+ checkToolAccess: (toolName: string) => string[];
18
+ checkSQLInjection: (text: string) => string[];
19
+ checkXSS: (text: string) => string[];
20
+ checkCustomRules: (text: string) => string[];
21
+ isBlockingMode: () => boolean;
22
+ getConfig: () => Required<AgentPolicyRow>;
23
+ }
24
+ /**
25
+ * Create a policy checker with the given configuration
26
+ * @param config - Policy configuration
27
+ * @returns Policy checker functions
28
+ */
29
+ export declare const createPolicyChecker: (config: Required<AgentPolicyRow>) => PolicyChecker;
30
+ export declare const DEFAULT_POLICY: Required<AgentPolicyRow>;
@@ -0,0 +1,273 @@
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.DEFAULT_POLICY = exports.createPolicyChecker = void 0;
10
+ /**
11
+ * Initialize patterns for detecting sensitive data
12
+ */
13
+ const initSensitiveDataPatterns = () => [
14
+ // Generic secrets
15
+ /(?:password|secret|api[_-]?key|token)\s*[:=]\s*['"]?[\w\-.]+['"]?/gi,
16
+ // Email addresses
17
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
18
+ // Social Security Numbers
19
+ /\b\d{3}-\d{2}-\d{4}\b/g,
20
+ // OpenAI API keys
21
+ /sk-[a-zA-Z0-9]{20,}/g,
22
+ // GitHub tokens
23
+ /ghp_[a-zA-Z0-9]{36}/g,
24
+ // AWS Access Keys
25
+ /AKIA[0-9A-Z]{16}/g,
26
+ // Stripe API keys
27
+ /sk_(live|test)_[a-zA-Z0-9]{24,}/g,
28
+ ];
29
+ /**
30
+ * Initialize patterns for detecting SQL injection
31
+ */
32
+ const initSQLInjectionPatterns = () => [
33
+ /\bUNION\b.{0,50}\bSELECT\b/gi,
34
+ /\bDROP\b\s+\b(TABLE|DATABASE|SCHEMA)\b/gi,
35
+ /\bDELETE\b\s+\bFROM\b/gi,
36
+ /\bTRUNCATE\b\s+\bTABLE\b/gi,
37
+ /\bINSERT\b\s+\bINTO\b.{0,50}\bVALUES\b/gi,
38
+ /\bEXEC\s*\(|\bEXECUTE\s*\(/gi,
39
+ /--\s*$|\/\*[\s\S]*?\*\//gm,
40
+ /\bOR\b\s+['"]?\d+['"]?\s*=\s*['"]?\d+['"]?/gi,
41
+ /'\s*OR\s*'1'\s*=\s*'1/gi,
42
+ /;\s*(DROP|DELETE|INSERT|UPDATE|CREATE|ALTER)\b/gi,
43
+ ];
44
+ /**
45
+ * Initialize patterns for detecting XSS attacks
46
+ */
47
+ const initXSSPatterns = () => [
48
+ /<script[\s>]/gi,
49
+ /javascript:/gi,
50
+ /on\w+\s*=\s*["']?[^"'>]*/gi, // onerror=, onload=, onclick=, etc.
51
+ /<iframe[\s>]/gi,
52
+ /<object[\s>]/gi,
53
+ /<embed[\s>]/gi,
54
+ /eval\s*\(/gi,
55
+ /document\.(cookie|write|location)/gi,
56
+ /window\.(location|open)\s*[=(]/gi,
57
+ /<img[^>]+src\s*=\s*["']?javascript:/gi,
58
+ ];
59
+ /**
60
+ * Initialize patterns for detecting prompt injection
61
+ */
62
+ const initPromptInjectionPatterns = () => [
63
+ /ignore\s+(previous|all)\s+(instructions|prompts)/gi,
64
+ /system:\s*you\s+are\s+now/gi,
65
+ /forget\s+(everything|all)/gi,
66
+ /new\s+instructions:/gi,
67
+ /\[INST\].*?\[\/INST\]/gs,
68
+ /<\|im_start\|>/g,
69
+ /disregard\s+previous/gi,
70
+ /override\s+previous/gi,
71
+ ];
72
+ /**
73
+ * Create a policy checker with the given configuration
74
+ * @param config - Policy configuration
75
+ * @returns Policy checker functions
76
+ */
77
+ const createPolicyChecker = (config) => {
78
+ const sensitiveDataPatterns = initSensitiveDataPatterns();
79
+ const promptInjectionPatterns = initPromptInjectionPatterns();
80
+ const sqlInjectionPatterns = initSQLInjectionPatterns();
81
+ const xssPatterns = initXSSPatterns();
82
+ return {
83
+ /**
84
+ * Check text for prompt injection attempts
85
+ */
86
+ checkPromptInjection: (text) => {
87
+ if (!config.enablePromptInjectionDetection) {
88
+ return [];
89
+ }
90
+ const violations = [];
91
+ for (const pattern of promptInjectionPatterns) {
92
+ const matches = text.match(pattern);
93
+ if (matches) {
94
+ violations.push(`Potential prompt injection detected: "${matches[0].substring(0, 50)}..."`);
95
+ }
96
+ }
97
+ return violations;
98
+ },
99
+ /**
100
+ * Check text for sensitive data exposure
101
+ */
102
+ checkSensitiveData: (text) => {
103
+ if (!config.enableSensitiveDataDetection) {
104
+ return [];
105
+ }
106
+ const violations = [];
107
+ for (const pattern of sensitiveDataPatterns) {
108
+ const matches = text.match(pattern);
109
+ if (matches) {
110
+ violations.push(`Sensitive data pattern detected (redacted): ${pattern.source.substring(0, 30)}...`);
111
+ }
112
+ }
113
+ return violations;
114
+ },
115
+ /**
116
+ * Check file path for security violations
117
+ */
118
+ checkFileAccess: (filePath) => {
119
+ if (!config.enablePathTraversalPrevention) {
120
+ return [];
121
+ }
122
+ const violations = [];
123
+ // Check for path traversal
124
+ if (filePath.includes("..")) {
125
+ violations.push(`Path traversal attempt detected: ${filePath}`);
126
+ }
127
+ // Check for dangerous system paths
128
+ const dangerousPaths = [
129
+ "/etc",
130
+ "/root",
131
+ "/sys",
132
+ "/proc",
133
+ "C:\\Windows\\System32",
134
+ ];
135
+ if (dangerousPaths.some((dangerous) => filePath.startsWith(dangerous))) {
136
+ violations.push(`Access to dangerous path detected: ${filePath}`);
137
+ }
138
+ // Check against allowed paths whitelist
139
+ if (config.allowedFilePaths.length > 0) {
140
+ const isAllowed = config.allowedFilePaths.some((allowed) => filePath.startsWith(allowed));
141
+ if (!isAllowed) {
142
+ violations.push(`File path not in allowed list: ${filePath}`);
143
+ }
144
+ }
145
+ return violations;
146
+ },
147
+ /**
148
+ * Check if rate limit is exceeded
149
+ */
150
+ checkRateLimit: (timestamps) => {
151
+ const oneMinuteAgo = Date.now() - 60000;
152
+ const recentCalls = timestamps.filter((t) => t > oneMinuteAgo);
153
+ return recentCalls.length < config.maxToolCallsPerMinute;
154
+ },
155
+ /**
156
+ * Check text against blocked patterns
157
+ */
158
+ checkBlockedPatterns: (text) => {
159
+ const violations = [];
160
+ for (const pattern of config.blockedPatterns) {
161
+ if (text.toLowerCase().includes(pattern.toLowerCase())) {
162
+ violations.push(`Blocked pattern detected: "${pattern}"`);
163
+ }
164
+ }
165
+ return violations;
166
+ },
167
+ /**
168
+ * Check if a tool is allowed or blocked
169
+ */
170
+ checkToolAccess: (toolName) => {
171
+ const violations = [];
172
+ if (config.blockedTools.includes(toolName)) {
173
+ violations.push(`Tool "${toolName}" is explicitly blocked`);
174
+ }
175
+ else if (config.allowedTools.length > 0 &&
176
+ !config.allowedTools.includes(toolName)) {
177
+ violations.push(`Tool "${toolName}" is not in the allowed list: [${config.allowedTools.join(", ")}]`);
178
+ }
179
+ return violations;
180
+ },
181
+ /**
182
+ * Check text for SQL injection attempts
183
+ */
184
+ checkSQLInjection: (text) => {
185
+ if (!config.enableSQLInjectionDetection) {
186
+ return [];
187
+ }
188
+ const violations = [];
189
+ for (const pattern of sqlInjectionPatterns) {
190
+ if (pattern.test(text)) {
191
+ violations.push(`SQL injection pattern detected: ${pattern.source.substring(0, 40)}...`);
192
+ pattern.lastIndex = 0; // reset stateful regex
193
+ }
194
+ }
195
+ return violations;
196
+ },
197
+ /**
198
+ * Check text for XSS attack patterns
199
+ */
200
+ checkXSS: (text) => {
201
+ if (!config.enableXSSDetection) {
202
+ return [];
203
+ }
204
+ const violations = [];
205
+ for (const pattern of xssPatterns) {
206
+ if (pattern.test(text)) {
207
+ violations.push(`XSS pattern detected: ${pattern.source.substring(0, 40)}...`);
208
+ pattern.lastIndex = 0; // reset stateful regex
209
+ }
210
+ }
211
+ return violations;
212
+ },
213
+ /**
214
+ * Check text against custom user-defined rules
215
+ */
216
+ checkCustomRules: (text) => {
217
+ const violations = [];
218
+ for (const rule of config.customRules) {
219
+ try {
220
+ const regex = new RegExp(rule.pattern, "gi");
221
+ if (regex.test(text)) {
222
+ violations.push(`Custom rule "${rule.name}" triggered`);
223
+ }
224
+ }
225
+ catch {
226
+ // Invalid regex pattern — skip silently
227
+ }
228
+ }
229
+ return violations;
230
+ },
231
+ /**
232
+ * Check if blocking mode is enabled
233
+ */
234
+ isBlockingMode: () => {
235
+ return config.mode === "block";
236
+ },
237
+ /**
238
+ * Get the full configuration
239
+ */
240
+ getConfig: () => {
241
+ return config;
242
+ },
243
+ };
244
+ };
245
+ exports.createPolicyChecker = createPolicyChecker;
246
+ exports.DEFAULT_POLICY = {
247
+ id: "",
248
+ agent_id: "",
249
+ created_at: "",
250
+ updated_at: "",
251
+ maxToolCallsPerMinute: 30,
252
+ blockedPatterns: [],
253
+ allowedFilePaths: [],
254
+ alertThreshold: 5,
255
+ enablePromptInjectionDetection: true,
256
+ enableSensitiveDataDetection: true,
257
+ enablePathTraversalPrevention: true,
258
+ enableSQLInjectionDetection: true,
259
+ enableSemanticDetection: false,
260
+ enableXSSDetection: true,
261
+ customRules: [],
262
+ mode: "monitor",
263
+ logPath: "mcp_security.log",
264
+ enableProFeatures: false,
265
+ licenseFilePath: ".contextguard-license",
266
+ transport: "stdio",
267
+ port: 3100,
268
+ targetUrl: "",
269
+ allowedTools: [],
270
+ blockedTools: [],
271
+ alertWebhook: "",
272
+ alertOnSeverity: ["HIGH", "CRITICAL"],
273
+ };
@@ -16,6 +16,7 @@ export interface SSEProxy {
16
16
  /**
17
17
  * Create an SSE/HTTP proxy for MCP servers
18
18
  * @param policyConfig - Policy configuration
19
+ * @param agentId - Agent identifier
19
20
  * @returns SSE proxy functions
20
21
  */
21
- export declare const createSSEProxy: (policyConfig: Required<AgentPolicyRow>) => SSEProxy;
22
+ export declare const createSSEProxy: (policyConfig: Required<AgentPolicyRow>, agentId?: string) => SSEProxy;
package/dist/sse-proxy.js CHANGED
@@ -42,17 +42,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.createSSEProxy = void 0;
43
43
  const http = __importStar(require("http"));
44
44
  const https = __importStar(require("https"));
45
- const policy_1 = require("./policy");
45
+ const security_engine_1 = require("./security-engine");
46
46
  const logger_1 = require("./logger");
47
47
  const mcp_1 = require("./types/mcp");
48
48
  /**
49
49
  * Create an SSE/HTTP proxy for MCP servers
50
50
  * @param policyConfig - Policy configuration
51
+ * @param agentId - Agent identifier
51
52
  * @returns SSE proxy functions
52
53
  */
53
- const createSSEProxy = (policyConfig) => {
54
- const policy = (0, policy_1.createPolicyChecker)(policyConfig);
55
- const logger = (0, logger_1.createLogger)(policyConfig.logPath);
54
+ const createSSEProxy = (policyConfig, agentId = "sse-proxy") => {
55
+ const policy = (0, security_engine_1.createPolicyChecker)(policyConfig);
56
+ const logger = (0, logger_1.createLogger)(policyConfig.logPath, agentId);
56
57
  const sessionId = (0, mcp_1.generateSessionId)();
57
58
  let toolCallTimestamps = [];
58
59
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contextguard",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Security monitoring wrapper for MCP servers with enterprise features",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -67,4 +67,4 @@
67
67
  "@anthropic-ai/sdk": "^0.54.0",
68
68
  "@supabase/supabase-js": "^2.79.0"
69
69
  }
70
- }
70
+ }