@opentrust/guards 7.3.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/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@opentrust/guards",
3
+ "version": "7.3.3",
4
+ "description": "AI agent security plugin for OpenClaw: prompt injection detection, PII sanitization, and monitoring",
5
+ "type": "module",
6
+ "main": "index.ts",
7
+ "openclaw": {
8
+ "extensions": [
9
+ "./index.ts"
10
+ ]
11
+ },
12
+ "scripts": {
13
+ "postinstall": "node scripts/postinstall.mjs",
14
+ "build": "tsc",
15
+ "typecheck": "tsc --noEmit",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest"
18
+ },
19
+ "keywords": [
20
+ "openclaw",
21
+ "opentrust",
22
+ "prompt-injection",
23
+ "security",
24
+ "ai-safety",
25
+ "guard",
26
+ "data-sanitization"
27
+ ],
28
+ "author": "OpenTrust",
29
+ "license": "Apache-2.0",
30
+ "homepage": "https://github.com/opentrust/opentrust#readme",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/opentrust/opentrust.git",
34
+ "directory": "guards"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "files": [
40
+ "index.ts",
41
+ "plugin/",
42
+ "agent/",
43
+ "memory/",
44
+ "platform-client/",
45
+ "dist/",
46
+ "scripts/",
47
+ "openclaw.plugin.json",
48
+ "tsconfig.json"
49
+ ],
50
+ "devDependencies": {
51
+ "@types/node": "^22.19.13",
52
+ "typescript": "^5.9.3",
53
+ "vitest": "^2.1.9"
54
+ }
55
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * DashboardClient — 与 OpenTrust Dashboard 通信的 SDK
3
+ *
4
+ * 功能:
5
+ * - Agent 注册和更新
6
+ * - 心跳保活
7
+ * - 内容检测请求
8
+ * - 工具调用观测上报
9
+ * - 权限查询
10
+ *
11
+ * 同时支持本地嵌入式和远程独立部署的 Dashboard
12
+ */
13
+
14
+ import type {
15
+ DashboardClientConfig,
16
+ DashboardDetectRequest,
17
+ DashboardDetectResponse,
18
+ AgentRegisterRequest,
19
+ ToolCallObservationRequest,
20
+ AgentPermission,
21
+ } from "./types.js";
22
+
23
+ /**
24
+ * Dashboard 客户端类
25
+ *
26
+ * 使用方式:
27
+ * ```ts
28
+ * const client = new DashboardClient({
29
+ * dashboardUrl: "http://localhost:53667",
30
+ * sessionToken: "sk-og-xxx",
31
+ * });
32
+ * await client.registerAgent({ name: "My Agent" });
33
+ * client.startHeartbeat(60_000);
34
+ * ```
35
+ */
36
+ export class DashboardClient {
37
+ private config: Required<DashboardClientConfig>;
38
+
39
+ /**
40
+ * 创建 Dashboard 客户端
41
+ * @param config - 客户端配置
42
+ */
43
+ constructor(config: DashboardClientConfig) {
44
+ this.config = {
45
+ dashboardUrl: config.dashboardUrl.replace(/\/$/, ""), // 移除尾部斜杠
46
+ sessionToken: config.sessionToken,
47
+ agentId: config.agentId ?? "",
48
+ timeoutMs: config.timeoutMs ?? 30000,
49
+ };
50
+ }
51
+
52
+ /**
53
+ * 获取当前 Agent ID
54
+ */
55
+ get agentId(): string {
56
+ return this.config.agentId;
57
+ }
58
+
59
+ /**
60
+ * 发送 HTTP 请求
61
+ * 自动添加认证头和超时控制
62
+ *
63
+ * @param path - API 路径
64
+ * @param options - 请求选项
65
+ * @returns 响应数据
66
+ */
67
+ private async request<T>(path: string, options: RequestInit = {}): Promise<T> {
68
+ // 超时控制
69
+ const controller = new AbortController();
70
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
71
+
72
+ try {
73
+ const headers: Record<string, string> = {
74
+ "Content-Type": "application/json",
75
+ Authorization: `Bearer ${this.config.sessionToken}`,
76
+ ...((options.headers as Record<string, string>) || {}),
77
+ };
78
+
79
+ const res = await fetch(`${this.config.dashboardUrl}${path}`, {
80
+ ...options,
81
+ headers,
82
+ signal: controller.signal,
83
+ });
84
+
85
+ if (!res.ok) {
86
+ const text = await res.text();
87
+ throw new Error(`Dashboard API ${res.status}: ${text}`);
88
+ }
89
+
90
+ return (await res.json()) as T;
91
+ } finally {
92
+ clearTimeout(timeoutId);
93
+ }
94
+ }
95
+
96
+ // ── 检测 API ───────────────────────────────────────
97
+
98
+ /**
99
+ * 发送内容检测请求
100
+ * 检测消息内容是否包含安全风险
101
+ *
102
+ * @param req - 检测请求
103
+ * @returns 检测结果
104
+ */
105
+ async detect(req: DashboardDetectRequest): Promise<DashboardDetectResponse> {
106
+ return this.request<DashboardDetectResponse>("/api/detect", {
107
+ method: "POST",
108
+ body: JSON.stringify({
109
+ ...req,
110
+ agentId: req.agentId || this.config.agentId || undefined,
111
+ }),
112
+ });
113
+ }
114
+
115
+ // ── Agent 管理 API ────────────────────────────────
116
+
117
+ /**
118
+ * 注册 Agent
119
+ * 如果同名 Agent 已存在,则更新;否则创建新的
120
+ *
121
+ * @param req - 注册请求
122
+ * @returns 注册结果
123
+ */
124
+ async registerAgent(req: AgentRegisterRequest): Promise<{ success: boolean; data?: { id: string } }> {
125
+ // 检查是否已存在同名 Agent(实现 upsert 语义)
126
+ try {
127
+ const list = await this.request<{ success: boolean; data: { id: string; name: string }[] }>("/api/agents");
128
+ if (list.success && list.data) {
129
+ const existing = list.data.find((a) => a.name === req.name);
130
+ if (existing) {
131
+ // 更新已有 Agent
132
+ this.config.agentId = existing.id;
133
+ await this.request(`/api/agents/${existing.id}`, {
134
+ method: "PUT",
135
+ body: JSON.stringify({
136
+ status: "active",
137
+ ...(req.provider && { provider: req.provider }),
138
+ ...(req.metadata && { metadata: req.metadata }),
139
+ }),
140
+ }).catch(() => {}); // 更新失败不影响返回
141
+ return { success: true, data: { id: existing.id } };
142
+ }
143
+ }
144
+ } catch { /* 查询失败时继续创建 */ }
145
+
146
+ // 创建新 Agent
147
+ const result = await this.request<{ success: boolean; data?: { id: string } }>("/api/agents", {
148
+ method: "POST",
149
+ body: JSON.stringify(req),
150
+ });
151
+ if (result.success && result.data?.id) this.config.agentId = result.data.id;
152
+ return result;
153
+ }
154
+
155
+ /**
156
+ * 发送心跳
157
+ * 告知 Dashboard 该 Agent 仍然活跃
158
+ */
159
+ async heartbeat(): Promise<void> {
160
+ if (!this.config.agentId) return;
161
+ await this.request(`/api/agents/${this.config.agentId}/heartbeat`, { method: "POST" });
162
+ }
163
+
164
+ /**
165
+ * 更新 Agent Profile
166
+ * 同步 Agent 的元数据到 Dashboard
167
+ *
168
+ * @param profile - Profile 数据
169
+ */
170
+ async updateProfile(profile: Record<string, unknown>): Promise<void> {
171
+ if (!this.config.agentId) return;
172
+ await this.request(`/api/agents/${this.config.agentId}`, {
173
+ method: "PUT",
174
+ body: JSON.stringify({ metadata: profile }),
175
+ });
176
+ }
177
+
178
+ /**
179
+ * 启动心跳定时器
180
+ * 定期发送心跳保持 Agent 活跃状态
181
+ *
182
+ * @param intervalMs - 心跳间隔(毫秒),默认 60 秒
183
+ * @returns 定时器句柄
184
+ */
185
+ startHeartbeat(intervalMs = 60_000): NodeJS.Timeout {
186
+ return setInterval(() => { this.heartbeat().catch(() => {}); }, intervalMs);
187
+ }
188
+
189
+ // ── 工具调用观测 API ───────────────────────────────
190
+
191
+ /**
192
+ * 上报工具调用
193
+ * 记录 Agent 的工具使用情况,用于安全分析
194
+ *
195
+ * @param data - 工具调用数据
196
+ */
197
+ async reportToolCall(data: ToolCallObservationRequest): Promise<void> {
198
+ await this.request("/api/observations", { method: "POST", body: JSON.stringify(data) });
199
+ }
200
+
201
+ /**
202
+ * 获取 Agent 权限列表
203
+ * 查询 Agent 访问过的资源和权限模式
204
+ *
205
+ * @param agentId - Agent ID(可选,默认使用当前 Agent)
206
+ * @returns 权限列表
207
+ */
208
+ async getPermissions(agentId?: string): Promise<AgentPermission[]> {
209
+ const id = agentId || this.config.agentId;
210
+ if (!id) return [];
211
+ const result = await this.request<{ success: boolean; data: AgentPermission[] }>(
212
+ `/api/observations/agents/${id}/permissions`,
213
+ );
214
+ return result.data ?? [];
215
+ }
216
+
217
+ // ── 健康检查 API ───────────────────────────────────
218
+
219
+ /**
220
+ * 检查 Dashboard 健康状态
221
+ * @returns 是否健康
222
+ */
223
+ async checkHealth(): Promise<boolean> {
224
+ try {
225
+ const res = await fetch(`${this.config.dashboardUrl}/health`);
226
+ const json = (await res.json()) as { status: string };
227
+ return json.status === "ok";
228
+ } catch {
229
+ return false;
230
+ }
231
+ }
232
+ }
233
+
234
+ // 导出类型
235
+ export {
236
+ type DashboardClientConfig,
237
+ type DashboardDetectRequest,
238
+ type DashboardDetectResponse,
239
+ type ToolCallObservationRequest,
240
+ type AgentPermission,
241
+ } from "./types.js";
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Dashboard API 通信类型定义
3
+ */
4
+
5
+ /**
6
+ * 检测请求
7
+ * 发送消息内容进行安全检测
8
+ */
9
+ export type DashboardDetectRequest = {
10
+ /** 消息列表 */
11
+ messages: unknown[];
12
+ /** 消息格式 */
13
+ format?: "openai" | "anthropic" | "gemini" | "raw";
14
+ /** 角色类型 */
15
+ role?: "system" | "user" | "assistant" | "tool";
16
+ /** Agent ID */
17
+ agentId?: string;
18
+ };
19
+
20
+ /**
21
+ * 检测响应
22
+ * 返回消息内容的安全评估结果
23
+ */
24
+ export type DashboardDetectResponse = {
25
+ /** 请求是否成功 */
26
+ success: boolean;
27
+ /** 检测数据 */
28
+ data?: {
29
+ /** 是否安全 */
30
+ safe: boolean;
31
+ /** 判定结果 */
32
+ verdict: "safe" | "unsafe";
33
+ /** 风险类别 */
34
+ categories: string[];
35
+ /** 敏感度评分 */
36
+ sensitivity_score: number;
37
+ /** 检测发现列表 */
38
+ findings: Array<{ scanner: string; name: string; description: string }>;
39
+ /** 检测耗时(毫秒) */
40
+ latency_ms: number;
41
+ /** 请求 ID */
42
+ request_id: string;
43
+ /** 策略动作 */
44
+ policy_action?: "block" | "alert" | "log";
45
+ };
46
+ /** 是否被阻断 */
47
+ blocked?: boolean;
48
+ /** 错误信息 */
49
+ error?: string;
50
+ };
51
+
52
+ /**
53
+ * Agent 注册请求
54
+ */
55
+ export type AgentRegisterRequest = {
56
+ /** Agent 名称 */
57
+ name: string;
58
+ /** Agent 描述 */
59
+ description?: string;
60
+ /** AI 模型提供商 */
61
+ provider?: string;
62
+ /** 附加元数据 */
63
+ metadata?: Record<string, unknown>;
64
+ };
65
+
66
+ /**
67
+ * Dashboard 客户端配置
68
+ */
69
+ export type DashboardClientConfig = {
70
+ /** Dashboard API 地址 */
71
+ dashboardUrl: string;
72
+ /** 认证 Token(通常是 Core 平台的 API Key) */
73
+ sessionToken: string;
74
+ /** Agent ID(可选,注册后自动设置) */
75
+ agentId?: string;
76
+ /** 请求超时时间(毫秒) */
77
+ timeoutMs?: number;
78
+ };
79
+
80
+ /**
81
+ * 工具调用观测请求
82
+ * 用于上报 Agent 的工具使用情况
83
+ */
84
+ export type ToolCallObservationRequest = {
85
+ /** Agent ID */
86
+ agentId: string;
87
+ /** 会话 Key */
88
+ sessionKey?: string;
89
+ /** 工具名称 */
90
+ toolName: string;
91
+ /** 调用参数 */
92
+ params?: Record<string, unknown>;
93
+ /** 调用阶段 */
94
+ phase: "before" | "after";
95
+ /** 执行结果 */
96
+ result?: unknown;
97
+ /** 错误信息 */
98
+ error?: string;
99
+ /** 执行耗时(毫秒) */
100
+ durationMs?: number;
101
+ /** 是否被阻断 */
102
+ blocked?: boolean;
103
+ /** 阻断原因 */
104
+ blockReason?: string;
105
+ };
106
+
107
+ /**
108
+ * Agent 权限记录
109
+ * 表示 Agent 对某个资源的访问权限
110
+ */
111
+ export type AgentPermission = {
112
+ /** 记录 ID */
113
+ id: string;
114
+ /** Agent ID */
115
+ agentId: string;
116
+ /** 工具名称 */
117
+ toolName: string;
118
+ /** 权限类别 */
119
+ category: string | null;
120
+ /** 访问模式 */
121
+ accessPattern: string | null;
122
+ /** 访问目标列表 */
123
+ targetsJson: string[];
124
+ /** 调用次数 */
125
+ callCount: number;
126
+ /** 错误次数 */
127
+ errorCount: number;
128
+ /** 首次访问时间 */
129
+ firstSeen: string;
130
+ /** 最近访问时间 */
131
+ lastSeen: string;
132
+ };
@@ -0,0 +1,151 @@
1
+ /**
2
+ * og_status / og_activate 命令处理器
3
+ *
4
+ * 提供两个用户命令:
5
+ * - og_status: 显示 OpenTrust 连接状态、凭证信息
6
+ * - og_activate: 注册新 Agent 或显示激活状态
7
+ */
8
+
9
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
10
+ import type { Logger } from "../agent/types.js";
11
+ import type { PluginState } from "./state.js";
12
+ import type { CoreCredentials } from "../agent/config.js";
13
+ import {
14
+ registerWithCore,
15
+ } from "../agent/config.js";
16
+ import type { DashboardClient } from "../platform-client/index.js";
17
+
18
+ /**
19
+ * 注册用户命令
20
+ *
21
+ * @param api - OpenClaw 插件 API
22
+ * @param log - 日志器
23
+ * @param state - 插件状态
24
+ * @param config - 插件配置
25
+ * @param initDashboardClient - Dashboard 客户端初始化函数
26
+ */
27
+ export function registerCommands(
28
+ api: OpenClawPluginApi,
29
+ log: Logger,
30
+ state: PluginState,
31
+ config: Required<import("../agent/types.js").OpenClawGuardConfig>,
32
+ initDashboardClient: (creds: CoreCredentials) => void,
33
+ ): void {
34
+ // ── og_status 命令:显示状态 ───────────────────────
35
+ api.registerCommand({
36
+ name: "og_status",
37
+ description: "Show OpenTrust status",
38
+ requireAuth: true,
39
+ handler: async () => {
40
+ const creds = state.coreCredentials;
41
+ const lines = ["**OpenTrust Status**", ""];
42
+
43
+ if (creds) {
44
+ // 显示 Agent 基本信息
45
+ lines.push(`- Agent ID: ${creds.agentId}`);
46
+ lines.push(`- API Key: ${creds.apiKey.slice(0, 12)}...`); // 只显示前 12 位
47
+
48
+ if (creds.email) {
49
+ // 已激活状态
50
+ lines.push(`- Email: ${creds.email}`);
51
+ lines.push(`- Status: active`);
52
+ } else if (creds.claimUrl) {
53
+ // 待激活状态
54
+ lines.push(`- Status: pending activation`);
55
+ lines.push(`- Activate: ${creds.claimUrl}`);
56
+ } else {
57
+ // 已激活但无邮箱(本地模式)
58
+ lines.push(`- Status: active`);
59
+ }
60
+
61
+ lines.push(`- Login: ${config.coreUrl}/login`);
62
+ } else {
63
+ // 正在注册
64
+ lines.push("- Status: registering...");
65
+ }
66
+
67
+ // 显示配置信息
68
+ lines.push("", `- blockOnRisk: ${config.blockOnRisk}`);
69
+
70
+ return { text: lines.join("\n") };
71
+ },
72
+ });
73
+
74
+ // ── og_activate 命令:注册或显示激活状态 ────────────
75
+ api.registerCommand({
76
+ name: "og_activate",
77
+ description: "Register or show activation status",
78
+ requireAuth: true,
79
+ handler: async () => {
80
+ // 情况 1: 已完全激活
81
+ if (state.coreCredentials?.email) {
82
+ return {
83
+ text: [
84
+ "**OpenTrust: Active**", "",
85
+ `Agent ID: ${state.coreCredentials.agentId}`,
86
+ `Email: ${state.coreCredentials.email}`, "",
87
+ "Behavioral detection is active.", "",
88
+ `Login: ${config.coreUrl}/login`,
89
+ ].join("\n"),
90
+ };
91
+ }
92
+
93
+ // 情况 2: 已注册但待激活
94
+ if (state.coreCredentials?.claimUrl) {
95
+ return {
96
+ text: [
97
+ "**OpenTrust: Pending Activation**", "",
98
+ `Agent ID: ${state.coreCredentials.agentId}`, "",
99
+ "Enter your email to activate:",
100
+ ` ${state.coreCredentials.claimUrl}`, "",
101
+ "After activation you get **30,000 free** detections.", "",
102
+ `Login: ${config.coreUrl}/login`,
103
+ ].join("\n"),
104
+ };
105
+ }
106
+
107
+ // 情况 3: 未注册,执行注册流程
108
+ try {
109
+ log.info(`Registering with ${config.coreUrl}...`);
110
+ const result = await registerWithCore(
111
+ config.agentName,
112
+ "OpenClaw AI Agent secured by OpenTrust",
113
+ config.coreUrl,
114
+ );
115
+
116
+ // 保存注册结果
117
+ state.lastRegisterResult = result;
118
+ state.coreCredentials = result.credentials;
119
+ state.behaviorDetector!.setCredentials(result.credentials);
120
+
121
+ // 初始化 Dashboard 客户端
122
+ initDashboardClient(result.credentials);
123
+
124
+ log.info("Registration successful!");
125
+
126
+ return {
127
+ text: [
128
+ "**OpenTrust: Activate Your Agent**", "",
129
+ `Agent ID: ${result.credentials.agentId}`, "",
130
+ "Enter your email to activate:",
131
+ ` ${result.activateUrl}`, "",
132
+ "After activation you get **30,000 free** detections.", "",
133
+ `Login: ${result.loginUrl}`,
134
+ ].join("\n"),
135
+ };
136
+ } catch (err) {
137
+ // 注册失败
138
+ return {
139
+ text: [
140
+ "**OpenTrust: Registration Failed**", "",
141
+ `Could not reach ${config.coreUrl}.`,
142
+ `Error: ${err}`, "",
143
+ "Possible fixes:",
144
+ "- Set `coreUrl` in plugin config to point to your instance",
145
+ "- Or set `apiKey` directly in plugin config to skip registration",
146
+ ].join("\n"),
147
+ };
148
+ }
149
+ },
150
+ });
151
+ }