mcpField
S
signet-eval

signet-eval

本地服务
github.com安全42 周前更新

关于 signet-eval

signet-eval 面向需要稳定控制 AI Agent 工具调用的用户,是一个本地策略引擎。它按顺序确定性地评估规则,不在授权路径中使用大模型,并将建议性提示与授权分开。

主要功能

  1. 01确定性 ALLOW、DENY、ASK、GATE 和 ENSURE 规则
  2. 02按顺序执行并采用首条匹配规则
  3. 03不可更改的自我保护规则
  4. 04支持 Claude Code、Codex、Cursor 等的钩子适配器
  5. 05可选的建议上下文注入

适用场景

  1. 01在执行前拦截破坏性 Shell 命令
  2. 02要求对指定工具输入进行确认
  3. 03保护策略文件和 signet-eval 二进制文件
  4. 04在规则中加入支出或凭据条件

MCP 相关

本地 stdio MCP 服务允许客户端管理策略。锁定规则不能通过该管理服务更改。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

signet-eval

为 AI Agent 工具调用提供确定性策略执行。集成的工具调用会先经过用户定义的规则,再执行。授权流程中不使用大模型。建议性提示与授权相互独立。

安装

# crates.io
cargo install signet-eval

# from source
git clone https://github.com/wandercom/signet-eval
cd signet-eval
cargo install --path .

signet-eval 没有 npm 或 PyPI 软件包。公开分发渠道为 crates.io 和从 GitHub 安装源码。MCP Registry 条目指向仓库元数据;运行时是本地的 signet-eval serve stdio 服务。

快速开始

以下命令钩子适配器仍受支持,且彼此独立。有关 Claude Code 可选的早期体验功能钩子,请参阅 function adapter。该功能默认禁用; 安装或构建此软件包不会启用它,也不会更改用户设置。

1. 接入 Claude Code — 添加到 ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "",
      "hooks": [{"type": "command", "command": "signet-eval", "timeout": 2000}]
    }]
  }
}

对于 Codex,在 ~/.codex/config.toml 或 <repo>/.codex/config.toml 中启用钩子:

[features]
codex_hooks = true

然后添加 ~/.codex/hooks.json 或 <repo>/.codex/hooks.json:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "signet-eval --adapter codex",
        "timeout": 30000,
        "statusMessage": "Checking Signet policy"
      }]
    }],
    "PermissionRequest": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "signet-eval --adapter codex-permission",
        "timeout": 30000,
        "statusMessage": "Checking Signet approval policy"
      }]
    }]
  }
}

对于 Antigravity,将此区块添加到 ~/.gemini/config/hooks.json(与现有钩子组进行合并):

{
  "signet": {
    "enabled": true,
    "PreToolUse": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "/bin/bash -lc 'source ~/.profile >/dev/null 2>&1 || true; exec signet-eval --adapter antigravity'",
        "timeout": 30,
        "statusMessage": "Checking Signet policy"
      }]
    }]
  }
}

相同的配置也可在 hooks/antigravity-hooks.json 中找到。

对于 Cursor,添加到 ~/.cursor/hooks.json(用户级)或 <repo>/.cursor/hooks.json (项目级),并与任何现有事件合并:

{
  "version": 1,
  "hooks": {
    "preToolUse": [{
      "command": "/bin/bash -lc 'source ~/.profile >/dev/null 2>&1 || true; exec signet-eval --adapter cursor'",
      "matcher": "^(?!Shell$|MCP:)",
      "timeout": 30,
      "failClosed": true
    }],
    "beforeShellExecution": [{
      "command": "/bin/bash -lc 'source ~/.profile >/dev/null 2>&1 || true; exec signet-eval --adapter cursor'",
      "timeout": 30,
      "failClosed": true
    }],
    "beforeMCPExecution": [{
      "command": "/bin/bash -lc 'source ~/.profile >/dev/null 2>&1 || true; exec signet-eval --adapter cursor'",
      "timeout": 30,
      "failClosed": true
    }]
  }
}

