跳到主要内容

PII Checker 用户使用指南

PII Checker 用于检测 Agent 输入和输出中的个人数据与凭据。它返回结构化 verdict,生成安全的 evidence 和可选脱敏文本,并记录经过清理的 Security Event,供审计和 Observability 关联使用。

V2 第一阶段​

V2 RPM 提供 Rust agent-sec-cli 和 agent-sec-daemon。在专属 Linux 验证环境使用 ./scripts/rpm-build.sh agent-sec-core-v2 构建的 RPM;本次迁移不切换现有 Agent 宿主或服务。 安装该 RPM 后,启动系统服务,由服务创建受保护的运行目录:

sudo systemctl start agent-sec-core

通过 daemon 的默认 socket 发起扫描:

agent-sec-cli scan-pii --text 'contact alice@company.cn' --source manual

默认端点为 /run/agent-sec-core/daemon.sock。非空的 AGENT_SEC_DAEMON_SOCKET 可覆盖默认值, agent-sec-cli --socket /absolute/path/to/daemon.sock scan-pii ... 优先于环境变量。 CLI 不自动启动 daemon,也不回退到 Python。文件和 stdin 由 CLI 读取;daemon 只接收文本, 不接收调用者指定的输入文件路径或规则路径。空文本合法;--text-stdin 是 --stdin 的别名。 扫描完成并返回 pass、warn、deny 时退出 0;扫描或连接失败退出 1,CLI 用法错误退出 2。 deny 是检测分类,不表示 PDP 已拒绝操作。

未指定 --max-bytes 时,V2 不默认截断输入。显式正数上限保留有效 UTF-8 前缀;非法 UTF-8 报错。请求业务部分满足 V2 的 4 MiB 预算,包含 JSON 转义及信封开销;传播成员另有 32 KiB 请求预算,响应仍为 4 MiB。超限显式失败。 span 以 Unicode 字符计数,与 Python 位置语义一致,不使用 UTF-8 字节或 UTF-16 单元。

V2 保留原有顶层结果字段,在 summary 中增加证据元数据:

字段含义
execution_statuscompleted 或 failed
coverage.statuscomplete、partial 或 unavailable
coverage.reasons输入截断、规则无效、匹配受限或扫描失败的安全错误码
input_sha256检测器收到文本的 SHA-256,不代表未收到的原始内容
scanned_input_sha256、scanned_bytes实际扫描前缀的摘要及字节数
scanner_version检测语义版本;V2 当前为 2.0.1
ruleset_idscanner 版本及不可变内置/自定义规则配置的标识

bytes_scanned 保留 V1 前缀计数,可能包含被 scanned_bytes 排除的不完整 UTF-8 尾部。 partial 结果仍可能为 pass:verdict 只聚合已发现的 findings。判断证据是否完整时必须检查 coverage。

V2 在匹配过程中限制保留的 finding 明细,增量构造脱敏文本,并将返回报告限制在 512 KiB 的格式化 JSON 内。超限报告仅保留每种 类型/严重级别的首条 finding,省略原始证据;若明细或证据发生缩减,设置 summary.findings_truncated=true。 verdict、summary.total、类别/严重级别统计、摘要和扫描 coverage 仍代表扫描范围内全部已检测 结果。Hook 提示使用这些总数并说明明细已省略;此时返回的 findings 是代表性证据,不是完整位置列表。

若完整 redacted_text 仍使报告超限,V2 用 [REDACTED: output size limit] 替代整段文本, 并设置 summary.redacted_text_omitted=true,不会返回未完成脱敏的尾部。两个标记在 false 时省略。 仅缩减输出不会使扫描 coverage 变成 partial;已完成扫描仍退出 0。独立的 PiiScanner 也遵守相同上限;省略明细或脱敏输出后仍继续检测,完整统计匹配数并聚合 verdict。

