pi-ast-guard

基于 AST 的 Pi 代理安全防护扩展 — 解析 Bash 命令,拦截危险操作(继承自 pi-damage-control)

Packages

Package details

extension

Install pi-ast-guard from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-ast-guard
Package
pi-ast-guard
Version
1.0.4
Published
Aug 4, 2026
Downloads
513/mo · 513/wk
Author
rainmanhhh
License
MIT
Types
extension
Size
233.2 KB
Dependencies
4 dependencies · 4 peers
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-ast-guard

npm

Languages: 简体中文 | English

简介

pi-ast-guard(原名 pi-damage-control)是一款基于 AST 的代理安全防护扩展,专为 Pi 编码代理设计。 它能够在破坏性 shell 命令或文件操作执行之前进行拦截,同时避免因普通文本、markdown、heredoc 和任务描述中的内容而产生误报。

继承说明:本项目继承自 pi-damage-control(原作者 Baishampayan Ghose,原仓库已删除), 在保留原有 AST 解析方案与策略引擎的基础上继续维护与改进。

安装

pi install npm:pi-ast-guard
# 或者
# pi install git:github.com/rainmanhhh/pi-ast-guard

DNA 模式(Do Not Ask)

不想每次操作都弹窗确认?/ag:dna 一键进入 DNA 模式:遇到 ask 不再询问,而是按策略自动审批或拒绝block 规则照常拦截,安全底线不丢。

自动回答由 4 个参数决定(工作区内/外 × 读/写),外加工具黑白名单与累计违反上限,可在 settings.dna 中配置:

参数 默认 含义
dna.readInside allow 工作区内读取
dna.readOutside allow 工作区外读取(仅读取,风险低)
dna.writeInside allow 工作区内写/删/移动
dna.writeOutside block 工作区外写/删/移动(高风险,默认拒绝)
dna.maxViolations 3 本次 DNA 模式下累计自动拒绝的总上限(所有规则含工具黑白名单),达到后强制中断会话并清零(对话结束也清零)
dna.allowTools [] 工具白名单(非空时启用白名单模式,仅允许名单中的工具调用)
dna.blockTools [] 工具黑名单(白名单为空时启用黑名单模式,禁止名单中的工具调用)

工作区 = cwd + settings.extraDirs。命令类 ask(如发布命令)在 DNA 模式下默认放行;自动拒绝会拦截操作并引导 AI 评估替代方案(勿绕过规则),本次模式内累计违反达到上限(dna.maxViolations,默认 3)才强制中断。

工作原理

  • 使用 just-bash 解析 Bash 命令 AST,无需正则表达式回退
  • 根据 config/default-policy.yaml 中的语义化命令规则进行评估
  • 从 Bash 命令和 Pi 文件工具中提取文件操作意图
  • 对零访问(zero-access)、只读(read-only)、禁止删除(no-delete)和工作区外写入路径应用路径策略
  • 当策略动作为 ask 时弹出四选对话框(同意一次 / 本会话允许 / 本会话拒绝 / 拒绝一次);当 UI 不可用时自动拒绝(fail-closed)

配置方式

创建项目级策略文件:

.pi/ast-guard.yml

或全局策略文件:

~/.pi/agent/ast-guard.yml

策略分层加载:全局策略(~/.pi/agent/ast-guard.yml)为基础层(缺失时用内置默认策略),项目策略(.pi/ast-guard.yml)存在时在其上合并——settings 字段级覆盖,rulesid 覆盖(同 id 项目优先),不同 id 的规则全部保留。

内置默认策略文件:config/default-policy.yaml

策略语言

顶层策略结构:

settings:
  language: auto
  parseFailure: ask
  showStatus: true
  # 额外工作区目录:与 cwd 共同构成「完整的工作区」,outsideWorkdir: true 的语义变为「在工作区目录列表之外」
  extraDirs: []
  # DNA 模式(Do Not Ask)下 ask 的自动回答
  dna:
    readInside: allow
    readOutside: allow
    writeInside: allow
    writeOutside: block
    maxViolations: 3
rules: []

settings.language 控制扩展 UI 提示的语言(zh 中文 / en 英文 / auto 跟随系统,默认 auto),包括通知、拦截/确认对话框、命令描述与默认策略规则文案;auto 通过系统区域设置探测(Windows 取系统区域,Unix-like 取 LANG/LC_ALL,无则英文)。跟随项目策略,/ag:status(已合并 reload)后生效。

