@dcrays/mobook-security-plugin 2.0.12 → 2.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/index.js +83 -9
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -137,34 +137,46 @@ const BANK_CARD_REGEX = /\b[3-6]\d{15,18}\b/g;
|
|
|
137
137
|
|
|
138
138
|
// ---- 脱敏辅助:部分保留 + 星号遮盖 ----
|
|
139
139
|
function maskKeep(str, keepHead, keepTail = 0, minStars = 4) {
|
|
140
|
-
if (str.length <= keepHead + keepTail) return "
|
|
140
|
+
if (str.length <= keepHead + keepTail) return "\\*".repeat(str.length);
|
|
141
141
|
const stars = Math.max(minStars, str.length - keepHead - keepTail);
|
|
142
|
-
return str.slice(0, keepHead) + "
|
|
142
|
+
return str.slice(0, keepHead) + "\\*".repeat(stars) + (keepTail > 0 ? str.slice(-keepTail) : "");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---- 智能识别:检测文本是否已脱敏 ----
|
|
146
|
+
function looksLikeMasked(text) {
|
|
147
|
+
// 只识别"明显的脱敏模式":数字/字母 + 连续4个以上星号 + 数字/字母/@
|
|
148
|
+
// 例如:138****5678、1**********@163.com
|
|
149
|
+
// 不误判 Markdown 格式(---、**粗体**)
|
|
150
|
+
return /[a-zA-Z0-9]\*{4,}[a-zA-Z0-9@]/.test(text);
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
const SENSITIVE_PATTERNS = [
|
|
154
|
+
// 优先级1:邮箱(避免被手机号规则破坏)
|
|
155
|
+
{
|
|
156
|
+
pattern: /\b([a-zA-Z0-9._%+-]+)@((?:qq|163|126|sina|sohu|gmail|outlook|hotmail|yahoo)\.[a-z]{2,})\b/gi,
|
|
157
|
+
replacer: (_m, local, domain) => maskKeep(local, 2, 0) + "@" + domain, // jq*****@gmail.com
|
|
158
|
+
name: "个人邮箱"
|
|
159
|
+
},
|
|
160
|
+
// 优先级2:身份证
|
|
146
161
|
{
|
|
147
162
|
pattern: ID_CARD_REGEX,
|
|
148
163
|
validator: verifyIdCard,
|
|
149
164
|
replacer: (m) => maskKeep(m, 3, 4), // 310***********1234
|
|
150
165
|
name: "身份证号"
|
|
151
166
|
},
|
|
167
|
+
// 优先级3:手机号(排除邮箱上下文)
|
|
152
168
|
{
|
|
153
|
-
pattern: /\b1[3-9]\d{9}\b/g,
|
|
169
|
+
pattern: /\b1[3-9]\d{9}\b(?!@)/g, // 负向前瞻:后面不能是 @
|
|
154
170
|
replacer: (m) => maskKeep(m, 3, 4), // 138****5678
|
|
155
171
|
name: "手机号"
|
|
156
172
|
},
|
|
173
|
+
// 优先级4:银行卡
|
|
157
174
|
{
|
|
158
175
|
pattern: BANK_CARD_REGEX,
|
|
159
176
|
validator: verifyLuhn,
|
|
160
177
|
replacer: (m) => maskKeep(m, 4, 4), // 6222***********1234
|
|
161
178
|
name: "银行卡号"
|
|
162
179
|
},
|
|
163
|
-
{
|
|
164
|
-
pattern: /\b([a-zA-Z0-9._%+-]+)@((?:qq|163|126|sina|sohu|gmail|outlook|hotmail|yahoo)\.[a-z]{2,})\b/gi,
|
|
165
|
-
replacer: (_m, local, domain) => maskKeep(local, 1, 0) + "@" + domain, // j*****@gmail.com
|
|
166
|
-
name: "个人邮箱"
|
|
167
|
-
},
|
|
168
180
|
{
|
|
169
181
|
pattern: /\b(sk-[a-zA-Z0-9-]{20,})\b/g,
|
|
170
182
|
replacer: (m) => maskKeep(m, 5, 0), // sk-pr**************
|
|
@@ -198,6 +210,12 @@ const SENSITIVE_PATTERNS = [
|
|
|
198
210
|
];
|
|
199
211
|
|
|
200
212
|
function sanitizeContent(content) {
|
|
213
|
+
// 智能识别:如果整体内容看起来已脱敏,跳过处理
|
|
214
|
+
if (looksLikeMasked(content)) {
|
|
215
|
+
log(`SKIP: 检测到已脱敏文本,跳过二次脱敏 | 长度=${content.length}`);
|
|
216
|
+
return { sanitized: content, matches: [] };
|
|
217
|
+
}
|
|
218
|
+
|
|
201
219
|
let sanitized = content;
|
|
202
220
|
const matches = [];
|
|
203
221
|
|
|
@@ -209,6 +227,9 @@ function sanitizeContent(content) {
|
|
|
209
227
|
let hasMatch = false;
|
|
210
228
|
sanitized = sanitized.replace(rule.pattern, (...args) => {
|
|
211
229
|
const match = args[0];
|
|
230
|
+
// 跳过已脱敏的匹配
|
|
231
|
+
if (looksLikeMasked(match)) return match;
|
|
232
|
+
|
|
212
233
|
if (rule.validator(match)) {
|
|
213
234
|
hasMatch = true;
|
|
214
235
|
return rule.replacer(...args);
|
|
@@ -219,7 +240,13 @@ function sanitizeContent(content) {
|
|
|
219
240
|
} else if (rule.replacer) {
|
|
220
241
|
// 使用 replacer 函数
|
|
221
242
|
rule.pattern.lastIndex = 0;
|
|
222
|
-
const replaced = sanitized.replace(rule.pattern, (...args) =>
|
|
243
|
+
const replaced = sanitized.replace(rule.pattern, (...args) => {
|
|
244
|
+
const match = args[0];
|
|
245
|
+
// 跳过已脱敏的匹配
|
|
246
|
+
if (looksLikeMasked(match)) return match;
|
|
247
|
+
|
|
248
|
+
return rule.replacer(...args);
|
|
249
|
+
});
|
|
223
250
|
if (replaced !== sanitized) {
|
|
224
251
|
matches.push(rule.name);
|
|
225
252
|
sanitized = replaced;
|
|
@@ -722,6 +749,53 @@ export default function register(api) {
|
|
|
722
749
|
}
|
|
723
750
|
}
|
|
724
751
|
|
|
752
|
+
// ============================================================
|
|
753
|
+
// 禁止 OpenClaw 自动更新
|
|
754
|
+
// ============================================================
|
|
755
|
+
|
|
756
|
+
// 路径1:gateway 工具 update.run
|
|
757
|
+
if (toolName === "gateway" && params?.action === "update.run") {
|
|
758
|
+
alert(`BLOCKED: 尝试通过 gateway 更新 OpenClaw`);
|
|
759
|
+
return {
|
|
760
|
+
block: true,
|
|
761
|
+
blockReason: "🛡️ 安全插件拦截:禁止自动更新 OpenClaw,需人工确认后手动执行"
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// 路径2:gateway config 修改自动更新相关配置
|
|
766
|
+
if (toolName === "gateway" && ["config.patch", "config.apply"].includes(params?.action)) {
|
|
767
|
+
const raw = params?.raw || JSON.stringify(params);
|
|
768
|
+
if (/auto.?update|"updates?"/i.test(raw)) {
|
|
769
|
+
alert(`BLOCKED: 尝试通过配置修改 OpenClaw 自动更新设置 | action=${params.action}`);
|
|
770
|
+
return {
|
|
771
|
+
block: true,
|
|
772
|
+
blockReason: "🛡️ 安全插件拦截:禁止修改 OpenClaw 自动更新配置,需人工确认"
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// 路径3:exec 命令安装/更新 OpenClaw
|
|
778
|
+
if (toolName === "exec") {
|
|
779
|
+
const cmd = params.command || "";
|
|
780
|
+
const openclawUpdatePatterns = [
|
|
781
|
+
/npm\s+(i|install|update|up|upgrade)\s+.*\bopenclaw\b/i,
|
|
782
|
+
/yarn\s+(global\s+)?add\s+.*\bopenclaw\b/i,
|
|
783
|
+
/yarn\s+(global\s+)?upgrade\s+.*\bopenclaw\b/i,
|
|
784
|
+
/pnpm\s+(add|update|install|upgrade)\s+.*\bopenclaw\b/i,
|
|
785
|
+
/npx\s+.*\bopenclaw\b.*update/i,
|
|
786
|
+
/brew\s+(upgrade|reinstall)\s+.*\bopenclaw\b/i,
|
|
787
|
+
/\bopenclaw\s+update\b/i,
|
|
788
|
+
/\bopenclaw\s+gateway\s+update\b/i,
|
|
789
|
+
];
|
|
790
|
+
if (openclawUpdatePatterns.some(p => p.test(cmd))) {
|
|
791
|
+
alert(`BLOCKED: 尝试通过命令行更新 OpenClaw | cmd: ${cmd.slice(0, 200)}`);
|
|
792
|
+
return {
|
|
793
|
+
block: true,
|
|
794
|
+
blockReason: "🛡️ 安全插件拦截:禁止通过包管理器更新 OpenClaw,需人工确认后手动执行"
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
725
799
|
return {};
|
|
726
800
|
});
|
|
727
801
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "mobook-security-plugin",
|
|
3
3
|
"name": "MoBook 安全防护插件",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.15",
|
|
5
5
|
"description": "MoBook 企业级安全插件 - 危险命令分级拦截、敏感信息全渠道脱敏(身份证GB11643/银行卡Luhn校验)、LLM响应拦截、操作审计",
|
|
6
6
|
"entry": "./index.js",
|
|
7
7
|
"type": "plugin",
|
|
8
8
|
"configSchema": {}
|
|
9
|
-
}
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcrays/mobook-security-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MoBook 企业级安全插件 - 危险命令分级拦截、敏感信息全渠道脱敏(身份证GB11643/银行卡Luhn校验)、LLM响应拦截、操作审计",
|
|
6
6
|
"main": "index.js",
|
|
@@ -20,4 +20,4 @@
|
|
|
20
20
|
"./index.js"
|
|
21
21
|
]
|
|
22
22
|
}
|
|
23
|
-
}
|
|
23
|
+
}
|