daemon 最多同时执行两个 PII 扫描,不排队;额外请求返回 scan_busy。工作线程直到实际执行 退出才释放名额,传输层超时不会提前释放。Executor 在入口检查已有取消状态,在匹配调用之间和 长扫描循环中检查继承的 deadline,失败分别返回 scan_cancelled 或 scan_deadline_exceeded。 这些失败仍经相同审计投影和 Finalizer 收尾,返回 ok=false、verdict error、 coverage unavailable,CLI 退出 1。单次正则调用受引擎回溯上限约束,deadline 不能强制 中断该调用;不增加通用取消框架或重试队列。 传输层或 CLI 超时可能先于扫描报告返回,此时调用者收到传输失败,工作线程仍完成安全错误的 终态审计并释放执行名额。

检测语义版本​

summary.scanner_version 标识检测行为,独立于 AgentSecCore 包版本和 RPC schema。 V2 的成功/失败扫描报告及审计结果均记录 2.0.1;内置 finding 的 engine 为 regex_v2, 自定义 finding 为 fancy_regex。ruleset_id 包含此版本、内置模式及自定义配置, 即使 YAML 内容不变,引擎行为变化仍可追溯。CI 的 commit SHA 标识被测构建,不是检测语义版本。

2.0.0 保留 11 类检测、置信度、Unicode span 和脱敏,并明确调整以下 V1 行为:

  • JWT 候选包含紧凑的空 claims({}),修复 V1 payload 最短长度门槛造成的漏报。 大整数和深层 JSON 对象采用结构校验,不继承 Python 整数转换或递归限制。非法 JSON 仍拒绝; 检测不验证签名真实性,也不授权 Token 的使用。
  • 中国身份证的日期和校验位统一转换 decimal 数字,包括全角日期、数字校验位及 X/x, 同时保留原文和字符位置。
  • 银行卡检测排除能通过 Luhn 的全零占位符;命中不代表号码已经发行。2.0.1 增加下述结构校验。
  • 自定义模式使用下述原生方言。与 Python 的行为不同,本身不会使有效 V2 规则变成 invalid, 也不会因此将 coverage 标记为 partial。

保留的 V1 语料验证不变行为,独立的正反例和资源限制用例定义 V2 变化。这些检查覆盖支持的格式, 不代表对任意数据零误报漏报。短密钥、无标签密钥、11 类之外的格式和上下文歧义, 仍需结合业务规则及有代表性的样本评估。

支持的银行卡格式​

2.0.1 及保留的 Python 检测器依次要求完整数字格式、受支持网络的前缀与长度组合、Luhn 校验。 前缀与长度采用固定版本的卡组织定义:

卡组织支持的数字位数
Visa16、18、19
Mastercard16
American Express15
Diners Club14、16、19
Discover16、19
JCB16、17、18、19
UnionPay14、15、16、17、18、19

支持连续数字。分组形式只接受统一的单个 ASCII 空格或连字符:Amex 为 4-6-5, 14 位 Diners 为 4-6-4,其他支持的组合从左每四位一组、末组保留余数,如 4-4-4-4-3。 重复或混用分隔符、非常规分组均拒绝。保留现有 Unicode 十进制数字归一化与字符 span。 所有网络均要求 Luhn;全零值在网络前缀校验时拒绝。

检测器校验数字、空格、连字符构成的完整表达式,不从不合法表达式内部重新匹配: 1-4111111111111111 被拒绝,card-4111111111111111 和 /cards/4111111111111111/ 仍可检出。末尾没有后续数字的空格、连字符不进入 finding。 逗号和换行分隔表达式;仅用空格连接的两个卡号构成一个被拒绝的表达式。 同一规则消除版本/时间戳路径误报,不对路径设置豁免。

这是明确限定的格式和网络集合,不查询卡号是否真实发行。集合外的格式可能漏检, 无关数字标识也仍可能满足全部条件。severity、置信度计算和 Host 策略行为不变。

使用随包 Skill​

安装 agent-sec-cli 且 Agent 能发现 pii-checker Skill 后,可以要求它检查指定文件中的 个人信息或凭证,或生成脱敏副本。V2 需先启动 daemon。Skill 报告脱敏证据,仅在用户要求时改写输入文件。 扫描完成且没有命中,不代表内容一定不含敏感信息。