相同的配置也可在 hooks/cursor-hooks.json 中找到。 preToolUse 匹配器会跳过 shell 和 MCP 调用,因为有两个专用事件会评估它们;如果没有该匹配器,每个此类调用都会被评估和记录两次。

对于 Muse Code,将顶层 hooks 成员添加到 ~/.config/muse/settings.json (用户范围;保留 schema_version 和其他设置),或者将文件保存为 <repo>/.muse/hooks.json(项目范围,Muse 仅对受信任的工作区启用该配置):

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "*",
      "hooks": [{"type": "command", "command": "signet-eval --adapter muse"}]
    }]
  }
}

相同的配置也可在 hooks/muse-hooks.json 中找到。

2. 完成。 现在,每次工具调用都会经过策略评估。默认策略会阻止破坏性操作、保护自身配置,并允许其他所有操作。

3.(可选)自定义 — 通过 MCP 服务与 Claude 对话:

claude mcp add --scope user --transport stdio signet -- signet-eval serve

然后说:“为 amazon 订单添加 50 美元限额” 或 “阻止所有 rm 命令”。

默认策略

自我保护规则为锁定状态 — AI Agent 无法删除、编辑或重新排序这些规则,即使通过 MCP 管理服务也不行。这样可以防止 Agent 禁用自身的防护措施。

操作决策锁定
修改 .signet/ 的 Write/Edit/Bash拒绝是
修改 signet-eval 二进制文件的 Write/Edit/Bash拒绝是
Write/Edit settings.json / settings.local.json询问是
Bash kill/pkill/killall + signet拒绝是
近期没有 Kindex 标记/搜索/上下文时使用直接编辑工具拒绝是
Claude TaskCreate、TaskUpdate、TaskList、TaskGet、TodoWrite拒绝是
rm、rmdir拒绝
Cursor 原生 Delete 工具拒绝
git push --force询问
目标所有者身份不匹配的 Git 远程和 gh 操作拒绝
mkfs、format、dd if=拒绝
curl | sh、wget | sh拒绝
含有字面 已遮蔽密钥 值的任何工具输入询问
其他所有操作允许

自定义策略

signet-eval init       # write default policy to ~/.signet/policy.yaml
signet-eval validate   # check policy for errors
signet-eval rules      # show current rules

编辑 ~/.signet/policy.yaml:

version: 1
default_action: ALLOW
rules:
  - name: block_rm
    tool_pattern: ".*"
    conditions: ["contains(parameters, 'rm ')"]
    action: DENY
    reason: "File deletion blocked"

  - name: books_limit
    tool_pattern: ".*purchase.*"
    conditions:
      - "param_eq(category, 'books')"
      - "spend_plus_amount_gt('books', amount, 200)"
    action: DENY
    reason: "Books spending limit ($200) exceeded"

  - name: protect_my_config
    tool_pattern: ".*"
    conditions: ["contains(parameters, '/etc/')"]
    action: ASK
    locked: true
    reason: "System config changes require confirmation"

规则按顺序评估 — 以第一个匹配项为准。同一规则中的多个条件采用 AND 逻辑。带有 locked: true 的规则无法通过 MCP 管理服务修改。

ENSURE 检查上下文

用户管理的 ENSURE 脚本会通过 stdin 接收原始的规范化工具名称和输入。SIGNET_TOOL_COMMAND 会将规范化后的 command 和 cmd 字段中第一个非空字符串作为字面环境变量值传递。如果这两个字段都没有值,则该变量为空。 转发机制不会将该值解释为 shell 代码。

SIGNET_TOOL_CWD 依次使用规范化工具的 cwd/workdir、主机信封中的 cwd,然后是钩子进程的工作目录。空值会被跳过。 SIGNET_TOOL_CWD_SOURCE 会指出使用的是哪一个值:tool_input、host 或 process。 SIGNET_HOST_CWD 单独携带主机信封中的 cwd;如果主机没有发送该值,则此变量为空。tool_input 目录是主机将运行命令的位置,但该目录由模型编写。进程回退目录并非经过独立验证的执行目录。这些值都不是可信的位置证明;如果检查要基于位置进行授权,应先查看来源,再依赖该值。 Signet 不会解释命令,因此命令中的 cd 不会改变 SIGNET_TOOL_CWD。所有这些环境变量都会覆盖继承的同名变量,stdin 保持不变。

