feat: 升级至 v0.3.16 — 工单修复 51 项 + 审查修复 30 项(安全加固/适配器/工具系统/数据层/前端)
This commit is contained in:
@@ -157,13 +157,32 @@ export class PolicyEngine {
|
||||
argsStr = String(args);
|
||||
}
|
||||
|
||||
// #14 修复: 深度递归扫描所有字符串字段值,防止 Unicode 转义/嵌套对象/字符串拼接绕过
|
||||
// 原 JSON.stringify 后整体正则匹配可被 \u0029 编码、嵌套对象伪装、正则注入等方式绕过
|
||||
// 现对每个字符串字段值单独匹配,同时保留整体 argsStr 兜底匹配
|
||||
const stringValues = this.deepScanStrings(args);
|
||||
|
||||
// v0.2.0: allowedPatterns 白名单校验 — 若定义了白名单,参数必须匹配其中之一
|
||||
// #14: 字段级匹配 + 整体兜底,任一匹配即通过
|
||||
if (policy.allowedPatterns && policy.allowedPatterns.length > 0) {
|
||||
let matchedAllowed = false;
|
||||
for (const pattern of policy.allowedPatterns) {
|
||||
if (pattern.test(argsStr)) {
|
||||
matchedAllowed = true;
|
||||
break;
|
||||
// 先做字段级匹配
|
||||
for (const val of stringValues) {
|
||||
for (const pattern of policy.allowedPatterns) {
|
||||
if (pattern.test(val)) {
|
||||
matchedAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchedAllowed) break;
|
||||
}
|
||||
// 兜底:整体 argsStr 匹配
|
||||
if (!matchedAllowed) {
|
||||
for (const pattern of policy.allowedPatterns) {
|
||||
if (pattern.test(argsStr)) {
|
||||
matchedAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matchedAllowed) {
|
||||
@@ -176,7 +195,22 @@ export class PolicyEngine {
|
||||
}
|
||||
}
|
||||
|
||||
// #14 修复: deniedPatterns 字段级深度扫描 — 对每个字符串值单独匹配
|
||||
// 防止危险内容隐藏在嵌套对象或 Unicode 编码中绕过整体 stringify 匹配
|
||||
if (policy.deniedPatterns) {
|
||||
for (const val of stringValues) {
|
||||
for (const pattern of policy.deniedPatterns) {
|
||||
if (pattern.test(val)) {
|
||||
return {
|
||||
authorized: false,
|
||||
reason: 'Command blocked by security policy',
|
||||
level: policy.requiredLevel,
|
||||
requiresConfirmation: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
// 兜底:整体 argsStr 匹配(保留原有行为,捕获跨字段拼接的危险模式)
|
||||
for (const pattern of policy.deniedPatterns) {
|
||||
if (pattern.test(argsStr)) {
|
||||
return {
|
||||
@@ -264,4 +298,37 @@ export class PolicyEngine {
|
||||
|
||||
// v0.3.0 修复: cleanupFrequencyRecords 已删除 — checkFrequency 和 recordCall 已做内联清理,
|
||||
// 该方法属于死代码,删除以减少维护负担
|
||||
|
||||
/**
|
||||
* #14 修复: 深度递归扫描对象,提取所有字符串字段值
|
||||
*
|
||||
* 替代 JSON.stringify 后整体正则匹配的方式,防止:
|
||||
* - Unicode 转义绕过(\u0029 等编码)
|
||||
* - 嵌套对象伪装({a: {b: 'dangerous'}})
|
||||
* - 字符串拼接绕过('rm ' + '-rf /' 在 stringify 后是合法字符串)
|
||||
* - 正则注入(输入中包含正则元字符破坏匹配逻辑)
|
||||
*
|
||||
* @param obj 待扫描的对象
|
||||
* @returns 所有字符串字段值的数组
|
||||
*/
|
||||
// 审查修复: 添加 visited Set 参数防止循环引用导致无限递归栈溢出
|
||||
private deepScanStrings(obj: unknown, visited: Set<unknown> = new Set()): string[] {
|
||||
const results: string[] = [];
|
||||
if (typeof obj === 'string') {
|
||||
results.push(obj);
|
||||
} else if (Array.isArray(obj)) {
|
||||
if (visited.has(obj)) return results;
|
||||
visited.add(obj);
|
||||
for (const item of obj) {
|
||||
results.push(...this.deepScanStrings(item, visited));
|
||||
}
|
||||
} else if (obj && typeof obj === 'object') {
|
||||
if (visited.has(obj)) return results;
|
||||
visited.add(obj);
|
||||
for (const v of Object.values(obj)) {
|
||||
results.push(...this.deepScanStrings(v, visited));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ export class SandboxManager {
|
||||
*/
|
||||
scanCode(code: string): { safe: boolean; reason?: string } {
|
||||
// C-5 修复: 补齐 28 个模式 + 加强 base64/$() 检测
|
||||
// #13 修复: 补全 require('fs')、动态 import、process.binding、new Function、Reflect.get 等绕过模式
|
||||
// @see project_memory.md — sandbox scanCode must include 28 patterns with 'i' flag
|
||||
// and base64/$() detection to prevent encoding bypass
|
||||
const dangerousPatterns = [
|
||||
@@ -102,6 +103,22 @@ export class SandboxManager {
|
||||
/require\s*\(\s*['"]child_process['"]\s*\)/i,
|
||||
/import\s+.*from\s+['"]fs['"]/i,
|
||||
/import\s+.*from\s+['"]child_process['"]/i,
|
||||
// #13 新增: require('fs') 同步 require 形式(1)
|
||||
/require\s*\(\s*['"]fs['"]\s*\)/i,
|
||||
// #13 新增: 动态 import('fs') / import('child_process')(2)
|
||||
/\bimport\s*\(\s*['"](?:fs|child_process|os|net|http|https|crypto|dns|cluster)['"]\s*\)/i,
|
||||
// 审查修复 (M8): 兜底检测所有动态 import 调用 — 原模式只匹配字符串字面量,
|
||||
// 动态 import 用变量 import(m) 可绕过。沙箱中应禁止所有动态 import。
|
||||
// 注意:此模式较宽泛,会拦截 import(safeModule),但沙箱 fail-closed 策略可接受。
|
||||
/\bimport\s*\(/i,
|
||||
// #13 新增: process.binding('fs') 底层绑定(1)
|
||||
/\bprocess\.binding\s*\(/i,
|
||||
// #13 新增: new Function() 函数构造(1)
|
||||
/\bnew\s+Function\s*\(/i,
|
||||
// #13 新增: Reflect.get 字符串拼接绕过(1)
|
||||
/\bReflect\.get\s*\(/i,
|
||||
// #13 新增: Function('return process') 等函数构造执行(1)
|
||||
/\bFunction\s*\(\s*['"]return\s+(?:process|require|global|globalThis)['"]\s*\)/i,
|
||||
// 代码执行(3)
|
||||
/\beval\s*\(/i,
|
||||
/process\.exit/i,
|
||||
|
||||
Reference in New Issue
Block a user