安全守卫系统
安全守卫系统提供针对 Stata dofile 中危险命令和操作的保护。它充当 LLM 生成代码与实际执行之间的安全层。
概述
安全守卫默认启用,以防止意外执行破坏性操作。它在执行前针对危险命令和模式黑名单验证所有 dofile 代码。
关键功能
- 基于黑名单的验证:阻止已知的危险命令
- 模式匹配:使用正则表达式检测潜在危险操作
- 逐行分析:提供带行号的详细风险报告
- 可配置:可根据需要禁用(不推荐)
- 安全代码零误报:仅标记真正危险的操作
危险命令
安全守卫阻止以下危险命令。Stata 会将任何无歧义的前缀解析为完整命令(例如 sh 解析为 shell),因此黑名单同时枚举完整命令名和 Stata 接受的最短缩写形式,GuardValidator 在执行前对两种形式都进行匹配。
Shell 执行命令
| 命令 | 缩写 | 描述 | 示例 |
|---|---|---|---|
! |
- | Unix 风格 shell 转义 | ! ls -la |
!! |
- | 扩展 shell 命令 | !! vi file.do |
shell |
sh |
Shell 命令执行 | shell dir |
xshell |
xsh |
Mac/Unix(GUI) 扩展 shell | xshell vi file.do |
winexec |
winex |
Windows 程序执行 | winexec notepad.exe |
unixcmd |
unixc |
Unix 命令执行(macOS/Linux) | unixcmd ls |
文件操作
| 命令 | 缩写 | 描述 | 风险 |
|---|---|---|---|
erase |
era |
文件删除 | 数据丢失 |
rm |
- | 文件删除(别名) | 数据丢失 |
rmdir |
rmd |
目录删除 | 数据丢失 |
copy |
- | 文件复制 | 可能覆盖文件 |
代码执行
| 命令 | 描述 | 风险 |
|---|---|---|
run |
运行另一个 do 文件 | 不受信任的代码执行 |
do |
执行 do 文件 | 不受信任的代码执行 |
include |
包含另一个 do 文件 | 不受信任的代码执行 |
Dofile 目录边界
自 v1.16.2 起,stata_do 会拒绝任何解析后位于受信任根目录之外的 dofile。边界校验在 Guard 验证器之前运行,对 MCP server 模式和 CLI stata-mcp tool do 入口同时生效。
允许的根目录
dofile 的绝对解析路径必须位于以下目录之一,才会被接受:
| 根目录 | 来源 |
|---|---|
<WORKING_DIR>/<FOLDER_TAG>/stata-mcp-dofile/ |
STATA_MCP_FOLDER.DO |
<WORKING_DIR> |
配置的工作目录 |
WORKING_DIR 取自 STATA_MCP__CWD(或兼容旧版的 STATA_MCP_CWD),dofile 根目录是其下的 stata-mcp-dofile/ 子目录。符号链接会先被解析再校验,因此指向白名单外的链接会被拒绝。
拒绝行为
当解析后的路径位于允许根目录之外时,stata_do 返回 Access denied: Dofile '<path>' is outside allowed directories. 错误,并附带允许的根目录列表;同时日志会写入一条 [SECURITY VIOLATION] 警告,记录请求路径、解析路径以及当前配置的根目录。
操作建议
将 dofile 放在配置的工作目录下,或让 MCP-for-Stata 自动生成到 stata-mcp-dofile/。若需要执行位于其他位置的 dofile,应通过把 STATA_MCP__CWD 指向其上级目录来纳入白名单,而不是放宽校验。
Local Macro 展开检测
仅检查每行首个 token 的朴素黑名单可以被绕过:把危险命令藏进 local macro,稍后通过展开调用。自 v1.16.2 起 GuardValidator 通过两轮扫描堵上了这个缺口。
检测逻辑
- 第一轮遍历所有
local <name> "<value>"(以及local <name> = "<value>")定义,若 value 去空格后整体落在DANGEROUS_COMMANDS中,则把 local 名加入污染集合。 - 第二轮扫描所有非注释行,匹配任何污染 local 的
`name'展开,并在SecurityReport中记录为macro风险。
local 名称的匹配规则是 \w+,因此带后缀或前缀的写法如 local cmd_x "shell" 也会被跟踪。Stata 前缀(capture、quietly、noisily 等)在两轮扫描前都会被剥离,无法掩盖污染定义。
反例
local cmd_x "shell"
`cmd_x' rm -rf /tmp/important
不开启 macro 跟踪时,第二行以反引号开头,会绕过首 token 检查。启用 macro 跟踪后,验证器会对第二行报告 macro 风险,stata_do 拒绝执行。
覆盖范围说明
macro 跟踪只匹配单 token 危险值。拼接式赋值如 local cmd = "she" + "ll" 或运行时构造的内容超出静态校验范围,属于已知限制。当 dofile 内容来自外部输入时,请保持 Guard 启用并审查生成的代码。
Guard 禁用警告
STATA_MCP__IS_GUARD=false(或在 ~/.statamcp/config.toml 中写入 [SECURITY] IS_GUARD = false)会整体禁用验证器。Guard 禁用后,所有黑名单、模式与 macro 检查都会被跳过,dofile 内容会原样进入 Stata。每次 stata_do 调用都会向日志写入 [SECURITY] Guard is disabled. Dangerous dofile commands will not be blocked.;server 启动读取配置时也会写入同样一行。
仅在受控环境中禁用 Guard,例如 Docker 沙箱安装或临时虚拟机,受控任务完成后立即恢复启用。即便 Guard 被禁用,上文所述的目录边界校验仍然生效。
配置
启用/禁用安全守卫
选项 1:配置文件
编辑 ~/.statamcp/config.toml:
[SECURITY]
IS_GUARD = true # 默认:true
选项 2:环境变量
# 启用(默认)
export STATA_MCP__IS_GUARD=true
# 禁用(不推荐)
export STATA_MCP__IS_GUARD=false
默认行为
- 默认:启用(
IS_GUARD = true) - 推荐:生产使用时保持启用
- 开发:可禁用以进行测试(谨慎使用)
使用
基本使用
使用 stata_do 工具时自动应用安全守卫:
# 当 IS_GUARD 启用时(默认)
result = stata_mcp.stata_do(code="""
sysuse auto
regress price mpg weight
""")
# 安全代码正常执行
安全验证示例
当检测到危险代码时:
result = stata_mcp.stata_do(code="""
sysuse auto
! rm -rf / # 危险命令
""")
# Error: Security validation failed
# ❌ Security validation failed. Found dangerous items:
# - Line 3: command '!'
编程使用
如果您将 MCP-for-Stata 作为库使用:
from stata_mcp.guard import GuardValidator
# 创建验证器
validator = GuardValidator()
# 验证代码
code = """
sysuse auto
regress price mpg weight
"""
report = validator.validate(code)
if report.is_safe:
print("✅ Code is safe to execute")
else:
print(f"❌ Found {len(report.dangerous_items)} dangerous items:")
for item in report.dangerous_items:
print(f" {item}")
安全报告
SecurityReport 对象
@dataclass
class SecurityReport:
is_safe: bool # True 表示未发现危险项
dangerous_items: List[RiskItem] # 检测到的风险列表
RiskItem 对象
@dataclass
class RiskItem:
type: str # "command" 或 "pattern"
content: str # 危险内容
line: int # 行号(从 1 开始)
示例输出
# 安全代码
report = validator.validate("sysuse auto")
print(report)
# Output: ✅ Code passed security validation
# 危险代码
report = validator.validate("! rm file.txt")
print(report)
# Output:
# ❌ Security validation failed. Found dangerous items:
# - Line 1: command '!'
危险模式
安全守卫使用正则表达式模式检测危险操作:
Shell 命令模式
r"!\s*\w+" # 带命令的 Shell 转义:! ls
r"!!\s*\w+" # 扩展 shell:!! vi file.do
r"shell\s+\w+" # Shell 命令:shell dir
r"xshell\s+\w+" # 扩展 shell:xshell vi file.do
r"winexec\s+\S+" # Windows 执行:winexec program.exe
r"unixcmd\s+\w+" # Unix 命令:unixcmd ls
文件操作模式
r"erase\s+.*" # 文件删除:erase file.dta
r"rm\s+.*" # 文件删除:rm file.dta
r"rmdir\s+.*" # 目录删除:rmdir mydir
r"copy\s+.*" # 文件复制:copy file1.dta file2.dta
代码执行模式
r"run\s+.*" # 运行 do 文件:run script.do
r"\bdo\s+.*" # 执行 do 文件:do script.do
r"include\s+.*" # 包含 do 文件:include setup.do
验证流程
逐步验证
- 代码输入:接收 dofile 代码字符串
- 行分割:将代码分割成行以跟踪行号
- 过滤:跳过空行和注释(以
*开头) - 命令检查:针对危险命令检查每一行
- 模式检查:针对危险模式检查每一行
- 报告生成:创建包含所有发现的 SecurityReport
验证示例
code = """
* This is a comment
sysuse auto
! rm dangerous.txt # Line 3
regress price mpg
"""
report = validator.validate(code)
# Report:
# ❌ Security validation failed. Found dangerous items:
# - Line 3: command '!'
最佳实践
1. 保持安全守卫启用
[SECURITY]
IS_GUARD = true # 始终保持启用
2. 审查安全报告
始终审查安全验证报告:
report = validator.validate(code)
if not report.is_safe:
# 记录或通知危险项
for item in report.dangerous_items:
logger.warning(f"Dangerous item found: {item}")
3. 为允许的操作使用白名单
如果您需要执行某些危险操作:
- 手动审查代码
- 移除危险命令
- 使用安全的替代方案
示例:
* 代替:! rm tempfile.dta
* 使用:erase tempfile.dta (仍被阻止,但显示意图)
* 更好的方法:使用 Stata 内置的安全操作
capture erase tempfile.dta
4. 教育用户
记录哪些命令被阻止及其原因:
## 被阻止的命令
以下命令因安全原因被阻止:
- Shell 命令(!、shell、xshell 等)
- 文件删除(erase、rm)
- 外部代码执行(run、do、include)
请使用安全的替代方案或联系管理员获取帮助。
故障排除
误报
如果您认为某个命令被错误标记:
- 审查该命令:它是否真的危险?
- 检查模式:它是否匹配危险模式?
- 考虑替代方案:是否有更安全的方法完成任务?
禁用安全守卫
⚠️ 警告:不推荐禁用安全守卫。
仅在以下情况下禁用: - 您处于受信任的环境中 - 所有代码都经过人工审查 - 您了解风险
# 临时禁用(仅当前会话)
export STATA_MCP__IS_GUARD=false
stata-mcp
# 永久禁用(添加到配置)
# 编辑 ~/.statamcp/config.toml
[SECURITY]
IS_GUARD = false
自定义黑名单
您可以通过修改代码来扩展黑名单:
from stata_mcp.guard.blacklist import DANGEROUS_COMMANDS
# 添加自定义危险命令
DANGEROUS_COMMANDS.add("my_dangerous_command")
# 使用自定义验证器
from stata_mcp.guard import GuardValidator
validator = GuardValidator()
validator.dangerous_commands.add("another_command")
安全考虑
守卫防护的内容
✅ 防止: - Shell 命令执行 - 文件删除操作 - 不受信任的代码执行 - 系统级操作
❌ 不能防止: - Stata 内的数据修改 - 无限循环 - 内存耗尽 - Stata 崩溃
限制
安全守卫: - 不分析数据流 - 不跟踪变量值 - 不防止资源耗尽 - 不能替代适当的代码审查
纵深防御
安全守卫是一层保护。结合以下措施:
- 监控:启用 RAM 监控(参见监控文档)
- 沙盒:对不受信任的代码使用隔离环境
- 代码审查:人工审查生成的代码
- 备份:定期备份重要数据
与 MCP 工具的集成
自动集成
安全守卫自动与以下工具集成:
stata_do:执行 Stata do 文件(默认验证)write_dofile:创建 do 文件(执行时验证)
验证流程
用户请求 → MCP 工具 → 安全守卫 → 验证
↓
通过? → 执行
↓
失败? → 返回错误
可扩展性
创建自定义验证器
您可以为特定用例创建自定义验证器:
from stata_mcp.guard import GuardValidator, RiskItem, SecurityReport
class CustomValidator(GuardValidator):
"""带附加规则的自定义验证器。"""
def validate(self, code: str) -> SecurityReport:
# 获取基础验证结果
report = super().validate(code)
# 添加自定义验证逻辑
if "custom_dangerous_thing" in code:
report.dangerous_items.append(
RiskItem(
type="custom",
content="custom_dangerous_thing",
line=code.find("custom_dangerous_thing")
)
)
report.is_safe = False
return report
组合多个验证器
from stata_mcp.guard import GuardValidator
# 创建多个验证器
basic_validator = GuardValidator()
custom_validator = CustomValidator()
# 使用两者进行验证
code = "some stata code"
report1 = basic_validator.validate(code)
report2 = custom_validator.validate(code)
# 合并结果
if report1.is_safe and report2.is_safe:
print("✅ All validations passed")
示例
示例 1:安全代码执行
from stata_mcp.guard import GuardValidator
validator = GuardValidator()
safe_code = """
* Load sample data
sysuse auto
* Run regression
regress price mpg weight
* Display results
display "R-squared: " + string(e(r2))
"""
report = validator.validate(safe_code)
print(report)
# Output: ✅ Code passed security validation
示例 2:危险代码检测
dangerous_code = """
sysuse auto
* This will be blocked
! rm -rf /important/data
regress price mpg
"""
report = validator.validate(dangerous_code)
print(report)
# Output:
# ❌ Security validation failed. Found dangerous items:
# - Line 5: command '!'
示例 3:多项违规
multiple_violations = """
sysuse auto
shell delete file.txt
run untrusted_script.do
"""
report = validator.validate(multiple_violations)
print(f"Found {len(report.dangerous_items)} violations:")
for item in report.dangerous_items:
print(f" {item}")
# Output:
# Found 2 violations:
# Line 3: command 'shell'
# Line 4: pattern 'run\s+.*'
总结
安全守卫系统为自动化 Stata 执行提供基本保护:
- ✅ 默认启用以确保安全
- ✅ 阻止危险命令(shell、文件删除等)
- ✅ 基于模式的检测实现全面覆盖
- ✅ 详细报告带行号
- ✅ 可配置以适应不同用例
- ✅ 禁用时零开销(不推荐)
对于生产使用,始终保持安全守卫启用并定期审查验证报告。