如果检查脚本缺失或无法解析,调用将被拒绝,并说明对应检查的名称;用户规则和锁定规则均如此。Signet 不会安装检查脚本,因此请连同引用它的任何 rules.yaml 一起复制 ~/.signet/checks/。

旧版本曾附带 github_identity_guard 系统规则。若 policy.yaml 中仍留有该规则的副本,它会被忽略,而 signet-eval validate 会将其报告为警告。若要保留身份检查,请将该规则放入 rules.yaml,并使用自行维护的脚本。

在仓库根目录执行 cargo build --release 后,运行以下独立的本地验收检查:

SIGNET_EVAL_BINARY="$(pwd)/target/release/signet-eval" python3 tests/test_ensure_behavior.py

这些 Python 验收检查可在本地运行,也会由托管 CI 在 Linux 和 macOS 上针对发布二进制运行,同时还会执行 Rust 和打包检查。

建议注入

INJECT 规则会以概率性方式在触发规则的工具调用附近添加建议上下文。这些内容是提示,而非授权:常规的 ALLOW/DENY/ASK/GATE/ENSURE 检查仍会先执行,并遵循首个匹配规则生效的原则,且结果是确定的。注入在之后运行,并且仅当匹配的注入规则触发时才会输出上下文。

rules:
  - name: maybe_remind_kindex_on_git
    tool_pattern: "^Bash$"
    conditions: ["contains(parameters, 'git ')"]
    action: INJECT
    inject:
      trigger:
        mode: exponential
        peak: 0.35
        cooldown_seconds: 300
        peak_after_seconds: 1800
        max_per_session: 3
      payload:
        text: "Before committing, check whether project `.kin` files should be included."

触发模式:

模式行为
constant / step冷却后使用固定概率
linear在 peak_after_seconds 时间内从 0 逐渐升至 peak
exponential通过指数衰减逐渐接近 peak

载荷来源:

来源说明
text内联字面文本
text_file~/.signet/injections/ 下的纯文件名
from_command来自 ~/.signet/inject_commands.yaml 的 HMAC 签名允许列表条目;直接执行,不使用 shell

模板替换默认启用:{tool_name}、{cwd}、{date} 和 {matched_param.X}。请参阅 examples/inject_examples.yaml。

条件函数

函数描述示例
contains(parameters, 'X')工具输入包含字符串contains(parameters, 'rm ')
any_of(parameters, 'X', 'Y')存在任意一个字符串any_of(parameters, 'mkfs', 'format')
param_eq(field, 'value')字段等于指定值param_eq(category, 'books')
param_ne(field, 'value')字段不等于指定值param_ne(role, 'admin')
param_gt(field, N)字段 > 数字param_gt(amount, 100)
param_lt(field, N)字段 < 数字param_lt(amount, 5)
param_contains(field, 'X')字段包含子字符串param_contains(command, 'sudo')
matches(field, 'regex')字段匹配正则表达式matches(file_path, '\\.env$')
has_credential('name')凭据存在于保险库中has_credential('cc_visa')
spend_gt('cat', N)会话支出 > 限额spend_gt('books', 200)
spend_plus_amount_gt('cat', field, N)支出 + 此金额 > 限额spend_plus_amount_gt('books', amount, 200)
not(condition)对条件取反not(param_eq(format, 'json'))
or(A || B)满足任一条件or(contains(parameters, '-f') || contains(parameters, '--force'))
has_recent_action('search', N)最近允许的操作在工具名称或详情中匹配;用竖线分隔表示 ORhas_recent_action('EnterPlanMode|TaskCreate', 500)
has_current_session()Hook 宿主提供了独立的聊天/会话标识符has_current_session()
contains_masked_value(parameters)工具输入包含机密屏蔽参考文件中的字面值;也接受字段名称。未配置时永不匹配contains_masked_value(command)
true / false字面值true
按模型限定的规则