RPM 安装通过 agent-sec-skills 分发此 Skill,ANOLISA raw 包将其放入共享 Skill 目录。 Cosh-NG 会发现该目录;OpenClaw 和 Hermes adapter 声明了 pii-checker,用于分发到各自的 Skill 目录。其他 Agent 安装需通过其自身的发现路径提供此 Skill。

扫描文本​

必须且只能提供一种输入来源:内联文本、标准输入或 UTF-8 文件。

# 内联文本
agent-sec-cli scan-pii --text "contact alice@example.com"

# 标准输入
printf '%s' 'token=secret-value-1234567890' \
| agent-sec-cli scan-pii --stdin --redact-output

# UTF-8 文件
agent-sec-cli scan-pii --input ./agent-output.txt --format text

常用选项:

选项作用
--format json|text选择结构化 JSON 或可读文本;默认是 json
--redact-output返回 redacted_text;不会修改输入文件
--include-low-confidence包含低于默认置信度阈值的 finding
--raw-evidence仅在本地 CLI 输出中包含原始 evidence
--max-bytes N最多扫描 N 个 UTF-8 字节,并标记结果已截断
--source SOURCE标记审计上下文,例如 user_input 或 tool_output

支持的 source 包括 user_input、model_input、tool_input、tool_output、model_output、 observability、manual 和 unknown。

内置检测​

内置检测器结合正则匹配、格式校验和基于上下文的置信度调整。

类别类型默认严重级别
个人数据email、phone_cn、credit_card、cn_idwarn
凭据private_key、bearer_token、api_key、jwtdeny
阿里云凭据aliyun_access_key_id、aliyun_access_key_secretdeny
敏感字段generic_secret_fielddeny

信用卡号、中国身份证号和 JWT candidate 在生成 finding 前会经过格式校验。周边安全关键词可能提高 置信度,example、dummy、test、sample 等测试标记可能降低置信度。低于默认 0.5 阈值的 finding 会被忽略,除非使用 --include-low-confidence。

邮箱检测支持句末 ASCII 句号及连续句号组成的省略号;连续句号后必须是输入结束、空白或支持的 句末标点、闭合分隔符。例如,alice@company.co.uk. 的 finding 仅包含 alice@company.co.uk, 句号保留在 finding span 和脱敏替换范围之外。.123、.c、.-bad、._bad 和 ..evil 等 非法域名后缀会被拒绝,不会截取较短邮箱作为 finding。保留域名和远程身份仍沿用原有的低置信度处理。

Verdict 与脱敏​

Scanner 将 findings 聚合为一个 verdict:

Verdict含义
pass置信度过滤后没有 finding
warn存在 finding,但没有 deny 严重级别
deny至少一个 finding 的严重级别为 deny

每个 finding 包含类型、类别、严重级别、置信度、span、detector metadata 和脱敏 evidence。 --redact-output 还会返回扫描文本的脱敏副本。重叠 finding 会全部保留,其重叠 span 会合并并只 替换一次;不同 span 发生重叠时,合并后的完整范围会被完全脱敏,避免较短命中留下敏感后缀。

--raw-evidence 仅用于本地排查,原始 evidence 永远不会写入 Security Event。各宿主集成消费相同的 verdict 和 finding schema;宿主只观察 finding 还是阻断操作,由该宿主配置的 PII policy 决定。

宿主 Hook Policy​

设置 PII_CHECKER_HOOK_ENABLED=false 可完全跳过宿主 PII hook。启用后, 有原生提示能力的宿主支持 observe、warn、ask、block,默认 observe。Hermes 只支持 observe / block;旧 warn / ask 会降级为 observe 并写宿主诊断,绝不 把 advisory 注入助手最终回复。Hermes 的 block 只能在 pre_tool_call 对 deny 生效; 模型输出通过 post_llm_call 执行 audit-only 扫描,但由于 Hermes 没有插件可用的 pre-stream output gate,不会修改或阻断模型输出。其它宿主或 hook 事件无法执行确认/阻断时 可 fallback 为 warn;后置 hook 不会声称撤销已经发生的 外部副作用。环境变量 policy 优先于 Hermes/OpenClaw capability 配置。debug 映射为 observe,deny 映射为 block。仅 Qwen Code 在未设置 PII_CHECKER_HOOK_ENABLED 时 额外兼容旧开关 PII_CHECKER_ENABLED。

