mateclaw/mateclaw-server/src/main/java/vip/mate/tool/guard/DefaultToolGuard.java
matevip 94cf812207 feat(tool): add execute_code for running agent-authored code (#257)
Add an execute_code built-in tool that runs python/bash/node code the agent
writes on the fly, so a documentation-only skill (a SKILL.md with no bundled
scripts) can be acted on. Scoped runs inject the skill's secrets and run in
the skill directory; otherwise a private scratch directory is used. Host
secret env vars are scrubbed from the subprocess. execute_code is an
agent-wide capability, registered in the tool catalog (V143), and screened
by the tool guard with a dedicated set of destructive-pattern rules.

Tests cover python/bash/node execution, scratch-dir fallback, env scrubbing,
argument decoding, and guard gating.
2026-06-09 08:05:50 +08:00

221 lines
9.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package vip.mate.tool.guard;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
/**
* 默认工具安全守卫
* 基于正则模式匹配检测危险操作,采用规则驱动的安全守卫模式
* <p>
* 对于本地命令执行工具(如 execute_shell_command分为两级处理
* <ul>
* <li>BLOCK极端破坏性模式如 rm -rf /、dd if=xxx of=/dev/、mkfs——直接拒绝不允许审批覆盖</li>
* <li>NEEDS_APPROVAL一般高风险命令如 git push --force、chmod 777、DROP TABLE——需要用户审批后才能执行</li>
* </ul>
* 非 shell 工具仍使用原 BLOCK 策略。
*
* @author MateClaw Team
*/
@Slf4j
@Component
public class DefaultToolGuard implements ToolGuard {
/** 被视为本地命令执行工具的工具名集合 */
private static final Set<String> SHELL_TOOL_NAMES = Set.of(
"execute_shell_command",
"shell_execute",
"run_command",
"execute_code"
);
/** 文件写入类工具 —— 默认需要用户审批 */
private static final Set<String> FILE_WRITE_TOOL_NAMES = Set.of(
"write_file",
"edit_file"
);
/** 定时任务变更工具 —— 创建和删除需要用户审批 */
private static final Set<String> CRON_APPROVAL_TOOL_NAMES = Set.of(
"create_cron_job",
"delete_cron_job"
);
/** 极端破坏性模式 —— 即使是 shell 工具也直接 BLOCK不允许审批覆盖 */
private final List<DangerousPattern> absoluteBlockPatterns;
/** 一般高风险模式 —— 对 shell 工具走 NEEDS_APPROVAL对其他工具走 BLOCK */
private final List<DangerousPattern> highRiskPatterns;
public DefaultToolGuard() {
this.absoluteBlockPatterns = loadAbsoluteBlockPatterns();
this.highRiskPatterns = loadHighRiskPatterns();
}
@Override
public ToolGuardResult check(String toolName, String arguments) {
if (arguments == null || arguments.isEmpty()) {
return ToolGuardResult.allow();
}
String combined = (toolName != null ? toolName + " " : "") + arguments;
boolean isShellTool = toolName != null && SHELL_TOOL_NAMES.contains(toolName);
// 第一层:极端破坏性模式 —— 无论什么工具都直接 BLOCK
for (DangerousPattern pattern : absoluteBlockPatterns) {
if (pattern.matches(combined)) {
log.warn("[ToolGuard] BLOCKED (absolute): tool={}, pattern={}, reason={}",
toolName, pattern.regex(), pattern.reason());
return ToolGuardResult.block(pattern.reason(), pattern.regex());
}
}
// 第二层:高风险模式
for (DangerousPattern pattern : highRiskPatterns) {
if (pattern.matches(combined)) {
if (isShellTool) {
// Shell 工具命中高风险模式 → 需要用户审批
log.info("[ToolGuard] NEEDS_APPROVAL: tool={}, pattern={}, reason={}",
toolName, pattern.regex(), pattern.reason());
return ToolGuardResult.needsApproval(pattern.reason(), pattern.regex());
} else {
// 非 shell 工具命中高风险模式 → 直接 BLOCK
log.warn("[ToolGuard] BLOCKED: tool={}, pattern={}, reason={}",
toolName, pattern.regex(), pattern.reason());
return ToolGuardResult.block(pattern.reason(), pattern.regex());
}
}
}
// Shell 工具即使未命中任何模式,也需要审批(任何本地命令执行都是敏感操作)
if (isShellTool) {
log.info("[ToolGuard] NEEDS_APPROVAL (shell tool default): tool={}", toolName);
return ToolGuardResult.needsApproval("Shell command execution requires user approval", "shell_tool_default");
}
// 文件写入/编辑工具需要审批
if (toolName != null && FILE_WRITE_TOOL_NAMES.contains(toolName)) {
log.info("[ToolGuard] NEEDS_APPROVAL (file write tool): tool={}", toolName);
return ToolGuardResult.needsApproval("File write/edit operation requires user approval", "file_write_tool_default");
}
// 定时任务创建/删除需要审批
if (toolName != null && CRON_APPROVAL_TOOL_NAMES.contains(toolName)) {
log.info("[ToolGuard] NEEDS_APPROVAL (cron job tool): tool={}", toolName);
return ToolGuardResult.needsApproval("Cron job create/delete requires user approval", "cron_tool_default");
}
return ToolGuardResult.allow();
}
/**
* 极端破坏性模式 —— 直接 BLOCK不允许审批覆盖
* 这些命令一旦执行可能造成不可逆的系统级损坏
*/
private List<DangerousPattern> loadAbsoluteBlockPatterns() {
return List.of(
new DangerousPattern(
"rm\\s+-(rf|fr)\\s+/\\s*$",
"filesystem_destroy",
"Recursive force delete root directory"),
new DangerousPattern(
"mkfs\\b",
"filesystem_destroy",
"Filesystem formatting command"),
new DangerousPattern(
"dd\\s+if=.+of=/dev/",
"filesystem_destroy",
"Direct disk write operation"),
new DangerousPattern(
"\\bkill\\s+-9\\s+1\\b",
"system_danger",
"Kill init/systemd process"),
new DangerousPattern(
"curl.*\\|\\s*(sh|bash|zsh)",
"code_injection",
"Pipe download to shell execution"),
new DangerousPattern(
"wget.*\\|\\s*(sh|bash|zsh)",
"code_injection",
"Pipe download to shell execution")
);
}
/**
* 一般高风险模式 —— shell 工具走 NEEDS_APPROVAL其他工具走 BLOCK
*/
private List<DangerousPattern> loadHighRiskPatterns() {
return List.of(
// 文件系统
new DangerousPattern(
"rm\\s+-(rf|fr)",
"filesystem_destroy",
"Recursive force delete"),
new DangerousPattern(
"rm\\s+/",
"filesystem_destroy",
"Delete from root path"),
new DangerousPattern(
"rmdir\\s+/",
"filesystem_destroy",
"Delete directory from root path"),
// SQL
new DangerousPattern(
"DROP\\s+(TABLE|DATABASE|INDEX|VIEW|SCHEMA)",
"sql_destroy",
"SQL DROP statement"),
new DangerousPattern(
"TRUNCATE\\s+TABLE",
"sql_destroy",
"SQL TRUNCATE TABLE statement"),
new DangerousPattern(
"DELETE\\s+FROM\\s+\\w+\\s*;",
"sql_destroy",
"Unconditional DELETE (missing WHERE clause)"),
new DangerousPattern(
"ALTER\\s+TABLE\\s+\\w+\\s+DROP",
"sql_destroy",
"ALTER TABLE DROP operation"),
// System
new DangerousPattern(
"\\bshutdown\\b",
"system_danger",
"System shutdown command"),
new DangerousPattern(
"\\breboot\\b",
"system_danger",
"System reboot command"),
new DangerousPattern(
"chmod\\s+777",
"system_danger",
"Overly permissive file permissions"),
// Code injection
new DangerousPattern(
"eval\\s*\\(",
"code_injection",
"Dynamic code execution (eval)"),
// Credentials
new DangerousPattern(
"(password|secret|api[_-]?key|token)\\s*=\\s*['\"]?\\S{8,}",
"credential_exposure",
"Potential credential exposure"),
// Git
new DangerousPattern(
"git\\s+push\\s+.*--force",
"git_danger",
"Git force push"),
new DangerousPattern(
"git\\s+reset\\s+--hard",
"git_danger",
"Git hard reset")
);
}
}