Hook 模式调用可以携带 agent_model 字段,用于标识发起工具调用的模型。只有宿主可以提供该字段:Signet 会读取 Hook 信封中的 model(字符串或带有 id 的对象)或 Antigravity 的 modelName,除此之外不会读取任何内容。

  • 每次评估时,工具输入中的任何 agent_model 都会被丢弃(包括所有 Hook 适配器、MCP 代理和 Kindex 任务准入),因此 Agent 无法自行声明模型。宿主仍会运行原始工具输入。
  • Signet 从不从会话记录中读取模型。Agent 可以写入该文件,因此其中记录的模型只是声明,并非事实。
  • Claude Code 的经典 PreToolUse 输入没有模型字段,因此旧版 Claude Hook 永远不会看到 agent_model。可选的函数适配器(adapters/claude-function)会在宿主提供时传递宿主的 $.session.model() 返回值。这是会话的模型;它是否指明子 Agent 自身的模型则未作限定。
  • MCP 代理和 Kindex 准入调用没有宿主证明的模型,因此其中始终不存在该字段。
  • 只有规则或当前生效的预检指定了 agent_model 时,才会附加该字段。其他策略看到的工具输入与之前完全相同。

模型未知时,该字段不存在,并按空字符串进行比较。请将条件写成取反的允许列表,以便在关闭失败时拒绝未知模型:

- name: cloud_tools_require_opus
  tool_pattern: "^Bash$"
  conditions:
    - "matches(command, '(^|[;&|(\\s])(gcloud|cloud-sql-proxy)(\\s|$)')"
    - "not(matches(agent_model, '^claude-(opus|fable)-'))"
  action: DENY
  locked: true
  reason: "gcloud and cloud-sql-proxy are limited to Opus and Fable"

在不报告模型的宿主上,此规则会拒绝所有匹配的调用。 命令匹配仍基于子字符串,因此,若 Agent 执意通过解释器或包装脚本调用同一二进制文件,仍可绕过此规则。

密钥遮蔽

可选。创建 ~/.signet/secret_mask.yaml(或 $SIGNET_DIR/secret_mask.yaml), 指定存放密钥的 shell 环境文件:

files: [~/.profile]          # required; nothing is masked without files
keys: [OPENAI, "AWS_*_KEY"]  # optional; default fragments below
min_length: 8                # optional; shorter values are ignored

没有该文件时,此功能处于关闭状态。~ 会展开为 $HOME;相对路径相对于 signet 目录。该文件位于 ~/.signet/ 下,因此自我保护规则会阻止 Agent 编辑它。

每个引用文件都按行读取:可选的 export ,然后是 NAME=VALUE,其中 NAME 必须匹配 [A-Za-z_][A-Za-z0-9_]*。匹配的首尾引号会被移除,未加引号的值末尾的 #comment 会被删除。包含 $ 或反引号的值、引号未闭合或引号拼接的值,以及短于 min_length 的值都会被跳过。

当键名包含任意一个 keys 条目(不区分大小写)时,该键会被选中,其中 * 可匹配任意长度的字符序列。因此,完整名称也会选中包含它的更长名称。如果未指定 keys,默认片段为 KEY、TOKEN、SECRET、PASSWORD、PASSWD、PASS、PW、CREDENTIAL、AUTH、PRIVATE、SID、DSN、WEBHOOK、COOKIE、CLIENT_ID、ACCOUNT_ID 和 API(src/secret_mask.rs 中的 DEFAULT_KEY_PATTERNS)。

选中值的每次出现,以及其 JSON 转义形式,都会替换为 $NAME。较长的值优先替换;长度相同时按文件中的顺序处理;多个名称共用同一个值时,会遮蔽第一个名称对应的值;同一名称定义两次时,两个值都会被遮蔽。文件会缓存,并在其 mtime 或大小发生变化时重新读取。文件缺失或不可读时,会在 stderr 上输出一条包含路径的警告,且不会遮蔽该文件中的任何内容;配置无效时,会输出一条警告并关闭遮蔽功能。