PII_CHECKER_HOOK_ENABLED 和 PII_CHECKER_MODE 被全部六个宿主读取。另有两个变量只被部分 宿主读取:

环境变量默认值读取该变量的宿主
PII_CHECKER_TIMEOUT5Qoder、Codex、Qwen Code(Qwen Code 上限为 8 秒)
PII_CHECKER_INCLUDE_LOW_CONFIDENCEfalseQoder、Qwen Code

cosh、Hermes 和 OpenClaw 不读取这两个环境变量。Hermes 可通过 capability 配置同时支持 这两项(timeout 和 include_low_confidence)。OpenClaw 只支持 piiIncludeLowConfidence;PII scanner CLI 超时固定为 10 秒。cosh 使用固定超时,且从不 请求低置信度 finding。

宿主 Agent 在加载插件时读取这些变量。修改后需重启承载该 hook 的 Agent 进程; hook 和 agent-sec-core 并不是需要单独重启的 policy 服务。

scanner verdict deny 描述扫描风险,hook policy block 决定 adapter 是否执行阻断。

OpenClaw 输入保护​

在正式版 OpenClaw >=2026.5.12 上,PII Checker 使用 before_agent_run, 只扫描 prompt 中的本轮用户输入,不扫描 system prompt、会话历史、媒体载荷或消息元数据。 prompt 缺失、不是字符串或仅含空白时跳过扫描,不回退读取其他字段。 仍记录为 source=model_input,用于标识模型运行入口 hook,不代表覆盖完整上下文。 检查对象是 OpenClaw 在运行入口提供的本轮输入,不覆盖运行中的每一次模型请求, 也不覆盖宿主独立的辅助模型调用(例如会话标题生成),不保证能读取用户原始输入。

较旧的受支持版本(>=2026.4.14)、预发布版以及无法识别的版本,使用 before_dispatch 和 source=user_input,只扫描 OpenClaw 提供的入站文本。 启动日志会注明选择的输入 hook,使用旧路径时会给出兼容提示。PII 输入 hook 只注册一个。

before_agent_run 需要 plugins.entries.agent-sec.hooks.allowConversationAccess=true;部署脚本已在支持该配置的宿主上设置它。 block 模式下,deny 结果会阻断此处的模型提交;observe 记录扫描,warn 记录告警, ask 因此 gate 不支持确认而降级为告警。扫描器失败时告警后放行。插件不会用脱敏文本替换模型输入。

pass 只描述实际扫描的文本。如果 OpenClaw 已将凭据打码,实际模型请求也只包含打码后的内容, 没有重新识别出原始凭据并不意味着发生泄露。应结合扫描输入和实际模型请求验证保护效果, 而非要求每条原始用户消息都产生凭据 finding。

before_tool_call 保留工具参数检查及确认/阻断行为。after_tool_call 只能记录或告警, 即使配置为 block,也不能脱敏或扣留工具结果;结果仍可能进入后续模型上下文,这是已有的执行控制缺口。 llm_output 同样只观测或告警,不脱敏或阻断交付。这些 hook 不构成覆盖所有模型请求的完整防泄露保证。

自定义正则规则​

内置规则随二进制发布,自定义规则独立配置:

Runtime自定义文件更新生效方式
V2/etc/agent-sec/pii-checker/rules.yamldaemon 启动时校验编译,重启后更新
V1~/.config/agent-sec/pii-checker/rules.yaml下一次扫描加载新内容

管理员可通过 agent-sec-daemon --pii-rules /absolute/path/rules.yaml --socket /run/agent-sec-core/daemon.sock 指定其他绝对路径。所有调用者共享同一份不可变规则集合;V2 不读取调用者 HOME,不按 owner 选择规则,也不自动导入旧用户目录。扫描 RPC 不能覆盖此选择。

YAML 顶层是数组。每条规则包含唯一的自定义类型、一个正则表达式和可选严重级别。

- type: dogfood_order_no
regex: '(?<=order_no[=:])DFT-[A-Z0-9]{8}'
severity: warn

