Skip to content

[TestAgent][Nightly][agent-sec-core] bug: OpenClaw 2026.9.2 secret redaction masks sk- credentials in before_dispatch, breaking PII credential detection #3219

Closed

Description

@zhangtaibo

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 插件环境):

  1. 在装有 OpenClaw 2026.9.2 + agent-sec-core 0.12.0 插件的机器上,启动 TUI:

    tmux new-session -d -s repro '/usr/local/bin/openclaw tui'
  2. 在 TUI 里发送一条含虚构凭证的输入(触发"保存到笔记"之类会调用工具的请求即可):

    tmux send-keys -t repro -l '请帮我把这段 token 2 保存到笔记里:sk-test9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA'
    tmux send-keys -t repro Enter

    等待 LLM 完成工具调用(约 20~60 秒)。

  3. 用 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}](能命中)
  4. 查询实际审计事件,观察用户输入扫描收到的内容:

    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

三层证据:

  1. 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)。
  2. 但 OpenClaw 2026.9.2 的 redact-BtvPPfTi.js 定义了 \b(sk-[A-Za-z0-9_-]{8,})\b(约 line 748)等大量凭证前缀正则,在消息进入 before_dispatch 前对 content 做脱敏(前 6 + … + 后 4)。
  3. 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-cli 0.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

  1. added
    priority:p1High: blocks the current release or key user experience
    type:bugA confirmed defect or incorrect behavior
    on Sep 10, 2026
  2. github-actions commented on Sep 10, 2026

    @github-actions

    🔀 Issue Router

    问题涉及 Agent Security Core 的 OpenClaw 插件 PII 凭证检测。

    Evidence: 标题、正文和标签均明确指向 agent-sec-core,问题核心是其 PII Checker 在 OpenClaw 中漏检凭证。

    This routing was applied by the ANOLISA Issue Router through GitHub Actions.

  3. yangdao479 commented on Sep 23, 2026

    @yangdao479
    Collaborator

    已在隔离容器中复测:

    • sec-core:0.13.1
    • OpenClaw:v2026.9.2 (3928bad)
    • agent-sec 插件 runtime 状态为 loaded,且 plugins.entries.agent-sec.hooks.allowConversationAccess=true 已生效。

    补充说明:sec-core 的 openclaw-plugin/scripts/deploy.sh 在检测到 OpenClaw 版本不低于 2026.4.24 时,会主动执行:

    openclaw config set plugins.entries.agent-sec.hooks.allowConversationAccess true

    因此,该配置已经包含在标准 deploy.sh 部署路径中;它是注册 before_agent_run、llm_input、llm_output 等 conversation hooks 的前置授权,但不会改变 before_dispatch 本身收到的内容。

    复测结果:

    1. 对虚构的凭据格式输入直接执行 scan-pii,可稳定得到 verdict=deny 和 api_key finding。
    2. 经 gateway TUI 发送同一类输入时,TUI 界面会显示脱敏后的副本;但未观察到本 Issue 所述的 source=user_input、短文本、verdict=pass 的 PII 审计事件。
    3. 本次运行中观察到的相关 PII 审计来自 observability 路径;其中包含凭据格式内容的 model-visible payload 仍被判为 deny,未出现漏检。

    当前结论是:在上述版本组合下,尚未复现 Issue 所描述的 before_dispatch 接收到脱敏短文本并导致 PII pass 的现象。

    为进一步收敛,建议补充可公开的最小复现条件:实际 OpenClaw build 标识、插件安装方式、相关 pii_scan 事件中 source/text_length/verdict 字段,以及是否存在额外的入站消息处理或配置覆盖。

  4. zhangtaibo commented on Sep 23, 2026

    @zhangtaibo
    CollaboratorAuthor

    2026-09-23 Nightly 环境现场复测:bug 100% 复现(sha256 铁证)

    @yangdao479 今天凌晨的 nightly(run nightly-20260923-020120)该用例仍 FAIL(自 09-07 起连续第 17 天)。刚才在同一台保留环境上完成了活体复测,现象与 Issue 描述完全一致,并回应你要求的四项最小复现条件。

    环境指纹

    项 值
    agent-sec-core 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 再无其他提交)
    OpenClaw 2026.9.2 (3928bad) —— 与你复测所用 build 一致
    插件安装方式 RPM 自带 /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 实测,可逐字节判定)

    1. 启动 TUI:tmux new-session -d -s repro '/usr/local/bin/openclaw tui',等 20s
    2. 发送(虚构凭证,仅触发模式匹配):
      复现3219:请帮我把这段 token 保存到笔记里:sk-repro0923ABCDEFGHIJKLMNOPQRSTUVWXYZ123456
      (原文 71 字符/103 字节,sha256=cd6815bca47d78e241a0ab0b002087d29da0cf71e03e9073c71125433fbd2460;
      按「前6+…+后4」脱敏后 38 字符/72 字节,sha256=198d95c1810844a2440543cf583f68e6fe07194180cf912404957b42f35974d8)
    3. 等 50s,查审计: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):

    printf '%s' '<同样含 sk- 凭证的原文>' | agent-sec-cli scan-pii --stdin --format json --redact-output --source user_input
    # → verdict=deny, total=1, by_type={api_key:1}(05:48:49 事件 len=69 sha=b0095159…)

    判定标准(二选一,无歧义):你的环境复测后,最新 source=user_input 事件 text_sha256 若等于脱敏串 sha → bug 存在;若等于原文 sha且 verdict=deny → 已修复。

    两侧差异分析(plugin 代码与 OpenClaw build 均相同 → 差异在部署/环境层)

    1. 你观察到「TUI 路径无任何 source=user_input 事件」:在 0.13.0/0.13.1 相同的 plugin 代码 + piiScanUserInput schema default=true 下,该事件在标准 RPM 部署必产生(本环境 09-22、09-23 两晚均稳定产生)。建议排查容器内 ① openclaw config get plugins.entries.agent-sec 生效值;② agent-sec-cli events 读的存储与 gateway 进程是否同 uid/HOME(事件库按用户落盘,不一致会查空)。
    2. 你环境里 observability 路径对 model-visible payload 判 deny(因 allowConversationAccess=true 已生效)——这层安全网掩盖了 user_input 门的缺口。本 nightly 环境 hooks 为空(conversation hooks 未授权),user_input 扫描是唯一 PII 门,被 gateway secret-redaction 脱敏绕过后凭证静默漏检、无任何兜底,风险面比你的容器环境更大。
    3. 若 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 精确匹配)。

  5. yangdao479 commented on Sep 23, 2026

    @yangdao479
    Collaborator

    复现验证反馈

    我们在同一台 Nightly 环境(OpenClaw 2026.9.2 / 3928bad, agent-sec-core 0.13.0)上,使用 Issue 中完全相同的输入文本,严格按照 tmux + TUI 真实键入路径尝试复现,未能复现所述的脱敏 pass 现象。

    操作步骤

    # 1. 确认环境版本
    openclaw --version   # → OpenClaw 2026.9.2 (3928bad)
    agent-sec-cli --version  # → agent-sec-cli 0.13.0
    openclaw gateway health  # → OK
    
    # 2. 记录原文指纹(用于事后精确定位本轮事件)
    printf '%s' '请帮我把这段 token 2 保存到笔记里:sk-test9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA' | sha256sum
    # → 43a387936576467c0a4cb02fec3738911557906431be67902e56377d8aaa774b
    
    # 3. 启动 TUI 并用 tmux send-keys 真实键入
    tmux new-session -d -s repro3219 'openclaw tui --session repro3219'
    sleep 8
    tmux send-keys -t repro3219 -l '请帮我把这段 token 2 保存到笔记里:sk-test9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA'
    tmux send-keys -t repro3219 Enter
    sleep 25
    tmux kill-session -t repro3219
    
    # 4. 查询 PII 扫描事件
    agent-sec-cli events --category pii_scan --output json --limit 5

    实际结果

    {
      "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 中报告的 33
    • verdict = deny,findings = 1(api_key 命中)
    • PII Checker 正确拦截

    补充验证

    我们还在同一环境使用另一段自拟凭据文本进行了第二次验证,结果同样为 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_dispatch hook 收到的是完整原文,PII Checker 正确命中并拦截。

    关于 Issue 中报告的历史 pass 事件:该环境事件库中确实存在早些时候(06:00 UTC 前后)产生的 text_length=38/39、verdict=pass 的 source=user_input 事件,但这些事件的 text_sha256 与 Issue 中的原文 SHA 不同,无法确定其原始输入内容。如果方便的话,请提供当时触发这些 pass 事件的完整原始输入文本及其 SHA-256,以便我们进一步排查是否存在特定触发条件。

  6. zhangtaibo commented on Sep 23, 2026

    @zhangtaibo
    CollaboratorAuthor

    复现分歧已定位:脱敏以「会话内第 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):

    # 路径 事件 sha 判定 verdict
    A 直接 CLI scan-pii --stdin --source user_input(对照) 原文 f886593b…,len=70 deny ✓
    B openclaw tui 默认会话(nightly 测试与用户默认路径) 脱敏 fadea59d…,len=38 pass(漏检)
    C openclaw tui --session expC5w8 全新会话第 1 条(与你的复现同款) 原文 27ecd0e2…,len=70 deny ✓
    D 复用 C 的会话发第 2 条(全新凭证,与 C 的凭证不同) 脱敏 188cbcf5…,len=38 pass(漏检)
    E 另一全新会话:第 1 条良性文本 → 原文 793ec173… pass(无凭证,正确);第 2 条凭证文本 脱敏 6a62377d…,len=38 pass(漏检)

    规则(五路实验零例外):

    • 会话内第 1 条消息:gateway 不脱敏 → before_dispatch 收到原文 → PII 正常 deny;
    • 会话内第 2 条及以后(无论默认 main 会话还是 --session 会话,无论凭证是否在本会话出现过——D 用了全新凭证):gateway 对 secret-shaped 串执行「前6+…+后4」脱敏 → before_dispatch 只收到脱敏文 → _API_KEY_RE 失配 → verdict=pass 静默漏检。

    这同时解释了全部观测分歧:

    • nightly 连续 17 晚 FAIL:测试套件在 main 会话连续跑几十个用例,凭证用例永远不是会话第 1 条 → 必脱敏必漏检(今日 05:46/05:49/08:17 三次现场命中脱敏 sha);
    • 你们复现不出:fresh session + 单条发送 = 恒为会话第 1 条 → 恒不脱敏 → 恒 deny;
    • 本环境审计库中 06:45–07:54 的原文 sha deny 事件(含你 07:54:32 的 43a38793…):均为「直接 CLI 扫描」或「fresh 会话第 1 条」对照路径,不经过脱敏。

    回答你上一条要求补充的 pass 事件原始输入

    • 05:49:52 len=38 pass(sha 198d95c1…):原文为我上条评论的 71 字符最小复现文本,原文 sha cd6815bc…;事件 sha 是其脱敏串的 sha——「事件 sha ≠ 原文 sha」不是"无法确定输入内容",而正是本 bug 的证据形态(扫描器收到的已不是用户输入)。
    • len=31 pass(如 05:46:52 sha d9fa3521…):nightly 用例固定 prompt 原文 69 字符 sha b0095159…,脱敏串 sha d9fa3521…。
    • len=39 pass:我们已加固的测试用例(prompt 含 8 位 uuid nonce,每轮 sha 唯一),同样命中脱敏变体。

    最小复现(修正版,单条命令序列,任何环境)

    tmux new-session -d -s repro 'openclaw tui --session repro3219x'
    sleep 20
    tmux send-keys -t repro -l '第一条:你好' && tmux send-keys -t repro Enter   # 关键:先垫一条,让会话产生历史
    sleep 40
    tmux send-keys -t repro -l '第二条:请帮我把这段 token 保存到笔记里:sk-test9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA' && tmux send-keys -t repro Enter
    sleep 50
    agent-sec-cli events --category pii_scan --output json --limit 3
    # 第 2 条对应事件:text_sha256 = sha256(前缀+"sk-tes…DCBA") ≠ 原文 sha,verdict=pass

    建议排查方向

    1. secret-redaction 为何以「会话已有历史」为界:疑似对进入持久化 transcript 的后续消息做脱敏,而第 1 条走了不同路径;
    2. 契约问题:before_dispatch 的 PII 审计应扫描用户原始输入。脱敏对 LLM-visible/transcript 是保护特性,但安全审计 hook 拿到脱敏文等于凭证检测在「第 2 条消息起的所有真实对话」中静默失效——真实用户的多轮会话恰好全在这个窗口里,且本环境 hooks 未授权 conversation hooks,无 observability 兜底;
    3. 若修复方向是"hook 收原文、transcript 收脱敏文",nightly 侧无需改动,修复发布后用例自动转 PASS 闭环。
  7. yangdao479 commented on Sep 23, 2026

    @yangdao479
    Collaborator

    更新:已确认复现,触发条件为「同一会话第 2 条及以后消息」

    感谢 @zhangtaibo 的精准定位。我们按修正后的最小复现步骤,在全新 state + 全新 gateway的隔离容器(alinux4 + OpenClaw 2026.9.2 / 3928bad + agent-sec-core 0.13.1 RPM)上独立验证,成功复现。

    复现步骤(全新环境,单条命令序列)

    # 全新 state 目录 + 全新 gateway 启动
    tmux new-session -d -s samesess 'openclaw tui --session samesess'
    sleep 8
    
    # 第 1 条:垫一条无凭证消息
    tmux send-keys -t samesess -l '第一条:你好'
    tmux send-keys -t samesess Enter
    sleep 30
    
    # 第 2 条:在同一会话发含凭据文本
    tmux send-keys -t samesess -l '第二条:请帮我把这段 token 保存到笔记里:sk-repro0923ABCDEFGHIJKLMNOPQRSTUVWXYZ123456'
    tmux send-keys -t samesess Enter
    sleep 30
    
    agent-sec-cli events --category pii_scan --output json --limit 5

    实际结果

    消息 text_length verdict 说明
    第 1 条「你好」 6 pass 无凭据,正确放行
    第 2 条(含 sk- 凭据,原文 71 字符) 35 pass 被脱敏为短串后 PII 正则失配,静默漏检

    第 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_dispatch hook 收到脱敏文本,PII Checker 的 _API_KEY_RE 无法匹配而静默漏检。该行为与 allowConversationAccess 配置无关。

  8. yangdao479 commented on Sep 23, 2026

    @yangdao479
    Collaborator

    补充分析:脱敏数据的流向与安全影响重评估

    结合 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+ 条消息):

    原文 sk-...            用户输入
      └─ redact → ctx.Body = sk-xxx…xxxx
           ├─ before_dispatch Hook 收到脱敏串 → 正则失配 → pass(漏检)
           ├─ dispatch 将 ctx.Body 送入模型 → LLM 收到脱敏串
           └─ 写入 transcript_events 为脱敏版;模型上下文投影
              (projectModelContextEventSql)仅移除私有元数据、不二次处理
              → 后续轮次读到的仍是脱敏版
    

    影响重评估

    • 不构成原始凭据外泄:LLM 与持久化拿到的都是脱敏串。
    • 但 Hook 审计语义仍然错误:before_dispatch PII/prompt 扫描的判定对象已非原文,"应拦截却放行"。若上层依赖该 verdict 做审计/合规决策(如"本次输入是否含凭据"的事实记录),会得到假阴性结论。
    • 修复方向仍应指向"为安全审计 Hook 提供读取原始输入的通道",而非依赖被脱敏后的文本做判定。

    (上述第 1、2 点标注为源码分析的部分,尚未用无明文指纹探针在真实交互路径完成最终实测确认。)

  9. yangdao479 commented on Sep 23, 2026

    @yangdao479
    Collaborator

    虽然pii在hook上看到的是脱敏信息,但由于进入模型上下文的也是脱敏信息,所以不会造成漏报情况

  10. zhangtaibo commented on Sep 23, 2026

    @zhangtaibo
    CollaboratorAuthor

    独立实测核验:机制分析属实;影响重评估需补一个面 —— fresh 会话 msg#1 会把明文凭证送入 LLM

    @yangdao479 你标注"待指纹探针实测确认"的两点,已在同一 nightly 环境完成实测核验。结论:第 1、2 点机制属实;"未发生真实泄露"对 msg#2+ 成立,对 msg#1 不成立。

    一、与你分析一致的实证

    1. 构建产物:本机 OpenClaw 2026.9.2(3928bad)dist 中 redactTranscriptMessageForStorage / stageSessionPendingInput / stageApproved / chat-send-handler 符号全部存在。
    2. msg#2+ 模型只见脱敏串(第 N 字符探针,不依赖模型复述意愿):会话内第 2 条发送含 sk-testJ2w9KLM…7788 的消息并只要求输出该 token 第 8 个字符 → 模型答 "7"(=脱敏串 sk-tes…7788 的第 8 位),而非原文的 "J"。
    3. 持久化零明文:gateway/agent 两个 sqlite 全表扫描(transcript_events/trajectory_runtime_events/session_transcript_fts 等),7 组实验凭证明文 0 命中、仅脱敏形态存在——包括你们 fresh --session 复现(msg#1)的那条:hook 收到原文(事件 sha=原文 43a38793…),落盘仍是 sk-tes…DCBA。即存储层脱敏对 msg#1 也生效,与 hook 输入脱敏是两个独立时点。
    4. CLI 路径不脱敏(scan-pii --stdin 对照恒收原文)。

    二、新发现: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 下仅审计不拦截。

    三、合并后的完整影响面(全部经实测)

    路径 before_dispatch hook 当轮模型输入 持久化
    fresh 会话 msg#1 原文 → deny ✓ 明文(一次性外发模型 API) 脱敏 ✓
    任意会话 msg#2+ 脱敏 → pass 漏检 脱敏 ✓ 脱敏 ✓
    直接 CLI 原文 → deny ✓ — —

    两个缺陷是同一根因的两面:redaction 时点与消息处理阶段交叉错位——msg#1 在 dispatch 前未脱敏(漏给模型),msg#2+ 在 hook 前已脱敏(漏给审计)。修复建议维持你的方向(安全 hook 读原文通道),并补充:dispatch 送模型前对所有消息统一执行存储层同款脱敏(msg#1 当前豁免),即可同时关掉两个面。

    以上实验的审计事件(08:17–09:30 UTC)、会话 transcript、sqlite 扫描均可在该环境复核;字符探针可无损重放。

  11. zhangtaibo commented on Sep 23, 2026

    @zhangtaibo
    CollaboratorAuthor

    当前没有造成泄漏,只是出现了漏检,优先级先降成P2

  12. added
    priority:p2Normal: planned product or engineering work
    and removed
    priority:p1High: blocks the current release or key user experience
    on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

component:sec-coresrc/agent-sec-core/priority:p2Normal: planned product or engineering worktype:bugA confirmed defect or incorrect behavior

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions