@wardnmesh/sdk-node 0.2.1
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 +47 -0
- package/README.md +178 -0
- package/bin/setup.js +119 -0
- package/dist/agent-guard.d.ts +28 -0
- package/dist/agent-guard.js +206 -0
- package/dist/config/security-limits.d.ts +42 -0
- package/dist/config/security-limits.js +52 -0
- package/dist/detectors/base.d.ts +22 -0
- package/dist/detectors/base.js +51 -0
- package/dist/detectors/pattern.d.ts +7 -0
- package/dist/detectors/pattern.js +76 -0
- package/dist/detectors/semantic-detector.d.ts +16 -0
- package/dist/detectors/semantic-detector.js +96 -0
- package/dist/detectors/sequence.d.ts +11 -0
- package/dist/detectors/sequence.js +182 -0
- package/dist/detectors/state.d.ts +8 -0
- package/dist/detectors/state.js +86 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +31 -0
- package/dist/integrations/vercel.d.ts +7 -0
- package/dist/integrations/vercel.js +34 -0
- package/dist/middleware/express.d.ts +36 -0
- package/dist/middleware/express.js +54 -0
- package/dist/middleware/nextjs.d.ts +3 -0
- package/dist/middleware/nextjs.js +40 -0
- package/dist/state/session-manager.d.ts +13 -0
- package/dist/state/session-manager.js +22 -0
- package/dist/telemetry/reporter.d.ts +32 -0
- package/dist/telemetry/reporter.js +86 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.js +14 -0
- package/dist/update-checker.d.ts +21 -0
- package/dist/update-checker.js +218 -0
- package/dist/utils/logger.d.ts +40 -0
- package/dist/utils/logger.js +79 -0
- package/dist/utils/rule-validator.d.ts +15 -0
- package/dist/utils/rule-validator.js +143 -0
- package/dist/utils/safe-regex.d.ts +53 -0
- package/dist/utils/safe-regex.js +220 -0
- package/dist/wardn.d.ts +67 -0
- package/dist/wardn.js +443 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
ELASTIC LICENSE 2.0
|
|
2
|
+
|
|
3
|
+
I. SHARING AND INTEGRATION
|
|
4
|
+
|
|
5
|
+
1. You may not provide the software to third parties as a hosted or managed
|
|
6
|
+
service, where the service provides users with access to any substantial
|
|
7
|
+
set of the features or functionality of this software.
|
|
8
|
+
|
|
9
|
+
2. You may not move, change, disable, or circumvent the license key functionality
|
|
10
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
11
|
+
software that is protected by the license key.
|
|
12
|
+
|
|
13
|
+
3. You may not alter, remove, or obscure any licensing, copyright, or other
|
|
14
|
+
notices of the licensor in the software. Any use of the licensor’s trademarks
|
|
15
|
+
is subject to applicable law.
|
|
16
|
+
|
|
17
|
+
II. LIMITATIONS
|
|
18
|
+
|
|
19
|
+
1. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
2. This license does not grant you any right to any of the licensor's trademarks
|
|
28
|
+
or other brand features.
|
|
29
|
+
|
|
30
|
+
III. GRANT OF RIGHTS
|
|
31
|
+
|
|
32
|
+
1. Subject to the terms and conditions of this license, you are granted a
|
|
33
|
+
non-exclusive, royalty-free, worldwide, non-sublicensable license to use,
|
|
34
|
+
copy, distribute, make available, and prepare derivative works of the software.
|
|
35
|
+
|
|
36
|
+
2. Provide the software to third parties as a hosted or managed service, where
|
|
37
|
+
the service provides users with access to any substantial set of the features
|
|
38
|
+
or functionality of this software, is allowed only if you have a separate
|
|
39
|
+
written agreement with the licensor.
|
|
40
|
+
|
|
41
|
+
IV. TERMINATION
|
|
42
|
+
|
|
43
|
+
1. If you violate any term of this license, your rights under this license will
|
|
44
|
+
terminate immediately.
|
|
45
|
+
|
|
46
|
+
2. If your rights under this license terminate, you must stop using the
|
|
47
|
+
software and destroy all copies of the software in your possession or control.
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @wardnmesh/sdk-node
|
|
2
|
+
|
|
3
|
+
**WardnMesh.AI** (formerly AgentGuard) is an active defense middleware for AI Agents. This SDK allows you to verify LLM inputs/outputs, block prompt injections, and prevent data exfiltration in real-time.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🛡️ **Active Defense**: Blocks prompt injections, jailbreaks, and PII leaks.
|
|
8
|
+
- ⚡ **Middleware First**: Easy integration with Express and Next.js.
|
|
9
|
+
- 🔍 **Telemetry**: Real-time violation reporting to Central Control Bus (CCB).
|
|
10
|
+
- 🧠 **Context Aware**: Tracks tool usage history to detect multi-step attacks (Sequence Detection).
|
|
11
|
+
- 🚀 **Fail-Open**: Designed to prioritize application availability (blocks are logged, errors are ignored by default).
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @wardnmesh/sdk-node
|
|
17
|
+
# or
|
|
18
|
+
yarn add @wardnmesh/sdk-node
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Initialize the Guard
|
|
24
|
+
|
|
25
|
+
Initialize the singleton `Wardn` instance at the start of your application.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import Wardn, { Rule, RiskLevel } from '@wardnmesh/sdk-node';
|
|
29
|
+
|
|
30
|
+
const rules: Rule[] = [
|
|
31
|
+
{
|
|
32
|
+
id: 'block-aws-keys',
|
|
33
|
+
name: 'Block AWS Keys',
|
|
34
|
+
category: 'safety',
|
|
35
|
+
severity: 'critical',
|
|
36
|
+
description: 'Prevents leakage of AWS Access Keys',
|
|
37
|
+
detector: {
|
|
38
|
+
type: 'pattern',
|
|
39
|
+
config: {
|
|
40
|
+
targetTool: 'llm_output',
|
|
41
|
+
targetParameter: 'content',
|
|
42
|
+
patterns: [
|
|
43
|
+
{ name: 'AWS Key ID', regex: 'AKIA[0-9A-Z]{16}', description: 'AWS Access Key ID' }
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
escalation: { 1: 'block' }
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
// Initialize (Singleton)
|
|
52
|
+
const guard = Wardn.init({
|
|
53
|
+
rules,
|
|
54
|
+
enabled: true,
|
|
55
|
+
maxHistorySize: 50, // Limit memory usage
|
|
56
|
+
telemetry: {
|
|
57
|
+
enabled: true,
|
|
58
|
+
serviceName: 'my-agent-service'
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Usage with Express
|
|
64
|
+
|
|
65
|
+
Use the `createExpressMiddleware` to automatically scan incoming requests.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import express from 'express';
|
|
69
|
+
import { createExpressMiddleware } from '@wardnmesh/sdk-node';
|
|
70
|
+
|
|
71
|
+
const app = express();
|
|
72
|
+
|
|
73
|
+
// IMPORTANT: body-parser or express.json() must be used BEFORE WardnMesh
|
|
74
|
+
app.use(express.json());
|
|
75
|
+
|
|
76
|
+
// Apply Middleware
|
|
77
|
+
app.use(createExpressMiddleware(guard, {
|
|
78
|
+
// Optional: Custom request extraction
|
|
79
|
+
extractRequest: (req) => ({
|
|
80
|
+
sessionId: req.headers['x-session-id'] as string,
|
|
81
|
+
prompt: req.body.user_prompt
|
|
82
|
+
}),
|
|
83
|
+
// Optional: Custom block handler
|
|
84
|
+
onBlock: (req, res, result) => {
|
|
85
|
+
res.status(400).json({ error: 'Security Violation Detected', violations: result.violations });
|
|
86
|
+
}
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
app.post('/chat', (req, res) => {
|
|
90
|
+
// If we reach here, the request is safe!
|
|
91
|
+
res.json({ response: 'Hello world' });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
app.listen(3000);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. Usage with Next.js (App Router)
|
|
98
|
+
|
|
99
|
+
Wrap your API Route handlers with `withAgentGuard`.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// app/api/chat/route.ts
|
|
103
|
+
import { NextResponse } from 'next/server';
|
|
104
|
+
import { withAgentGuard } from '@wardnmesh/sdk-node';
|
|
105
|
+
import { guard } from '@/lib/wardn'; // Import your initialized guard instance
|
|
106
|
+
|
|
107
|
+
export const POST = withAgentGuard(guard, async (req) => {
|
|
108
|
+
const body = await req.json();
|
|
109
|
+
// Process request...
|
|
110
|
+
return NextResponse.json({ message: 'Safe!' });
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
### `WardnConfig`
|
|
117
|
+
|
|
118
|
+
| Property | Type | Default | Description |
|
|
119
|
+
|----------|------|---------|-------------|
|
|
120
|
+
| `rules` | `Rule[]` | Required | Array of active security rules. |
|
|
121
|
+
| `enabled` | `boolean` | `true` | Master switch for the SDK. |
|
|
122
|
+
| `maxHistorySize` | `number` | `50` | Max tool calls to keep in session state (prevents memory bloat). |
|
|
123
|
+
| `telemetry.enabled` | `boolean` | `false` | Enable sending data to CCB. |
|
|
124
|
+
| `telemetry.serviceName` | `string` | `unknown` | Identifier for this service. |
|
|
125
|
+
|
|
126
|
+
### Interactive Setup (Recommended)
|
|
127
|
+
|
|
128
|
+
Run the initialization wizard to automatically configure telemetry and generate your `.env` file:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npx wardn-init
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Environment Variables
|
|
135
|
+
|
|
136
|
+
| Variable | Description |
|
|
137
|
+
|----------|-------------|
|
|
138
|
+
| `WARDN_API_KEY` | **Required** for telemetry. Get yours at <https://wardnmesh.ai> |
|
|
139
|
+
| `WARDN_API_URL` | Optional. Defaults to `https://api.wardnmesh.ai` |
|
|
140
|
+
| `WARDN_TELEMETRY_ENABLED` | Set to `true` to enable cloud syncing. |
|
|
141
|
+
|
|
142
|
+
## Custom State Management
|
|
143
|
+
|
|
144
|
+
By default, `Wardn` uses an in-memory state provider. For production (serverless/distributed), implement `SessionStateProvider`:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { SessionStateProvider } from '@wardnmesh/sdk-node';
|
|
148
|
+
|
|
149
|
+
class RedisStateProvider implements SessionStateProvider {
|
|
150
|
+
// ... implement getState and setState using Redis
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Wardn.init(config, new RedisStateProvider());
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Other Integration Options
|
|
157
|
+
|
|
158
|
+
### For Claude Code & Claude Desktop
|
|
159
|
+
|
|
160
|
+
If you're using **Claude Code CLI** or **Claude Desktop**, use the **WardnMesh MCP Server** instead of this SDK:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm install -g @wardnmesh/mcp-server
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The MCP Server provides:
|
|
167
|
+
- 🔧 **Auto-setup** for Claude Code hooks
|
|
168
|
+
- 🛡️ **Real-time scanning** of user prompts and tool arguments
|
|
169
|
+
- 🔍 **Security checks** for Bash commands, file operations, and content
|
|
170
|
+
|
|
171
|
+
**Learn more:** [MCP Server Documentation](../mcp-server/README.md)
|
|
172
|
+
|
|
173
|
+
### For Other Platforms
|
|
174
|
+
|
|
175
|
+
- **OpenAI API**: Use this SDK with custom middleware
|
|
176
|
+
- **LangChain**: Use this SDK with callback handlers
|
|
177
|
+
- **CrewAI**: Use the Python SDK (`pip install wardnmesh`)
|
|
178
|
+
- **Cursor/VS Code**: Coming soon - MCP Server integration
|
package/bin/setup.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const readline = require("readline");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const DO_NOT_TRACK_DOMAINS = ["localhost", "127.0.0.1"];
|
|
13
|
+
|
|
14
|
+
console.log("\x1b[36m%s\x1b[0m", "🛡️ WardnMesh SDK Setup");
|
|
15
|
+
console.log("-----------------------------------");
|
|
16
|
+
console.log(
|
|
17
|
+
"This script will help you configure WardnMesh for your project.\n"
|
|
18
|
+
);
|
|
19
|
+
console.log(
|
|
20
|
+
"\x1b[33m%s\x1b[0m",
|
|
21
|
+
"PRIVACY NOTICE: WardnMesh respects your user privacy."
|
|
22
|
+
);
|
|
23
|
+
console.log(
|
|
24
|
+
"\x1b[33m%s\x1b[0m",
|
|
25
|
+
"No personal data, raw prompts, or secrets are ever sent to our servers."
|
|
26
|
+
);
|
|
27
|
+
console.log(
|
|
28
|
+
"\x1b[33m%s\x1b[0m",
|
|
29
|
+
"Only security violation metadata (redacted) is synced for threat analysis.\n"
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const questions = [
|
|
33
|
+
{
|
|
34
|
+
key: "enableTelemetry",
|
|
35
|
+
text: "Would you like to enable real-time threat telemetry syncing to WardnMesh Cloud? (Y/n) ",
|
|
36
|
+
default: "Y",
|
|
37
|
+
type: "boolean",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: "apiKey",
|
|
41
|
+
text: "Enter your WardnMesh API Key (leave blank to skip): ",
|
|
42
|
+
type: "secret",
|
|
43
|
+
condition: (answers) => answers.enableTelemetry,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: "apiUrl",
|
|
47
|
+
text: "Enter WardnMesh API URL (default: https://api.wardnmesh.ai): ",
|
|
48
|
+
default: "https://api.wardnmesh.ai",
|
|
49
|
+
condition: (answers) => answers.enableTelemetry,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
async function ask(question) {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
rl.question(question.text, (answer) => {
|
|
56
|
+
resolve(answer.trim());
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function run() {
|
|
62
|
+
const config = {};
|
|
63
|
+
|
|
64
|
+
for (const q of questions) {
|
|
65
|
+
if (q.condition && !q.condition(config)) continue;
|
|
66
|
+
|
|
67
|
+
let answer = await ask(q.text);
|
|
68
|
+
|
|
69
|
+
if (q.type === "boolean") {
|
|
70
|
+
config[q.key] = answer.toLowerCase() !== "n";
|
|
71
|
+
} else {
|
|
72
|
+
config[q.key] = answer || q.default || "";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
rl.close();
|
|
77
|
+
|
|
78
|
+
console.log("\n\x1b[32mConfiguration Complete!\x1b[0m");
|
|
79
|
+
|
|
80
|
+
// Generate .env content
|
|
81
|
+
let envContent = "";
|
|
82
|
+
if (config.enableTelemetry) {
|
|
83
|
+
envContent += `\n# WardnMesh Security SDK\n`;
|
|
84
|
+
envContent += `WARDN_TELEMETRY_ENABLED=true\n`;
|
|
85
|
+
if (config.apiUrl) envContent += `WARDN_API_URL=${config.apiUrl}\n`;
|
|
86
|
+
if (config.apiKey) envContent += `WARDN_API_KEY=${config.apiKey}\n`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (envContent) {
|
|
90
|
+
const envPath = path.resolve(process.cwd(), ".env");
|
|
91
|
+
console.log(`\nAppending the following to ${envPath}:`);
|
|
92
|
+
console.log("\x1b[90m%s\x1b[0m", envContent);
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
fs.appendFileSync(envPath, envContent);
|
|
96
|
+
console.log("✅ .env updated successfully.");
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error("❌ Failed to write to .env:", err.message);
|
|
99
|
+
console.log("Please verify permissions or add the variables manually.");
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
console.log("No changes made to .env (Telemetry disabled).");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log("\nNext Step: Initialize Wardn in your code:");
|
|
106
|
+
console.log(`
|
|
107
|
+
import { Wardn } from '@wardnmesh/sdk-node';
|
|
108
|
+
|
|
109
|
+
Wardn.init({
|
|
110
|
+
rules: [/* your rules */],
|
|
111
|
+
telemetry: {
|
|
112
|
+
enabled: process.env.WARDN_TELEMETRY_ENABLED === 'true',
|
|
113
|
+
serviceName: 'my-agent-service'
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
run().catch(console.error);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AgentGuardConfig, ScanResult, AgentRequest } from "./types";
|
|
2
|
+
import { SessionStateProvider } from "./state/session-manager";
|
|
3
|
+
export declare class AgentGuard {
|
|
4
|
+
private static instance;
|
|
5
|
+
private config;
|
|
6
|
+
private stateProvider;
|
|
7
|
+
private telemetry;
|
|
8
|
+
private patternDetector;
|
|
9
|
+
private sequenceDetector;
|
|
10
|
+
private stateDetector;
|
|
11
|
+
private semanticDetector;
|
|
12
|
+
private constructor();
|
|
13
|
+
private initTelemetry;
|
|
14
|
+
static getInstance(): AgentGuard;
|
|
15
|
+
static init(config: AgentGuardConfig, stateProvider?: SessionStateProvider): AgentGuard;
|
|
16
|
+
/** Main entry point to scan an agent request */
|
|
17
|
+
scan(request: AgentRequest): Promise<ScanResult>;
|
|
18
|
+
/** Normalize request to ToolData format */
|
|
19
|
+
private normalizeRequest;
|
|
20
|
+
/** Run detector for a single rule */
|
|
21
|
+
private detectViolation;
|
|
22
|
+
/** Handle semantic detection */
|
|
23
|
+
private detectSemanticViolation;
|
|
24
|
+
/** Report violation to telemetry */
|
|
25
|
+
private reportViolation;
|
|
26
|
+
/** Report scan completion to telemetry */
|
|
27
|
+
private reportScanComplete;
|
|
28
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentGuard = void 0;
|
|
4
|
+
const pattern_1 = require("./detectors/pattern");
|
|
5
|
+
const sequence_1 = require("./detectors/sequence");
|
|
6
|
+
const state_1 = require("./detectors/state");
|
|
7
|
+
const session_manager_1 = require("./state/session-manager");
|
|
8
|
+
const reporter_1 = require("./telemetry/reporter");
|
|
9
|
+
const semantic_detector_1 = require("./detectors/semantic-detector");
|
|
10
|
+
/** Synchronous state adapter for in-request state management */
|
|
11
|
+
class SyncStateAdapter {
|
|
12
|
+
constructor(initialState, maxHistory = 50) {
|
|
13
|
+
this.maxHistory = maxHistory;
|
|
14
|
+
this.state = {
|
|
15
|
+
startTime: initialState.startTime || new Date().toISOString(),
|
|
16
|
+
toolCalls: initialState.toolCalls || [],
|
|
17
|
+
recentTools: initialState.recentTools || [],
|
|
18
|
+
detectedViolations: initialState.detectedViolations || [],
|
|
19
|
+
customState: initialState.customState || {},
|
|
20
|
+
currentFile: initialState.currentFile,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
getRecentTools(count) {
|
|
24
|
+
const tools = this.state.recentTools || [];
|
|
25
|
+
return count ? tools.slice(-count) : tools;
|
|
26
|
+
}
|
|
27
|
+
setCustomState(key, value) {
|
|
28
|
+
this.state.customState[key] = value;
|
|
29
|
+
}
|
|
30
|
+
getCustomState(key) {
|
|
31
|
+
return this.state.customState[key];
|
|
32
|
+
}
|
|
33
|
+
addToolCall(tool) {
|
|
34
|
+
if (!this.state.recentTools)
|
|
35
|
+
this.state.recentTools = [];
|
|
36
|
+
this.state.recentTools.push(tool);
|
|
37
|
+
if (this.state.recentTools.length > this.maxHistory) {
|
|
38
|
+
this.state.recentTools.shift();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exportState() {
|
|
42
|
+
return this.state;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
class AgentGuard {
|
|
46
|
+
constructor(config, stateProvider) {
|
|
47
|
+
this.config = config;
|
|
48
|
+
this.stateProvider = stateProvider || new session_manager_1.InMemorySessionStateProvider();
|
|
49
|
+
this.telemetry = this.initTelemetry();
|
|
50
|
+
this.patternDetector = new pattern_1.PatternDetector();
|
|
51
|
+
this.sequenceDetector = new sequence_1.SequenceDetector();
|
|
52
|
+
this.stateDetector = new state_1.StateDetector();
|
|
53
|
+
this.semanticDetector = semantic_detector_1.SemanticDetector.getInstance();
|
|
54
|
+
// Emit startup event
|
|
55
|
+
this.telemetry.emit({
|
|
56
|
+
eventType: "agent_started",
|
|
57
|
+
timestamp: new Date().toISOString(),
|
|
58
|
+
data: {
|
|
59
|
+
config: {
|
|
60
|
+
appName: this.config.telemetry?.serviceName,
|
|
61
|
+
ruleCount: this.config.rules.length,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
initTelemetry() {
|
|
67
|
+
if (this.config.telemetry?.enabled && process.env.CCB_ENDPOINT) {
|
|
68
|
+
return new reporter_1.CCBReporter(process.env.CCB_ENDPOINT, process.env.CCB_API_KEY || "", this.config.telemetry.serviceName || "unknown-service");
|
|
69
|
+
}
|
|
70
|
+
return new reporter_1.ConsoleReporter();
|
|
71
|
+
}
|
|
72
|
+
static getInstance() {
|
|
73
|
+
if (!AgentGuard.instance) {
|
|
74
|
+
throw new Error("AgentGuard not initialized. Call AgentGuard.init() first.");
|
|
75
|
+
}
|
|
76
|
+
return AgentGuard.instance;
|
|
77
|
+
}
|
|
78
|
+
static init(config, stateProvider) {
|
|
79
|
+
AgentGuard.instance = new AgentGuard(config, stateProvider);
|
|
80
|
+
return AgentGuard.instance;
|
|
81
|
+
}
|
|
82
|
+
/** Main entry point to scan an agent request */
|
|
83
|
+
async scan(request) {
|
|
84
|
+
const start = Date.now();
|
|
85
|
+
const violations = [];
|
|
86
|
+
const sessionId = request.sessionId || "default";
|
|
87
|
+
try {
|
|
88
|
+
const rawState = await this.stateProvider.getState(sessionId);
|
|
89
|
+
const stateAdapter = new SyncStateAdapter(rawState, this.config.maxHistorySize);
|
|
90
|
+
const toolData = this.normalizeRequest(request);
|
|
91
|
+
stateAdapter.addToolCall(toolData);
|
|
92
|
+
// Lazy load semantic model if needed
|
|
93
|
+
if (this.config.rules.some((r) => r.detector.type === "semantic")) {
|
|
94
|
+
await this.semanticDetector.init();
|
|
95
|
+
}
|
|
96
|
+
// Run all detectors
|
|
97
|
+
for (const rule of this.config.rules) {
|
|
98
|
+
const violation = await this.detectViolation(rule, toolData, stateAdapter);
|
|
99
|
+
if (violation) {
|
|
100
|
+
violations.push(violation);
|
|
101
|
+
this.reportViolation(violation, toolData, sessionId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
await this.stateProvider.setState(sessionId, stateAdapter.exportState());
|
|
105
|
+
const result = {
|
|
106
|
+
allowed: !violations.some((v) => v.severity === "critical"),
|
|
107
|
+
violations,
|
|
108
|
+
latencyMs: Date.now() - start,
|
|
109
|
+
metadata: { analyzedRules: this.config.rules.length },
|
|
110
|
+
};
|
|
111
|
+
this.reportScanComplete(result, sessionId);
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// Fail-Open Resilience: Never crash the application
|
|
116
|
+
console.error("[AgentGuard] Scan failed, failing open:", error);
|
|
117
|
+
this.telemetry.emit({
|
|
118
|
+
eventType: "error",
|
|
119
|
+
timestamp: new Date().toISOString(),
|
|
120
|
+
data: {
|
|
121
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
122
|
+
sessionId,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
allowed: true, // Fail-open
|
|
127
|
+
violations: [],
|
|
128
|
+
latencyMs: Date.now() - start,
|
|
129
|
+
metadata: {
|
|
130
|
+
error: true,
|
|
131
|
+
errorDetails: error instanceof Error ? error.message : "Unknown",
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Normalize request to ToolData format */
|
|
137
|
+
normalizeRequest(request) {
|
|
138
|
+
return {
|
|
139
|
+
toolName: request.toolName || "llm_input",
|
|
140
|
+
parameters: request.parameters || { prompt: request.prompt || "" },
|
|
141
|
+
result: { success: true },
|
|
142
|
+
duration: 0,
|
|
143
|
+
timestamp: new Date().toISOString(),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** Run detector for a single rule */
|
|
147
|
+
async detectViolation(rule, toolData, stateAdapter) {
|
|
148
|
+
switch (rule.detector.type) {
|
|
149
|
+
case "pattern":
|
|
150
|
+
return this.patternDetector.detect(toolData, rule, stateAdapter);
|
|
151
|
+
case "sequence":
|
|
152
|
+
return this.sequenceDetector.detect(toolData, rule, stateAdapter);
|
|
153
|
+
case "state":
|
|
154
|
+
return this.stateDetector.detect(toolData, rule, stateAdapter);
|
|
155
|
+
case "semantic":
|
|
156
|
+
return this.detectSemanticViolation(rule, toolData);
|
|
157
|
+
default:
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Handle semantic detection */
|
|
162
|
+
async detectSemanticViolation(rule, toolData) {
|
|
163
|
+
const prompt = toolData.parameters.prompt ||
|
|
164
|
+
JSON.stringify(toolData.parameters);
|
|
165
|
+
const config = rule.detector.config;
|
|
166
|
+
const { detected, reason, score } = await this.semanticDetector.scan(prompt, config.threshold || 0.75);
|
|
167
|
+
if (!detected)
|
|
168
|
+
return null;
|
|
169
|
+
return {
|
|
170
|
+
id: crypto.randomUUID(),
|
|
171
|
+
ruleId: rule.id,
|
|
172
|
+
ruleName: rule.name,
|
|
173
|
+
severity: rule.severity,
|
|
174
|
+
description: reason || "Semantic Violation",
|
|
175
|
+
context: {
|
|
176
|
+
toolName: toolData.toolName,
|
|
177
|
+
toolData,
|
|
178
|
+
additionalInfo: { score },
|
|
179
|
+
},
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** Report violation to telemetry */
|
|
184
|
+
reportViolation(violation, toolData, sessionId) {
|
|
185
|
+
this.telemetry.emit({
|
|
186
|
+
eventType: "violation_detected",
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
188
|
+
data: { violation, toolData },
|
|
189
|
+
metadata: { sessionId },
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/** Report scan completion to telemetry */
|
|
193
|
+
reportScanComplete(result, sessionId) {
|
|
194
|
+
this.telemetry.emit({
|
|
195
|
+
eventType: "scan_complete",
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
data: {
|
|
198
|
+
allowed: result.allowed,
|
|
199
|
+
latencyMs: result.latencyMs,
|
|
200
|
+
violationCount: result.violations.length,
|
|
201
|
+
},
|
|
202
|
+
metadata: { sessionId },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.AgentGuard = AgentGuard;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Limits Configuration
|
|
3
|
+
* Centralized configuration for all security-related limits and thresholds
|
|
4
|
+
*/
|
|
5
|
+
export declare const SECURITY_LIMITS: {
|
|
6
|
+
readonly MAX_RULES: 1000;
|
|
7
|
+
readonly MAX_PATTERNS_PER_RULE: 100;
|
|
8
|
+
readonly MAX_SCAN_LENGTH: 50000;
|
|
9
|
+
readonly MAX_PARAMETER_DEPTH: 10;
|
|
10
|
+
readonly REGEX_TIMEOUT_MS: 100;
|
|
11
|
+
readonly REGEX_CACHE_SIZE: 500;
|
|
12
|
+
readonly RULE_REFRESH_INTERVAL_MS: 60000;
|
|
13
|
+
readonly FETCH_TIMEOUT_MS: 10000;
|
|
14
|
+
readonly MIN_UPDATE_INTERVAL_MS: 1000;
|
|
15
|
+
readonly MAX_UPDATES_PER_MINUTE: 10;
|
|
16
|
+
readonly MAX_CACHE_FILE_SIZE_BYTES: number;
|
|
17
|
+
readonly MAX_SESSION_HISTORY: 50;
|
|
18
|
+
readonly UPDATE_CHECK_INTERVAL_MS: number;
|
|
19
|
+
readonly UPDATE_CHECK_CACHE_TTL_MS: number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Feature Flags
|
|
23
|
+
*/
|
|
24
|
+
export declare const FEATURE_FLAGS: {
|
|
25
|
+
readonly ENABLE_UPDATE_CHECK: true;
|
|
26
|
+
readonly ENABLE_TELEMETRY: true;
|
|
27
|
+
readonly ENABLE_SEMANTIC_DETECTION: false;
|
|
28
|
+
readonly ENABLE_AUTO_FIX: false;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Environment-specific Configuration
|
|
32
|
+
*/
|
|
33
|
+
export declare const ENV_CONFIG: {
|
|
34
|
+
readonly DEVELOPMENT: {
|
|
35
|
+
readonly LOG_LEVEL: "debug";
|
|
36
|
+
readonly STRICT_VALIDATION: false;
|
|
37
|
+
};
|
|
38
|
+
readonly PRODUCTION: {
|
|
39
|
+
readonly LOG_LEVEL: "info";
|
|
40
|
+
readonly STRICT_VALIDATION: true;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security Limits Configuration
|
|
4
|
+
* Centralized configuration for all security-related limits and thresholds
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ENV_CONFIG = exports.FEATURE_FLAGS = exports.SECURITY_LIMITS = void 0;
|
|
8
|
+
exports.SECURITY_LIMITS = {
|
|
9
|
+
// Rule Management
|
|
10
|
+
MAX_RULES: 1000,
|
|
11
|
+
MAX_PATTERNS_PER_RULE: 100,
|
|
12
|
+
// Content Scanning
|
|
13
|
+
MAX_SCAN_LENGTH: 50000, // 50KB max content to scan (ReDoS prevention)
|
|
14
|
+
MAX_PARAMETER_DEPTH: 10, // Max depth for nested parameter extraction
|
|
15
|
+
// Pattern Matching
|
|
16
|
+
REGEX_TIMEOUT_MS: 100, // Max time for a single regex match
|
|
17
|
+
REGEX_CACHE_SIZE: 500, // Max cached regex patterns
|
|
18
|
+
// Telemetry & Performance
|
|
19
|
+
RULE_REFRESH_INTERVAL_MS: 60000, // 1 minute
|
|
20
|
+
FETCH_TIMEOUT_MS: 10000, // 10 seconds for HTTP requests
|
|
21
|
+
// Rate Limiting
|
|
22
|
+
MIN_UPDATE_INTERVAL_MS: 1000, // Minimum 1 second between rule updates
|
|
23
|
+
MAX_UPDATES_PER_MINUTE: 10,
|
|
24
|
+
// Cache & Storage
|
|
25
|
+
MAX_CACHE_FILE_SIZE_BYTES: 10 * 1024 * 1024, // 10 MB
|
|
26
|
+
MAX_SESSION_HISTORY: 50,
|
|
27
|
+
// Update Management
|
|
28
|
+
UPDATE_CHECK_INTERVAL_MS: 24 * 60 * 60 * 1000, // 24 hours
|
|
29
|
+
UPDATE_CHECK_CACHE_TTL_MS: 6 * 60 * 60 * 1000, // 6 hours
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Feature Flags
|
|
33
|
+
*/
|
|
34
|
+
exports.FEATURE_FLAGS = {
|
|
35
|
+
ENABLE_UPDATE_CHECK: true,
|
|
36
|
+
ENABLE_TELEMETRY: true,
|
|
37
|
+
ENABLE_SEMANTIC_DETECTION: false, // Expensive, opt-in
|
|
38
|
+
ENABLE_AUTO_FIX: false, // Future feature
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Environment-specific Configuration
|
|
42
|
+
*/
|
|
43
|
+
exports.ENV_CONFIG = {
|
|
44
|
+
DEVELOPMENT: {
|
|
45
|
+
LOG_LEVEL: 'debug',
|
|
46
|
+
STRICT_VALIDATION: false,
|
|
47
|
+
},
|
|
48
|
+
PRODUCTION: {
|
|
49
|
+
LOG_LEVEL: 'info',
|
|
50
|
+
STRICT_VALIDATION: true,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ToolData, Violation, Rule, Detector, StateProvider, DetectorType } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base detector class
|
|
4
|
+
*
|
|
5
|
+
* Provides common functionality for all detectors.
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class BaseDetector implements Detector {
|
|
8
|
+
abstract detect(toolData: ToolData, rule: Rule, sessionState: StateProvider): Violation | null;
|
|
9
|
+
abstract getType(): DetectorType | string;
|
|
10
|
+
/**
|
|
11
|
+
* Generate unique violation ID
|
|
12
|
+
*/
|
|
13
|
+
protected generateViolationId(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Create violation object
|
|
16
|
+
*/
|
|
17
|
+
protected createViolation(rule: Rule, toolData: ToolData, additionalInfo?: Record<string, unknown>): Violation;
|
|
18
|
+
/**
|
|
19
|
+
* Extract value from object using JSON path
|
|
20
|
+
*/
|
|
21
|
+
protected extractValue(obj: Record<string, unknown>, path: string): unknown;
|
|
22
|
+
}
|