- type: dogfood_customer_token
regex: 'DFT-[A-Z0-9]{16}'
severity: deny
字段必填说明
type是小写 snake_case 自定义类型,在文件内唯一
regex是单个正则表达式;V2 使用原生 fancy-regex 语法
severity否warn 或 deny;默认 deny

正则的完整匹配范围就是 finding 和脱敏范围,普通捕获组和命名捕获组不会改变该范围。如果正则同时 匹配字段名和值,二者都会被脱敏;如需完整匹配只覆盖值,请使用 lookaround。同一个类型的多种格式 需要通过正则 \| 合并,同一个 type 不能定义多条规则。

自定义 finding 固定使用 category custom、confidence 1.0、detector custom_rule 和 engine V1 为 regex、V2 为 fancy_regex。它会使用 [DOGFOOD_ORDER_NO_REDACTED] 这类稳定类型标记进行完全脱敏,并进入与内置 finding 相同的 verdict、policy、Security Event 和 Observability 链路。

V1 不支持覆盖规则路径。两个版本都不合并多个规则文件。V2 检测规则属于检测配置, 不是 PAP Policy,也不会单独授予操作权限。

自定义规则校验与运行时限制​

整份自定义规则集以原子方式接受或拒绝。

限制或规则值
文件大小上限256 KiB
规则数量上限100
单条正则长度上限2,048 个字符
YAML 嵌套深度上限64
V2 正则分组递归限制64 个解析帧(包括根帧)
Type 格式^[a-z][a-z0-9_]{0,63}$
Severitywarn 或 deny
单条规则匹配限制V1:20 ms;V2:1,000,000 次回溯
单次扫描自定义匹配总预算200 ms
单次扫描自定义 finding 上限100

未知 YAML 字段、重复 type、内置类型名、非法正则以及能在空字符串上产生匹配的正则都会让整份 自定义规则集无效。运行时遇到的其他零长度匹配会被忽略。

deny 规则先于 warn 规则执行,同一 severity 内保持文件顺序。100 条上限只有在额外有效命中 被省略时才设置 truncated。V1 继续评估后续规则;V2 此时停止剩余自定义匹配,并标记覆盖不完整。 V2 在匹配操作之间检查 200 ms 总预算,不承诺单次匹配会在 20 ms 后被中断。 回溯或其他匹配限制触发时保留此前 findings。

V2 直接使用锁定版本的 fancy-regex 方言。引擎支持的 (?i) / (?x) 等 flag、 注释、lookaround 和集合运算均可使用,不因 Python 解释方式不同而改写或拒绝规则:

  • $ 默认断言输入末尾;(?m)$ 也匹配行末。\z 严格表示输入末尾,\Z 可匹配末尾换行之前。
  • \h / \H 表示十六进制/非十六进制字符;水平空白可明确写成 [ \t]。
  • (?i) 使用引擎的 Unicode 大小写折叠,不复刻 Python 对带点/无点 I 的处理。 字符类之外的 \< / \> 是单词边界断言。
  • 字符类支持嵌套集合和集合运算。命名组是否参与匹配的条件使用 (?(<name>)yes\|no); Python 的 (?(name)yes\|no) 写法在此有不同含义。

invalid_regex 仅用于实际解析、编译、引擎限制或加载时求值失败,例如不支持的 branch-reset 分组、变长 lookbehind。引擎将根帧计入分组递归限制:63 层分组可接受,64 层可能失败; 嵌套字符类采用独立的引擎限制。YAML 别名和多文档输入会拒绝;执行受限时报告 partial coverage。

迁移自定义规则时,应按有版本的 V2 方言验证正反例,覆盖 Unicode 和末尾换行。 编译成功本身不能证明任意管理员规则符合业务意图。

V2 各请求持续使用启动时规则集合,直到重启;下一次启动发现替换文件无效时禁用整份自定义集合, 不会悄悄沿用旧规则。V1 在下一次扫描加载。两者均继续内置检测;V2 标记覆盖不完整。

自定义规则状态​

每次默认扫描都会在 summary.custom_rules 中返回经过清理的自定义规则状态:

{
"custom_rules": {
"status": "loaded",
"rule_count": 2,
"runtime_error_count": 0,
"budget_exhausted": false,
"truncated": false
}
}