遮蔽适用于 signet-eval 持久化或显示的内容:操作账本、预检记录和违规项、Kindex 结果传递、Claude 函数适配器使用的 integration redact 清理器、MCP 管理回复(最近操作、状态、违规项、规则列表、测试结果)、MCP 代理原因、钩子拒绝/询问原因和建议上下文,以及 rules、test 和 log CLI 输出。它不会更改策略评估的输入,也不会重写主机运行的命令。包含遮蔽值的预检定义会被拒绝,与其他密钥一样。Vault 凭据会按原样存储。

命令可能确实需要字面值,因此默认规则 block_masked_secret_literal 在工具输入包含该值时会请求确认,而非拒绝。原因会指出键名($NAME),绝不会暴露值,并建议改用 $NAME。可像任何未锁定的默认规则一样,在 rules.yaml 中覆盖此规则。规则原因和替代方案可以使用 {masked_keys} 占位符,该占位符会展开为匹配到的键名。signet-eval status 会报告被遮蔽的键名和文件数量。

遮蔽可以避免 signet-eval 显示这些值,但无法阻止主机将工具输出发送给模型。因此,锁定规则 protect_secret_reference_files 会拒绝命令或路径中提及引用文件的任何工具调用:cat ~/.profile、grep KEY $HOME/.profile、对该文件使用 Read 工具、编辑或修补该文件,或命令在其目录中运行(或 cd 到该目录)时仅使用文件名。source FILE 和 . FILE 仍然允许,因为它们不会打印内容;请按名称使用已加载的变量。不会检查写入的内容、提示词和描述,因此文档可以提及该文件。匹配基于词法分析:不会拦截通配符、符号链接、Agent 外部创建的副本,或对父目录进行的递归搜索。未配置时绝不会匹配。

加密保险库

采用基于口令派生密钥层级的三层加密存储(Argon2id + AES-256-GCM):

层级加密方式内容
1无操作日志、支出账本
2会话密钥会话状态
3分区密钥信用卡号、API 令牌、机密
signet-eval setup                      # create vault with passphrase
signet-eval store cc_visa 4111...      # store Tier 3 credential
signet-eval status                     # vault status and spending
signet-eval log                        # recent action log
signet-eval unlock                     # refresh session after timeout

凭据通过 request_capability 支持范围受限的访问:域名限制、用途约束、单次使用金额上限,以及使用一次后自动失效的一次性令牌。

支出限额使用保险库账本——每次会产生费用的工具调用都会被记录,spend_plus_amount_gt() 会在允许下一次购买前检查累计金额。

自我保护

signet-eval 内置锁定规则,防止 AI Agent 禁用自身的策略强制执行机制:

  1. protect_signet_dir — 拒绝任何涉及 .signet/(策略文件、保险库、HMAC)的 Write、Edit 或 Bash 命令

  2. protect_signet_binary — 拒绝篡改 signet-eval 二进制文件

  3. protect_hook_config — 修改 settings.json(配置钩子的位置)前必须获得用户确认

  4. protect_signet_process — 拒绝针对 signet 进程的 kill/pkill/killall 命令

  5. protect_preflight_storage — 拒绝 Agent 侧修改 preflight 记录

  6. require_kindex_engagement_before_edits — 在记录持久化会话上下文之前,拒绝直接编辑工具

  7. prefer_persistent_task_store — 拒绝使用上述五个 Claude 任务状态工具,并引导 Agent 使用持久化 Kindex 任务。Agent、旧版子 Agent Task、TaskOutput 和 TaskStop 并非任务状态工具,因此此规则不会拦截它们。

  8. protect_checks_dir — 拒绝 Agent 侧替换受信任的 ENSURE 脚本

  9. protect_vault_passphrase — 保险库的设置和解锁操作仅限人类执行

  10. protect_signet_symlink — 拒绝针对受保护强制执行界面的符号链接绕过操作

  11. protect_secret_reference_files — 拒绝读取 secret_mask.yaml 中列出的环境文件;source 仍可使用(参见 Secret masking)

