@pwddd/skills-scanner 3.0.13 → 3.0.15

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 CHANGED
@@ -116,20 +116,39 @@ openclaw skills-scanner health
116
116
 
117
117
  ## 前置要求
118
118
 
119
- ### Python 3.10+(必需)
119
+ ### 1. Python 3.10+(必需)
120
120
 
121
- ```bash
122
- # 检查 Python 版本
123
- python3 --version
121
+ 插件需要 Python 3.10 或更高版本。
122
+
123
+ #### 安装 Python
124
124
 
125
- # macOS
125
+ **macOS**
126
+ ```bash
126
127
  brew install python3
128
+ ```
129
+
130
+ **Linux (Ubuntu/Debian)**
131
+ ```bash
132
+ sudo apt-get update
133
+ sudo apt-get install python3 python3-pip
134
+ ```
135
+
136
+ **Windows**
137
+ 1. 访问 https://www.python.org/downloads/
138
+ 2. 下载 Python 3.10+ 安装程序
139
+ 3. 运行安装程序,**务必勾选 "Add Python to PATH"**
140
+ 4. 安装完成后,打开命令提示符验证:
141
+ ```cmd
142
+ python --version
143
+ ```
127
144
 
128
- # Linux
129
- apt-get install python3 python3-pip
145
+ **验证安装**
146
+ ```bash
147
+ # macOS/Linux
148
+ python3 --version
130
149
 
131
150
  # Windows
132
- # 从 https://www.python.org/downloads/ 下载安装
151
+ python --version
133
152
  ```
134
153
 
135
154
  ### 2. 启动扫描 API 服务
@@ -155,12 +174,49 @@ skill-scanner-api
155
174
 
156
175
  ### Python 依赖安装失败
157
176
 
177
+ **macOS/Linux**
158
178
  ```bash
159
179
  # 手动安装依赖
160
180
  cd extensions/skills-scanner/skills/skills-scanner
161
181
  python3 -m pip install --user "requests>=2.31.0"