默认文件不存在时 status 为 absent(V2 显式指定文件不存在时为 invalid);校验成功时为 loaded,空数组也属于加载成功;读取、YAML 解析、schema 校验或正则编译失败时为 invalid。无效状态包含经过清理的 error_code;已加载或 无效内容可能包含其 SHA-256 摘要。运行时计数器不会包含输入文本或正则内容。直接执行 scan-pii 时还会向 stderr 输出经过清理的无效配置告警,同时保持成功退出。

当前可能出现的 error_code 如下:

错误码含义
read_error规则文件无法读取
file_too_large规则文件超过 256 KiB
invalid_utf8规则文件不是有效的 UTF-8
invalid_yamlYAML 内容无法安全解析
top_level_not_listYAML 顶层不是数组
too_many_rules文件包含超过 100 条规则
invalid_rule_schema规则缺少字段、包含未知字段、字段类型错误或使用不支持的字段值
invalid_rule_type规则 type 不符合要求的命名格式
duplicate_rule_type同一个自定义 type 出现多次
reserved_rule_type自定义 type 与内置 PII 类型冲突
invalid_regex正则语法不支持、无法编译、超出引擎限制或加载期求值失败
regex_matches_empty_text正则可在空字符串上产生零长度匹配
load_errorV1:未预期的加载器错误已按 fail-open 模式处理

Security Event 与 Observability​

每次扫描都会进入现有 pii_scan Security Event 链路。Event 包含 source、verdict、summary、 finding type、severity、category、span 和脱敏 evidence,不包含自定义规则路径、正则表达式或原始 敏感命中值。

自定义规则无效时,宿主 hook 保持 fail-open,不新增宿主侧告警。hook 调用以 Security Event 中 经过清理的 summary.custom_rules 状态作为结构化审计来源。

Observability 使用现有 trace context 和输入 hash 与 Security Event 建立关联,不重复存储 finding 明细。

V2 审计与规则迁移​

V2 使用公共 Action Runtime 和 Finalizer。正常执行生命周期内,完成、部分完成、扫描失败以及 已授权但扫描参数无效的请求,各产生一次扫描终态事件。无效信封、未知方法、授权失败和传输失败 由各自入口处理;崩溃或强制退出可能阻止 Finalizer 执行。

审计只持久化安全请求元数据、摘要、规则标识、coverage、脱敏 findings 和错误码。 原文、原始 evidence、完整 redacted_text、规则内容和可能含输入的异常文本被排除。 JSONL/SQLite sink 健康与扫描成功分别判断;现有 daemon 启动仍要求 SQLite 初始化成功。 V2 暂未提供事件查询 CLI。

Hook 可在 scan-pii 之前传入顶层 --trace-context '{"session_id":"session-1"}'。 保留 V1 snake_case/camelCase 别名归一化,字符串去除首尾空白并限制为 256 字符。 这些字段经公共 CLI 适配归入 OTel Context 并使用顶层 carrier 传播;opaque trace 标签不是 OpenTelemetry TraceId。PII RPC 参数不接受 traceContext,应使用公共顶层 traceContext / compatibility 信封。legacy trace 输入错误退出 1,普通 CLI 用法错误退出 2。 UID/GID/PID 始终来自 UDS peer; agent_name 只是有长度限制的调用者元数据,不授予权限。

迁移规则时,审核选定的 V1 文件、解决类型名冲突,再显式将批准的 YAML 放入管理员管理的 V2 文件。 启动或重启 daemon,检查 summary.custom_rules.status、ruleset_id 和 coverage,运行正反例。 不会自动汇总用户目录。回滚规则时恢复原中央文件并重启;回滚 runtime 时停止验证 daemon, 恢复 V1 RPM/入口及独立保留的用户规则。V2 不修改这些 V1 文件。

六种 Hook adapter 使用固定事件和真实 Rust PII 子进程验证,包括观测脱敏;尚未迁移的观测存储 在测试边界隔离。这属于 Hook 契约验收,不代表真实宿主切换或完整 PIP/PDP/PEP 接入。 详见两阶段设计。