路径规则

路径规则与命令规则同属顶层的 rules 列表。其 type 字段编码了路径策略的子类型:

  • path:zeroAccess — 禁止读、写、删除和移动操作
  • path:readOnly — 仅禁止写、删除和移动操作,读取允许
  • path:noDelete — 仅禁止删除和移动操作

每条路径规则示例:

- id: path-secrets-env
  type: path:zeroAccess
  action: block
  reason: 环境文件可能包含密钥信息
  match:
    path:
      any: .env*
      except: [.env.example]

match.path.any 可以是字符串或字符串列表:

match:
  path:
    any: [LICENSE, LICENSE.*, COPYING, COPYING.*]

支持的路径模式(glob 语义,* 按段匹配不跨 /** 匹配任意深度):

  • 精确匹配/文件名:README.md.env(无斜杠模式匹配任意位置的同名文件,如 ast-guard.yml 可命中 ~/.pi/ast-guard.yml
  • 目录:.git/node_modules/(含目录本身及其下所有内容)
  • 通配符:*.pemdocker-compose.*.ymldist/****/secrets/**** 段感知,**/secrets/** 命中任意深度含 secrets 段的路径)
  • 前缀:build-*/ 段结尾)
  • 当前工作目录宏:$CWD$CWD/…(工作区外用 outsideWorkdir: true
  • 相对模式解析到工作区根(cwd),~ 解析到 HOME

工作区外写入确认示例:

- id: path-outside-project-write
  type: path:readOnly
  reason: 工作区外写入需要确认
  match:
    path:
      outsideWorkdir: true
      except: [/tmp/, /dev/null]

命令规则

命令规则使用 type: command

- id: git-reset-hard
  type: command
  action: block
  reason: git reset --hard 会丢弃工作区变更
  match:
    command: git
    subcommand: reset
    flags:
      any: [--hard]

常用匹配字段:

  • command:精确的命令名称
  • commandAny:多个命令名称之一
  • subcommand:第一个非选项命令操作数
  • subcommandAny:多个子命令之一
  • argsAny:参数列表中任意一个匹配即可
  • argsAll:参数列表中全部必须匹配
  • argsNone:参数列表中不得出现任何匹配项
  • argsContainAny / argsContainAll:参数子串匹配
  • flags.any / flags.all / flags.none:语义化标志匹配
  • optionsBeforeSubcommand.value:子命令检测前的全局选项值,适用于 git -C repo ...
  • visibleTextAny / visibleTextAll / visibleTextNone:匹配可见静态文本,适用于 SQL 执行器

规则默认启用。审批对话框中的「本会话允许」会在当前会话内临时解除被触发规则的检查。

规则可配 priority(默认 0,值越大越优先):命中多条规则时只保留最高优先级的一组再按动作强度判定(block > ask > allow),因此高优先级 allow 规则可豁免低优先级 ask/block。来源偏移:项目 0 / home -0.3 / 内置默认 -0.6

动作选项:

  • allow — 放行
  • ask — 请求确认
  • block — 直接阻止

action 字段可选,省略时默认为 ask

- id: git-commit
  type: command
  action: ask
  reason: git commit 需要确认
  match:
    command: git
    subcommand: commit

完整示例:

settings:
  parseFailure: ask
  showStatus: true
rules:
  - id: path-secrets-env
    type: path:zeroAccess
    action: block
    reason: 环境文件可能包含密钥信息
    match:
      path:
        any: .env*
        except: [.env.example]
  - id: path-outside-project-write
    type: path:readOnly
    reason: 工作区外写入需要确认
    match:
      path:
        outsideWorkdir: true
        except: [/tmp/, /dev/null]
  - id: git-commit
    type: command
    action: ask
    reason: git commit 需要确认
    match:
      command: git
      subcommand: commit

审批对话框

当规则动作评估为 ask 时,弹出四选对话框(30 秒超时,超时即拒绝):

  • 同意一次 — 只放行当前这一次工具调用(单次生效,不影响下次)
  • 本会话允许 — 记录为会话级决策(决策层),当前会话内该规则在作用域内不再询问
  • 本会话拒绝 — 记录为会话级决策(决策层),当前会话内该规则在作用域内直接拦截、不再弹窗
  • 拒绝一次 — 拦截本次调用(单次生效,不影响下次)

会话级决策 = 规则匹配之上叠加的一层精确路径匹配:每条决策是 (规则, 作用域路径),查询时 deny 优先于 allow(无论新旧);同一 (规则, 作用域) 的新决策覆盖旧决策,不同作用域累积生效。

拒绝(含本会话拒绝、拒绝一次)、对话框取消/超时未应答,以及无 UI 自动拒绝,都会在拦截的同时中止当前轮(agent 停止执行,回到等待用户输入的状态);拦截消息仍作为工具结果发给 AI。

对话框被取消或超时均视为拒绝并拦截。无 UI 环境(print/JSON 模式)下,ask 决策自动拦截(fail-closed)。

作用域输入(粗粒度规则)

规则匹配域超出「工作区锚定的局部区域」(粗粒度)时,选中「本会话允许/拒绝」后额外弹一次输入框确定作用域:

输入 作用域
(空) 仅本次触发的目标文件(父目录存在即可,容忍尚未创建的文件)
. 目标文件所在目录
../.. 从目标文件的上两级目录起(相对输入均相对目标文件目录解析)
绝对路径 直接以该路径为作用域

输入会校验:不得含通配符;作用域必须与规则的匹配域相交(如 outsideWorkdir 规则不接受工作区内的绝对路径,/etc/** 规则不接受 /var);目录作用域必须真实存在;不通过则提示并重新输入。取消输入等同取消对话框(fail-closed,中止当前轮)。

粗粒度判定(任一命中即弹输入):

  • outsideWorkdir: true
  • 根锚定绝对路径且解析后字面深度 ≤ 2(如 /etc/**/var/log//home/user/data/** 深度 ≥ 3 视为细粒度)
  • 首段为通配符或覆盖工作区及以上(**/…*/…../**..../**

无斜杠模式(ast-guard.yml*.log不弹输入:选中后自动把作用域限定为本次触发的目标文件(同名其他位置下次仍会询问)。命令面规则(无路径可锚定)与细粒度路径规则保持规则级决策(覆盖该规则所有路径)。

最近决策与按序号清除

会话级决策记录到 /ag:status 面板的「最近决策」(保留最近 10 条),每行带序号(最新在前 = 1):

最近决策
1. 14:32 本会话允许 → outside-ask → C:/tmp/x.log (touch C:/tmp/x.log)
2. 14:31 本会话拒绝 → git-commit (git commit)

单次生效的同意/拒绝不影响下次 ask,不在面板展示。执行 /ag:forget 1,3 按序号清除单条决策(逗号分隔多个);清除后序号重排,新决策到达也会使序号位移,忘记前可先执行 /ag:status 查看最新序号。

可用命令

命令 说明
/ag:forget <序号...> 按序号清除会话决策(逗号分隔多个,如 1,3),清除后序号重排
/ag:dna 开启/关闭 Do Not Ask 模式
/ag:status 重新加载策略并显示状态面板(含带序号的「最近决策」);再次执行刷新

开发

使用 Bun 作为包管理器:

bun install
bun run test        # 运行单元测试(Vitest)
bun run test:watch  # 监听模式
bun run typecheck   # TypeScript 类型检查
bun run check       # 完整检查(lint + 测试 + 类型)
bun run bench       # 性能基准(mitata)

测试位于 tests/ 目录,按模块组织(bash/rules/policy/engine/extension/intents/)。 已知问题与待优化项见 docs/known-issues.md

本地开发与验证

~/.pi/agent/settings.json 中直接指向源码(改代码即时生效):

{
  "extensions": ["E:/workspace/pi-ast-guard/src/index.ts"]
}

然后进入任意测试项目运行 pi,状态栏出现 🛡 图标即加载成功。仓库内 bun run check 通过后提交(pre-commit hook 会自动运行 bun run lint)。 也可在 ast-guard-demo 测试沙箱(含 .envdist/、自定义策略与完整验证清单)中手动验证。

鸣谢

本项目继承自 pi-damage-control(原作者 Baishampayan Ghose,原仓库已删除)。 灵感来源于 claude-code-damage-control

许可证

MIT © rainmanhhh