Destructive Command Guard(dcg)Heredoc 模式编写指南:从规则 ID 到 ast-grep 模式库的完整实战
【免费下载链接】destructive_command_guardThe Destructive Command Guard (dcg) is for blocking dangerous git and shell commands from being executed by agents.项目地址: https://gitcode.com/GitHub_Trending/de/destructive_command_guard
本篇技术指南围绕 Destructive Command Guard(dcg)的Heredoc Pattern Authoring Guide(docs/patterns.md)展开,系统讲解 dcg 如何识别隐藏在 heredoc、here-string 与内联解释器参数(如python -c)中的破坏性命令,以及贡献者与高级用户如何编写、测试和放行(allowlist)这些模式。读完本文,你将掌握heredoc.<language>.<operation>稳定规则 ID 的命名与派生规则、ast-grep 模式语法、各语言内置模式清单,以及添加新模式、新语言和编写正/负测试夹具的完整流程,并能结合 src/heredoc.rs、src/ast_matcher.rs 与 src/config.rs 的源码实现理解底层工作原理。
为什么需要 Heredoc 检测:绕过场景与威胁模型
dcg 的核心职责是拦截 Agent 可能执行的破坏性 git 与 shell 命令。传统的做法是对原始命令字符串做正则模式匹配,但这种方案存在明显的盲区:破坏性代码可以被嵌入到 heredoc、here-string 或内联解释器参数中,绕过基于外层命令字符串的检测。
一个典型的绕过示例(见 docs/adr-001-heredoc-scanning.md):
cat <<EOF | bash rm -rf /important EOF外层命令cat <<EOF | bash本身不匹配任何破坏性模式,但嵌入的代码是危险的。类似地,python -c "import shutil; shutil.rmtree('/x')"、bash -c 'git reset --hard'、here-string<<< "rm -rf /"都属于同一类威胁。
因此 dcg 引入了专门针对 heredoc/内联脚本的检测管道。该检测受 ADR-001 约束,核心约束包括:
- 性能:dcg 会在每条 bash 命令上运行,总延迟预算为典型 <10ms、最坏 <50ms;
- 误报控制:不能阻塞合法的 heredoc(部署脚本、SQL 迁移、配置生成);
- 依赖最小化:减少二进制体积影响,避免外部进程依赖;
- 可维护性:模式库必须在无需改动核心代码的前提下可扩展;
- 有界失败(bounded failure):层内提取/解析失败走配置的有界回退,外层评估截止时间耗尽属于不确定结果(
Indeterminate),绝不能将其解释为"命令安全"的证据。
分层检测管道:从快速拒绝到 AST 匹配
heredoc 模式只在检测到 heredoc 或内联脚本时才被求值,这正是分层管道(tiered pipeline)的意义。docs/patterns.md 给出的简化管道为:
command -> quick reject -> heredoc trigger -> extract content + detect language -> ast match (language patterns) -> decision (allow/warn/deny)对应 ADR-001 中的四层架构,各层职责与预算如下:
| 层级 | 职责 | 预算 |
|---|---|---|
| Tier 0:Pack Gate | pack_aware_quick_reject(),SIMD 加速关键词搜索,无关键词直接放行 | <10μs |
| Tier 1:Trigger | RegexSet并行匹配,检测 heredoc/内联指示符,对支持的形式保证零漏报 | <100μs |
| Tier 2:Extract | 有界提取内容并识别语言 | <1ms 典型 |
| Tier 3:AST Scan | ast-grep-core 结构化模式匹配,20ms 硬超时 | <5ms 典型,20ms 最大 |
Tier 1 触发器定义在 src/heredoc.rs 的HEREDOC_TRIGGER_PATTERNS(共 19 个模式),覆盖:
- here-string 运算符
<<<; - Python/Ruby/Perl/Node/PHP/Lua 的内联执行(
-c/-e/-E/-p/-r),支持版本号后缀(python3.11)、.exe扩展名与交错 flag(bash -lc、python -I -c); - Shell 内联执行
sh|bash|zsh|fish -c; - PowerShell
-Command/-EncodedCommand与cmd /c、Invoke-Expression/iex(覆盖 Windows 场景,见 repro_1o5h_cmd_pwsh_stdin_consumer.rs 等测试); - 管道到解释器(
| python、| bash)与| xargs; eval/exec带引号参数、mise exec -c、ssh host '<command>'等内联脚本包装形式。
此外,contains_active_heredoc_operator()用一个感知引号的扫描器检测真正的 heredoc 运算符(<</<<-),可抑制引号字面量中的明显误报,同时保留$()/反引号替换等真实 shell 语法的触发能力。Tier 1 的"零漏报"不变量保证:只要 Tier 2 能提取出 heredoc,Tier 1 就必然触发。
Tier 2 提取限制(src/heredoc.rs 的ExtractionLimits)默认值为:
| 限制项 | 默认值 |
|---|---|
max_body_bytes | 1MB(每个 heredoc) |
max_body_lines | 10,000(每个 heredoc) |
max_heredocs | 10(每条命令) |
timeout_ms | 50ms 总预算 |
语言检测优先级(ScriptLanguage::detect,src/heredoc.rs):
- 命令前缀解释器(
python -c→ Python,最高置信度CommandPrefix,含管道接收方检测); - shebang 行(
#!/usr/bin/env python3→ Python,高置信度Shebang); - 内容启发式(import、
require(、package、use strict等,中/低置信度ContentHeuristics); - 未知(
Unknown)。
支持的语言枚举为Bash | Go | Php | Python | Ruby | Perl | JavaScript | TypeScript | Unknown,置信度信息可供dcg explain输出。
Tier 3 AST 匹配在 src/ast_matcher.rs 中实现,使用ast-grep-core做结构化匹配。关键参数:生产环境 AST 硬超时 20ms(AST_TIMEOUT_MS),直接解析输入上限 1MB(MAX_AST_INPUT_BYTES),与 Tier 2 的 1MB 体量上限对齐。find_matches()会先检查语言是否被 ast-grep 支持(Perl 走专用的有界正则回退路径),再按模式逐个匹配,并在解析、每个模式、每次匹配迭代之间检查超时;超时通过mpsc通道 +AtomicBool取消标志实现,避免泄漏工作线程。
稳定规则 ID:allowlist 与 explain 的键
稳定规则 ID 是 allowlist 的前提。命名约定为:
heredoc.<language>.<operation>例如:
heredoc.python.shutil_rmtree规则 ID 是 allowlist 与explain输出中使用的键。格式进一步规范为{category}.{language}.{operation}[.{variant}](见 docs/pattern-library-design.md),并遵循以下稳定性规则:
- 永不重命名已有模式 ID;
- 用deprecate(添加
.deprecated后缀)代替删除; - 新变体使用新 ID,不修改已有 ID;
- ID 即 allowlist 键,用户按 ID 引用。
在 src/suggestions.rs 中,建议映射使用冒号分隔的pack_id:pattern_name规范格式(如heredoc.python:shutil_rmtree),与 allowlist 模块的RuleId格式保持一致——这是模式元数据在建议系统中的一种表示变体。
模式语法:ast-grep 模式语言约定
Heredoc 模式使用ast-grep 模式语法(由ast-grep-core实现,见 docs/pattern-library-design.md)。本仓库常用的约定:
$$$匹配任意子树(任意数量的子节点);$X捕获单个 AST 节点;- 模式是语言相关的,只在该语言解析出的代码上求值(结构化匹配天然规避注释/字符串字面量中的误报)。
示例模式:
shutil.rmtree($$$)child_process.execSync($$$)exec.Command($$$).Run()
Perl 模式走专用路径:由于 ast-grep-language 不支持 Perl,src/ast_matcher.rs 通过针对字符串字面量与 shell 负载的有界正则扫描实现(见下文"Perl 扫描")。
模式元数据结构
模式元数据在 docs/pattern-library-design.md 中定义,并在 src/ast_matcher.rs 中以CompiledPattern落地:
pub struct CompiledPattern { pub pattern_str: String, // 模式字符串(调试/日志) pub rule_id: String, // 稳定规则 ID pub reason: String, // 人类可读的原因(<100 字符) pub severity: Severity, // 匹配严重级别 pub suggestion: Option<String>, // 安全替代建议 }CompiledPattern::new(...)在default_patterns()中批量构造,并在precompile_patterns()中编译为 ast-grepPattern,编译失败的模式被静默跳过(fail-open,由测试保证默认模式合法)。
严重级别(Severity)语义
src/ast_matcher.rs 定义四档严重级别,与 docs/pattern-library-design.md 的默认模式表对应:
| 级别 | 语义 | 默认行为 | 用户覆盖 |
|---|---|---|---|
| Critical | 不可逆 + 高置信度,总是阻塞 | Block | 仅 allowlist |
| High | 默认阻塞,可放行 | Block | 按 ID allowlist |
| Medium | 默认告警 | Warn | 通过配置 Block |
| Low | 仅记录日志 | Log | 可提升为 Warn/Block |
Severity::blocks_by_default()对 Critical 与 High 返回 true。注意:许多 Medium 模式会在匹配时被"细化"(refine)升级,这是误报控制的核心机制(详见下文)。
模式库存放位置
内置模式清单定义在:
- src/ast_matcher.rs(
default_patterns()与 Perl 扫描器)——权威来源; - 部分模式的建议映射在 src/suggestions.rs(
register_heredoc_suggestions(),如heredoc.python:shutil_rmtree建议先用os.listdir()预览、或用shutil.move()归档替代删除;heredoc.javascript:fs_rmsync建议先用fs.readdirSync()列出内容); - 高层设计与理由记录在 docs/adr-001-heredoc-scanning.md 与 docs/pattern-library-design.md。
内置模式清单(当前默认值)
以下清单源自 src/ast_matcher.rs 的default_patterns(),反映当前内置规则 ID。请使用这些 ID 进行 allowlist 与测试。
Bash
| Rule ID | Pattern | Reason | Severity |
|---|---|---|---|
heredoc.bash.rm_r | rm -r $$$ | rm -rrecursively deletes | High |
heredoc.bash.rm_rf | rm -rf $$$ | rm -rfrecursively deletes files/directories | Critical |
heredoc.bash.git_reset_hard | git reset --hard | discards uncommitted changes | Critical |
heredoc.bash.git_clean_fd | git clean -fd | deletes untracked files | High |
Go
| Rule ID | Pattern | Reason | Severity |
|---|---|---|---|
heredoc.go.os_remove | os.Remove($$$) | deletes files | High |
heredoc.go.os_removeall | os.RemoveAll($$$) | recursively deletes directories | Critical |
heredoc.go.exec_command | exec.Command($$$) | executes shell commands | Medium |
heredoc.go.exec_command_run | exec.Command($$$).Run() | executes shell commands | Medium |
heredoc.go.exec_command_output | exec.Command($$$).Output() | executes shell commands | Medium |
heredoc.go.exec_command_combined_output | exec.Command($$$).CombinedOutput() | executes shell commands | Medium |
JavaScript (Node)
| Rule ID | Pattern | Reason | Severity |
|---|---|---|---|
heredoc.javascript.fs_rm | fs.rm($$$) | deletes files/directories | Medium* |
heredoc.javascript.fs_rmdir | fs.rmdir($$$) | deletes directories | Medium* |
heredoc.javascript.fs_rmsync | fs.rmSync($$$) | deletes files/directories | Medium* |
heredoc.javascript.fs_rmdirsync | fs.rmdirSync($$$) | deletes directories | Medium* |
heredoc.javascript.fs_unlink | fs.unlink($$$) | deletes files | Low |
heredoc.javascript.fs_unlinksync | fs.unlinkSync($$$) | deletes files | Low |
heredoc.javascript.fspromises_rm | fsPromises.rm($$$) | deletes files/directories | Medium* |
heredoc.javascript.fspromises_rmdir | fsPromises.rmdir($$$) | deletes directories | Medium* |
heredoc.javascript.execsync | child_process.execSync($$$) | executes shell commands | Medium* |
heredoc.javascript.require_execsync | require('child_process').execSync($$$) | executes shell commands | Medium* |
heredoc.javascript.spawnsync | child_process.spawnSync($$$) | executes shell commands | Medium* |
* 这些模式在匹配时会被细化:递归删除或灾难性字面量路径才升级为阻塞;详见"派生规则 ID"。
TypeScript
| Rule ID | Pattern | Reason | Severity |
|---|---|---|---|
heredoc.typescript.fs_rm | fs.rm($$$) | deletes files/directories | Medium* |
heredoc.typescript.fs_rmdir | fs.rmdir($$$) | deletes directories | Medium* |
heredoc.typescript.fs_rmsync | fs.rmSync($$$) | deletes files/directories | Medium* |
heredoc.typescript.fs_rmdirsync | fs.rmdirSync($$$) | deletes directories | Medium* |
heredoc.typescript.fs_unlink | fs.unlink($$$) | deletes files | Low |
heredoc.typescript.fs_unlinksync | fs.unlinkSync($$$) | deletes files | Low |
heredoc.typescript.fspromises_rm | fsPromises.rm($$$) | deletes files/directories | Medium* |
heredoc.typescript.fspromises_rmdir | fsPromises.rmdir($$$) | deletes directories | Medium* |
heredoc.typescript.execsync | child_process.execSync($$$) | executes shell commands | Medium* |
heredoc.typescript.require_execsync | require('child_process').execSync($$$) | executes shell commands | Medium* |
heredoc.typescript.spawnsync | child_process.spawnSync($$$) | executes shell commands | Medium* |
heredoc.typescript.deno_remove | Deno.remove($$$) | deletes files/directories | Medium* |
Python
| Rule ID | Pattern | Reason | Severity |
|---|---|---|---|
heredoc.python.shutil_rmtree | shutil.rmtree($$$) | recursively deletes directories | Critical |
heredoc.python.os_remove | os.remove($$$) | deletes files | High |
heredoc.python.os_rmdir | os.rmdir($$$) | deletes directories | High |
heredoc.python.os_unlink | os.unlink($$$) | deletes files | High |
heredoc.python.pathlib_unlink | pathlib.Path($$$).unlink($$$)与Path($$$).unlink($$$) | deletes files | High |
heredoc.python.pathlib_rmdir | pathlib.Path($$$).rmdir($$$)与Path($$$).rmdir($$$) | deletes directories | High |
heredoc.python.subprocess_run | subprocess.run($$$) | executes shell commands | Medium* |
heredoc.python.subprocess_call | subprocess.call($$$) | executes shell commands | Medium* |
heredoc.python.subprocess_popen | subprocess.Popen($$$) | spawns shell processes | Medium* |
heredoc.python.os_system | os.system($$$) | executes shell commands | Medium* |
heredoc.python.os_popen | os.popen($$$) | executes shell commands | Medium* |
注意 pathlib 模式同时注册了pathlib.Path(...)与裸Path(...)两个变体,以覆盖from pathlib import Path的直接导入形式。Python shell 执行类模式刻意保持 Medium("不要仅凭 shell=True 就阻塞"),在匹配时通过负载分析升级。
Ruby
| Rule ID | Pattern | Reason | Severity |
|---|---|---|---|
heredoc.ruby.file_delete | File.delete($$$) | deletes files | Medium* |
heredoc.ruby.file_unlink | File.unlink($$$) | deletes files | Medium* |
heredoc.ruby.dir_delete | Dir.delete($$$) | deletes directories | Medium* |
heredoc.ruby.dir_rmdir | Dir.rmdir($$$) | deletes directories | Medium* |
heredoc.ruby.fileutils_rm | FileUtils.rm($$$) | deletes files | Medium* |
heredoc.ruby.fileutils_remove | FileUtils.remove($$$) | deletes files | Medium* |
heredoc.ruby.fileutils_remove_dir | FileUtils.remove_dir($$$) | deletes directories | Medium* |
heredoc.ruby.fileutils_rm_rf | FileUtils.rm_rf($$$) | recursively deletes directories | Medium* |
heredoc.ruby.system | system($$$) | executes shell commands | Medium* |
heredoc.ruby.exec | exec($$$) | replaces process with shell command | Medium* |
heredoc.ruby.kernel_system | Kernel.system($$$) | executes shell commands | Medium* |
heredoc.ruby.kernel_exec | Kernel.exec($$$) | replaces process with shell command | Medium* |
heredoc.ruby.open3_capture3 | Open3.capture3($$$) | executes shell commands | Medium* |
heredoc.ruby.open3_popen3 | Open3.popen3($$$) | executes shell commands | Medium* |
heredoc.ruby.backticks | `$$$` | executes shell commands | Medium* |
Perl
Perl 扫描使用定向正则 + shell 负载分析(ast-grep 不支持 Perl,见 src/ast_matcher.rs 的find_matches_perl())。发出的规则 ID 如下:
heredoc.perl.file_path.rmtreeheredoc.perl.file_path.<fn_name>(对应File::Path::<fn_name>,如remove_tree)heredoc.perl.unlinkheredoc.perl.rmdirheredoc.perl.system.<suffix>heredoc.perl.exec.<suffix>heredoc.perl.backticks.<suffix>heredoc.perl.qx.<suffix>
受支持的<suffix>值(来自 shell 负载检测detect_shell_payload()):
git_reset_hardgit_clean_fdrm_rfrm_rf_catastrophic
Perl 扫描前会先mask_perl_comments()将注释/字符串区域掩码为空白,避免注释中的system("rm -rf /")等文本触发误报;unlink/rmdir默认仅 Low(warn-only),File::Path::rmtree按目标路径是否为灾难性路径决定 Critical/Medium。正则在构造 matcher 时预编译(precompile_perl_patterns()),避免首次使用消耗匹配超时预算。
派生规则 ID:匹配时的精细化
部分模式会根据检测到的参数细化规则 ID(src/ast_matcher.rs 的refine_match_meta()):
- JavaScript/TypeScript
fs.*与fsPromises.*:字面量灾难性路径(catastrophic path)会在规则 ID 后追加.catastrophic,例如heredoc.javascript.fs_rmsync.catastrophic;非递归且非灾难性路径可能保持 warn-only; - TypeScript
deno_remove:灾难性路径追加.catastrophic; - Ruby
FileUtils/File/Dir:灾难性路径追加.catastrophic; - Ruby
system/exec/Open3/backticks 与 Perl shell 调用:字面量负载产生带.rm_rf、.rm_rf_catastrophic等后缀的规则 ID; - Python exec sink(
os.system/os.popen/subprocess.run|call|Popen):当调用参数区域中检测到真正的破坏性 shell 负载时升级为阻塞(至少 High),动态参数保持 warn-only(fail-open)。
所有派生规则 ID 都是合法的 allowlist 目标。
灾难性路径判定
is_catastrophic_path()(src/ast_matcher.rs)的判定逻辑:
/、~、~/前缀始终灾难性;/tmp、/var/tmp前缀除非包含路径穿越(如/tmp/../etc)否则安全;/etc、/home、/usr、/bin、/sbin、/lib、/lib64、/var、/boot、/root、/opt、/sys、/proc、/dev、/mnt、/media、/srv、/run等系统目录均为灾难性。
detect_shell_payload()对负载按;、换行、|、&分段,再对rm(要求同时具备-r/-R与-f,支持组合旗标、分离旗标、长旗标--recursive/--force与--选项终止符)和git(reset --hard、clean -fd)做旗标感知的判定,并通过next_shell_command()剥离sudo/command/env包装前缀。
添加新模式:七步流程
按 docs/patterns.md 的规范,添加新模式的步骤为:
- 选择一个稳定规则 ID(
heredoc.<language>.<operation>)。不要重命名已有 ID,需要废弃时用弃用流程; - 在 src/ast_matcher.rs 中添加
CompiledPattern::new(...)条目,用于语言特定的 AST 匹配;或扩展 Perl 扫描器做基于正则的匹配; - 提供简洁的原因(人类可读,100 字符以内);
- 设置严重级别(Critical / High / Medium / Low),考虑误报风险与灾难性目标;
- 存在更安全替代方案时添加建议(suggestion);
- 在 src/ast_matcher.rs 中添加测试(正例与负例夹具);
- 更新文档(本文档与相关设计说明)。
default_patterns()中的CompiledPattern::new参数顺序为(pattern_str, rule_id, reason, severity, suggestion),源码注释(如// Lowered per bead: avoid "code execution exists" as default deny)记录了严重级别权衡的历史依据。
添加新语言:六步流程
- 在 src/heredoc.rs 中把语言加入
ScriptLanguage枚举; - 在 src/config.rs 的 heredoc 语言解析中映射字符串别名(当前支持:
bash/sh/shell→ Bash,python/py→ Python,ruby/rb→ Ruby,perl/pl→ Perl,javascript/js/node→ JavaScript,typescript/ts→ TypeScript,php→ Php,go/golang→ Go,unknown→ Unknown;特殊值all扫描全部语言); - 在 src/ast_matcher.rs 的
script_language_to_ast_lang()中映射到 ast-grep 语言; - 在 src/ast_matcher.rs 中添加默认模式;
- 视需要在 src/suggestions.rs 添加建议;
- 在 src/ast_matcher.rs 添加正例与负例夹具。
另外,语言检测逻辑(ScriptLanguage::from_command、from_shebang、from_content)也需要相应扩展,以支持新解释器的识别。
误报控制与已知限制
docs/patterns.md 明确列出的限制与误报控制策略,与源码实现一一对应:
- Shell 执行辅助函数(如
execSync、system、os.system)只有在检测到字面量负载时才升级为高严重级别。动态负载(变量拼接等)仅 warn,以避免误报——对应refine_*_match中的 fail-open 逻辑; - 部分文件删除 API 在匹配时细化。非递归或非灾难性路径可能保持 warn-only(例如
fs.rmSync('./dist', {recursive: true})只产生非阻塞的heredoc.javascript.fs_rmsync,而fs.rmSync('/etc', ...)产生阻塞的.catastrophic变体,见 src/ast_matcher.rs 测试javascript_positive_fixtures模块); - 模式只对支持的语言求值,且只在检测到 heredoc 触发时求值。支持语言之外的、非 heredoc 的破坏性代码不在范围内;
- exec-sink 后备扫描(
scan_executing_sink_fallback):针对别名/间接导入的 shell 执行 sink(如const cp = require("child_process"); cp.execSync("rm -rf /etc")),采用"名字锚定 + 仅字面量"扫描,且对调用参数区域做括号平衡遍历,能捕获subprocess.run(["sh", "-c", "rm -rf /etc"])这类列表参数形式(#136); - Ruby 特殊形式(
%x(...)、反引号)有专门的正则通道;FileUtils/fs.rmSync还有命中超时前的高信号文件系统 sink 后备(scan_filesystem_sink_fallback),并要求调用位于行首且目标为灾难性字面量,以规避注释/字符串误报。
从架构层面看(docs/adr-001-heredoc-scanning.md),Tier 2 提取超时/格式错误、Tier 3 AST 解析失败或未知语言时,默认运行有界回退扫描(bounded fallback scanner),仅当未发现高风险信号时才放行;可通过配置fallback_on_timeout = false/fallback_on_parse_error = false改为阻塞。外层评估截止时间耗尽则返回Indeterminate(可审查的客户端会询问,其他客户端阻塞)——局部解析失败应降级为有界分析而非挂起 hook,而外部预算耗尽不是"安全"的证据。
Heredoc 配置与 allowlist 实操
Heredoc 扫描行为由 src/config.rs 的HeredocConfig控制:
[heredoc] enabled = true timeout_ms = 50 max_body_bytes = 1048576 max_body_lines = 10000 max_heredocs = 10 languages = ["python", "bash"] # 缺省为 all fallback_on_parse_error = true fallback_on_timeout = true [heredoc.allowlist] # 支持命令前缀、内容模式、内容哈希三种机制CLI 还提供--heredoc-scan、--heredoc-timeout、--heredoc-languages覆盖项(见 docs/adr-001-heredoc-scanning.md 的 Developer Notes),最终合并进HeredocSettings。
规则级别的 allowlist 按稳定规则 ID 进行(docs/pattern-library-design.md 示例):
# dcg.toml [allow] rules = [ "heredoc.python.subprocess_run.rm_rf", # 人工审查过 "heredoc.bash.rm_rf", # CI 需要 ]allowlist 的作用域由规则 ID 限定,并可附加文件模式、过期时间与原因。注意:内容级 allowlist(模式/哈希/命令前缀)与规则 ID allowlist 是两个独立维度,前者针对"已知安全的 heredoc 内容"整体放行,后者针对"特定规则命中"放行。
测试要求:正例、负例与灾难性夹具
新增规则必须在 src/ast_matcher.rs 的tests模块中添加测试:
- 每条新规则至少一个正例和一个负例夹具;
- 确保注释/字符串不匹配(误报防护),例如
# shutil.rmtree('/tmp')是注释、'shutil.rmtree(x)'是字符串字面量、shutil.copy(path)是不同函数,均不应匹配(docs/pattern-library-design.md 的夹具格式示例); - 适用时包含灾难性路径夹具,如
fs.rmSync('/etc', { recursive: true })应产生.catastrophic且阻塞、fs.rmSync('./dist', { recursive: true })应仅告警(src/ast_matcher.rs 的javascript_positive_fixtures模块给出了完整对照)。
运行测试:
cargo test ast_matcherheredoc 全链路测试:
cargo test heredoc测试环境下AST_TIMEOUT_MS放宽到 5000ms(生产为 20ms),因为完整测试套件在并行 CI 主机上可能被调度延迟;生产构建保留严格的 20ms 层内上限。仓库还提供 fuzz 目标(fuzz/fuzz_targets/ast_matcher_fuzz.rs、fuzz/fuzz_targets/heredoc_fuzz.rs 等)与回归测试语料(tests/corpus,含bypass_attempts、false_positives、true_positives等分类),可用于验证模式库的鲁棒性。
小结
Heredoc 模式体系是 dcg 防御 Agent 命令注入的关键一环:通过 Tier 0 快速拒绝 → Tier 1 触发器 → Tier 2 有界提取与语言检测 → Tier 3 ast-grep 结构化匹配的分层管道,在严格延迟预算内识别嵌入脚本中的破坏性操作;通过heredoc.<language>.<operation>稳定规则 ID、{category}.{language}.{operation}[.{variant}]派生规则与四档严重级别,实现精确、可解释、可 allowlist 的安全策略。贡献者只需遵循本文的模式语法、ID 稳定性、严重级别权衡与正/负夹具测试要求,即可在 src/ast_matcher.rs 中安全地扩展模式库。
【免费下载链接】destructive_command_guardThe Destructive Command Guard (dcg) is for blocking dangerous git and shell commands from being executed by agents.项目地址: https://gitcode.com/GitHub_Trending/de/destructive_command_guard
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考