mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(guard): align tool guard rule names with runtime @Tool method names and add audit config
This commit is contained in:
parent
e14759638e
commit
bcf37d95fe
@ -42,7 +42,7 @@ public class ShellExecuteTool {
|
||||
|
||||
@Tool(description = "在本地服务器上执行 Shell 命令。用于执行系统命令、查看文件、运行脚本等操作。"
|
||||
+ "Windows 下使用 cmd.exe,Linux/macOS 下使用 /bin/sh。"
|
||||
+ "注意:每次执行都需要用户审批确认。返回包含 exitCode、stdout、stderr、timedOut 的结构化结果。")
|
||||
+ "危险操作(如 rm -rf、格式化磁盘等)会触发安全审批。返回包含 exitCode、stdout、stderr、timedOut 的结构化结果。")
|
||||
public String execute_shell_command(
|
||||
@ToolParam(description = "要执行的 Shell 命令") String command,
|
||||
@ToolParam(description = "超时秒数,默认 60 秒", required = false) Integer timeoutSeconds) {
|
||||
|
||||
@ -24,6 +24,7 @@ public class ToolGuardSchemaMigration implements ApplicationRunner {
|
||||
createGuardRuleTable();
|
||||
createGuardConfigTable();
|
||||
createAuditLogTable();
|
||||
migrateGuardConfigAuditColumns();
|
||||
}
|
||||
|
||||
private void createGuardRuleTable() {
|
||||
@ -108,6 +109,16 @@ public class ToolGuardSchemaMigration implements ApplicationRunner {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 mate_tool_guard_config 补充审计配置列(向已有表兼容迁移)
|
||||
*/
|
||||
private void migrateGuardConfigAuditColumns() {
|
||||
safeExecute("ALTER TABLE mate_tool_guard_config ADD COLUMN audit_enabled BOOLEAN NOT NULL DEFAULT TRUE");
|
||||
safeExecute("ALTER TABLE mate_tool_guard_config ADD COLUMN audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO'");
|
||||
safeExecute("ALTER TABLE mate_tool_guard_config ADD COLUMN audit_retention_days INT NOT NULL DEFAULT 90");
|
||||
log.info("[ToolGuardSchemaMigration] audit columns migration done");
|
||||
}
|
||||
|
||||
private void safeExecute(String sql) {
|
||||
try {
|
||||
jdbcTemplate.execute(sql);
|
||||
|
||||
@ -186,6 +186,13 @@ public class ShellCommandGuardian implements ToolGuardGuardian {
|
||||
"此命令可能被用于远程控制,请勿执行"));
|
||||
|
||||
// === 高风险(HIGH)===
|
||||
list.add(new ShellRule("SHELL_RM",
|
||||
"(^|[;&|]|\\s)rm\\s",
|
||||
GuardSeverity.HIGH, GuardCategory.COMMAND_INJECTION,
|
||||
"rm 删除命令",
|
||||
"检测到 rm 删除操作,可能导致文件永久丢失",
|
||||
"请确认要删除的文件列表,考虑使用 trash 替代 rm"));
|
||||
|
||||
list.add(new ShellRule("SHELL_RM_RF",
|
||||
"rm\\s+-(rf|fr)",
|
||||
GuardSeverity.HIGH, GuardCategory.COMMAND_INJECTION,
|
||||
|
||||
@ -22,6 +22,13 @@ public class ToolGuardConfigEntity {
|
||||
private Boolean fileGuardEnabled;
|
||||
private String sensitivePathsJson;
|
||||
|
||||
/** 审计日志总开关 */
|
||||
private Boolean auditEnabled;
|
||||
/** 最低记录等级(INFO/LOW/MEDIUM/HIGH/CRITICAL) */
|
||||
private String auditMinSeverity;
|
||||
/** 审计日志保留天数(0=永不清理) */
|
||||
private Integer auditRetentionDays;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
@ -8,16 +8,20 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import vip.mate.tool.guard.model.*;
|
||||
import vip.mate.tool.guard.repository.ToolGuardAuditLogMapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工具安全审计服务
|
||||
* <p>
|
||||
* 支持审计开关、最低记录等级过滤、过期日志自动清理。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -26,13 +30,33 @@ public class ToolGuardAuditService {
|
||||
|
||||
private final ToolGuardAuditLogMapper auditMapper;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ToolGuardConfigService configService;
|
||||
|
||||
/**
|
||||
* 异步记录审计日志
|
||||
* <p>
|
||||
* 受审计开关和最低等级过滤控制:
|
||||
* <ul>
|
||||
* <li>auditEnabled=false → 不记录</li>
|
||||
* <li>maxSeverity 低于 auditMinSeverity → 不记录</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Async
|
||||
public void record(ToolInvocationContext context, GuardEvaluation evaluation, String pendingId) {
|
||||
try {
|
||||
// 审计开关检查
|
||||
if (!configService.isAuditEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 等级过滤:无 finding 时 maxSeverity 为 null,视为 INFO 级
|
||||
GuardSeverity minSeverity = configService.getAuditMinSeverity();
|
||||
GuardSeverity actualSeverity = evaluation.maxSeverity() != null
|
||||
? evaluation.maxSeverity() : GuardSeverity.INFO;
|
||||
if (!actualSeverity.isAtLeast(minSeverity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ToolGuardAuditLogEntity entity = new ToolGuardAuditLogEntity();
|
||||
entity.setConversationId(context.conversationId());
|
||||
entity.setAgentId(context.agentId());
|
||||
@ -99,6 +123,27 @@ public class ToolGuardAuditService {
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时清理过期审计日志(每天凌晨 3 点)
|
||||
*/
|
||||
@Scheduled(cron = "0 0 3 * * ?")
|
||||
public void cleanExpiredAuditLogs() {
|
||||
try {
|
||||
int days = configService.getAuditRetentionDays();
|
||||
if (days <= 0) return;
|
||||
|
||||
LocalDateTime cutoff = LocalDateTime.now().minusDays(days);
|
||||
int deleted = auditMapper.delete(
|
||||
new LambdaQueryWrapper<ToolGuardAuditLogEntity>()
|
||||
.lt(ToolGuardAuditLogEntity::getCreateTime, cutoff));
|
||||
if (deleted > 0) {
|
||||
log.info("[ToolGuardAudit] Cleaned {} expired records (older than {} days)", deleted, days);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("[ToolGuardAudit] Failed to clean expired logs: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String serializeFindings(GuardEvaluation evaluation) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(evaluation.findingsToMapList());
|
||||
|
||||
@ -11,6 +11,8 @@ import org.springframework.stereotype.Service;
|
||||
import vip.mate.tool.guard.model.ToolGuardConfigEntity;
|
||||
import vip.mate.tool.guard.repository.ToolGuardConfigMapper;
|
||||
|
||||
import vip.mate.tool.guard.model.GuardSeverity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@ -51,6 +53,9 @@ public class ToolGuardConfigService {
|
||||
if (config.getDeniedToolsJson() != null) existing.setDeniedToolsJson(config.getDeniedToolsJson());
|
||||
if (config.getFileGuardEnabled() != null) existing.setFileGuardEnabled(config.getFileGuardEnabled());
|
||||
if (config.getSensitivePathsJson() != null) existing.setSensitivePathsJson(config.getSensitivePathsJson());
|
||||
if (config.getAuditEnabled() != null) existing.setAuditEnabled(config.getAuditEnabled());
|
||||
if (config.getAuditMinSeverity() != null) existing.setAuditMinSeverity(config.getAuditMinSeverity());
|
||||
if (config.getAuditRetentionDays() != null) existing.setAuditRetentionDays(config.getAuditRetentionDays());
|
||||
configMapper.updateById(existing);
|
||||
// 通知 AgentService 刷新缓存(denied 工具列表变更需要重建 agent 的工具集)
|
||||
eventPublisher.publishEvent(new ToolGuardConfigChangedEvent(this));
|
||||
@ -90,6 +95,27 @@ public class ToolGuardConfigService {
|
||||
return parseJsonList(json);
|
||||
}
|
||||
|
||||
// ==================== 审计配置 ====================
|
||||
|
||||
public boolean isAuditEnabled() {
|
||||
return Boolean.TRUE.equals(getConfig().getAuditEnabled());
|
||||
}
|
||||
|
||||
public GuardSeverity getAuditMinSeverity() {
|
||||
String s = getConfig().getAuditMinSeverity();
|
||||
if (s == null || s.isBlank()) return GuardSeverity.INFO;
|
||||
try {
|
||||
return GuardSeverity.valueOf(s);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return GuardSeverity.INFO;
|
||||
}
|
||||
}
|
||||
|
||||
public int getAuditRetentionDays() {
|
||||
Integer days = getConfig().getAuditRetentionDays();
|
||||
return days != null ? days : 90;
|
||||
}
|
||||
|
||||
// ==================== 内部方法 ====================
|
||||
|
||||
private ToolGuardConfigEntity createDefaultConfig() {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package vip.mate.tool.guard.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
@ -9,17 +10,23 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import vip.mate.tool.guard.model.GuardCategory;
|
||||
import vip.mate.tool.guard.model.GuardSeverity;
|
||||
import vip.mate.tool.guard.model.ToolGuardConfigEntity;
|
||||
import vip.mate.tool.guard.model.ToolGuardRuleEntity;
|
||||
import vip.mate.tool.guard.repository.ToolGuardConfigMapper;
|
||||
import vip.mate.tool.guard.repository.ToolGuardRuleMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 规则种子服务
|
||||
* <p>
|
||||
* 首次启动时将内置规则写入 DB。
|
||||
* 已存在则跳过(通过 rule_id UNIQUE 约束)。
|
||||
* 启动时完成三项工作:
|
||||
* <ol>
|
||||
* <li>迁移旧版工具名(类名 → @Tool 方法名)+ 清理旧 legacy 规则</li>
|
||||
* <li>按 rule_id 逐条 upsert 内置规则(不存在则插入,已存在则同步更新)</li>
|
||||
* <li>迁移 guardedToolsJson 中的旧工具名</li>
|
||||
* </ol>
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ -28,38 +35,176 @@ import java.util.List;
|
||||
public class ToolGuardRuleSeedService implements ApplicationRunner {
|
||||
|
||||
private final ToolGuardRuleMapper ruleMapper;
|
||||
private final ToolGuardConfigMapper configMapper;
|
||||
|
||||
/** 旧类名 → 新 @Tool 方法名 */
|
||||
private static final Map<String, String> TOOL_NAME_RENAMES = Map.of(
|
||||
"ShellExecuteTool", "execute_shell_command",
|
||||
"WriteFileTool", "write_file",
|
||||
"EditFileTool", "edit_file"
|
||||
);
|
||||
|
||||
/** 旧 SQL 种子中的 legacy rule_id,已被 Java 种子的新规则完全覆盖 */
|
||||
private static final Set<String> LEGACY_RULE_IDS = Set.of(
|
||||
"write_file_any",
|
||||
"edit_file_any",
|
||||
"shell_rm_approval",
|
||||
"shell_rm_rf_block",
|
||||
"shell_write_system_file",
|
||||
"shell_chmod_777"
|
||||
);
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
migrateOldData();
|
||||
seedBuiltinRules();
|
||||
}
|
||||
|
||||
// ==================== 旧数据迁移 ====================
|
||||
|
||||
/**
|
||||
* 将 DB 中旧版数据统一迁移:
|
||||
* <ul>
|
||||
* <li>rule 表旧工具名(类名 → @Tool 方法名)</li>
|
||||
* <li>清理 legacy rule_id(旧 SQL 种子残留)</li>
|
||||
* <li>config 表 guardedToolsJson 中的旧工具名</li>
|
||||
* </ul>
|
||||
*/
|
||||
private void migrateOldData() {
|
||||
try {
|
||||
// 1. 迁移 rule 表旧工具名
|
||||
for (var entry : TOOL_NAME_RENAMES.entrySet()) {
|
||||
int updated = ruleMapper.update(null,
|
||||
new LambdaUpdateWrapper<ToolGuardRuleEntity>()
|
||||
.eq(ToolGuardRuleEntity::getToolName, entry.getKey())
|
||||
.set(ToolGuardRuleEntity::getToolName, entry.getValue()));
|
||||
if (updated > 0) {
|
||||
log.info("[RuleSeed] Migrated {} rules: {} -> {}", updated, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 清理旧 SQL 种子残留的 legacy 规则
|
||||
cleanupLegacyRules();
|
||||
|
||||
// 3. 迁移 config 表 guardedToolsJson
|
||||
migrateGuardedToolsJson();
|
||||
} catch (Exception e) {
|
||||
log.warn("[RuleSeed] Migration failed (table may not exist): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除旧 SQL 种子中残留的 legacy builtin 规则。
|
||||
* 这些规则的 rule_id 与新 Java 种子不重叠,增量升级后会形成冗余重复。
|
||||
*/
|
||||
private void cleanupLegacyRules() {
|
||||
for (String legacyId : LEGACY_RULE_IDS) {
|
||||
int deleted = ruleMapper.delete(
|
||||
new LambdaQueryWrapper<ToolGuardRuleEntity>()
|
||||
.eq(ToolGuardRuleEntity::getRuleId, legacyId)
|
||||
.eq(ToolGuardRuleEntity::getBuiltin, true));
|
||||
if (deleted > 0) {
|
||||
log.info("[RuleSeed] Removed legacy rule: {}", legacyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateGuardedToolsJson() {
|
||||
try {
|
||||
List<ToolGuardConfigEntity> configs = configMapper.selectList(null);
|
||||
for (ToolGuardConfigEntity config : configs) {
|
||||
String json = config.getGuardedToolsJson();
|
||||
if (json == null || json.isBlank()) continue;
|
||||
|
||||
String updated = json;
|
||||
for (var entry : TOOL_NAME_RENAMES.entrySet()) {
|
||||
updated = updated.replace("\"" + entry.getKey() + "\"", "\"" + entry.getValue() + "\"");
|
||||
}
|
||||
if (!updated.equals(json)) {
|
||||
config.setGuardedToolsJson(updated);
|
||||
configMapper.updateById(config);
|
||||
log.info("[RuleSeed] Migrated guardedToolsJson: {} -> {}", json, updated);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("[RuleSeed] guardedToolsJson migration skipped: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 规则种子 ====================
|
||||
|
||||
/**
|
||||
* 按 rule_id 逐条 upsert 内置规则:
|
||||
* <ul>
|
||||
* <li>不存在 → 插入</li>
|
||||
* <li>已存在 → 同步更新 pattern / severity / decision / priority / toolName 等字段</li>
|
||||
* </ul>
|
||||
* 这样后续版本修正了 regex 或 severity,已有部署也能在重启时自动拿到更新。
|
||||
*/
|
||||
void seedBuiltinRules() {
|
||||
try {
|
||||
Long existingCount = ruleMapper.selectCount(
|
||||
// 加载已有 builtin 规则(按 rule_id 索引)
|
||||
List<ToolGuardRuleEntity> existingList = ruleMapper.selectList(
|
||||
new LambdaQueryWrapper<ToolGuardRuleEntity>()
|
||||
.eq(ToolGuardRuleEntity::getBuiltin, true));
|
||||
if (existingCount > 0) {
|
||||
log.info("[RuleSeed] {} builtin rules already exist, skipping seed", existingCount);
|
||||
return;
|
||||
}
|
||||
Map<String, ToolGuardRuleEntity> existingMap = existingList.stream()
|
||||
.collect(Collectors.toMap(ToolGuardRuleEntity::getRuleId, e -> e, (a, b) -> a));
|
||||
|
||||
List<ToolGuardRuleEntity> rules = buildBuiltinRules();
|
||||
int inserted = 0;
|
||||
int updated = 0;
|
||||
int unchanged = 0;
|
||||
|
||||
for (ToolGuardRuleEntity rule : rules) {
|
||||
try {
|
||||
ruleMapper.insert(rule);
|
||||
inserted++;
|
||||
} catch (Exception e) {
|
||||
log.debug("[RuleSeed] Rule {} already exists", rule.getRuleId());
|
||||
ToolGuardRuleEntity existing = existingMap.get(rule.getRuleId());
|
||||
if (existing == null) {
|
||||
// 新规则 → 插入
|
||||
try {
|
||||
ruleMapper.insert(rule);
|
||||
inserted++;
|
||||
} catch (Exception e) {
|
||||
log.debug("[RuleSeed] Rule {} insert failed: {}", rule.getRuleId(), e.getMessage());
|
||||
}
|
||||
} else if (needsUpdate(existing, rule)) {
|
||||
// 已存在但内容有变化 → 更新
|
||||
ruleMapper.update(null,
|
||||
new LambdaUpdateWrapper<ToolGuardRuleEntity>()
|
||||
.eq(ToolGuardRuleEntity::getRuleId, rule.getRuleId())
|
||||
.set(ToolGuardRuleEntity::getName, rule.getName())
|
||||
.set(ToolGuardRuleEntity::getDescription, rule.getDescription())
|
||||
.set(ToolGuardRuleEntity::getPattern, rule.getPattern())
|
||||
.set(ToolGuardRuleEntity::getSeverity, rule.getSeverity())
|
||||
.set(ToolGuardRuleEntity::getCategory, rule.getCategory())
|
||||
.set(ToolGuardRuleEntity::getDecision, rule.getDecision())
|
||||
.set(ToolGuardRuleEntity::getToolName, rule.getToolName())
|
||||
.set(ToolGuardRuleEntity::getRemediation, rule.getRemediation())
|
||||
.set(ToolGuardRuleEntity::getPriority, rule.getPriority()));
|
||||
updated++;
|
||||
} else {
|
||||
unchanged++;
|
||||
}
|
||||
}
|
||||
log.info("[RuleSeed] Seeded {} builtin rules", inserted);
|
||||
log.info("[RuleSeed] Builtin rules: {} inserted, {} updated, {} unchanged",
|
||||
inserted, updated, unchanged);
|
||||
} catch (Exception e) {
|
||||
log.warn("[RuleSeed] Failed to seed rules (table may not exist): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断已有 builtin 规则是否需要更新(任一核心字段有变化即需要)
|
||||
*/
|
||||
private boolean needsUpdate(ToolGuardRuleEntity existing, ToolGuardRuleEntity expected) {
|
||||
return !Objects.equals(existing.getPattern(), expected.getPattern())
|
||||
|| !Objects.equals(existing.getSeverity(), expected.getSeverity())
|
||||
|| !Objects.equals(existing.getCategory(), expected.getCategory())
|
||||
|| !Objects.equals(existing.getDecision(), expected.getDecision())
|
||||
|| !Objects.equals(existing.getToolName(), expected.getToolName())
|
||||
|| !Objects.equals(existing.getPriority(), expected.getPriority())
|
||||
|| !Objects.equals(existing.getName(), expected.getName())
|
||||
|| !Objects.equals(existing.getRemediation(), expected.getRemediation());
|
||||
}
|
||||
|
||||
private List<ToolGuardRuleEntity> buildBuiltinRules() {
|
||||
List<ToolGuardRuleEntity> rules = new ArrayList<>();
|
||||
|
||||
@ -97,6 +242,10 @@ public class ToolGuardRuleSeedService implements ApplicationRunner {
|
||||
"execute_shell_command", "此命令可能被用于远程控制", 200));
|
||||
|
||||
// === HIGH Shell Rules ===
|
||||
rules.add(rule("SHELL_RM", "rm 删除命令", "(^|[;&|]|\\s)rm\\s",
|
||||
GuardSeverity.HIGH, GuardCategory.COMMAND_INJECTION, "NEEDS_APPROVAL",
|
||||
"execute_shell_command", "请确认要删除的文件列表,考虑使用 trash 替代 rm", 150));
|
||||
|
||||
rules.add(rule("SHELL_RM_RF", "递归强制删除", "rm\\s+-(rf|fr)",
|
||||
GuardSeverity.HIGH, GuardCategory.COMMAND_INJECTION, "NEEDS_APPROVAL",
|
||||
"execute_shell_command", "使用 rm -ri 或指定具体文件", 150));
|
||||
|
||||
@ -1720,149 +1720,26 @@ VALUES (
|
||||
|
||||
-- ==================== ToolGuard Default Config & Rule Seed Data ====================
|
||||
|
||||
-- Global security config (single row)
|
||||
MERGE INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, create_time, update_time)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
-- Global security config (single row, insert only if not exists)
|
||||
-- Note: tool names in guarded_tools_json must match @Tool method names (execute_shell_command / write_file / edit_file)
|
||||
-- Use SELECT + INSERT to avoid overwriting user-modified config on restart (H2 MERGE would overwrite all columns)
|
||||
INSERT INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, audit_enabled, audit_min_severity, audit_retention_days,
|
||||
create_time, update_time)
|
||||
SELECT
|
||||
1000000001,
|
||||
TRUE,
|
||||
'all',
|
||||
'["WriteFileTool","EditFileTool","ShellExecuteTool"]',
|
||||
'["write_file","edit_file","execute_shell_command"]',
|
||||
'[]',
|
||||
TRUE,
|
||||
'["/etc","/usr","/bin","/sbin","/boot","/sys","/proc","/dev"]',
|
||||
TRUE, 'INFO', 90,
|
||||
NOW(), NOW()
|
||||
);
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM mate_tool_guard_config WHERE id = 1000000001);
|
||||
|
||||
-- Security rule: WriteFileTool — any path write requires approval (HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300001,
|
||||
'write_file_any',
|
||||
'File write requires approval',
|
||||
'Any file write operation requires user confirmation to prevent accidental overwrite of important files',
|
||||
'WriteFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'Please confirm write path and content are correct before allowing execution',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- Security rule: EditFileTool — any file edit requires approval (HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300002,
|
||||
'edit_file_any',
|
||||
'File edit requires approval',
|
||||
'Any file content replacement operation requires user confirmation',
|
||||
'EditFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'Please confirm edit path and replacement content are correct before allowing execution',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- Security rule: ShellExecuteTool — delete commands require approval (HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300003,
|
||||
'shell_rm_approval',
|
||||
'rm command requires approval',
|
||||
'rm / rmdir commands may cause permanent file loss, requires user confirmation',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(^|[;&|]|\s)rm\s',
|
||||
NULL,
|
||||
'Consider using trash command instead of rm, or confirm file list before allowing execution',
|
||||
TRUE, TRUE, 20,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- Security rule: ShellExecuteTool — forced recursive delete blocked (CRITICAL)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300004,
|
||||
'shell_rm_rf_block',
|
||||
'rm -rf blocked',
|
||||
'rm -rf forced recursive delete is extremely dangerous, blocked directly',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'CRITICAL',
|
||||
'BLOCK',
|
||||
'(?i)rm\s+(-[a-z]*r[a-z]*f[a-z]*|-[a-z]*f[a-z]*r[a-z]*)\s+(/|~|\$HOME|\*|\.\s*$)',
|
||||
NULL,
|
||||
'Absolutely forbidden to execute rm -rf on root directory, Home directory, or wildcards',
|
||||
TRUE, TRUE, 5,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- Security rule: ShellExecuteTool — writing system config files requires approval (HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300005,
|
||||
'shell_write_system_file',
|
||||
'System file write requires approval',
|
||||
'Writing to system directories like /etc or /usr via Shell requires user confirmation',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(>\s*|tee\s+|cp\s+.*\s+)(/etc/|/usr/|/bin/|/sbin/|/boot/)',
|
||||
NULL,
|
||||
'Please confirm the system file and content to modify before allowing execution',
|
||||
TRUE, TRUE, 15,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- Security rule: ShellExecuteTool — chmod 777 requires approval (MEDIUM)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300006,
|
||||
'shell_chmod_777',
|
||||
'chmod 777 requires approval',
|
||||
'chmod 777 grants full permissions to all users, security risk',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'MEDIUM',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)chmod\s+(777|a\+rwx|o\+rwx)',
|
||||
NULL,
|
||||
'Please confirm if full permissions for all users are truly needed',
|
||||
TRUE, TRUE, 30,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
-- Security rules are managed by ToolGuardRuleSeedService (Java) as single source of truth.
|
||||
-- Removed 6 legacy SQL rules (rule_id: write_file_any, edit_file_any, shell_rm_approval,
|
||||
-- shell_rm_rf_block, shell_write_system_file, shell_chmod_777).
|
||||
-- Their superset is registered in ToolGuardRuleSeedService.buildBuiltinRules() with correct tool names.
|
||||
|
||||
@ -1719,149 +1719,22 @@ ON DUPLICATE KEY UPDATE agent_id=VALUES(agent_id), filename=VALUES(filename), co
|
||||
|
||||
-- ==================== ToolGuard Default Config & Rule Seed Data ====================
|
||||
|
||||
-- Global security config (single row)
|
||||
INSERT INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, create_time, update_time)
|
||||
-- Global security config (single row, insert only if not exists, never overwrite user config)
|
||||
-- Note: tool names in guarded_tools_json must match @Tool method names (execute_shell_command / write_file / edit_file)
|
||||
INSERT IGNORE INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, audit_enabled, audit_min_severity, audit_retention_days,
|
||||
create_time, update_time)
|
||||
VALUES (
|
||||
1000000001,
|
||||
TRUE,
|
||||
'all',
|
||||
'["WriteFileTool","EditFileTool","ShellExecuteTool"]',
|
||||
'["write_file","edit_file","execute_shell_command"]',
|
||||
'[]',
|
||||
TRUE,
|
||||
'["/etc","/usr","/bin","/sbin","/boot","/sys","/proc","/dev"]',
|
||||
TRUE, 'INFO', 90,
|
||||
NOW(), NOW()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE enabled=VALUES(enabled), guard_scope=VALUES(guard_scope), guarded_tools_json=VALUES(guarded_tools_json), denied_tools_json=VALUES(denied_tools_json), file_guard_enabled=VALUES(file_guard_enabled), sensitive_paths_json=VALUES(sensitive_paths_json), update_time=VALUES(update_time);
|
||||
);
|
||||
|
||||
-- Security rule: WriteFileTool — any path write requires approval (HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300001,
|
||||
'write_file_any',
|
||||
'File write requires approval',
|
||||
'Any file write operation requires user confirmation to prevent accidental overwrite of important files',
|
||||
'WriteFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'Please confirm write path and content are correct before allowing execution',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- Security rule: EditFileTool — any file edit requires approval (HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300002,
|
||||
'edit_file_any',
|
||||
'File edit requires approval',
|
||||
'Any file content replacement operation requires user confirmation',
|
||||
'EditFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'Please confirm edit path and replacement content are correct before allowing execution',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- Security rule: ShellExecuteTool — delete commands require approval (HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300003,
|
||||
'shell_rm_approval',
|
||||
'rm command requires approval',
|
||||
'rm / rmdir commands may cause permanent file loss, requires user confirmation',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(^|[;&|]|\s)rm\s',
|
||||
NULL,
|
||||
'Consider using trash command instead of rm, or confirm file list before allowing execution',
|
||||
TRUE, TRUE, 20,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- Security rule: ShellExecuteTool — forced recursive delete blocked (CRITICAL)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300004,
|
||||
'shell_rm_rf_block',
|
||||
'rm -rf blocked',
|
||||
'rm -rf forced recursive delete is extremely dangerous, blocked directly',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'CRITICAL',
|
||||
'BLOCK',
|
||||
'(?i)rm\s+(-[a-z]*r[a-z]*f[a-z]*|-[a-z]*f[a-z]*r[a-z]*)\s+(/|~|\$HOME|\*|\.\s*$)',
|
||||
NULL,
|
||||
'Absolutely forbidden to execute rm -rf on root directory, Home directory, or wildcards',
|
||||
TRUE, TRUE, 5,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- Security rule: ShellExecuteTool — writing system config files requires approval (HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300005,
|
||||
'shell_write_system_file',
|
||||
'System file write requires approval',
|
||||
'Writing to system directories like /etc or /usr via Shell requires user confirmation',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(>\s*|tee\s+|cp\s+.*\s+)(/etc/|/usr/|/bin/|/sbin/|/boot/)',
|
||||
NULL,
|
||||
'Please confirm the system file and content to modify before allowing execution',
|
||||
TRUE, TRUE, 15,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- Security rule: ShellExecuteTool — chmod 777 requires approval (MEDIUM)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300006,
|
||||
'shell_chmod_777',
|
||||
'chmod 777 requires approval',
|
||||
'chmod 777 grants full permissions to all users, security risk',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'MEDIUM',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)chmod\s+(777|a\+rwx|o\+rwx)',
|
||||
NULL,
|
||||
'Please confirm if full permissions for all users are truly needed',
|
||||
TRUE, TRUE, 30,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
-- Security rules are managed by ToolGuardRuleSeedService (Java) as single source of truth.
|
||||
-- Removed 6 legacy SQL rules. Their superset is registered in ToolGuardRuleSeedService.buildBuiltinRules() with correct tool names.
|
||||
|
||||
@ -1721,149 +1721,22 @@ ON DUPLICATE KEY UPDATE agent_id=VALUES(agent_id), filename=VALUES(filename), co
|
||||
|
||||
-- ==================== ToolGuard 默认配置与规则种子数据 ====================
|
||||
|
||||
-- 全局安全配置(只有一行)
|
||||
INSERT INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, create_time, update_time)
|
||||
-- 全局安全配置(只有一行,仅首次初始化时插入,不覆盖用户修改)
|
||||
-- 注意:guarded_tools_json 中的工具名必须与 @Tool 方法名一致(execute_shell_command / write_file / edit_file)
|
||||
INSERT IGNORE INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, audit_enabled, audit_min_severity, audit_retention_days,
|
||||
create_time, update_time)
|
||||
VALUES (
|
||||
1000000001,
|
||||
TRUE,
|
||||
'all',
|
||||
'["WriteFileTool","EditFileTool","ShellExecuteTool"]',
|
||||
'["write_file","edit_file","execute_shell_command"]',
|
||||
'[]',
|
||||
TRUE,
|
||||
'["/etc","/usr","/bin","/sbin","/boot","/sys","/proc","/dev"]',
|
||||
TRUE, 'INFO', 90,
|
||||
NOW(), NOW()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE enabled=VALUES(enabled), guard_scope=VALUES(guard_scope), guarded_tools_json=VALUES(guarded_tools_json), denied_tools_json=VALUES(denied_tools_json), file_guard_enabled=VALUES(file_guard_enabled), sensitive_paths_json=VALUES(sensitive_paths_json), update_time=VALUES(update_time);
|
||||
);
|
||||
|
||||
-- 安全规则:WriteFileTool — 任意路径写入需要审批(HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300001,
|
||||
'write_file_any',
|
||||
'文件写入需审批',
|
||||
'任何文件写入操作都需要用户确认,防止意外覆盖重要文件',
|
||||
'WriteFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'请确认写入路径和内容正确后再允许执行',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- 安全规则:EditFileTool — 任意文件编辑需要审批(HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300002,
|
||||
'edit_file_any',
|
||||
'文件编辑需审批',
|
||||
'任何文件内容替换操作都需要用户确认',
|
||||
'EditFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'请确认编辑路径和替换内容正确后再允许执行',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — 删除命令需审批(HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300003,
|
||||
'shell_rm_approval',
|
||||
'rm 命令需审批',
|
||||
'rm / rmdir 命令可能导致文件永久丢失,需要用户确认',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(^|[;&|]|\s)rm\s',
|
||||
NULL,
|
||||
'考虑使用 trash 命令替代 rm,或确认要删除的文件列表后再允许执行',
|
||||
TRUE, TRUE, 20,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — 强制递归删除直接拦截(CRITICAL)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300004,
|
||||
'shell_rm_rf_block',
|
||||
'rm -rf 直接拦截',
|
||||
'rm -rf 强制递归删除极度危险,直接拦截',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'CRITICAL',
|
||||
'BLOCK',
|
||||
'(?i)rm\s+(-[a-z]*r[a-z]*f[a-z]*|-[a-z]*f[a-z]*r[a-z]*)\s+(/|~|\$HOME|\*|\.\s*$)',
|
||||
NULL,
|
||||
'绝对禁止对根目录、Home 目录或通配符执行 rm -rf',
|
||||
TRUE, TRUE, 5,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — 写入系统配置文件需审批(HIGH)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300005,
|
||||
'shell_write_system_file',
|
||||
'写入系统文件需审批',
|
||||
'通过 Shell 向 /etc / /usr 等系统目录写入内容需要用户确认',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(>\s*|tee\s+|cp\s+.*\s+)(/etc/|/usr/|/bin/|/sbin/|/boot/)',
|
||||
NULL,
|
||||
'请确认要修改的系统文件和内容后再允许执行',
|
||||
TRUE, TRUE, 15,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — chmod 777 需审批(MEDIUM)
|
||||
INSERT INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
VALUES (
|
||||
1000300006,
|
||||
'shell_chmod_777',
|
||||
'chmod 777 需审批',
|
||||
'chmod 777 给予所有用户完全权限,存在安全风险',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'MEDIUM',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)chmod\s+(777|a\+rwx|o\+rwx)',
|
||||
NULL,
|
||||
'请确认是否真的需要给予所有用户完全权限',
|
||||
TRUE, TRUE, 30,
|
||||
NOW(), NOW(), 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE rule_id=VALUES(rule_id), name=VALUES(name), description=VALUES(description), tool_name=VALUES(tool_name), param_name=VALUES(param_name), category=VALUES(category), severity=VALUES(severity), decision=VALUES(decision), pattern=VALUES(pattern), exclude_pattern=VALUES(exclude_pattern), remediation=VALUES(remediation), builtin=VALUES(builtin), enabled=VALUES(enabled), priority=VALUES(priority), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
-- 安全规则由 ToolGuardRuleSeedService (Java) 统一种子化,不在 SQL 中重复维护
|
||||
-- 已移除旧的 6 条 SQL 规则,其超集已在 ToolGuardRuleSeedService.buildBuiltinRules() 中以正确的工具名注册。
|
||||
|
||||
@ -1724,149 +1724,26 @@ VALUES (
|
||||
|
||||
-- ==================== ToolGuard 默认配置与规则种子数据 ====================
|
||||
|
||||
-- 全局安全配置(只有一行)
|
||||
MERGE INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, create_time, update_time)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
-- 全局安全配置(只有一行,仅首次初始化时插入)
|
||||
-- 注意:guarded_tools_json 中的工具名必须与 @Tool 方法名一致(execute_shell_command / write_file / edit_file)
|
||||
-- 使用 SELECT + INSERT 确保已存在时不覆盖(H2 的 MERGE 会覆盖所有列,会重置用户修改的配置)
|
||||
INSERT INTO mate_tool_guard_config (id, enabled, guard_scope, guarded_tools_json, denied_tools_json,
|
||||
file_guard_enabled, sensitive_paths_json, audit_enabled, audit_min_severity, audit_retention_days,
|
||||
create_time, update_time)
|
||||
SELECT
|
||||
1000000001,
|
||||
TRUE,
|
||||
'all',
|
||||
'["WriteFileTool","EditFileTool","ShellExecuteTool"]',
|
||||
'["write_file","edit_file","execute_shell_command"]',
|
||||
'[]',
|
||||
TRUE,
|
||||
'["/etc","/usr","/bin","/sbin","/boot","/sys","/proc","/dev"]',
|
||||
TRUE, 'INFO', 90,
|
||||
NOW(), NOW()
|
||||
);
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM mate_tool_guard_config WHERE id = 1000000001);
|
||||
|
||||
-- 安全规则:WriteFileTool — 任意路径写入需要审批(HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300001,
|
||||
'write_file_any',
|
||||
'文件写入需审批',
|
||||
'任何文件写入操作都需要用户确认,防止意外覆盖重要文件',
|
||||
'WriteFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'请确认写入路径和内容正确后再允许执行',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- 安全规则:EditFileTool — 任意文件编辑需要审批(HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300002,
|
||||
'edit_file_any',
|
||||
'文件编辑需审批',
|
||||
'任何文件内容替换操作都需要用户确认',
|
||||
'EditFileTool',
|
||||
'path',
|
||||
'file_write',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'.+',
|
||||
NULL,
|
||||
'请确认编辑路径和替换内容正确后再允许执行',
|
||||
TRUE, TRUE, 10,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — 删除命令需审批(HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300003,
|
||||
'shell_rm_approval',
|
||||
'rm 命令需审批',
|
||||
'rm / rmdir 命令可能导致文件永久丢失,需要用户确认',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(^|[;&|]|\s)rm\s',
|
||||
NULL,
|
||||
'考虑使用 trash 命令替代 rm,或确认要删除的文件列表后再允许执行',
|
||||
TRUE, TRUE, 20,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — 强制递归删除直接拦截(CRITICAL)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300004,
|
||||
'shell_rm_rf_block',
|
||||
'rm -rf 直接拦截',
|
||||
'rm -rf 强制递归删除极度危险,直接拦截',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'CRITICAL',
|
||||
'BLOCK',
|
||||
'(?i)rm\s+(-[a-z]*r[a-z]*f[a-z]*|-[a-z]*f[a-z]*r[a-z]*)\s+(/|~|\$HOME|\*|\.\s*$)',
|
||||
NULL,
|
||||
'绝对禁止对根目录、Home 目录或通配符执行 rm -rf',
|
||||
TRUE, TRUE, 5,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — 写入系统配置文件需审批(HIGH)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300005,
|
||||
'shell_write_system_file',
|
||||
'写入系统文件需审批',
|
||||
'通过 Shell 向 /etc / /usr 等系统目录写入内容需要用户确认',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'HIGH',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)(>\s*|tee\s+|cp\s+.*\s+)(/etc/|/usr/|/bin/|/sbin/|/boot/)',
|
||||
NULL,
|
||||
'请确认要修改的系统文件和内容后再允许执行',
|
||||
TRUE, TRUE, 15,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
|
||||
-- 安全规则:ShellExecuteTool — chmod 777 需审批(MEDIUM)
|
||||
MERGE INTO mate_tool_guard_rule (id, rule_id, name, description, tool_name, param_name,
|
||||
category, severity, decision, pattern, exclude_pattern, remediation,
|
||||
builtin, enabled, priority, create_time, update_time, deleted)
|
||||
KEY (id)
|
||||
VALUES (
|
||||
1000300006,
|
||||
'shell_chmod_777',
|
||||
'chmod 777 需审批',
|
||||
'chmod 777 给予所有用户完全权限,存在安全风险',
|
||||
'ShellExecuteTool',
|
||||
'command',
|
||||
'shell_execution',
|
||||
'MEDIUM',
|
||||
'NEEDS_APPROVAL',
|
||||
'(?i)chmod\s+(777|a\+rwx|o\+rwx)',
|
||||
NULL,
|
||||
'请确认是否真的需要给予所有用户完全权限',
|
||||
TRUE, TRUE, 30,
|
||||
NOW(), NOW(), 0
|
||||
);
|
||||
-- 安全规则由 ToolGuardRuleSeedService (Java) 统一种子化,不在 SQL 中重复维护
|
||||
-- 已移除旧的 6 条 SQL 规则(rule_id: write_file_any, edit_file_any, shell_rm_approval,
|
||||
-- shell_rm_rf_block, shell_write_system_file, shell_chmod_777),
|
||||
-- 它们的超集已在 ToolGuardRuleSeedService.buildBuiltinRules() 中以正确的工具名注册。
|
||||
|
||||
@ -347,6 +347,9 @@ CREATE TABLE IF NOT EXISTS mate_tool_guard_config (
|
||||
denied_tools_json TEXT,
|
||||
file_guard_enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
sensitive_paths_json TEXT,
|
||||
audit_enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO',
|
||||
audit_retention_days INT NOT NULL DEFAULT 90,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@ -357,6 +357,9 @@ CREATE TABLE IF NOT EXISTS mate_tool_guard_config (
|
||||
denied_tools_json TEXT,
|
||||
file_guard_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
sensitive_paths_json TEXT,
|
||||
audit_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO',
|
||||
audit_retention_days INT NOT NULL DEFAULT 90,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
@ -488,6 +488,15 @@ export default {
|
||||
audit: {
|
||||
title: 'Audit Logs',
|
||||
desc: 'View tool security check records',
|
||||
config: {
|
||||
title: 'Audit Configuration',
|
||||
enabled: 'Enable Audit Logging',
|
||||
enabledHint: 'When disabled, tool invocation audit logs will not be recorded',
|
||||
minSeverity: 'Minimum Severity',
|
||||
minSeverityHint: 'Only record audit events at this severity level and above',
|
||||
retentionDays: 'Retention Days',
|
||||
retentionDaysHint: 'Audit logs older than this will be auto-cleaned (0=never)',
|
||||
},
|
||||
stats: {
|
||||
total: 'Total Checks',
|
||||
blocked: 'Blocked',
|
||||
|
||||
@ -488,6 +488,15 @@ export default {
|
||||
audit: {
|
||||
title: '审计日志',
|
||||
desc: '查看工具安全检查记录',
|
||||
config: {
|
||||
title: '审计配置',
|
||||
enabled: '启用审计日志',
|
||||
enabledHint: '关闭后将不再记录工具调用审计日志',
|
||||
minSeverity: '最低记录等级',
|
||||
minSeverityHint: '仅记录达到此等级及以上的审计事件',
|
||||
retentionDays: '保留天数',
|
||||
retentionDaysHint: '超过此天数的审计日志将被自动清理(0=永不清理)',
|
||||
},
|
||||
stats: {
|
||||
total: '总检查',
|
||||
blocked: '已阻止',
|
||||
|
||||
@ -584,6 +584,9 @@ export interface GuardConfig {
|
||||
deniedToolsJson?: string
|
||||
fileGuardEnabled: boolean
|
||||
sensitivePathsJson?: string
|
||||
auditEnabled?: boolean
|
||||
auditMinSeverity?: string
|
||||
auditRetentionDays?: number
|
||||
}
|
||||
|
||||
export interface AuditLogEntry {
|
||||
|
||||
@ -7,6 +7,46 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Audit Config -->
|
||||
<div class="config-panel">
|
||||
<h3 class="config-title">{{ t('security.audit.config.title') }}</h3>
|
||||
<div class="config-grid">
|
||||
<div class="config-item">
|
||||
<div class="config-label-row">
|
||||
<label>{{ t('security.audit.config.enabled') }}</label>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" v-model="auditConfig.auditEnabled" @change="saveAuditConfig" />
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="config-hint">{{ t('security.audit.config.enabledHint') }}</p>
|
||||
</div>
|
||||
<div class="config-item">
|
||||
<label>{{ t('security.audit.config.minSeverity') }}</label>
|
||||
<select v-model="auditConfig.auditMinSeverity" @change="saveAuditConfig" class="filter-select">
|
||||
<option value="INFO">{{ t('security.severity.INFO') }}</option>
|
||||
<option value="LOW">{{ t('security.severity.LOW') }}</option>
|
||||
<option value="MEDIUM">{{ t('security.severity.MEDIUM') }}</option>
|
||||
<option value="HIGH">{{ t('security.severity.HIGH') }}</option>
|
||||
<option value="CRITICAL">{{ t('security.severity.CRITICAL') }}</option>
|
||||
</select>
|
||||
<p class="config-hint">{{ t('security.audit.config.minSeverityHint') }}</p>
|
||||
</div>
|
||||
<div class="config-item">
|
||||
<label>{{ t('security.audit.config.retentionDays') }}</label>
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="auditConfig.auditRetentionDays"
|
||||
@change="saveAuditConfig"
|
||||
class="filter-input retention-input"
|
||||
min="0"
|
||||
max="3650"
|
||||
/>
|
||||
<p class="config-hint">{{ t('security.audit.config.retentionDaysHint') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
@ -148,6 +188,37 @@ const auditTotal = ref(0)
|
||||
const auditFilters = reactive({ toolName: '', decision: '' })
|
||||
const expandedRows = ref(new Set<number>())
|
||||
|
||||
// Audit config
|
||||
const auditConfig = reactive({
|
||||
auditEnabled: true,
|
||||
auditMinSeverity: 'INFO',
|
||||
auditRetentionDays: 90,
|
||||
})
|
||||
|
||||
async function loadAuditConfig() {
|
||||
try {
|
||||
const res: any = await securityApi.getGuardConfig()
|
||||
const data = res.data || {}
|
||||
auditConfig.auditEnabled = data.auditEnabled ?? true
|
||||
auditConfig.auditMinSeverity = data.auditMinSeverity || 'INFO'
|
||||
auditConfig.auditRetentionDays = data.auditRetentionDays ?? 90
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAuditConfig() {
|
||||
try {
|
||||
await securityApi.updateGuardConfig({
|
||||
auditEnabled: auditConfig.auditEnabled,
|
||||
auditMinSeverity: auditConfig.auditMinSeverity,
|
||||
auditRetentionDays: auditConfig.auditRetentionDays,
|
||||
})
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAuditLogs() {
|
||||
try {
|
||||
const params: any = {
|
||||
@ -183,7 +254,7 @@ function toggleExpand(id: number) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadAuditLogs(), loadAuditStats()])
|
||||
await Promise.all([loadAuditConfig(), loadAuditLogs(), loadAuditStats()])
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -192,6 +263,95 @@ onMounted(async () => {
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
/* Config Panel */
|
||||
.config-panel {
|
||||
background: var(--mc-bg-elevated);
|
||||
border: 1px solid var(--mc-border-light);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.config-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--mc-text-primary);
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
.config-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.config-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.config-item label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--mc-text-primary);
|
||||
}
|
||||
|
||||
.config-label-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.config-hint {
|
||||
font-size: 11px;
|
||||
color: var(--mc-text-tertiary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.retention-input {
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
/* Toggle Switch */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.toggle-switch input { opacity: 0; width: 0; height: 0; }
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
inset: 0;
|
||||
background: var(--mc-border);
|
||||
border-radius: 22px;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.toggle-slider::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .toggle-slider {
|
||||
background: var(--mc-accent, #3b82f6);
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .toggle-slider::before {
|
||||
transform: translateX(18px);
|
||||
}
|
||||
|
||||
/* Stats Grid */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user