Repository navigation
[TestAgent][Nightly][agent-sec-core] bug: OpenClaw 2026.9.2 secret redaction masks sk- credentials in before_dispatch, breaking PII credential detection #3219
Description
Component
agent-sec-core(OpenClaw 适配层插件 openclaw-plugin 的 PII Checker 能力)
Bug Description
OpenClaw 2026.9.2 在消息进入插件 before_dispatch 钩子前,对用户输入内容做了网关级 secret-redaction,把形如 sk-… 的凭证串脱敏为「前 6 字符 + … + 后 4 字符」(例如 sk-test9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA → sk-tes…DCBA)。
agent-sec 的 PII Checker 通过 before_dispatch 钩子调用 inboundPiiScanText(event) 读取 event.content 做凭证检测,但读到的已经是脱敏后的文本。脱敏后的 sk-tes…DCBA 不再匹配 agent-sec 的内置凭证正则 _API_KEY_RE(要求 sk- 后跟 [A-Za-z0-9_=-]{16,}),于是 scan-pii 返回 verdict=pass、findings=[],用户输入的凭证被静默漏检。
该问题由 AgenticOS Nightly 自动化测试在 2026-09-11 nightly(run_id=nightly-20260911-020033,anolisa main fd2646a)如实上报 FAIL 后定位。
Steps to Reproduce
产品侧手工操作(无需 agentic-os-tests 测试仓,只需 OpenClaw + agent-sec 插件环境):
-
在装有 OpenClaw 2026.9.2 + agent-sec-core 0.12.0 插件的机器上,启动 TUI:
tmux new-session -d -s repro '/usr/local/bin/openclaw tui' -
在 TUI 里发送一条含虚构凭证的输入(触发"保存到笔记"之类会调用工具的请求即可):
tmux send-keys -t repro -l '请帮我把这段 token 2 保存到笔记里:sk-test9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA' tmux send-keys -t repro Enter等待 LLM 完成工具调用(约 20~60 秒)。
-
用 agent-sec CLI 直接扫同一段文本作对照(证明扫描器本身能命中):
printf '%s' '请帮我把这段 token 2 保存到笔记里:sk-test9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA' \ | agent-sec-cli scan-pii --stdin --format json --redact-output --source user_input # → verdict=deny, findings=[{type:api_key, confidence:0.63}](能命中)
-
查询实际审计事件,观察用户输入扫描收到的内容:
agent-sec-cli events --category pii_scan --output json --limit 5
找到
source=user_input的事件,其request.text_length只有 33(原文 65 字符),summary.bytes_scanned只有 61(原文 91 字节),verdict=pass、findings=[]。
Actual Behavior
before_dispatch 拿到的是脱敏后的内容。实测触发一次含凭证的输入后,agent-sec-cli events 中 source=user_input 事件:
{
"request": {
"source": "user_input",
"text_length": 33,
"text_sha256": "11b8d5f10dc3928e85a4f3d913a15e8a601e5f23120deeefa9d5ac4839b2413d",
"redact_output": true,
"input_truncated": false
},
"result": {
"ok": true,
"verdict": "pass",
"summary": {
"total": 0,
"by_type": {},
"source": "user_input",
"bytes_scanned": 61,
"truncated": false
},
"findings": [],
"elapsed_ms": 0
}
}对 text_sha256 逆向比对确认:content 实际为 请帮我把这段 token 2 保存到笔记里:sk-tes…DCBA(凭证被脱敏为前 6 + … + 后 4)。而同一段原文直接用 scan-pii 扫能命中 api_key(confidence 0.63、verdict=deny)。主跑与 --timeout=240 重试均稳定 FAIL,非时序 flaky。
Expected Behavior
用户输入中的凭证(API Key / Token,含 sk- 前缀)应被 PII Checker 识别并产生带凭证命中标记的 pii_scan 审计事件(契约见测试断言 test_openclaw_intercept_tmux.py::TestOpenClawPIIChecker::test_credential_input_logs_pii,期望 combined 输出含 secret/token/credential/api_key/sk-/hit 等标记)。agent-sec 的 _API_KEY_RE(src/agent-sec-core/agent-sec-cli/src/agent_sec_cli/pii_checker/detectors/regex.py:190)本应命中 sk-… 凭证串。
Root Cause
三层证据:
- agent-sec 扫描器本身能命中——
regex.py:190的正则\b(?:sk|pk|rk|gh[pousr]|xox[baprs])[-_][A-Za-z0-9_=-]{16,}\b能匹配sk-test9876543210…,confidence 0.63(scanner.py的LOW_CONFIDENCE_THRESHOLD=0.5之上,保留为 finding)。 - 但 OpenClaw 2026.9.2 的
redact-BtvPPfTi.js定义了\b(sk-[A-Za-z0-9_-]{8,})\b(约 line 748)等大量凭证前缀正则,在消息进入before_dispatch前对content做脱敏(前 6 +…+ 后 4)。 - agent-sec 插件
capabilities/pii-scan.ts的before_dispatch钩子用inboundPiiScanText(event)(helpers/pii-text.ts:26)读event.content,此时已是被 OpenClaw 脱敏的文本,故扫描sk-tes…DCBA不匹配_API_KEY_RE→findings=[]。
是 regression 还是 by-design:agent-sec 插件代码自 2026-08-14(9797f8cbe fix(sec-core): refine PII detection and notices)后未再改动;pii-text.ts 自 07-06 未改。破坏点是 OpenClaw 框架升级到 2026.9.2(其引入 secret-redaction 与 allowConversationAccess 挂钩访问门禁)后,agent-sec 仍依赖 before_dispatch 拿原文的假设失效,属上游框架升级导致的回归,非 agent-sec 自身按 commit message 的合理变更。
Suggested Fix
agent-sec openclaw-plugin 需改用能拿到未脱敏原文的挂钩来扫用户输入凭证。OpenClaw 2026.9.2 中,before_agent_run / before_agent_reply / llm_input / llm_output / agent_end 属 conversation-access 挂钩,可读原文,但要求非内置插件显式配置 plugins.entries.agent-sec.hooks.allowConversationAccess=true(当前 gateway 日志已提示 llm_input/llm_output/agent_end 被该门禁阻断)。
方向性 diff 草稿(以 plugin 侧 adapt 为主,具体以 OpenClaw 2026.9.2 语义为准):
// src/agent-sec-core/openclaw-plugin/src/capabilities/pii-scan.ts
// 方案 A:把 user_input 凭证检测从 before_dispatch 迁到 before_agent_run(可读原文,需 allowConversationAccess)
// 并确保 openclaw.json / deploy.sh 写入:
// "plugins": { "entries": { "agent-sec": { "hooks": { "allowConversationAccess": true } } } }
//
// 方案 B:若 keep before_dispatch,则需要 OpenClaw 对安全类挂钩提供未脱敏原文,
// 或在插件侧识别脱敏形态(sk-tes…DCBA)无法可靠反推,不推荐。
// 示意(方案 A 钩子注册):
// api.on("before_agent_run", async (event, ctx) => {
// const text = inboundPiiScanText(event); // 此处应读到原文
// const scanResult = await scanPiiText(api, cfg, event, ctx, text, "user_input");
// ...
// });同时需要在 deploy.sh / 安装脚本中,为 OpenClaw 2026.9.2 的插件配置追加 hooks.allowConversationAccess=true(与 --accept-capabilities(#3105/#3106)同族的上游 2026.9.2 适配)。
已在 ECS 验证根因:scan-pii --stdin 直接扫原文命中 api_key(verdict=deny),而经 tui 实发后 before_dispatch 事件 text_length=33(原文脱敏后),findings=[]。
Environment
- ALinux 4 ECS(cn-hongkong,47.86.205.162)
- OpenClaw 2026.9.2 (build 3928bad)
- agent-sec-core 0.12.0-1.alnx4(
agent-sec-cli0.12.0) - anolisa main:
fd2646ab223a98e3d1bdd3124ab4bfe25853dd21 - agentic-os-tests:
9db3703aeb50f66e4d7c499be52380d0a177bd21
Relevant Log Output
gateway 启动时(反映 OpenClaw 2026.9.2 对 conversation-access 挂钩的门禁,user_input 扫描赖以工作的 llm_input/llm_output 被阻断):
[gateway] [plugins] typed hook "llm_output" blocked because non-bundled plugins must set plugins.entries.agent-sec.hooks.allowConversationAccess=true
[gateway] [plugins] typed hook "llm_input" blocked because non-bundled plugins must set plugins.entries.agent-sec.hooks.allowConversationAccess=true
触发凭证输入后,插件日志全程只有 [pii-checker] pass(无 deny 命中),对应 findings=[]。
Additional Context
由 AgenticOS Nightly([Nightly] 前缀)发现并定位。历史:同签名失败曾现 2026-07-01 regression、2026-09-07 regression,09-08/09-10 nightly 通过后于 09-11 在 0.12.0 新构建上稳定复现——与 OpenClaw 2026.9.2 secret-redaction 的引入/回滚周期吻合。
Activity
github-actions commentedon Sep 10, 2026
🔀 Issue Router
sec-core99%问题涉及 Agent Security Core 的 OpenClaw 插件 PII 凭证检测。
Evidence: 标题、正文和标签均明确指向 agent-sec-core,问题核心是其 PII Checker 在 OpenClaw 中漏检凭证。
This routing was applied by the ANOLISA Issue Router through GitHub Actions.
yangdao479 commentedon Sep 23, 2026
已在隔离容器中复测:
3928bad)agent-sec插件 runtime 状态为loaded,且plugins.entries.agent-sec.hooks.allowConversationAccess=true已生效。补充说明:sec-core 的
openclaw-plugin/scripts/deploy.sh在检测到 OpenClaw 版本不低于2026.4.24时,会主动执行:因此,该配置已经包含在标准
deploy.sh部署路径中;它是注册before_agent_run、llm_input、llm_output等 conversation hooks 的前置授权,但不会改变before_dispatch本身收到的内容。复测结果:
scan-pii,可稳定得到verdict=deny和api_keyfinding。source=user_input、短文本、verdict=pass的 PII 审计事件。deny,未出现漏检。当前结论是:在上述版本组合下,尚未复现 Issue 所描述的
before_dispatch接收到脱敏短文本并导致 PIIpass的现象。为进一步收敛,建议补充可公开的最小复现条件:实际 OpenClaw build 标识、插件安装方式、相关
pii_scan事件中source/text_length/verdict字段,以及是否存在额外的入站消息处理或配置覆盖。zhangtaibo commentedon Sep 23, 2026
2026-09-23 Nightly 环境现场复测:bug 100% 复现(sha256 铁证)
@yangdao479 今天凌晨的 nightly(run
nightly-20260923-020120)该用例仍 FAIL(自 09-07 起连续第 17 天)。刚才在同一台保留环境上完成了活体复测,现象与 Issue 描述完全一致,并回应你要求的四项最小复现条件。环境指纹
0.13.0-1.alnx4(nightly 从 main@0bc97f95源码构建;该 commit 领先83f319ec(openclaw plugin supports 2.0)15 个提交、落后 0 —— 与你 0.13.1 dev build 的 plugin 代码相同,v0.13.0 tag 之后 sec-core 再无其他提交)2026.9.2 (3928bad)—— 与你复测所用 build 一致/opt/agent-sec/openclaw-plugin/scripts/deploy.sh标准部署openclaw config get plugins.entries.agent-sec→enabled=true, config={piiScanUserInput:true(schema default,无显式覆盖), piiIncludeLowConfidence:false, promptScanBlock:false, codeScanRequireApproval:false};hooks为空(conversation hooks 未授权,见下文差异分析)最小复现(2026-09-23 05:49 UTC 实测,可逐字节判定)
tmux new-session -d -s repro '/usr/local/bin/openclaw tui',等 20s复现3219:请帮我把这段 token 保存到笔记里:sk-repro0923ABCDEFGHIJKLMNOPQRSTUVWXYZ123456(原文 71 字符/103 字节,
sha256=cd6815bca47d78e241a0ab0b002087d29da0cf71e03e9073c71125433fbd2460;按「前6+…+后4」脱敏后 38 字符/72 字节,
sha256=198d95c1810844a2440543cf583f68e6fe07194180cf912404957b42f35974d8)agent-sec-cli events --category pii_scan --output json --limit 5实测最新事件(命中脱敏 sha,逐字节一致):
{ "timestamp": "2026-09-23T05:49:52Z", "details": { "request": { "source": "user_input", "text_length": 38, "text_sha256": "198d95c1810844a2440543cf583f68e6fe07194180cf912404957b42f35974d8", "redact_output": true, "input_truncated": false }, "result": { "ok": true, "verdict": "pass", "summary": { "total": 0, "by_type": {}, "bytes_scanned": 72 } } } }对照组(同一分钟,同环境直接扫原文,不经 gateway):
判定标准(二选一,无歧义):你的环境复测后,最新
source=user_input事件text_sha256若等于脱敏串 sha → bug 存在;若等于原文 sha且verdict=deny→ 已修复。两侧差异分析(plugin 代码与 OpenClaw build 均相同 → 差异在部署/环境层)
source=user_input事件」:在 0.13.0/0.13.1 相同的 plugin 代码 +piiScanUserInputschema default=true 下,该事件在标准 RPM 部署必产生(本环境 09-22、09-23 两晚均稳定产生)。建议排查容器内 ①openclaw config get plugins.entries.agent-sec生效值;②agent-sec-cli events读的存储与 gateway 进程是否同 uid/HOME(事件库按用户落盘,不一致会查空)。deny(因allowConversationAccess=true已生效)——这层安全网掩盖了 user_input 门的缺口。本 nightly 环境hooks为空(conversation hooks 未授权),user_input 扫描是唯一 PII 门,被 gateway secret-redaction 脱敏绕过后凭证静默漏检、无任何兜底,风险面比你的容器环境更大。83f319ec(supports 2.0)在 OpenClaw 2.0 下改变了 user_input 扫描时机(脱敏前取原文),请确认该行为在 2026.9.2 网关下是否同样生效;若 0.13.1 已修复,发布后 nightly 侧会自动闭环(连续 3 晚 PASS → pending_review → 关单)。附注(测试侧,与产品行为无关):我们的用例断言较宽(对最近 3 条事件做 marker 匹配),当审计窗口内残留有 deny 对照事件时用例可能 PASS——今天现场重跑 pytest 即因此出现一次假 PASS,但审计事件的 sha256 硬证据如上,bug 本身 100% 复现。我们会在测试仓侧单独加固该断言(按发送文本的期望 sha256 精确匹配)。
yangdao479 commentedon Sep 23, 2026
复现验证反馈
我们在同一台 Nightly 环境(OpenClaw 2026.9.2 / 3928bad, agent-sec-core 0.13.0)上,使用 Issue 中完全相同的输入文本,严格按照 tmux + TUI 真实键入路径尝试复现,未能复现所述的脱敏 pass 现象。
操作步骤
实际结果
{ "timestamp": "2026-09-23T07:54:32.677914+00:00", "source": "user_input", "text_length": 65, "text_sha256": "43a387936576467c0a4cb02fec3738911557906431be67902e56377d8aaa774b", "verdict": "deny", "findings": 1 }关键指标:
text_sha256=43a38793...与原文 SHA-256 完全一致,证明before_dispatch收到的是未经脱敏的完整原文text_length= 65(原文字符数),非 Issue 中报告的 33verdict= deny,findings= 1(api_key 命中)补充验证
我们还在同一环境使用另一段自拟凭据文本进行了第二次验证,结果同样为
text_length=71(原文长度)、原文 SHA 一致、verdict=deny。此外,在独立的隔离容器中(alinux4 + agent-sec-core 0.13.1 RPM + 分别安装 OpenClaw 2026.8.1 / 2026.9.1 / 2026.9.2),使用相同的 tmux send-keys TUI 真实输入方式,三个版本均返回
verdict=deny、text_length与原文一致。结论
在当前 Nightly 环境(OpenClaw 2026.9.2 + agent-sec-core 0.13.0)上,使用 Issue 相同的输入文本和复现步骤,
before_dispatchhook 收到的是完整原文,PII Checker 正确命中并拦截。关于 Issue 中报告的历史 pass 事件:该环境事件库中确实存在早些时候(06:00 UTC 前后)产生的
text_length=38/39、verdict=pass的source=user_input事件,但这些事件的text_sha256与 Issue 中的原文 SHA 不同,无法确定其原始输入内容。如果方便的话,请提供当时触发这些 pass 事件的完整原始输入文本及其 SHA-256,以便我们进一步排查是否存在特定触发条件。zhangtaibo commentedon Sep 23, 2026
复现分歧已定位:脱敏以「会话内第 2 条及以后消息」为触发条件;fresh
--session单条复现结构性无法命中@yangdao479 你两次复现(含隔离容器三版本)都用了
openclaw tui --session <新会话名>+ 单条发送——这恰好是唯一不触发脱敏的路径。我们在同一台 nightly 环境(OpenClaw 2026.9.2/3928bad + agent-sec-core 0.13.0-1.alnx4,同一 gateway 进程 pid 53704,02:27 UTC 启动至今未重启)做了五路受控实验,每条文本带唯一 nonce、sha256 本地预先登记,全部可在该环境审计库核对(08:17–08:25 UTC):scan-pii --stdin --source user_input(对照)f886593b…,len=70openclaw tui默认会话(nightly 测试与用户默认路径)fadea59d…,len=38openclaw tui --session expC5w8全新会话第 1 条(与你的复现同款)27ecd0e2…,len=70188cbcf5…,len=38793ec173…pass(无凭证,正确);第 2 条凭证文本6a62377d…,len=38规则(五路实验零例外):
--session会话,无论凭证是否在本会话出现过——D 用了全新凭证):gateway 对 secret-shaped 串执行「前6+…+后4」脱敏 → before_dispatch 只收到脱敏文 →_API_KEY_RE失配 → verdict=pass 静默漏检。这同时解释了全部观测分歧:
43a38793…):均为「直接 CLI 扫描」或「fresh 会话第 1 条」对照路径,不经过脱敏。回答你上一条要求补充的 pass 事件原始输入
198d95c1…):原文为我上条评论的 71 字符最小复现文本,原文 shacd6815bc…;事件 sha 是其脱敏串的 sha——「事件 sha ≠ 原文 sha」不是"无法确定输入内容",而正是本 bug 的证据形态(扫描器收到的已不是用户输入)。d9fa3521…):nightly 用例固定 prompt 原文 69 字符 shab0095159…,脱敏串 shad9fa3521…。最小复现(修正版,单条命令序列,任何环境)
建议排查方向
yangdao479 commentedon Sep 23, 2026
更新:已确认复现,触发条件为「同一会话第 2 条及以后消息」
感谢 @zhangtaibo 的精准定位。我们按修正后的最小复现步骤,在全新 state + 全新 gateway的隔离容器(alinux4 + OpenClaw 2026.9.2 / 3928bad + agent-sec-core 0.13.1 RPM)上独立验证,成功复现。
复现步骤(全新环境,单条命令序列)
实际结果
第 2 条事件的
text_length=35(远小于原文 71),text_sha256与原文 SHA 不一致,findings=0,verdict=pass——与 Issue 报告的现象完全吻合。回溯说明
我们上一条评论中「未能复现」的原因现已明确:当时每次都使用全新
--session+ 单条发送,恰好只触发了「会话第 1 条」路径(不脱敏)。这一路径差异正是 @zhangtaibo 五路实验中 C 路径的等价复制。结论
确认 Issue #3219 成立:OpenClaw 2026.9.2 对同一会话第 2 条及以后的消息执行 secret redaction(前6+…+后4),导致
before_dispatchhook 收到脱敏文本,PII Checker 的_API_KEY_RE无法匹配而静默漏检。该行为与allowConversationAccess配置无关。yangdao479 commentedon Sep 23, 2026
补充分析:脱敏数据的流向与安全影响重评估
结合 OpenClaw v2026.9.2(tag
3928bad9)源码,对本 issue 的两个关键问题做补充。以下区分已复现的观测事实与源码分析结论(待指纹探针实测确认)。1. 为什么同一会话第 2 条起才脱敏(源码分析,待实测确认)
观测事实(已复现):同一会话第 1 条含
sk-的消息before_dispatch收到原文 →deny;第 2 条起收到脱敏串sk-xxx…xxxx→pass(漏检)。源码层面一个符合该观测的候选路径:
src/gateway/server-methods/chat-send-handler.ts中,仅当entry?.sessionId存在且客户端为 Operator UI(TUI)时,才走stageApproved→stageSessionPendingInput→redactTranscriptMessageForStorage脱敏,并用脱敏后的 pending-input 覆写ctx.Body。新建会话的第 1 条消息entry?.sessionId为空,跳过该分支,ctx.Body保持原文。这也解释了该缺陷仅在真实交互 TUI 路径出现,CLI 客户端不走此分支。(精确改写点仍需 SHA-256 指纹探针在交互路径上最终确认。)2. 脱敏数据是否进入模型上下文?漏检是否造成真实泄露?(源码分析)
结论:脱敏后的数据同样进入模型上下文,模型看到的也是脱敏串。因此就"原始凭据是否泄露给 LLM / 是否被明文持久化"而言,这一轮并未发生真实泄露——但安全保障来自 OpenClaw 自身的 redaction,而非 PII Hook。
数据流(第 2+ 条消息):
影响重评估
before_dispatchPII/prompt 扫描的判定对象已非原文,"应拦截却放行"。若上层依赖该 verdict 做审计/合规决策(如"本次输入是否含凭据"的事实记录),会得到假阴性结论。(上述第 1、2 点标注为源码分析的部分,尚未用无明文指纹探针在真实交互路径完成最终实测确认。)
yangdao479 commentedon Sep 23, 2026
虽然pii在hook上看到的是脱敏信息,但由于进入模型上下文的也是脱敏信息,所以不会造成漏报情况
zhangtaibo commentedon Sep 23, 2026
独立实测核验:机制分析属实;影响重评估需补一个面 —— fresh 会话 msg#1 会把明文凭证送入 LLM
@yangdao479 你标注"待指纹探针实测确认"的两点,已在同一 nightly 环境完成实测核验。结论:第 1、2 点机制属实;"未发生真实泄露"对 msg#2+ 成立,对 msg#1 不成立。
一、与你分析一致的实证
redactTranscriptMessageForStorage/stageSessionPendingInput/stageApproved/chat-send-handler符号全部存在。sk-testJ2w9KLM…7788的消息并只要求输出该 token 第 8 个字符 → 模型答 "7"(=脱敏串sk-tes…7788的第 8 位),而非原文的 "J"。transcript_events/trajectory_runtime_events/session_transcript_fts等),7 组实验凭证明文 0 命中、仅脱敏形态存在——包括你们 fresh--session复现(msg#1)的那条:hook 收到原文(事件 sha=原文43a38793…),落盘仍是sk-tes…DCBA。即存储层脱敏对 msg#1 也生效,与 hook 输入脱敏是两个独立时点。二、新发现:msg#1 明文当轮外发模型 API
同款字符探针,fresh 会话(
openclaw tui --session expH6r4)第 1 条消息含sk-testH6r4KLM…7788,要求只输出该 token 第 8 个字符 → 模型答 "H"(=原文第 8 位)。即 msg#1 的ctx.Body未脱敏(正是你分析的entry?.sessionId为空跳过分支),明文凭证随当轮 dispatch 发送至外部模型 API(本环境为 DashScope qwen3.7-plus)。落盘虽被存储层脱敏,但对模型提供方的一次性明文暴露已经发生;此路径 hook 正确 deny(收到原文),但promptScanBlock=false下仅审计不拦截。三、合并后的完整影响面(全部经实测)
两个缺陷是同一根因的两面:redaction 时点与消息处理阶段交叉错位——msg#1 在 dispatch 前未脱敏(漏给模型),msg#2+ 在 hook 前已脱敏(漏给审计)。修复建议维持你的方向(安全 hook 读原文通道),并补充:dispatch 送模型前对所有消息统一执行存储层同款脱敏(msg#1 当前豁免),即可同时关掉两个面。
以上实验的审计事件(08:17–09:30 UTC)、会话 transcript、sqlite 扫描均可在该环境复核;字符探针可无损重放。
zhangtaibo commentedon Sep 23, 2026
当前没有造成泄漏,只是出现了漏检,优先级先降成P2