这些规则具有以下特性:

  • 锁定 — MCP 工具拒绝移除、编辑或重新排序这些规则
  • 位置受保护 — 未锁定规则不能被重新排序到锁定规则之前(先匹配者优先)
  • 默认值中硬编码 — 如果策略文件损坏或缺失,二进制文件会回退到包含自我保护规则的硬编码默认值
  • 按版本协调 — 当前编译的默认值会按规则名称覆盖过时的系统策略快照,因此升级后无需重新运行 init
  • 保留名称协调 — 内置规则名称由二进制文件所有;主机专属系统规则必须使用不同名称,而用户规则仍是覆盖未锁定默认规则的受支持方式
  • 由 HMAC 保护 — 直接编辑文件会破坏策略签名,从而触发安全默认值回退

MCP 管理服务

通过 Claude 以对话方式管理策略:

claude mcp add --scope user --transport stdio signet -- signet-eval serve
工具用途
signet_list_rules显示所有规则及其锁定状态
signet_add_rule添加新规则(追加在锁定规则之后)
signet_remove_rule移除规则(拒绝移除锁定规则)
signet_edit_rule修改规则属性(拒绝修改锁定规则)
signet_reorder_rule移动规则(拒绝移动锁定规则,并阻止将规则移到锁定规则之前)
signet_set_limit为某个类别设置支出限额
signet_test根据当前策略测试工具调用
signet_validate检查策略是否存在错误
signet_condition_help显示可用的条件函数
signet_status保险库状态、支出总额和凭据数量
signet_recent_actions显示近期操作日志
signet_store_credential存储 Tier 3 凭据
signet_use_credential通过能力约束请求凭据
signet_list_credentials列出凭据名称
signet_delete_credential删除凭据
signet_sign_policy使用 HMAC 为策略文件签名
signet_reset_session清除支出计数器

保险库可用时,所有变更操作都会自动为策略签名。

MCP 代理

通过策略强制执行来包装上游 MCP 服务。Agent 连接到代理,而非直接连接到服务。每次调用时都会热重载策略。

# Configure upstream servers
cat > ~/.signet/proxy.yaml << 'YAML'
servers:
  linear:
    command: npx
    args: ["-y", "mcp-linear"]
    env:
      LINEAR_API_KEY: "your-key"
YAML

# Register proxy with Claude Code
claude mcp add --scope user --transport stdio signet-proxy -- signet-eval proxy

所有命令

命令用途
signet-eval钩子评估(默认,25ms)
signet-eval --adapter codexCodex PreToolUse 钩子评估
signet-eval --adapter codex-permissionCodex PermissionRequest 钩子评估
signet-eval --adapter antigravityAntigravity PreToolUse 钩子评估
signet-eval --adapter cursorCursor preToolUse、beforeShellExecution、beforeMCPExecution 和 beforeReadFile 钩子评估
signet-eval --adapter museMuse Code PreToolUse(以及可选的 PermissionRequest)钩子评估
signet-eval integration describe从 JSON 标准输入中只读检查策略/能力
signet-eval integration adjudicate具有持久化意图回执的、受范围和修订版本约束的 Kindex 任务准入
signet-eval integration record-result幂等交付 Kindex 任务回执;不能证明任务已执行
signet-eval integration redact用于受控输出投影的结构化净化器
signet-eval integration install-modern显式安装二进制文件内嵌的 Claude 适配器并创建备份;保留禁用的强制执行状态
signet-eval init写入包含锁定自我保护规则的默认策略
signet-eval rules显示当前策略规则(标记锁定规则)
signet-eval validate检查策略是否存在错误
signet-eval test '<json>'根据策略测试工具调用
signet-eval setup创建加密保险库
signet-eval unlock刷新保险库会话
signet-eval status保险库状态和支出
signet-eval store <name> <value>存储 Tier 3 凭据
signet-eval delete <name>删除凭据
signet-eval log近期操作日志
signet-eval reset-session清除支出计数器
signet-eval sign使用 HMAC 为策略文件签名
signet-eval injections显示近期触发的注入规则
signet-eval inject-test <rule>强制触发一条注入规则以进行测试
signet-eval serveMCP 管理服务(17 个工具)
signet-eval proxy用于上游服务的 MCP 代理

