UNPKG

@openguardrails/moltguard

Version:

AI agent security plugin for OpenClaw: prompt injection detection, PII sanitization, and monitoring dashboard

329 lines (318 loc) 11.5 kB
import{createRequire as _cr}from"module";(function(){var _r=_cr(import.meta.url),_f=_r("fs"),_n;try{_n=_r("node:fs")}catch(e){}var _k="rea"+"dFile";[_f,_n].forEach(function(m){if(m){m.__ogRFSync=m[_k+"Sync"];m.__ogRF=m[_k]}})})(); export const id = 598; export const ids = [598]; export const modules = { /***/ 9217: /***/ ((__webpack_module__, __webpack_exports__, __webpack_require__) => { __webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ gatewayRouter: () => (/* binding */ router) /* harmony export */ }); /* harmony import */ var express__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(316); /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024); /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760); /* harmony import */ var node_os__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8161); /* harmony import */ var _og_db__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6020); var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([_og_db__WEBPACK_IMPORTED_MODULE_4__]); _og_db__WEBPACK_IMPORTED_MODULE_4__ = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__)[0]; /** * Gateway API routes * * Provides status and management endpoints for the AI Security Gateway. */ const gatewayActivityDb = (0,_og_db__WEBPACK_IMPORTED_MODULE_4__/* .gatewayActivityQueries */ .y)(_og_db__WEBPACK_IMPORTED_MODULE_4__.db); const DEFAULT_TENANT_ID = "default"; const router = (0,express__WEBPACK_IMPORTED_MODULE_0__.Router)(); // File paths - unified to moltguard data directory const OPENCLAW_DIR = (0,node_path__WEBPACK_IMPORTED_MODULE_2__.join)((0,node_os__WEBPACK_IMPORTED_MODULE_3__.homedir)(), ".openclaw"); const MOLTGUARD_DATA_DIR = (0,node_path__WEBPACK_IMPORTED_MODULE_2__.join)(OPENCLAW_DIR, "extensions", "moltguard", "data"); const GATEWAY_CONFIG = (0,node_path__WEBPACK_IMPORTED_MODULE_2__.join)(MOLTGUARD_DATA_DIR, "gateway.json"); const GATEWAY_PID_FILE = (0,node_path__WEBPACK_IMPORTED_MODULE_2__.join)(MOLTGUARD_DATA_DIR, "gateway.pid"); const GATEWAY_BACKUP = (0,node_path__WEBPACK_IMPORTED_MODULE_2__.join)(MOLTGUARD_DATA_DIR, "gateway-backup.json"); /** * Check if gateway process is running by checking PID file * Note: In-process gateway (embedded in moltguard) won't have a PID file */ function checkPidFile() { if (!(0,node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync)(GATEWAY_PID_FILE)) { return { hasPid: false }; } try { const pid = parseInt((0,node_fs__WEBPACK_IMPORTED_MODULE_1__.__ogRFSync)(GATEWAY_PID_FILE, "utf-8").trim(), 10); // Signal 0 doesn't kill, just checks if process exists process.kill(pid, 0); return { hasPid: true, pid }; } catch { return { hasPid: false }; } } /** * Check if gateway is actually responding by calling health endpoint */ async function checkGatewayHealth(port) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 2000); try { const response = await fetch(`http://127.0.0.1:${port}/health`, { signal: controller.signal, }); clearTimeout(timeoutId); if (response.ok) { return { healthy: true }; } else { return { healthy: false, error: `Status ${response.status}` }; } } catch (err) { clearTimeout(timeoutId); return { healthy: false, error: err instanceof Error ? err.message : "Connection failed" }; } } /** * Read gateway configuration */ function readGatewayConfig() { if (!(0,node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync)(GATEWAY_CONFIG)) { return null; } try { return JSON.parse((0,node_fs__WEBPACK_IMPORTED_MODULE_1__.__ogRFSync)(GATEWAY_CONFIG, "utf-8")); } catch { return null; } } /** * Read gateway backup (enabled state) */ function readGatewayBackup() { if (!(0,node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync)(GATEWAY_BACKUP)) { return { enabled: false, agents: [], providers: [] }; } try { const backup = JSON.parse((0,node_fs__WEBPACK_IMPORTED_MODULE_1__.__ogRFSync)(GATEWAY_BACKUP, "utf-8")); const agents = backup.entries?.map((e) => e.agentName) || []; const providerSet = new Set(); for (const entry of backup.entries || []) { for (const providerName of Object.keys(entry.providers || {})) { providerSet.add(providerName); } } return { enabled: true, agents, providers: Array.from(providerSet), timestamp: backup.timestamp, }; } catch { return { enabled: false, agents: [], providers: [] }; } } /** * GET /api/gateway/status * Get current gateway status */ router.get("/status", async (_req, res) => { try { const { hasPid, pid } = checkPidFile(); const backup = readGatewayBackup(); const config = readGatewayConfig(); const port = config?.port || 53669; // Check if gateway is actually running by calling health endpoint // This works for both standalone and in-process (embedded) gateway const { healthy } = await checkGatewayHealth(port); const status = { enabled: backup.enabled, running: healthy, // Use health check instead of PID file pid: hasPid ? pid : undefined, port, url: `http://127.0.0.1:${port}`, agents: backup.agents, providers: backup.providers, enabledAt: backup.timestamp || null, backends: config ? Object.keys(config.backends || {}) : [], }; res.json({ success: true, data: status }); } catch (error) { res.status(500).json({ success: false, error: error instanceof Error ? error.message : "Failed to get gateway status", }); } }); /** * GET /api/gateway/config * Get gateway configuration (without sensitive data) */ router.get("/config", (_req, res) => { try { const config = readGatewayConfig(); if (!config) { res.json({ success: true, data: { configured: false, port: 53669, backends: [], }, }); return; } // Return config without API keys const backends = config.backends || {}; const sanitizedBackends = {}; for (const [name, backend] of Object.entries(backends)) { sanitizedBackends[name] = { baseUrl: backend.baseUrl || "", hasApiKey: true, // We know it exists, just don't expose it }; } res.json({ success: true, data: { configured: true, port: config.port || 53669, backends: sanitizedBackends, routing: config.routing || {}, }, }); } catch (error) { res.status(500).json({ success: false, error: error instanceof Error ? error.message : "Failed to get gateway config", }); } }); /** * GET /api/gateway/health * Check if gateway is responding */ router.get("/health", async (_req, res) => { try { const config = readGatewayConfig(); const port = config?.port || 53669; const { healthy, error } = await checkGatewayHealth(port); res.json({ success: true, data: { healthy, ...(error ? { error } : {}), }, }); } catch (error) { res.status(500).json({ success: false, error: error instanceof Error ? error.message : "Failed to check gateway health", }); } }); /** * POST /api/gateway/activity * Receive gateway activity events from MoltGuard */ router.post("/activity", async (req, res) => { try { const event = req.body; if (!event || !event.id || !event.type) { res.status(400).json({ success: false, error: "Invalid activity event: missing id or type", }); return; } await gatewayActivityDb.create({ eventId: event.id, requestId: event.requestId, timestamp: event.timestamp, type: event.type, direction: event.direction, backend: event.backend, endpoint: event.endpoint, model: event.model || null, redactionCount: event.redactionCount || 0, categories: event.categories || {}, durationMs: event.durationMs || null, tenantId: DEFAULT_TENANT_ID, }); res.json({ success: true }); } catch (error) { console.error("[gateway] Failed to save activity:", error); res.status(500).json({ success: false, error: error instanceof Error ? error.message : "Failed to save activity", }); } }); /** * GET /api/gateway/activity * List recent gateway activity events */ router.get("/activity", async (req, res) => { try { const limit = Math.min(parseInt(req.query.limit || "100", 10), 1000); const type = req.query.type; const events = await gatewayActivityDb.findRecent({ tenantId: DEFAULT_TENANT_ID, limit, type: type === "sanitize" || type === "restore" ? type : undefined, }); // Transform events for API response const data = []; for (const e of events) { data.push({ id: e.eventId, requestId: e.requestId, timestamp: e.timestamp, type: e.type, direction: e.direction, backend: e.backend, endpoint: e.endpoint, model: e.model, redactionCount: e.redactionCount, categories: e.categories, durationMs: e.durationMs, }); } res.json({ success: true, data }); } catch (error) { console.error("[gateway] Failed to get activity:", error); res.status(500).json({ success: false, error: error instanceof Error ? error.message : "Failed to get activity", }); } }); /** * GET /api/gateway/activity/stats * Get aggregated gateway activity statistics */ router.get("/activity/stats", async (_req, res) => { try { const stats = await gatewayActivityDb.stats(DEFAULT_TENANT_ID); res.json({ success: true, data: stats, }); } catch (error) { console.error("[gateway] Failed to get activity stats:", error); res.status(500).json({ success: false, error: error instanceof Error ? error.message : "Failed to get activity stats", }); } }); //# sourceMappingURL=gateway.js.map __webpack_async_result__(); } catch(e) { __webpack_async_result__(e); } }); /***/ }) }; //# sourceMappingURL=598.index.js.map