shellward 0.5.9 → 0.5.11
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/README.md +133 -14
- package/dist/audit-log.d.ts +8 -0
- package/dist/audit-log.js +72 -0
- package/dist/auto-check.d.ts +26 -0
- package/dist/auto-check.js +167 -0
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.js +75 -0
- package/dist/commands/check-updates.d.ts +2 -0
- package/dist/commands/check-updates.js +166 -0
- package/dist/commands/harden.d.ts +2 -0
- package/dist/commands/harden.js +218 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +56 -0
- package/dist/commands/scan-plugins.d.ts +2 -0
- package/dist/commands/scan-plugins.js +186 -0
- package/dist/commands/security.d.ts +2 -0
- package/dist/commands/security.js +109 -0
- package/dist/commands/upgrade-openclaw.d.ts +2 -0
- package/dist/commands/upgrade-openclaw.js +54 -0
- package/dist/core/engine.d.ts +66 -0
- package/dist/core/engine.js +572 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +137 -0
- package/dist/layers/data-flow-guard.d.ts +2 -0
- package/dist/layers/data-flow-guard.js +23 -0
- package/dist/layers/input-auditor.d.ts +2 -0
- package/dist/layers/input-auditor.js +33 -0
- package/dist/layers/outbound-guard.d.ts +2 -0
- package/dist/layers/outbound-guard.js +22 -0
- package/dist/layers/output-scanner.d.ts +2 -0
- package/dist/layers/output-scanner.js +16 -0
- package/dist/layers/prompt-guard.d.ts +2 -0
- package/dist/layers/prompt-guard.js +14 -0
- package/dist/layers/security-gate.d.ts +2 -0
- package/dist/layers/security-gate.js +49 -0
- package/dist/layers/session-guard.d.ts +2 -0
- package/dist/layers/session-guard.js +34 -0
- package/dist/layers/tool-blocker.d.ts +2 -0
- package/dist/layers/tool-blocker.js +28 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +337 -0
- package/dist/rules/dangerous-commands.d.ts +8 -0
- package/dist/rules/dangerous-commands.js +113 -0
- package/dist/rules/injection-en.d.ts +2 -0
- package/dist/rules/injection-en.js +115 -0
- package/dist/rules/injection-zh.d.ts +2 -0
- package/dist/rules/injection-zh.js +132 -0
- package/dist/rules/protected-paths.d.ts +2 -0
- package/dist/rules/protected-paths.js +75 -0
- package/dist/rules/sensitive-patterns.d.ts +21 -0
- package/dist/rules/sensitive-patterns.js +192 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.js +30 -0
- package/dist/update-check.d.ts +40 -0
- package/dist/update-check.js +147 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +8 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +31 -8
- package/src/audit-log.ts +8 -4
- package/src/auto-check.ts +2 -2
- package/src/commands/audit.ts +3 -3
- package/src/commands/check-updates.ts +4 -4
- package/src/commands/harden.ts +3 -3
- package/src/commands/index.ts +8 -8
- package/src/commands/scan-plugins.ts +3 -3
- package/src/commands/security.ts +3 -3
- package/src/commands/upgrade-openclaw.ts +2 -2
- package/src/core/engine.ts +8 -8
- package/src/index.ts +17 -17
- package/src/layers/data-flow-guard.ts +1 -1
- package/src/layers/input-auditor.ts +1 -1
- package/src/layers/outbound-guard.ts +1 -1
- package/src/layers/output-scanner.ts +1 -1
- package/src/layers/prompt-guard.ts +1 -1
- package/src/layers/security-gate.ts +1 -1
- package/src/layers/session-guard.ts +1 -1
- package/src/layers/tool-blocker.ts +1 -1
- package/src/mcp-server.ts +386 -0
- package/src/rules/dangerous-commands.ts +1 -1
- package/src/rules/injection-en.ts +1 -1
- package/src/rules/injection-zh.ts +1 -1
- package/src/rules/protected-paths.ts +1 -1
- package/src/rules/sensitive-patterns.ts +1 -1
- package/src/update-check.ts +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.svg" alt="ShellWard Logo" width="160" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# ShellWard
|
|
2
6
|
|
|
3
|
-
**AI Agent Security Middleware** — Protect AI agents from prompt injection, data exfiltration, and dangerous command execution.
|
|
7
|
+
**AI Agent Security Middleware** — Protect AI agents from prompt injection, data exfiltration, and dangerous command execution. ShellWard acts as an LLM security middleware and AI agent firewall, intercepting tool calls at runtime to enforce agent guardrails before damage is done.
|
|
4
8
|
|
|
5
9
|
8-layer defense-in-depth, DLP-style data flow control, zero dependencies. Works as **standalone SDK** or **OpenClaw plugin**.
|
|
6
10
|
|
|
7
11
|
[](https://www.npmjs.com/package/shellward)
|
|
8
12
|
[](./LICENSE)
|
|
9
|
-
[](#performance)
|
|
10
14
|
[](#performance)
|
|
11
15
|
|
|
12
16
|
[English](#demo) | [中文](#中文)
|
|
13
17
|
|
|
14
18
|
## Demo
|
|
15
19
|
|
|
16
|
-

|
|
17
21
|
|
|
18
22
|
> 7 real-world scenarios: server wipe → reverse shell → prompt injection → DLP audit → data exfiltration chain → credential theft → APT attack chain
|
|
19
23
|
|
|
@@ -50,13 +54,15 @@ Your AI agent has full access to tools — shell, email, HTTP, file system. One
|
|
|
50
54
|
|
|
51
55
|
| Platform | Integration | Note |
|
|
52
56
|
|----------|------------|------|
|
|
53
|
-
| **
|
|
54
|
-
| **
|
|
55
|
-
| **
|
|
57
|
+
| **Claude Desktop** | MCP Server | Add to `claude_desktop_config.json` — 7 security tools |
|
|
58
|
+
| **Cursor** | MCP Server | Add to `.cursor/mcp.json` |
|
|
59
|
+
| **OpenClaw** | MCP + Plugin + SDK | `openclaw plugins install shellward` — adapts to available hooks |
|
|
60
|
+
| **Claude Code** | MCP + SDK | Anthropic's official CLI agent |
|
|
56
61
|
| **LangChain** | SDK | LLM application framework |
|
|
57
62
|
| **AutoGPT** | SDK | Autonomous AI agents |
|
|
58
63
|
| **OpenAI Agents** | SDK | GPT agent platform |
|
|
59
64
|
| **Dify / Coze** | SDK | Low-code AI platforms |
|
|
65
|
+
| **Any MCP Client** | MCP Server | stdio JSON-RPC, zero dependencies |
|
|
60
66
|
| **Any AI Agent** | SDK | `npm install shellward` — 3 lines to integrate |
|
|
61
67
|
|
|
62
68
|
## Features
|
|
@@ -71,7 +77,59 @@ Your AI agent has full access to tools — shell, email, HTTP, file system. One
|
|
|
71
77
|
|
|
72
78
|
## Quick Start
|
|
73
79
|
|
|
74
|
-
|
|
80
|
+
### As MCP Server
|
|
81
|
+
|
|
82
|
+
ShellWard runs as a standalone MCP server over stdio — zero dependencies, no `@modelcontextprotocol/sdk` needed.
|
|
83
|
+
|
|
84
|
+
**Claude Desktop / Cursor / any MCP client:**
|
|
85
|
+
|
|
86
|
+
Add to your MCP config (`claude_desktop_config.json`, `.cursor/mcp.json`, etc.):
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"shellward": {
|
|
92
|
+
"command": "npx",
|
|
93
|
+
"args": ["tsx", "/path/to/shellward/src/mcp-server.ts"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**OpenClaw:**
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"shellward": {
|
|
105
|
+
"command": "npx",
|
|
106
|
+
"args": ["tsx", "/path/to/shellward/src/mcp-server.ts"]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**7 MCP tools available:**
|
|
113
|
+
|
|
114
|
+
| Tool | Description |
|
|
115
|
+
|------|-------------|
|
|
116
|
+
| `check_command` | Check if a shell command is safe (rm -rf, reverse shell, fork bomb...) |
|
|
117
|
+
| `check_injection` | Detect prompt injection in text (32+ rules, zh+en) |
|
|
118
|
+
| `scan_data` | Scan for PII & sensitive data (CN ID/phone/bank, API keys, SSN...) |
|
|
119
|
+
| `check_path` | Check if file path operation is safe (.env, .ssh, credentials...) |
|
|
120
|
+
| `check_tool` | Check if tool name is allowed (blocks payment/transfer tools) |
|
|
121
|
+
| `check_response` | Audit AI response for canary leaks & PII exposure |
|
|
122
|
+
| `security_status` | Get current security config & active layers |
|
|
123
|
+
|
|
124
|
+
**Environment variables:**
|
|
125
|
+
|
|
126
|
+
| Variable | Values | Default |
|
|
127
|
+
|----------|--------|---------|
|
|
128
|
+
| `SHELLWARD_MODE` | `enforce` / `audit` | `enforce` |
|
|
129
|
+
| `SHELLWARD_LOCALE` | `auto` / `zh` / `en` | `auto` |
|
|
130
|
+
| `SHELLWARD_THRESHOLD` | `0`-`100` | `60` |
|
|
131
|
+
|
|
132
|
+
### As SDK (any AI agent platform):
|
|
75
133
|
|
|
76
134
|
```bash
|
|
77
135
|
npm install shellward
|
|
@@ -211,7 +269,7 @@ password: "MyP@ssw0rd!" → Detected (Password)
|
|
|
211
269
|
| Command check throughput | 125,000/sec |
|
|
212
270
|
| Injection detection throughput | ~7,700/sec |
|
|
213
271
|
| Dependencies | 0 |
|
|
214
|
-
| Tests |
|
|
272
|
+
| Tests | 123 passing (incl. 11 MCP) |
|
|
215
273
|
|
|
216
274
|
## Vulnerability Database
|
|
217
275
|
|
|
@@ -224,6 +282,27 @@ password: "MyP@ssw0rd!" → Detected (Password)
|
|
|
224
282
|
|
|
225
283
|
Remote vuln DB syncs every 24h, falls back to local DB when offline.
|
|
226
284
|
|
|
285
|
+
## Use Cases
|
|
286
|
+
|
|
287
|
+
ShellWard is built for teams that need runtime security for AI agents — whether you are building autonomous coding assistants, customer-facing chatbots with tool access, or internal automation powered by LLMs. Common use cases include MCP security enforcement, tool call interception and filtering, and adding agent guardrails to any LLM-powered workflow.
|
|
288
|
+
|
|
289
|
+
## Why ShellWard?
|
|
290
|
+
|
|
291
|
+
| Capability | ShellWard | [agentguard](https://github.com/GoPlusSecurity/agentguard) | [pipelock](https://github.com/luckyPipewrench/pipelock) | [Sage](https://github.com/avast/sage) | [AgentSeal](https://github.com/AgentSeal/agentseal) |
|
|
292
|
+
|---|---|---|---|---|---|
|
|
293
|
+
| **DLP data flow** (read→send=block) | ✅ | ❌ | Proxy-based | ❌ | ❌ |
|
|
294
|
+
| **Chinese PII** (ID card, bank card) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
295
|
+
| **Chinese injection rules** | 18 rules | ❌ | ❌ | ❌ | ❌ |
|
|
296
|
+
| **Defense layers** | 8 | 3 | 11 (proxy) | ~2 | ~2 |
|
|
297
|
+
| **Zero dependencies** | ✅ (npm) | ✅ | Go binary | Cloud API | Python |
|
|
298
|
+
| **Runtime blocking** | ✅ | ✅ | ✅ (proxy) | ✅ | ❌ (scanner) |
|
|
299
|
+
| **Architecture** | In-process middleware | Hook-based guard | HTTP proxy | Hook + cloud | Scan + monitor |
|
|
300
|
+
| **Detection rules** | 32 | 24 | 36 DLP patterns | 200+ YAML | 191+ |
|
|
301
|
+
|
|
302
|
+
> ShellWard is the only tool with **DLP-style data flow tracking** + **Chinese language security** + **zero dependencies** in a single package.
|
|
303
|
+
>
|
|
304
|
+
> Recent research ([arXiv:2603.08665](https://arxiv.org/abs/2603.08665)) demonstrates GenAI discovering 38 real-world vulnerabilities in 7 hours — AI-powered attacks are scaling fast. Defense must be built into the agent layer.
|
|
305
|
+
|
|
227
306
|
## Author
|
|
228
307
|
|
|
229
308
|
[jnMetaCode](https://github.com/jnMetaCode) · Apache-2.0
|
|
@@ -234,7 +313,7 @@ Remote vuln DB syncs every 24h, falls back to local DB when offline.
|
|
|
234
313
|
|
|
235
314
|
**AI Agent 安全中间件** — 保护 AI 代理免受提示词注入、数据泄露、危险命令执行。8 层纵深防御,零依赖。
|
|
236
315
|
|
|
237
|
-

|
|
238
317
|
|
|
239
318
|
> 7 个真实攻击场景:服务器毁灭拦截 → 反弹 Shell → 注入检测 → DLP 审计 → 数据外泄链 → 凭证窃取 → APT 攻击链
|
|
240
319
|
|
|
@@ -244,22 +323,45 @@ Remote vuln DB syncs every 24h, falls back to local DB when offline.
|
|
|
244
323
|
|
|
245
324
|
| 平台 | 集成方式 | 说明 |
|
|
246
325
|
|------|---------|------|
|
|
247
|
-
| **
|
|
248
|
-
| **
|
|
249
|
-
| **
|
|
326
|
+
| **Claude Desktop** | MCP 服务器 | 添加到 `claude_desktop_config.json`,7 个安全工具 |
|
|
327
|
+
| **Cursor** | MCP 服务器 | 添加到 `.cursor/mcp.json` |
|
|
328
|
+
| **OpenClaw** | MCP + 插件 + SDK | `openclaw plugins install shellward`,开箱即用 |
|
|
329
|
+
| **Claude Code** | MCP + SDK | Anthropic 官方 CLI Agent |
|
|
250
330
|
| **LangChain** | SDK | LLM 应用开发框架 |
|
|
251
331
|
| **AutoGPT** | SDK | 自主 AI Agent |
|
|
252
332
|
| **OpenAI Agents** | SDK | GPT Agent 平台 |
|
|
253
333
|
| **Dify / Coze** | SDK | 低代码 AI 平台 |
|
|
334
|
+
| **任意 MCP 客户端** | MCP 服务器 | stdio JSON-RPC,零依赖 |
|
|
254
335
|
| **任意 AI Agent** | SDK | `npm install shellward`,3 行代码接入 |
|
|
255
336
|
|
|
256
337
|
### 安装
|
|
257
338
|
|
|
339
|
+
**MCP 服务器模式(推荐):**
|
|
340
|
+
|
|
341
|
+
在 MCP 配置中添加(适用于 Claude Desktop、Cursor、OpenClaw 等):
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
{
|
|
345
|
+
"mcpServers": {
|
|
346
|
+
"shellward": {
|
|
347
|
+
"command": "npx",
|
|
348
|
+
"args": ["tsx", "/path/to/shellward/src/mcp-server.ts"]
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
零依赖,原生实现 MCP 协议。提供 7 个安全工具:命令检查、注入检测、敏感数据扫描、路径保护、工具策略、响应审计、安全状态。
|
|
355
|
+
|
|
356
|
+
**OpenClaw 插件模式:**
|
|
357
|
+
|
|
258
358
|
```bash
|
|
259
|
-
# OpenClaw 插件
|
|
260
359
|
openclaw plugins install shellward
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**SDK 模式:**
|
|
261
363
|
|
|
262
|
-
|
|
364
|
+
```bash
|
|
263
365
|
npm install shellward
|
|
264
366
|
```
|
|
265
367
|
|
|
@@ -281,6 +383,23 @@ guard.checkOutbound('send_email', {...}) // → { allowed: false } (读过敏
|
|
|
281
383
|
- **数据外泄链**:读敏感数据 → send_email / HTTP POST / curl 外发 = 拦截
|
|
282
384
|
- **零依赖**、零配置、Apache-2.0
|
|
283
385
|
|
|
386
|
+
### 为什么选 ShellWard?
|
|
387
|
+
|
|
388
|
+
| 能力 | ShellWard | [agentguard](https://github.com/GoPlusSecurity/agentguard) | [pipelock](https://github.com/luckyPipewrench/pipelock) | [Sage](https://github.com/avast/sage) | [AgentSeal](https://github.com/AgentSeal/agentseal) |
|
|
389
|
+
|---|---|---|---|---|---|
|
|
390
|
+
| **DLP 数据流** (读→发=拦截) | ✅ | ❌ | Proxy 架构 | ❌ | ❌ |
|
|
391
|
+
| **中文 PII 检测** (身份证、银行卡) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
392
|
+
| **中文注入规则** | 18 条 | ❌ | ❌ | ❌ | ❌ |
|
|
393
|
+
| **防御层数** | 8 层 | 3 层 | 11 层(proxy) | ~2 层 | ~2 层 |
|
|
394
|
+
| **零依赖** | ✅ (npm) | ✅ | Go 二进制 | 需云 API | 需 Python |
|
|
395
|
+
| **运行时拦截** | ✅ | ✅ | ✅ (proxy) | ✅ | ❌ (扫描器) |
|
|
396
|
+
| **架构** | 进程内中间件 | Hook 守护 | HTTP 代理 | Hook + 云端 | 扫描 + 监控 |
|
|
397
|
+
| **检测规则数** | 32 | 24 | 36 DLP 模式 | 200+ YAML | 191+ |
|
|
398
|
+
|
|
399
|
+
> ShellWard 是唯一同时具备 **DLP 数据流追踪** + **中文语言安全** + **零依赖** 的 AI Agent 安全工具。
|
|
400
|
+
>
|
|
401
|
+
> 最新研究 ([arXiv:2603.08665](https://arxiv.org/abs/2603.08665)) 显示 GenAI 在 7 小时内发现 38 个真实漏洞 — AI 驱动的攻击正在规模化,防御必须内建到 Agent 层。
|
|
402
|
+
|
|
284
403
|
### 作者
|
|
285
404
|
|
|
286
405
|
[jnMetaCode](https://github.com/jnMetaCode) · Apache-2.0
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AuditEntry, ShellWardConfig } from './types.js';
|
|
2
|
+
export declare class AuditLog {
|
|
3
|
+
private config;
|
|
4
|
+
private rotating;
|
|
5
|
+
constructor(config: ShellWardConfig);
|
|
6
|
+
write(entry: Pick<AuditEntry, 'level' | 'layer' | 'action' | 'detail'> & Record<string, unknown>): void;
|
|
7
|
+
private rotateIfNeeded;
|
|
8
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/audit-log.ts — JSONL audit log, zero dependencies
|
|
2
|
+
import { appendFileSync, mkdirSync, renameSync, statSync, writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { getHomeDir } from './utils.js';
|
|
5
|
+
const LOG_DIR = join(getHomeDir(), '.openclaw', 'shellward');
|
|
6
|
+
const LOG_FILE = join(LOG_DIR, 'audit.jsonl');
|
|
7
|
+
const MAX_SIZE_BYTES = 100 * 1024 * 1024; // 100 MB
|
|
8
|
+
const RISK_SCORES = {
|
|
9
|
+
CRITICAL: 10,
|
|
10
|
+
HIGH: 7,
|
|
11
|
+
MEDIUM: 4,
|
|
12
|
+
LOW: 2,
|
|
13
|
+
INFO: 0,
|
|
14
|
+
};
|
|
15
|
+
export class AuditLog {
|
|
16
|
+
config;
|
|
17
|
+
rotating = false;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
try {
|
|
21
|
+
mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
|
|
22
|
+
// Ensure log file exists with restricted permissions (owner-only)
|
|
23
|
+
try {
|
|
24
|
+
statSync(LOG_FILE);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
writeFileSync(LOG_FILE, '', { mode: 0o600 });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch { /* directory may already exist */ }
|
|
31
|
+
}
|
|
32
|
+
write(entry) {
|
|
33
|
+
try {
|
|
34
|
+
const record = {
|
|
35
|
+
...entry,
|
|
36
|
+
level: entry.level,
|
|
37
|
+
layer: entry.layer,
|
|
38
|
+
action: entry.action,
|
|
39
|
+
detail: entry.detail,
|
|
40
|
+
ts: new Date().toISOString(),
|
|
41
|
+
mode: this.config.mode,
|
|
42
|
+
riskScore: RISK_SCORES[entry.level] ?? 0,
|
|
43
|
+
};
|
|
44
|
+
appendFileSync(LOG_FILE, JSON.stringify(record) + '\n', { mode: 0o600 });
|
|
45
|
+
this.rotateIfNeeded();
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
// Log failure must not break plugin, but warn via stderr
|
|
49
|
+
try {
|
|
50
|
+
process.stderr.write(`[ShellWard] audit log write failed: ${e?.message}\n`);
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
rotateIfNeeded() {
|
|
56
|
+
if (this.rotating)
|
|
57
|
+
return;
|
|
58
|
+
try {
|
|
59
|
+
const stat = statSync(LOG_FILE);
|
|
60
|
+
if (stat.size > MAX_SIZE_BYTES) {
|
|
61
|
+
this.rotating = true;
|
|
62
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
63
|
+
renameSync(LOG_FILE, `${LOG_FILE}.${ts}.bak`);
|
|
64
|
+
writeFileSync(LOG_FILE, '', { mode: 0o600 });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch { /* ignore */ }
|
|
68
|
+
finally {
|
|
69
|
+
this.rotating = false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface AutoCheckResult {
|
|
2
|
+
openclawVulns: {
|
|
3
|
+
id: string;
|
|
4
|
+
severity: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}[];
|
|
7
|
+
pluginRisks: {
|
|
8
|
+
plugin: string;
|
|
9
|
+
risk: string;
|
|
10
|
+
}[];
|
|
11
|
+
mcpRisks: {
|
|
12
|
+
config: string;
|
|
13
|
+
risk: string;
|
|
14
|
+
}[];
|
|
15
|
+
rootWarning: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 执行全部自动检查,返回结果(供启动时告警用)
|
|
19
|
+
*/
|
|
20
|
+
export declare function runAutoCheck(locale?: 'zh' | 'en'): Promise<AutoCheckResult>;
|
|
21
|
+
/**
|
|
22
|
+
* 启动时执行检查,发现问题时通过 logger 告警
|
|
23
|
+
*/
|
|
24
|
+
export declare function runAutoCheckOnStartup(logger: {
|
|
25
|
+
warn: (s: string) => void;
|
|
26
|
+
}, locale: 'zh' | 'en'): void;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// src/auto-check.ts — 启动时自动安全检查,减少人为操作
|
|
2
|
+
// 异步执行,不阻塞启动;发现问题时通过 logger 告警
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { getHomeDir } from './utils.js';
|
|
7
|
+
import { fetchVulnDB, compareVersions } from './update-check.js';
|
|
8
|
+
const OPENCLAW_DIR = join(getHomeDir(), '.openclaw');
|
|
9
|
+
const SUSPICIOUS_PATTERNS = [
|
|
10
|
+
{ pattern: /eval\s*\(/, name: 'eval()' },
|
|
11
|
+
{ pattern: /\/dev\/tcp|nc\s+-e|ncat/, name: 'reverse shell' },
|
|
12
|
+
{ pattern: /webhook|exfil|callback.*http/i, name: 'data exfil' },
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* 获取 OpenClaw 版本
|
|
16
|
+
*/
|
|
17
|
+
function getOpenClawVersion() {
|
|
18
|
+
try {
|
|
19
|
+
const out = execSync('openclaw --version 2>&1', { timeout: 5000 }).toString().trim();
|
|
20
|
+
const match = out.match(/(\d{4}\.\d+\.\d+|\d+\.\d+\.\d+)/);
|
|
21
|
+
return match ? match[1] : 'unknown';
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return 'unknown';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 检查 OpenClaw 是否受已知漏洞影响
|
|
29
|
+
*/
|
|
30
|
+
async function checkOpenClawVulns(version, locale = 'en') {
|
|
31
|
+
const vulns = [];
|
|
32
|
+
try {
|
|
33
|
+
const { vulns: db } = await fetchVulnDB();
|
|
34
|
+
const list = db.length > 0 ? db : [
|
|
35
|
+
{ affectedBelow: '1.0.111', severity: 'HIGH', id: 'CVE-2025-59536', description_zh: 'RCE via Hooks/MCP', description_en: 'RCE via Hooks/MCP' },
|
|
36
|
+
{ affectedBelow: '2.0.65', severity: 'MEDIUM', id: 'CVE-2026-21852', description_zh: 'API Key exfil', description_en: 'API Key exfil' },
|
|
37
|
+
];
|
|
38
|
+
for (const v of list) {
|
|
39
|
+
if (version !== 'unknown' && compareVersions(version, v.affectedBelow) < 0) {
|
|
40
|
+
vulns.push({
|
|
41
|
+
id: v.id,
|
|
42
|
+
severity: v.severity || 'MEDIUM',
|
|
43
|
+
description: locale === 'zh'
|
|
44
|
+
? (v.description_zh || v.description_en || v.id)
|
|
45
|
+
: (v.description_en || v.description_zh || v.id),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch { /* ignore */ }
|
|
51
|
+
return vulns;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 快速扫描插件中的高风险模式
|
|
55
|
+
*/
|
|
56
|
+
function scanPluginsQuick() {
|
|
57
|
+
const risks = [];
|
|
58
|
+
const dirs = [
|
|
59
|
+
join(OPENCLAW_DIR, 'extensions'),
|
|
60
|
+
join(OPENCLAW_DIR, 'plugins'),
|
|
61
|
+
];
|
|
62
|
+
for (const dir of dirs) {
|
|
63
|
+
if (!existsSync(dir))
|
|
64
|
+
continue;
|
|
65
|
+
try {
|
|
66
|
+
for (const name of readdirSync(dir)) {
|
|
67
|
+
const p = join(dir, name);
|
|
68
|
+
if (name.startsWith('.'))
|
|
69
|
+
continue;
|
|
70
|
+
try {
|
|
71
|
+
const files = readdirSync(p);
|
|
72
|
+
for (const f of files) {
|
|
73
|
+
if (!/\.(ts|js)$/.test(f))
|
|
74
|
+
continue;
|
|
75
|
+
const content = readFileSync(join(p, f), 'utf-8').slice(0, 50000);
|
|
76
|
+
for (const { pattern, name: riskName } of SUSPICIOUS_PATTERNS) {
|
|
77
|
+
if (pattern.test(content)) {
|
|
78
|
+
risks.push({ plugin: name, risk: riskName });
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch { /* skip */ }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch { /* skip */ }
|
|
88
|
+
}
|
|
89
|
+
return risks;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 扫描 MCP 配置中的可疑项
|
|
93
|
+
*/
|
|
94
|
+
function scanMcpConfig() {
|
|
95
|
+
const risks = [];
|
|
96
|
+
const configPaths = [
|
|
97
|
+
join(OPENCLAW_DIR, 'mcp.json'),
|
|
98
|
+
join(OPENCLAW_DIR, 'config', 'mcp.json'),
|
|
99
|
+
join(OPENCLAW_DIR, 'settings.json'),
|
|
100
|
+
];
|
|
101
|
+
for (const p of configPaths) {
|
|
102
|
+
if (!existsSync(p))
|
|
103
|
+
continue;
|
|
104
|
+
try {
|
|
105
|
+
const content = readFileSync(p, 'utf-8');
|
|
106
|
+
if (/webhook|exfil|callback|pastebin|requestbin/i.test(content)) {
|
|
107
|
+
risks.push({ config: p, risk: 'suspicious URL in config' });
|
|
108
|
+
}
|
|
109
|
+
if (/command.*:.*["'](?:curl|wget|nc)\s/i.test(content)) {
|
|
110
|
+
risks.push({ config: p, risk: 'network command in MCP' });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch { /* skip */ }
|
|
114
|
+
}
|
|
115
|
+
return risks;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 执行全部自动检查,返回结果(供启动时告警用)
|
|
119
|
+
*/
|
|
120
|
+
export async function runAutoCheck(locale = 'en') {
|
|
121
|
+
const ocVersion = getOpenClawVersion();
|
|
122
|
+
const [openclawVulns, pluginRisks, mcpRisks] = await Promise.all([
|
|
123
|
+
checkOpenClawVulns(ocVersion, locale),
|
|
124
|
+
Promise.resolve(scanPluginsQuick()),
|
|
125
|
+
Promise.resolve(scanMcpConfig()),
|
|
126
|
+
]);
|
|
127
|
+
const rootWarning = typeof process.getuid === 'function' && process.getuid() === 0;
|
|
128
|
+
return { openclawVulns, pluginRisks, mcpRisks, rootWarning };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 启动时执行检查,发现问题时通过 logger 告警
|
|
132
|
+
*/
|
|
133
|
+
export function runAutoCheckOnStartup(logger, locale) {
|
|
134
|
+
runAutoCheck(locale).then(result => {
|
|
135
|
+
const zh = locale === 'zh';
|
|
136
|
+
const lines = [];
|
|
137
|
+
if (result.openclawVulns.length > 0) {
|
|
138
|
+
lines.push(zh ? '⚠️ OpenClaw 存在已知漏洞:' : '⚠️ OpenClaw has known vulnerabilities:');
|
|
139
|
+
for (const v of result.openclawVulns) {
|
|
140
|
+
lines.push(` ${v.id} [${v.severity}]: ${v.description}`);
|
|
141
|
+
}
|
|
142
|
+
lines.push(zh ? ' 请运行 /check-updates 查看详情并升级' : ' Run /check-updates for details and upgrade');
|
|
143
|
+
}
|
|
144
|
+
if (result.pluginRisks.length > 0) {
|
|
145
|
+
lines.push(zh ? '⚠️ 插件扫描发现可疑模式:' : '⚠️ Plugin scan found suspicious patterns:');
|
|
146
|
+
for (const r of result.pluginRisks.slice(0, 3)) {
|
|
147
|
+
lines.push(` ${r.plugin}: ${r.risk}`);
|
|
148
|
+
}
|
|
149
|
+
if (result.pluginRisks.length > 3) {
|
|
150
|
+
lines.push(zh ? ` ... 共 ${result.pluginRisks.length} 项` : ` ... ${result.pluginRisks.length} total`);
|
|
151
|
+
}
|
|
152
|
+
lines.push(zh ? ' 请运行 /scan-plugins 查看详情' : ' Run /scan-plugins for details');
|
|
153
|
+
}
|
|
154
|
+
if (result.mcpRisks.length > 0) {
|
|
155
|
+
lines.push(zh ? '⚠️ MCP 配置存在可疑项:' : '⚠️ Suspicious items in MCP config:');
|
|
156
|
+
for (const r of result.mcpRisks) {
|
|
157
|
+
lines.push(` ${r.config}: ${r.risk}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (result.rootWarning) {
|
|
161
|
+
lines.push(zh ? '⚠️ 正在以 root 运行,建议使用普通用户' : '⚠️ Running as root, consider using non-root user');
|
|
162
|
+
}
|
|
163
|
+
if (lines.length > 0) {
|
|
164
|
+
logger.warn((zh ? '[ShellWard] 自动安全检查:\n' : '[ShellWard] Auto security check:\n') + lines.join('\n'));
|
|
165
|
+
}
|
|
166
|
+
}).catch(() => { });
|
|
167
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/commands/audit.ts — /audit command: view recent audit log entries
|
|
2
|
+
import { readFileSync, statSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { getHomeDir } from '../utils.js';
|
|
5
|
+
import { resolveLocale } from '../types.js';
|
|
6
|
+
const LOG_FILE = join(getHomeDir(), '.openclaw', 'shellward', 'audit.jsonl');
|
|
7
|
+
export function registerAuditCommand(api, config) {
|
|
8
|
+
const locale = resolveLocale(config);
|
|
9
|
+
api.registerCommand({
|
|
10
|
+
name: 'audit',
|
|
11
|
+
description: locale === 'zh'
|
|
12
|
+
? '📋 查看 ShellWard 审计日志 (用法: /audit [数量] [block|audit|critical])'
|
|
13
|
+
: '📋 View ShellWard audit log (usage: /audit [count] [block|audit|critical])',
|
|
14
|
+
acceptsArgs: true,
|
|
15
|
+
handler: (ctx) => {
|
|
16
|
+
const zh = locale === 'zh';
|
|
17
|
+
const args = (ctx.args || '').trim().split(/\s+/).filter(Boolean);
|
|
18
|
+
// Parse args: count and optional filter
|
|
19
|
+
let count = 20;
|
|
20
|
+
let filter = '';
|
|
21
|
+
for (const arg of args) {
|
|
22
|
+
if (/^\d+$/.test(arg)) {
|
|
23
|
+
count = Math.min(parseInt(arg), 100);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
filter = arg.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
statSync(LOG_FILE);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { text: zh ? '⚠️ 审计日志文件不存在,尚无安全事件记录。' : '⚠️ Audit log not found. No security events recorded yet.' };
|
|
34
|
+
}
|
|
35
|
+
const content = readFileSync(LOG_FILE, 'utf-8');
|
|
36
|
+
let lines = content.trim().split('\n').filter(Boolean);
|
|
37
|
+
// Apply filter
|
|
38
|
+
if (filter === 'block') {
|
|
39
|
+
lines = lines.filter(l => l.includes('"action":"block"'));
|
|
40
|
+
}
|
|
41
|
+
else if (filter === 'audit') {
|
|
42
|
+
lines = lines.filter(l => l.includes('"action":"audit"'));
|
|
43
|
+
}
|
|
44
|
+
else if (filter === 'redact') {
|
|
45
|
+
lines = lines.filter(l => l.includes('"action":"redact"'));
|
|
46
|
+
}
|
|
47
|
+
else if (filter === 'critical') {
|
|
48
|
+
lines = lines.filter(l => l.includes('"level":"CRITICAL"'));
|
|
49
|
+
}
|
|
50
|
+
else if (filter === 'high') {
|
|
51
|
+
lines = lines.filter(l => l.includes('"level":"HIGH"') || l.includes('"level":"CRITICAL"'));
|
|
52
|
+
}
|
|
53
|
+
// Get last N entries
|
|
54
|
+
const recent = lines.slice(-count);
|
|
55
|
+
if (recent.length === 0) {
|
|
56
|
+
return { text: zh ? '✅ 没有匹配的审计事件。' : '✅ No matching audit events.' };
|
|
57
|
+
}
|
|
58
|
+
const header = zh
|
|
59
|
+
? `📋 **审计日志** (最近 ${recent.length} 条${filter ? `, 过滤: ${filter}` : ''})`
|
|
60
|
+
: `📋 **Audit Log** (last ${recent.length} entries${filter ? `, filter: ${filter}` : ''})`;
|
|
61
|
+
const formatted = recent.map(line => {
|
|
62
|
+
try {
|
|
63
|
+
const e = JSON.parse(line);
|
|
64
|
+
const icon = e.action === 'block' ? '🚫' : e.action === 'audit' ? '📋' : e.action === 'redact' ? '🔒' : e.level === 'CRITICAL' ? '🔴' : 'ℹ️';
|
|
65
|
+
const time = e.ts?.slice(11, 19) || '??:??:??';
|
|
66
|
+
return `${icon} \`${time}\` **${e.layer}** ${e.action}: ${e.detail?.slice(0, 80) || ''}${e.pattern ? ` [${e.pattern}]` : ''}`;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return line.slice(0, 100);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return { text: `${header}\n\n${formatted.join('\n')}` };
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|