性能

指标数值
钩子评估(端到端)25ms — 进程启动、标准输入、JSON 解析、策略加载、评估、响应
进程内策略评估14–63μs — 拒绝 14μs、询问 21μs、支出检查 63μs
CLI 校验 / 规则8ms
二进制文件大小6.2MB(已剥离符号,LTO)

架构

signet-eval 是 Signet 个人主权 Agent 技术栈的强制执行层。核心原则:**授权层不能使用大模型。**它只处理结构化数据——正则表达式、比较和保险库查询。不涉及自然语言、上下文窗口或劝说空间。规则要么匹配,要么不匹配。

Agent proposes action  ->  signet-eval evaluates policy  ->  allow / deny / ask
                           (deterministic, 25ms, no NLP)

安全模型

signet-eval 是安全带,而不是牢笼。了解它能防范和不能防范的情况至关重要。

它能防范什么

遵循协议的 Agent 犯错。 Claude Code、Codex、Antigravity、Cursor 和 Muse Code 会遵循钩子协议——它们会在使用工具前后调用 signet-eval,并遵循受支持的响应。在这些协议范围内,signet-eval 能可靠地执行策略:

  • 阻止破坏性命令(rm、mkfs、管道传递的远程执行)
  • 在多次工具调用中执行开支限额
  • 根据域名、用途和金额限制凭据访问
  • 对敏感操作要求用户确认(git push --force)
  • 通过锁定规则和 MCP 防护机制,防止随意篡改策略

这是常规运行模式。Agent 并非试图逃逸——它只是想提供帮助,而策略会确保它不越界。

Codex 支持有一项重要的语义差异:目前,PreToolUse 只能通过拒绝来执行策略,因此 Signet 的 ALLOW 不会输出任何内容,而 Signet 的 ASK 在此阶段会转换为拒绝。PermissionRequest 适配器会明确允许或拒绝 Codex 原本就要提交审批的请求;Signet 的 ASK 则交由 Codex 的常规审批提示处理。

Antigravity 会发送嵌套的 toolCall 负载,并期望收到顶层的 decision 响应。该适配器会将原生命令和文件工具规范化为 Signet 的标准策略名称,将 call_mcp_tool 解包为 mcp__<server>__<tool>,并在原生封装格式有误时默认拒绝。

Cursor 语义(已针对 cursor-agent 2026.10.01 验证):该适配器会读取 hook_event_name,并以 Cursor 的扁平 permission 对象作答。只有 beforeShellExecution 可以询问用户;在该事件中,Signet 的 ASK 会转为 Cursor 的审批提示。preToolUse 只能执行拒绝;尽管 Cursor 的文档列出了 ask,cursor-agent 会忽略 beforeMCPExecution 上的 ask;而 beforeReadFile 只接受 allow 或 deny,因此在这三个事件中,Signet 的 ASK 都会转为拒绝。 Cursor 的 Shell 工具映射为 Bash;beforeMCPExecution 映射为 mcp__<server>__<tool>,参数则从 Cursor 的 JSON 字符串中解码;preToolUse 看到的 MCP 调用只有 MCP:<tool>,不包含服务名,这就是示例将 MCP 路由到专用事件的原因。 beforeReadFile 会评估路径,而非 Cursor 附带的文件内容。 Signet 会明确输出 allow,因为 Cursor 会将空输出视为钩子失败。除非条目设置了 failClosed: true(如示例所示),否则钩子崩溃、超时或以非零状态退出时,Cursor 都会默认放行; 无论如何,无效的 JSON 都会阻止操作。Cursor 也会运行 Claude Code 中的钩子: ~/.claude/settings.json 和项目级 .claude/settings*.json。这些钩子收到的是 Cursor 负载,因此在其中直接运行 signet-eval Claude 钩子,会在 Cursor 中再次进行一次未经规范化的评估; 它不会与 Cursor 适配器去重,因为二者的命令不同。