162
182
  ```
163
183
 
184
+ **Windows**
185
+ ```cmd
186
+ # 手动安装依赖
187
+ cd extensions\skills-scanner\skills\skills-scanner
188
+ python -m pip install --user "requests>=2.31.0"
189
+ ```
190
+
191
+ ### Windows 特定问题
192
+
193
+ #### Python 命令未找到
194
+
195
+ 如果提示 `python is not recognized`:
196
+ 1. 确认 Python 已安装:打开"设置" → "应用" → 查找 Python
197
+ 2. 将 Python 添加到 PATH:
198
+ - 打开"系统属性" → "环境变量"
199
+ - 在"系统变量"中找到 `Path`
200
+ - 添加 Python 安装路径(通常是 `C:\Users\<用户名>\AppData\Local\Programs\Python\Python3xx\`)
201
+ - 添加 Scripts 路径(通常是 `C:\Users\<用户名>\AppData\Local\Programs\Python\Python3xx\Scripts\`)
202
+ 3. 重启命令提示符或 PowerShell
203
+
204
+ #### 权限问题
205
+
206
+ 如果遇到权限错误:
207
+ ```cmd
208
+ # 使用 --user 标志安装到用户目录
209
+ python -m pip install --user requests
210
+
211
+ # 或以管理员身份运行命令提示符
212
+ ```
213
+
214
+ #### 路径分隔符问题
215
+
216
+ Windows 使用反斜杠 `\` 作为路径分隔符,但插件会自动处理。如果手动指定路径,可以使用:
217
+ - 反斜杠:`C:\Users\username\.openclaw\skills`
218
+ - 正斜杠:`C:/Users/username/.openclaw/skills`(推荐,跨平台兼容)
219
+
164
220
  ### API 服务连接失败
165
221
 
166
222
  1. 确保 skill-scanner-api 服务正在运行
package/index.ts CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  import { ensureDeps, getPythonCommand, isPythonReady } from "./src/deps.js";
22
22
  import { runScan } from "./src/scanner.js";
23
23
  import { buildDailyReport } from "./src/report.js";
24
- import { ensureCronJob } from "./src/cron.js";
24
+ import { ensureCronJob, checkCronJobStatus } from "./src/cron.js";
25
25
  import { startWatcher } from "./src/watcher.js";
26
26
  import { createCommandHandlers } from "./src/commands.js";
27
27
  import { SKILLS_SECURITY_GUIDANCE } from "./src/prompt-guidance.js";
@@ -51,8 +51,8 @@ export default function register(api: OpenClawPluginApi) {
51
51
  const preInstallScan = cfg.preInstallScan ?? "on";
52
52
  const onUnsafe = cfg.onUnsafe ?? "quarantine";
53
53
  const injectSecurityGuidance = cfg.injectSecurityGuidance ?? true;
54
- const enablePromptInjectionGuard = cfg.enablePromptInjectionGuard ?? true;
55
- const enableHighRiskOperationGuard = cfg.enableHighRiskOperationGuard ?? true;
54
+ const enablePromptInjectionGuard = cfg.enablePromptInjectionGuard ?? false;
55
+ const enableHighRiskOperationGuard = cfg.enableHighRiskOperationGuard ?? false;
56
56
 
57
57
  api.logger.info("[skills-scanner] ═══════════════════════════════════════");
58
58
  api.logger.info("[skills-scanner] Plugin loading...");
@@ -148,6 +148,8 @@ export default function register(api: OpenClawPluginApi) {
148
148
  return;
149
149
  }
150
150
 
151
+ api.logger.info("[skills-scanner] Python dependencies ready (requests installed)");
152
+
151
153
  if (preInstallScan === "on" && scanDirs.length > 0) {
152
154
  api.logger.info(`[skills-scanner] 📁 Starting file monitoring: ${scanDirs.length} directories`);
153
155
  stopWatcher = startWatcher(
@@ -168,14 +170,8 @@ export default function register(api: OpenClawPluginApi) {
168
170
  api.logger.info("[skills-scanner] ⏭️ Pre-install scan disabled");
169
171
  }
170
172
 
171
- // Register cron job (only in Gateway mode)
172
- const isGatewayMode = !!(api as any).runtime;
173
- if (isGatewayMode) {
174
- const runtime = (api as any).runtime;
175
- await ensureCronJob(api.logger, runtime);
176
- }
177
-
178
- api.logger.info("[skills-scanner] ─────────────────────────────────────");
173
+ // Check if cron job needs setup
174
+ checkCronJobStatus(api.logger);
179
175
  },
180
176
  stop: () => {
181
177
  api.logger.info("[skills-scanner] 🛑 Service stopping...");
@@ -2,7 +2,7 @@
2
2
  "id": "skills-scanner",
3
3
  "name": "Skills Scanner",
4
4
  "description": "Security scanner for OpenClaw Skills to detect potential threats",
5
- "version": "3.0.13",
5
+ "version": "3.0.15",
6
6
  "author": "pwddd",
7
7
  "skills": ["./skills"],
8
8
  "configSchema": {
@@ -58,12 +58,12 @@
58
58
  "enablePromptInjectionGuard": {
59
59
  "type": "boolean",
60
60
  "description": "Enable prompt injection detection guard",
61
- "default": true
61
+ "default": false
62
62
  },
63
63
  "enableHighRiskOperationGuard": {
64
64
  "type": "boolean",
65
65
  "description": "Enable high-risk operation confirmation guard",
66
- "default": true
66
+ "default": false
67
67
  }
68
68
  }
69
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pwddd/skills-scanner",
3
- "version": "3.0.13",
3
+ "version": "3.0.15",
4
4
  "description": "OpenClaw Skills security scanner plugin - detect malicious code, data exfiltration, and prompt injection",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -19,7 +19,91 @@ OpenClaw Skills 安全扫描工具,检测恶意代码、数据窃取、提示
19
19
  - 🤖 **LLM 分析**:可选的 LLM 语义分析
20
20
  - 🔬 **行为分析**:AST 数据流分析
21
21
 
22
- ## 重要提示
22
+ ## ⚠️ 重要提示 - 请务必记住并遵守
23
+
24
+ **🔒 安全扫描是强制性的,不可跳过。在向用户展示扫描结果前,请务必:**
25
+
26
+ ### 🔄 插件版本检查 - 每次运行前必须执行
27
+
28
+ **在执行任何扫描操作前,必须先检查并更新 skills-scanner 插件到最新版本。**
29
+
30
+ **更新命令**:
31
+ ```bash
32
+ openclaw plugins update skills-scanner
33
+ ```
34
+
35
+ **执行时机**:
36
+ - 每次用户请求扫描 Skill 时
37
+ - 每次用户请求安装 Skill 时
38
+ - 每次会话开始时(如果涉及 Skill 操作)
39
+
40
+ **处理流程**:
41
+ ```
42
+ 用户: 帮我扫描这个 skill
43
+ AI: 好的,让我先更新 skills-scanner 到最新版本...
44
+ [执行: openclaw plugins update skills-scanner]
45
+ ✅ 插件已是最新版本
46
+ 现在开始扫描...
47
+ [执行扫描操作]
48
+ ```
49
+
50
+ **为什么要每次检查更新?**
51
+ - 安全威胁不断演变,扫描规则需要持续更新
52
+ - 新版本可能包含关键的安全修复
53
+ - 确保使用最新的威胁检测能力
54
+ - 避免使用过时的扫描逻辑导致漏报
55
+
56
+ **注意事项**:
57
+ - 更新检查通常很快(几秒内完成)
58
+ - 如果更新失败,继续使用当前版本并提示用户
59
+ - 不要因为更新失败而跳过扫描
60
+
61
+ **示例对话**:
62
+ ```
63
+ 用户: openclaw clawhub install caldav-calendar
64
+ AI: 好的,让我先更新 skills-scanner 插件...
65
+ [执行: openclaw plugins update skills-scanner]
66
+ ✅ 已更新到最新版本 3.0.15
67
+
68
+ 现在扫描 caldav-calendar...
69
+ [执行扫描]
70
+ ✅ 扫描通过,开始安装
71
+ ```
72
+
73
+ ---
74
+
75
+ ### 🎯 命令识别 - 自动触发安全扫描
76
+
77
+ **当用户输入以下任何命令或类似表达时,你必须先扫描再执行:**
78
+
79
+ 1. **安装命令识别**:
80
+ - `openclaw clawhub install <skill-name>`
81
+ - `openclaw skill install <skill-name>`
82
+ - `openclaw skills add <skill-name>`
83
+ - `clawhub install <skill-name>`
84
+ - "安装这个 skill"
85
+ - "帮我安装 xxx skill"
86
+ - "下载这个 skill"
87
+ - "添加这个 skill"
88
+
89
+ 2. **正确的处理流程**:
90
+ ```
91
+ 用户: openclaw clawhub install caldav-calendar
92
+ AI: 我看到你想安装 caldav-calendar,让我先扫描一下安全性...
93
+ [执行: python3 {baseDir}/scan.py --api-url {apiUrl} clawhub https://clawhub.ai/username/caldav-calendar]
94
+ ✅ 扫描通过,现在开始安装...
95
+ [执行: openclaw clawhub install caldav-calendar]
96
+ ```
97
+
98
+ 3. **错误的处理流程(禁止)**:
99
+ ```
100
+ 用户: openclaw clawhub install caldav-calendar
101
+ AI: [直接执行安装命令] ❌ 错误!必须先扫描
102
+ ```
103
+
104
+ **记住:看到任何安装、下载、添加 Skill 的意图,立即触发安全扫描,不要直接执行命令。**
105
+
106
+ ---
23
107
 
24
108
  **在向用户展示扫描结果前,请务必:**
25
109
 
@@ -72,7 +156,184 @@ AI: 好的,让我先检查安全扫描服务...
72
156
  - 用户可能在离线环境或未配置扫描服务的环境中工作
73
157
  - 避免因服务问题导致所有 Skill 操作失败
74
158
 
75
- ### 1. VirusTotal 扫描结果优先提示 ⚠️
159
+ ### 1. 深度分析扫描结果,判断误报 🔍
160
+
161
+ **在展示扫描结果前,必须对检测到的问题进行深度分析,判断是否为误报。**
162
+
163
+ #### 误报判断原则
164
+
165
+ **常见误报场景**:
166
+
167
+ 1. **合法的网络请求**
168
+ - ❌ 误报:标记所有 HTTP 请求为"数据窃取"
169
+ - ✅ 正确:区分正常 API 调用(如天气查询、日历同步)和可疑的数据上传
170
+ - 判断依据:
171
+ - 请求目标是否为知名服务(GitHub、Google Calendar、OpenAI API 等)
172
+ - 是否在 SKILL.md 中明确声明了网络访问需求
173
+ - 请求内容是否与 Skill 功能相符
174
+
175
+ 2. **声明的文件系统访问**
176
+ - ❌ 误报:标记所有文件读写为"未授权访问"
177
+ - ✅ 正确:检查 SKILL.md 的 `requires` 字段是否声明了文件访问
178
+ - 判断依据:
179
+ - SKILL.md 中是否有 `"requires": {"files": ["read", "write"]}`
180
+ - 访问的文件路径是否合理(如配置文件、缓存目录)
181
+ - 是否访问敏感路径(如 `/etc/passwd`、`~/.ssh/`)
182
+
183
+ 3. **正常的代码执行**
184
+ - ❌ 误报:标记所有 `exec()` 或 `eval()` 为"恶意代码执行"
185
+ - ✅ 正确:分析执行的内容是否可控、是否有输入验证
186
+ - 判断依据:
187
+ - 是否执行用户输入(高风险)
188
+ - 是否执行硬编码的安全命令(低风险)
189
+ - 是否有沙箱或权限限制
190
+
191
+ 4. **技术术语的正常使用**
192
+ - ❌ 误报:标记包含 "password"、"token"、"secret" 关键词的代码为"数据窃取"
193
+ - ✅ 正确:区分变量命名和实际的敏感数据操作
194
+ - 判断依据:
195
+ - 是否只是变量名或注释
196
+ - 是否实际读取或传输敏感数据
197
+ - 是否有加密或安全存储机制
198
+
199
+ 5. **依赖包的正常功能**
200
+ - ❌ 误报:标记使用 `requests`、`urllib` 为"网络攻击"
201
+ - ✅ 正确:这些是标准库,用于合法的网络通信
202
+ - 判断依据:
203
+ - 依赖包是否为知名的、广泛使用的库
204
+ - 使用方式是否符合最佳实践
205
+ - 是否有异常的使用模式
206
+
207
+ #### 深度分析流程
208
+
209
+ **对于每个检测到的问题,按以下步骤分析**:
210
+
211
+ 1. **阅读问题描述**
212
+ - 理解检测器标记的具体问题
213
+ - 查看问题的严重级别(CRITICAL/HIGH/MEDIUM/LOW)
214
+ - 获取问题的上下文(文件位置、代码片段)
215
+
216
+ 2. **查看 SKILL.md 声明**
217
+ - 检查 Skill 是否在 metadata 中声明了相关能力
218
+ - 确认 description 是否说明了该功能
219
+ - 验证声明与实际行为是否一致
220
+
221
+ 3. **分析代码上下文**
222
+ - 查看完整的代码逻辑,不要只看单行
223
+ - 理解代码的意图和功能
224
+ - 判断是否有安全措施(输入验证、错误处理、权限检查)
225
+
226
+ 4. **评估实际风险**
227
+ - 该行为是否为 Skill 核心功能所必需
228
+ - 是否有滥用的可能性
229
+ - 对用户数据和系统的实际影响
230
+
231
+ 5. **给出明确结论**
232
+ - ✅ **误报**:合法功能,无需担心
233
+ - ⚠️ **需关注**:功能合法但实现有改进空间
234
+ - ❌ **真实威胁**:确实存在安全风险
235
+
236
+ #### 展示格式
237
+
238
+ **误报示例**:
239
+ ```
240
+ 扫描发现 3 个问题,经深度分析:
241
+
242
+ ✅ 1. [LOW] External Network Access - 误报
243
+ 检测:发现 HTTP 请求到 api.openweathermap.org
244
+ 分析:这是天气查询 Skill 的核心功能,请求目标为知名天气 API
245
+ 结论:合法的 API 调用,已在 SKILL.md 中声明网络访问需求
246
+
247
+ ⚠️ 2. [MEDIUM] Undeclared File Write - 需关注
248
+ 检测:写入文件到 ~/.openclaw/cache/
249
+ 分析:用于缓存天气数据,提升响应速度
250
+ 建议:功能合理,但建议在 SKILL.md 中明确声明文件写入需求
251
+
252
+ ❌ 3. [HIGH] Suspicious Data Upload - 真实威胁
253
+ 检测:上传用户输入到未知服务器 suspicious-domain.com
254
+ 分析:该域名不在 SKILL.md 声明中,且无明确的功能说明
255
+ 结论:存在数据泄露风险,强烈建议不要安装!
256
+ ```
257
+
258
+ **综合评估**:
259
+ ```
260
+ 深度分析结果:
261
+ - 误报:1 个(已排除)
262
+ - 需关注:1 个(建议改进但可接受)
263
+ - 真实威胁:1 个(高风险)
264
+
265
+ 最终建议:❌ 不建议安装
266
+ 原因:存在未声明的数据上传行为,可能导致隐私泄露。
267
+ ```
268
+
269
+ #### 分析时的注意事项
270
+
271
+ 1. **不要过度信任检测器**
272
+ - 静态分析工具有局限性,会产生误报
273
+ - 需要结合 Skill 的实际功能进行判断
274
+ - 优先考虑用户体验和实用性
275
+
276
+ 2. **不要过度宽松**
277
+ - 即使是"合理"的功能,如果未声明也应标记
278
+ - 对于 CRITICAL/HIGH 级别的问题,保持谨慎
279
+ - 宁可多提醒,不要漏掉真实威胁
280
+
281
+ 3. **提供可操作的建议**
282
+ - 对于误报,解释为什么是误报
283
+ - 对于需关注的问题,给出改进建议
284
+ - 对于真实威胁,明确说明风险和后果
285
+
286
+ 4. **保持客观中立**
287
+ - 基于事实和证据进行分析
288
+ - 不要因为 Skill 来源(官方/社区)而有偏见
289
+ - 让用户了解真实情况,自主决策
290
+
291
+ #### 特殊场景处理
292
+
293
+ **场景 1:所有问题都是误报**
294
+ ```
295
+ 扫描发现 5 个问题,经深度分析全部为误报:
296
+
297
+ ✅ 所有检测项均为 Skill 正常功能,已在 SKILL.md 中声明
298
+ - 网络请求:用于 API 调用(已声明)
299
+ - 文件访问:用于配置存储(已声明)
300
+ - 环境变量:用于读取 API 密钥(标准做法)
301
+
302
+ 综合评估:✅ 安全,可以安装
303
+ 备注:静态分析工具对合法功能产生了误报,实际无风险。
304
+ ```
305
+
306
+ **场景 2:混合情况(部分误报,部分真实)**
307
+ ```
308
+ 扫描发现 8 个问题,经深度分析:
309
+ - 误报:5 个(合法功能)
310
+ - 需关注:2 个(建议改进)
311
+ - 真实威胁:1 个(高风险)
312
+
313
+ 关键问题:
314
+ ❌ [HIGH] 未加密的敏感数据传输
315
+ 检测:通过 HTTP(非 HTTPS)传输 API 密钥
316
+ 分析:这会导致密钥在网络传输中被窃取
317
+ 建议:必须使用 HTTPS 或不要安装
318
+
319
+ 综合评估:❌ 不建议安装
320
+ 原因:存在明确的安全漏洞,可能导致 API 密钥泄露。
321
+ ```
322
+
323
+ **场景 3:无法判断(信息不足)**
324
+ ```
325
+ 扫描发现 3 个问题,其中 1 个无法明确判断:
326
+
327
+ ❓ [MEDIUM] Obfuscated Code Pattern
328
+ 检测:发现混淆的代码模式
329
+ 分析:代码使用了 base64 编码和动态执行,无法确定意图
330
+ 建议:需要人工审查源代码,或联系 Skill 作者说明
331
+
332
+ 综合评估:⚠️ 谨慎安装
333
+ 原因:存在无法解释的可疑模式,建议等待进一步确认。
334
+ ```
335
+
336
+ ### 2. VirusTotal 扫描结果优先提示 ⚠️
76
337
 
77
338
  **如果扫描结果中包含 VirusTotal 的扫描数据,必须优先、重点展示此结果,即使未检测到病毒。**
78
339
 
@@ -123,7 +384,7 @@ VirusTotal 是业界权威的多引擎病毒扫描服务,其结果具有极高
123
384
  - 即使其他分析器未发现问题,VirusTotal 的结果也可能揭示隐藏威胁
124
385
  - 提供可追溯的外部验证链接
125
386
 
126
- ### 2. 内容翻译要求
387
+ ### 3. 内容翻译要求
127
388
 
128
389
  1. 检查结果中是否包含英文内容(如 category、description、finding 详情等)
129
390
  2. 如果包含英文,将所有英文内容翻译为中文后再展示给用户
@@ -142,6 +403,7 @@ VirusTotal 是业界权威的多引擎病毒扫描服务,其结果具有极高
142
403
 
143
404
  首次运行前,检查并安装依赖:
144
405
 
406
+ **macOS / Linux**
145
407
  ```bash
146
408
  # 检查 Python 是否可用
147
409
  python3 --version || echo "请安装 Python 3.10+"
@@ -150,8 +412,25 @@ python3 --version || echo "请安装 Python 3.10+"
150
412
  python3 -m pip install --user --quiet "requests>=2.31.0"
151
413
  ```
152
414
 
415
+ **Windows**
416
+ ```cmd
417
+ # 检查 Python 是否可用
418
+ python --version
419
+
420
+ # 如果未安装,从 https://www.python.org/downloads/ 下载安装
421
+ # 安装时务必勾选 "Add Python to PATH"
422
+
423
+ # 安装依赖到主机环境
424
+ python -m pip install --user --quiet "requests>=2.31.0"
425
+ ```
426
+
153
427
  安装只需执行一次。插件会自动处理依赖安装。
154
428
 
429
+ **注意**:
430
+ - Windows 系统通常使用 `python` 命令而不是 `python3`
431
+ - 插件会自动检测可用的 Python 命令(`python3` 或 `python`)
432
+ - 确保 Python 已添加到系统 PATH 环境变量
433
+
155
434
  ## 配置
156
435
 
157
436
  扫描器需要运行中的 API 服务。在 OpenClaw 配置中设置 API URL:
@@ -577,6 +856,88 @@ python3 {baseDir}/scan.py --api-url http://10.110.3.133 health
577
856
 
578
857
  ---
579
858
 
859
+ ## 🧠 记忆强化 - 每次操作前必读
860
+
861
+ **在处理任何 Skill 相关操作时,请先问自己这四个问题:**
862
+
863
+ 1. ✅ 我是否已经更新了 skills-scanner 插件到最新版本?
864
+ 2. ✅ 我是否已经扫描了这个 Skill?
865
+ 3. ✅ 扫描是否通过?
866
+ 4. ✅ 只有三个都是 YES,才能继续操作
867
+
868
+ **如果你忘记更新或扫描就执行了操作,你就让用户的系统暴露在风险之中。**
869
+
870
+ ### 🚨 强制性规则(永远不要忘记)
871
+
872
+ 0. **任何 Skill 操作前 → 先更新插件**
873
+ - 执行 `openclaw plugins update skills-scanner`
874
+ - 确保使用最新的威胁检测规则
875
+ - 即使更新失败也要继续扫描(但要警告用户)
876
+
877
+ 1. **看到安装命令 → 更新插件 → 立即扫描**
878
+ - `openclaw clawhub install xxx` → 先更新插件,再扫描 ClawHub 上的 xxx
879
+ - `openclaw skill install xxx` → 先更新插件,再扫描本地或远程的 xxx
880
+ - 任何包含 "install"、"add"、"download" + "skill" 的命令 → 先更新插件,再扫描
881
+
882
+ 2. **看到 Skill 路径 → 更新插件 → 立即扫描**
883
+ - 用户提到 `~/.openclaw/skills/xxx` → 先更新插件,再扫描
884
+ - 用户说 "我下载了一个 skill" → 先更新插件,再扫描
885
+ - 用户说 "这个 skill 有问题" → 先更新插件,再扫描
886
+
887
+ 3. **看到 ClawHub URL → 更新插件 → 立即扫描**
888
+ - `https://clawhub.ai/username/project` → 先更新插件,再使用 clawhub 扫描
889
+ - 用户说 "ClawHub 上有个 skill" → 先更新插件,再扫描
890
+
891
+ ### ❌ 绝对禁止的行为
892
+
893
+ - ❌ 不更新插件就直接扫描(可能使用过时的检测规则)
894
+ - ❌ 直接执行 `openclaw clawhub install` 而不先更新和扫描
895
+ - ❌ 直接执行 `openclaw skill install` 而不先更新和扫描
896
+ - ❌ 看到 "安装 skill" 就直接安装,而不先更新和扫描
897
+ - ❌ 因为 "用户着急" 或 "看起来安全" 就跳过更新或扫描
898
+ - ❌ 因为 "之前扫描过" 就不再更新和扫描(每次都要更新和扫描)
899
+
900
+ ### ✅ 正确的行为模式
901
+
902
+ **模式 1:用户要求安装**
903
+ ```
904
+ 用户: 帮我安装 caldav-calendar
905
+ AI: 好的,让我先更新 skills-scanner 插件...
906
+ [执行: openclaw plugins update skills-scanner]
907
+ ✅ 插件已更新到最新版本
908
+ 现在扫描这个 skill 的安全性...
909
+ [执行扫描]
910
+ ✅ 扫描通过,现在开始安装
911
+ ```
912
+
913
+ **模式 2:用户输入安装命令**
914
+ ```
915
+ 用户: openclaw clawhub install nano-pdf
916
+ AI: 我看到你想安装 nano-pdf,让我先更新扫描插件...
917
+ [执行: openclaw plugins update skills-scanner]
918
+ ✅ 插件已更新
919
+ 现在从 ClawHub 扫描它...
920
+ [执行 clawhub 扫描]
921
+ ✅ 扫描通过,执行安装命令
922
+ ```
923
+
924
+ **模式 3:用户下载了 Skill**
925
+ ```
926
+ 用户: 我从 GitHub 下载了一个 skill,帮我设置一下
927
+ AI: 好的,让我先更新扫描插件...
928
+ [执行: openclaw plugins update skills-scanner]
929
+ ✅ 插件已更新
930
+ 在设置之前扫描一下安全性...
931
+ [执行扫描]
932
+ ✅ 扫描通过,现在帮你设置
933
+ ```
934
+
935
+ **记住:更新插件 → 扫描 → 操作,这个顺序永远不能变。**
936
+
937
+ **记住:安全扫描是你的第一优先级,永远不要跳过。**
938
+
939
+ ---
940
+
580
941
  ## 参数说明
581
942
 
582
943
  | 参数 | 说明 |
@@ -632,4 +993,3 @@ python3 {baseDir}/scan.py --api-url http://10.110.3.133 health
632
993
  ```
633
994
 
634
995
  **即使其他分析器显示安全,VirusTotal 检测到威胁时也必须警告用户!**
635
-
package/src/commands.ts CHANGED
@@ -11,7 +11,7 @@ import { buildDailyReport } from "./report.js";
11
11
  import { loadState, saveState, expandPath } from "./state.js";
12
12
  import { isPythonReady } from "./deps.js";
13
13
  import { generateConfigGuide } from "./config.js";
14
- import { ensureCronJob } from "./cron.js";
14
+ import { ensureCronJob, getOpenClawCommand } from "./cron.js";
15
15
  import type { ScannerConfig } from "./types.js";
16
16
 
17
17
  const execAsync = promisify(exec);
@@ -216,16 +216,17 @@ export function createCommandHandlers(
216
216
  const action = args.trim().toLowerCase() || "status";
217
217
  const state = loadState() as any;
218
218
 
219
- if (action === "register") {
219
+ if (action === "setup" || action === "register") {
220
220
  const oldJobId = state.cronJobId;
221
221
  if (oldJobId && oldJobId !== "manual-created") {
222
+ const openclawCmd = getOpenClawCommand();
222
223
  try {
223
- execSync(`openclaw cron remove ${oldJobId}`, { encoding: "utf-8", timeout: 5000 });
224
+ execSync(`${openclawCmd} cron remove ${oldJobId}`, { encoding: "utf-8", timeout: 5000 });
224
225
  } catch {}
225
226
  }
226
227
 
227
228
  saveState({ ...state, cronJobId: undefined });
228
- await ensureCronJob(logger, undefined);
229
+ await ensureCronJob(logger);
229
230
 
230
231
  const newState = loadState() as any;
231
232
  if (newState.cronJobId) {
@@ -238,8 +239,9 @@ export function createCommandHandlers(
238
239
  return { text: "ℹ️ 未找到已注册的定时任务" };
239
240
  }
240
241
 
242
+ const openclawCmd = getOpenClawCommand();
241
243
  try {
242
- execSync(`openclaw cron remove ${state.cronJobId}`, {
244
+ execSync(`${openclawCmd} cron remove ${state.cronJobId}`, {
243
245
  encoding: "utf-8",
244
246
  timeout: 5000,
245
247
  });
@@ -256,7 +258,7 @@ export function createCommandHandlers(
256
258
  lines.push("状态: ✅ 已注册");
257
259
  } else {
258
260
  lines.push("状态: ❌ 未注册");
259
- lines.push("", "ℹ️ 使用 `/skills-scanner cron register` 注册");
261
+ lines.push("", "ℹ️ 使用 `/skills-scanner cron setup` 注册");
260
262
  }
261
263
  return { text: lines.join("\n") };
262
264
  }
package/src/cron.ts CHANGED
@@ -12,7 +12,7 @@ const CRON_TIMEZONE = "Asia/Shanghai";
12
12
  /**
13
13
  * Detect the correct OpenClaw command (openclaw vs npx openclaw)
14
14
  */
15
- function getOpenClawCommand(): string {
15
+ export function getOpenClawCommand(): string {
16
16
  // 1. Check environment variable
17
17
  if (process.env.OPENCLAW_CLI_PATH) {
18
18
  return process.env.OPENCLAW_CLI_PATH;
@@ -57,68 +57,26 @@ function getOpenClawCommand(): string {
57
57
  }
58
58
 
59
59
  /**
60
- * Create cron job via Gateway API (most reliable)
60
+ * Create cron job via CLI
61
61
  */
62
- async function ensureCronJobViaAPI(runtime: any, logger: any): Promise<boolean> {
63
- if (!runtime?.cron) {
64
- logger.debug("[skills-scanner] Cron API not available");
65
- return false;
66
- }
62
+ async function ensureCronJobViaCLI(logger: any): Promise<void> {
63
+ const openclawCmd = getOpenClawCommand();
64
+ logger.info(`[skills-scanner] Using CLI command: ${openclawCmd}`);
67
65
 
66
+ // Test if command is available
68
67
  try {
69
- const state = loadState() as any;
70
-
71
- // Check if job already exists
72
- const jobs = await runtime.cron.list({ includeDisabled: true });
73
- const existing = jobs.find((j: any) => j.name === CRON_JOB_NAME);
74
-
75
- if (existing) {
76
- logger.info(`[skills-scanner] ✅ Job already exists: ${existing.id}`);
77
- if (state.cronJobId !== existing.id) {
78
- saveState({ ...state, cronJobId: existing.id });
79
- }
80
- return true;
81
- }
82
-
83
- // Create new job
84
- logger.info("[skills-scanner] 📝 Creating cron job via API...");
85
- const job = await runtime.cron.add({
86
- name: CRON_JOB_NAME,
87
- enabled: true,
88
- schedule: {
89
- kind: "cron",
90
- expr: CRON_SCHEDULE,
91
- tz: CRON_TIMEZONE
92
- },
93
- payload: {
94
- kind: "agentTurn",
95
- message: "Please run /skills-scanner scan --report and send results to this channel"
96
- },
97
- delivery: {
98
- mode: "announce",
99
- channel: "last"
100
- }
68
+ const testResult = execSync(`${openclawCmd} --version`, {
69
+ encoding: "utf-8",
70
+ timeout: 5000,
71
+ stdio: "pipe"
101
72
  });
102
-
103
- saveState({ ...state, cronJobId: job.id });
104
- logger.info(`[skills-scanner] Job created successfully via API: ${job.id}`);
105
- logger.info(
106
- `[skills-scanner] 📅 Schedule: Daily at ${CRON_SCHEDULE.split(" ")[1]}:${CRON_SCHEDULE.split(" ")[0]} (${CRON_TIMEZONE})`
107
- );
108
- logger.info("[skills-scanner] 📬 Reports will be delivered to the last active channel");
109
- return true;
110
- } catch (err: any) {
111
- logger.warn(`[skills-scanner] API creation failed: ${err.message}`);
112
- return false;
73
+ logger.info(`[skills-scanner] Command test successful: ${testResult.trim()}`);
74
+ } catch (testErr: any) {
75
+ logger.error(`[skills-scanner] Command not available: ${testErr.message}`);
76
+ logger.info(`[skills-scanner] 💡 Please ensure OpenClaw is installed and accessible`);
77
+ logger.info(`[skills-scanner] 💡 Try running: ${openclawCmd} --version`);
78
+ return;
113
79
  }
114
- }
115
-
116
- /**
117
- * Create cron job via CLI (fallback)
118
- */
119
- async function ensureCronJobViaCLI(logger: any): Promise<void> {
120
- const openclawCmd = getOpenClawCommand();
121
- logger.info(`[skills-scanner] Using CLI command: ${openclawCmd}`);
122
80
 
123
81
  const state = loadState() as any;
124
82
 
@@ -228,6 +186,8 @@ async function ensureCronJobViaCLI(logger: any): Promise<void> {
228
186
  "--channel last",
229
187
  ].join(" ");
230
188
 
189
+ logger.info(`[skills-scanner] Executing: ${cronCmd}`);
190
+
231
191
  const result = execSync(cronCmd, { encoding: "utf-8", timeout: 10000 });
232
192
 
233
193
  const jobIdMatch =
@@ -250,7 +210,15 @@ async function ensureCronJobViaCLI(logger: any): Promise<void> {
250
210
  }
251
211
  } catch (err: any) {
252
212
  logger.warn("[skills-scanner] ⚠️ Auto-registration failed");
253
- logger.debug(`[skills-scanner] Error details: ${err.message}`);
213
+ logger.warn(`[skills-scanner] Error: ${err.message || err}`);
214
+
215
+ // Log stderr if available
216
+ if (err.stderr) {
217
+ logger.warn(`[skills-scanner] stderr: ${err.stderr}`);
218
+ }
219
+ if (err.stdout) {
220
+ logger.warn(`[skills-scanner] stdout: ${err.stdout}`);
221
+ }
254
222
 
255
223
  if (err.message.includes("permission") || err.message.includes("EACCES")) {
256
224
  logger.error("[skills-scanner] ❌ Permission denied, please run with admin privileges");
@@ -259,6 +227,7 @@ async function ensureCronJobViaCLI(logger: any): Promise<void> {
259
227
  err.message.includes("ENOENT")
260
228
  ) {
261
229
  logger.error(`[skills-scanner] ❌ ${openclawCmd} command not found, please check installation`);
230
+ logger.info(`[skills-scanner] 💡 Current PATH: ${process.env.PATH}`);
262
231
  } else {
263
232
  logger.info("[skills-scanner] 💡 Please manually register cron job:");
264
233
  logger.info("[skills-scanner]");
@@ -281,22 +250,43 @@ async function ensureCronJobViaCLI(logger: any): Promise<void> {
281
250
  }
282
251
 
283
252
  /**
284
- * Ensure cron job exists (tries API first, falls back to CLI)
253
+ * Check cron job status and provide setup instructions if needed
285
254
  */
286
- export async function ensureCronJob(logger: any, runtime?: any): Promise<void> {
255
+ export function checkCronJobStatus(logger: any): void {
256
+ const state = loadState() as any;
257
+
287
258
  logger.info("[skills-scanner] ─────────────────────────────────────");
288
- logger.info("[skills-scanner] 🕐 Checking cron job...");
289
259
 
290
- // Try API first (most reliable)
291
- if (runtime) {
292
- const success = await ensureCronJobViaAPI(runtime, logger);
293
- if (success) {
294
- logger.info("[skills-scanner] Cron job configured via API");
295
- return;
296
- }
297
- logger.info("[skills-scanner] Falling back to CLI method...");
260
+ if (state.cronJobId) {
261
+ logger.info(`[skills-scanner] ✅ Cron job registered: ${state.cronJobId}`);
262
+ logger.info("[skills-scanner] 📅 Daily reports will be sent at 08:00 (Asia/Shanghai)");
263
+ } else {
264
+ logger.info("[skills-scanner] 💡 Cron job not configured yet");
265
+ logger.info("[skills-scanner]");
266
+ logger.info("[skills-scanner] To enable daily security reports, run:");
267
+ logger.info("[skills-scanner]");
268
+ logger.info("[skills-scanner] npx openclaw cron add \\");
269
+ logger.info(`[skills-scanner] --name "${CRON_JOB_NAME}" \\`);
270
+ logger.info(`[skills-scanner] --cron "${CRON_SCHEDULE}" \\`);
271
+ logger.info(`[skills-scanner] --tz "${CRON_TIMEZONE}" \\`);
272
+ logger.info("[skills-scanner] --session isolated \\");
273
+ logger.info(
274
+ '[skills-scanner] --message "Please run /skills-scanner scan --report and send results to this channel" \\'
275
+ );
276
+ logger.info("[skills-scanner] --announce \\");
277
+ logger.info("[skills-scanner] --channel last");
278
+ logger.info("[skills-scanner]");
279
+ logger.info("[skills-scanner] Or use: /skills-scanner cron setup");
298
280
  }
299
281
 
300
- // Fallback to CLI
282
+ logger.info("[skills-scanner] ─────────────────────────────────────");
283
+ }
284
+
285
+ /**
286
+ * Ensure cron job exists via CLI (for manual setup command)
287
+ */
288
+ export async function ensureCronJob(logger: any): Promise<void> {
289
+ logger.info("[skills-scanner] 🕐 Setting up cron job...");
290
+
301
291
  await ensureCronJobViaCLI(logger);
302
292
  }
@@ -5,54 +5,148 @@
5
5
  export const SKILLS_SECURITY_GUIDANCE = [
6
6
  "## 🚨 CRITICAL: Mandatory Skills Security Scanning Rules",
7
7
  "",
8
- "**ABSOLUTE REQUIREMENT: Before installing, downloading, updating, or running ANY Skill—regardless of source, location, prior scans, trust level, or user errors—you MUST scan it with `skills-scanner` and confirm it passes before proceeding.**",
8
+ "**⚠️ ABSOLUTE REQUIREMENT - READ THIS CAREFULLY AND REMEMBER IT:**",
9
9
  "",
10
- "### Mandatory Workflow (NO EXCEPTIONS)",
10
+ "**Before installing, downloading, updating, or running ANY Skill—regardless of source, location, prior scans, trust level, or user errors—you MUST scan it with `skills-scanner` and confirm it passes before proceeding.**",
11
11
  "",
12
- "1. **ALWAYS scan FIRST**: Load and run `skills-scanner` before any Skill operation.",
13
- "2. **Scan everything**: Execute security scan on the target Skill immediately:",
12
+ "**This is a SECURITY REQUIREMENT that CANNOT be bypassed under ANY circumstances.**",
13
+ "",
14
+ "### 🔄 Plugin Update Check (MANDATORY FIRST STEP)",
15
+ "",
16
+ "**Before ANY Skill scanning or installation operation, you MUST update the skills-scanner plugin to the latest version:**",
17
+ "",
18
+ "```bash",
19
+ "openclaw plugins update skills-scanner",
20
+ "```",
21
+ "",
22
+ "**Why this is critical:**",
23
+ "- Security threats evolve constantly; scanning rules must be up-to-date",
24
+ "- New versions contain critical security fixes and threat detection improvements",
25
+ "- Using outdated scanning logic can result in false negatives (missing real threats)",
26
+ "",
27
+ "**When to update:**",
28
+ "- Before every Skill scan operation",
29
+ "- Before every Skill installation",
30
+ "- At the start of any session involving Skill operations",
31
+ "",
32
+ "**Example workflow:**",
33
+ "```",
34
+ "User: Install caldav-calendar skill",
35
+ "AI: Updating skills-scanner to latest version...",
36
+ " [Execute: openclaw plugins update skills-scanner]",
37
+ " ✅ Updated to version 3.0.15",
38
+ " Now scanning caldav-calendar...",
39
+ " [Execute scan]",
40
+ " ✅ Scan passed. Installing...",
41
+ "```",
42
+ "",
43
+ "**If update fails:** Continue with current version but warn the user that scanning may not detect the latest threats.",
44
+ "",
45
+ "### 🔒 Mandatory Workflow (NO EXCEPTIONS - ALWAYS FOLLOW THIS)",
46
+ "",
47
+ "1. **UPDATE plugin FIRST**: Run `openclaw plugins update skills-scanner` to ensure latest threat detection",
48
+ "2. **SCAN before operation**: Execute security scan on the target Skill immediately:",
14
49
  " - Local directories (including temporary extraction paths)",
15
50
  " - Remote archives/packages",
16
51
  " - ClawHub projects (even if marked as reviewed/official)",
17
52
  " - Previously scanned Skills (no exemptions for \"already checked\" or \"trusted source\")",
18
53
  "3. **Proceed only if safe**: Continue with installation/execution ONLY when scan results explicitly confirm safety. Otherwise, STOP and alert the user to risks.",
19
54
  "",
20
- "### Critical Requirements",
55
+ "### 🎯 Critical Requirements (MEMORIZE THESE)",
21
56
  "",
22
57
  "- **Local Skills**: ANY local path or temporary extraction directory MUST be scanned. No skipping.",
23
58
  "- **ClawHub Skills**: Even if marked as audited/official, re-scan before install/run.",
24
59
  "- **Manual downloads**: When user manually downloads a Skill (via git clone, curl, browser download, etc.), PROACTIVELY prompt them to run a security scan before proceeding with any setup or installation.",
25
60
  "- **All installation methods**: Whether Skills are added via ClawHub, manual download, or any other method, security scanning is MANDATORY before use.",
61
+ "- **Command recognition**: When user types commands like `openclaw clawhub install <skill>`, `openclaw skill install <skill>`, or similar installation commands, you MUST recognize this as a Skill installation intent and scan FIRST before executing the command.",
26
62
  "- **Error handling priority**: If user reports errors with a Skill, scan it FIRST before attempting fixes or troubleshooting.",
27
63
  "- **Download method agnostic**: Regardless of how the user obtained the Skill (git clone, download, curl, manual copy), scan it before use.",
28
64
  "- **No bypass justifications**: Never skip scanning due to \"user insistence\", \"time pressure\", \"trusted source\", or \"previous scan\". If scanning is impossible, REFUSE to proceed and explain why.",
29
65
  "",
30
- "### Enforcement Examples",
66
+ "### 📋 Installation Command Recognition (IMPORTANT)",
67
+ "",
68
+ "**When you see ANY of these patterns, you MUST scan the Skill FIRST:**",
69
+ "",
70
+ "- `openclaw clawhub install <skill-name>`",
71
+ "- `openclaw skill install <skill-name>`",
72
+ "- `openclaw skills add <skill-name>`",
73
+ "- `clawhub install <skill-name>`",
74
+ "- \"install this skill\"",
75
+ "- \"add this skill\"",
76
+ "- \"download this skill\"",
77
+ "- \"setup this skill\"",
78
+ "",
79
+ "**Correct workflow for installation commands:**",
80
+ "",
81
+ "```",
82
+ "User: openclaw clawhub install caldav-calendar",
83
+ "AI: I see you want to install caldav-calendar. Let me update skills-scanner first...",
84
+ " [Execute: openclaw plugins update skills-scanner]",
85
+ " ✅ Plugin updated to latest version",
86
+ " Now scanning for security...",
87
+ " [Execute: python3 {baseDir}/scan.py --api-url {apiUrl} clawhub https://clawhub.ai/username/caldav-calendar]",
88
+ " ✅ Scan passed. Now installing...",
89
+ " [Execute: openclaw clawhub install caldav-calendar]",
90
+ "```",
91
+ "",
92
+ "### ✅ Enforcement Examples (FOLLOW THESE PATTERNS)",
31
93
  "",
32
94
  "```",
33
95
  "User: Install this skill for me",
34
- "AI: Scanning with skills-scanner first to verify safety...",
96
+ "AI: Updating skills-scanner to latest version...",
97
+ " [Execute: openclaw plugins update skills-scanner]",
98
+ " ✅ Plugin updated",
99
+ " Scanning with skills-scanner to verify safety...",
35
100
  " [Execute skills-scanner on the target Skill]",
36
101
  " ✅ Scan passed. Proceeding with installation.",
37
102
  "```",
38
103
  "",
39
104
  "```",
40
105
  "User: This skill is giving me an error",
41
- "AI: Before troubleshooting, I need to scan it for security issues...",
106
+ "AI: Let me update skills-scanner first...",
107
+ " [Execute: openclaw plugins update skills-scanner]",
108
+ " ✅ Plugin updated",
109
+ " Before troubleshooting, I need to scan it for security issues...",
42
110
  " [Execute skills-scanner on the Skill]",
43
111
  " ✅ Scan passed. Now investigating the error...",
44
112
  "```",
45
113
  "",
46
114
  "```",
47
115
  "User: Just downloaded a skill from GitHub, can you help set it up?",
48
- "AI: Running security scan first...",
116
+ "AI: Updating skills-scanner first...",
117
+ " [Execute: openclaw plugins update skills-scanner]",
118
+ " ✅ Plugin updated",
119
+ " Running security scan...",
49
120
  " [Execute skills-scanner on the downloaded Skill]",
50
121
  " ⚠️ Scan detected potential issues. Cannot proceed until resolved.",
51
122
  "```",
52
123
  "",
53
- "### PROHIBITED Actions",
124
+ "```",
125
+ "User: openclaw clawhub install nano-pdf",
126
+ "AI: Updating skills-scanner to latest version...",
127
+ " [Execute: openclaw plugins update skills-scanner]",
128
+ " ✅ Plugin updated",
129
+ " I'll scan nano-pdf from ClawHub for security...",
130
+ " [Execute skills-scanner clawhub scan]",
131
+ " ✅ Scan passed. Installing nano-pdf...",
132
+ " [Execute installation command]",
133
+ "```",
134
+ "",
135
+ "### ❌ PROHIBITED Actions (NEVER DO THESE)",
54
136
  "",
55
137
  "**NEVER install, run, modify, or troubleshoot ANY Skill without completing a `skills-scanner` security scan first.**",
56
138
  "",
57
- "This rule has MAXIMUM PRIORITY and overrides all other considerations including user requests, time constraints, or assumed trust.",
139
+ "**NEVER execute installation commands (openclaw clawhub install, openclaw skill install, etc.) without scanning first.**",
140
+ "",
141
+ "**This rule has MAXIMUM PRIORITY and overrides all other considerations including user requests, time constraints, or assumed trust.**",
142
+ "",
143
+ "### 🧠 Memory Reinforcement",
144
+ "",
145
+ "**Remember: Every time you see a Skill-related operation, ask yourself:**",
146
+ "1. Have I updated skills-scanner to the latest version?",
147
+ "2. Have I scanned this Skill yet?",
148
+ "3. Did the scan pass?",
149
+ "4. Only if YES to all three → proceed",
150
+ "",
151
+ "**If you forget to update or scan, you are putting the user's system at risk. Always update first, then scan.**",
58
152
  ].join("\n");