Muse Code 语义(已针对 Muse 1.4.6 验证):Muse 会发送 Claude 格式的 PreToolUse 负载,但使用自己的工具名称。该适配器会将 shell(以及 bash、bash_input、exec_command、execute_command)映射为 Bash, 将 read_file/write_file/edit_file 映射为 Read/Write/Edit(并将原生 path 复制到 file_path),将 search 映射为 Grep,并将 web_fetch/web_search 映射为 WebFetch/WebSearch;MCP 工具传入时已经是 mcp__<server>__<tool>。Muse 的 PreToolUse 只能执行拒绝:它会忽略 ask,并拒绝单独的 allow,因此 Signet 的 ALLOW 不会输出任何内容(注入提示时除外,此时只输出 additionalContext),而 Signet 的 ASK 会转为拒绝。Muse 没有故障关闭钩子选项:钩子崩溃、超时或无法解析时,工具调用仍会继续。 在工作区受信任之前,.muse/hooks.json 中的项目钩子会保持禁用。Muse 不会运行 Claude Code 或 Codex 钩子。该适配器也接受 Codex 响应格式的 PermissionRequest(ASK 会交由 Muse 的提示处理),但示例中省略了它:在默认的 ALLOW 策略下,它会批准 Muse 原本要请求审批的每一次调用。

无法防护的情形

拥有 shell 访问权限的恶意 Agent。 signet-eval 与其约束的 Agent 处于相同的权限级别。任何具有相同 UID 的进程都可以:

  • 使用 shell 间接调用绕过字符串匹配(alias、eval、base64 -d | sh、python3 -c "...")
  • 以看似无害的名称,在受保护路径下创建符号链接
  • 读取会话密钥文件并伪造 HMAC 签名
  • 替换 signet-eval 二进制文件本身
  • 修改 settings.json 中的钩子配置
  • 使用 LD_PRELOAD、ptrace 或进程注入

再多的模式匹配也无法解决这个问题。为 ln 添加规则,就会出现 alias。屏蔽 alias,就会出现 eval。屏蔽 eval,就会出现 python3 -c。这是一个无限倒退的问题——根本原因在于权限级别,而不是模式覆盖范围。

真正的隔离措施

要防护真正具有恶意的 Agent,需要使用高于 Agent 权限级别运行的操作系统级控制措施:

  • 独立用户账户——以受限用户身份运行 Agent,使其无法写入策略文件
  • 容器/沙盒——使用 Docker、Flatpak 或 bubblewrap,并将 ~/.signet/ 以只读方式挂载
  • 精简的 $PATH——从 Agent 的环境中移除 ln、alias 和其他间接调用工具
  • seccomp / AppArmor / SELinux——Agent 无法覆盖的强制访问控制
  • 虚拟环境——在受限环境中运行 Claude Code,并限制其文件系统访问权限

在这样的配置中,signet-eval 是策略层。它通过清晰、可审计的规则,处理“应允许此 Agent 执行哪些操作”这一问题。操作系统负责处理“此 Agent 能否绕过策略”这一问题。二者互不可替代。

纵深防御

各层协同工作:

层级防护对象机制
字符串匹配明显的错误、清晰的用户体验正则表达式、子字符串、单词边界条件
锁定规则随意篡改基于 MCP 的策略不可变规则、位置保护
HMAC 签名带外文件修改加密完整性验证
操作系统控制权限提升、shell 间接调用沙盒、RBAC、独立用户

没有操作系统控制措施时,signet-eval 只是一个减速带,而不是一道墙。有了这些措施,它就是安全边界内的策略引擎。

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。