Skip to content

finding(core): ObjectLogger 的脱敏表按子串匹配,一个叫 keys 的普通字段会被整块换成 ***REDACTED*** #5573

Closed

Description

@os-zhuang

在做 #5048(PR #5572)时实测发现,顺手记录。

现象

packages/core/src/logger.ts 的 redactSensitive 递归遍历 meta,判定条件是子串包含:

private redactSensitive(obj: any): any {
    ...
    const lower = key.toLowerCase();
    if (this.config.redact.some((p: string) => lower.includes(p.toLowerCase()))) {
        redacted[key] = '***REDACTED***';
    }

默认脱敏表(logger.ts:97,与 packages/spec/src/system/logging.zod.ts:71 的 schema 默认值一致)是 ['password', 'token', 'secret', 'key']。于是任何名字含这些子串的字段都会被整块替换,不问值是什么:

  • keys(含 key)
  • keyword / keywords、keyboard、monkey
  • tokenizer、tokens(计数!)
  • secretary

实测(format: 'json',值是一个普通的字符串数组):

log.warn('…', { issues: [{ code: 'unrecognized_keys', keys: ['visibleIf'], path: ['nodes', 0] }] })
→ {"…","issues":[{"code":"unrecognized_keys","keys":"***REDACTED***","path":["nodes",0]}]}

keys 里根本没有秘密,但读者拿到的是 ***REDACTED*** —— 而且这个字样会让读者以为确实有一个秘密被挡住了,比字段缺失更误导。

为什么是 finding 而不是 bug

全仓 grep 过,目前没有任何 in-tree 的 logger.* 调用在 meta 里放这类字段名(#5048 差点成为第一个 —— 那条路已经在 PR #5572 里绕开了:字段改名叫 unrecognized,并写了 pin 测试固定「原样转发会被脱敏」这个事实)。所以今天没有用户会撞上,归观察类。

但 ObjectLogger 是公开导出的,redact 也是 LoggerConfig 的公开配置项,host 侧插件自己写 logger.info(..., { keys: [...] }) 就会撞上,而且撞上时是静默的 —— 没有任何提示说脱敏器动过手。

取舍(不预设结论)

子串匹配不是笔误,它是为了一网打尽 apiKey / secretKey / privateKey / accessToken 这些真实拼法 —— 换成精确匹配会漏掉它们,那更危险。所以这不是「改成 equals 就好」,而是需要维护者定一个方向,几个选项:

  • A. 保持子串,只在词边界上匹配:key 匹配 apiKey/api_key(camelCase / snake_case 分词后是独立词),但不匹配 keys/monkey。能同时保住覆盖面和精度,实现是分词而不是 includes。
  • B. 保持子串,加一个例外表(keys、keyword、…)。便宜,但例外表永远追不齐,是治症状。
  • C. 值敏感:只脱敏标量,不脱敏数组/对象。恰好能救 keys: [...],但 apiKeys: ['sk-…'] 就漏了 —— 方向不对。
  • D. 不改,只记入文档,让作者自己避开这些字段名。等于把一个静默数据丢失的陷阱转成文档义务。

倾向 A(词边界),因为它在「不漏真秘密」和「不吃普通字段」之间不用二选一;但这动的是脱敏语义,属于安全相关默认值,不该由一个顺手的 finding 决定 —— 留给维护者。

影响面


Generated by Claude Code

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    and removed on Aug 5, 2026
  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    分诊(域改标 + 发现分诊轮判级):

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    维护者裁决(2026-08-06,经 services 车道 PM 转达):批准 A 案(词边界匹配)

    redactSensitive 由子串 includes 改为 camelCase/snake_case 分词后的词边界匹配:key 匹配 apiKey/api_key,不匹配 keys/monkey/keyword。

    硬性验收条件(裁决的一部分):

    • 落地 PR 必须带真秘密拼法矩阵守卫测试 —— apiKey/api_key/accessToken/refresh_token/secretKey/privateKey/clientSecret 等全数仍被脱敏(词边界不得漏真秘密);
    • 把 keys/keyword/tokens/monkey 钉为不脱敏(本单的症状面);
    • packages/spec/src/system/logging.zod.ts:71 的 schema 默认表若需同步,按契约面申报。

    摘 finding 换 pm:queue,domain:engine-core 座位执行(PR #5572 的 pin 测试「原样转发会被脱敏」届时按条改判)。


    Generated by Claude Code

  4. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:queueReady for the PM dispatch loop
    on Aug 6, 2026
  5. self-assigned this
    on Aug 6, 2026
  6. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    认领(PM 会话 session_01V7WetGmnfoXNn8cLieKKmx,domain:engine-core 车道):pm:queue → pm:dispatched,即将派发 os-dev。

    • 分支:claude/issue-5573-redact-word-boundary
    • worktree:../objectstack-5573
    • 文件面:packages/core/src/logger.ts(redactSensitive)+ 同包测试 + changeset;另按维护者裁决(评论 5199145168)允许触碰 PR fix(service-automation): flow 绑定失败的告警改用结构化 meta,不再把 Zod issue 数组塞进单行日志 (#5048) #5572 落的那个「原样转发会被脱敏」pin 测试所在文件(仅改判该 pin 的断言,以裁决为据,无论其在哪个包)。
    • 修法照维护者批复的 A 案:子串 includes 改为 camelCase/snake_case 分词后的词边界匹配。硬性验收(裁决原文):真秘密拼法矩阵(apiKey/api_key/accessToken/refresh_token/secretKey/privateKey/clientSecret 等)全数仍脱敏;keys/keyword/tokens/monkey 钉为不脱敏;默认表本身不动。若实现中发现 packages/spec/src/system/logging.zod.ts 需同步,停下申报,不自行改 spec。

    竞态复查与派发随后。


    Generated by Claude Code

  7. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    PM 评审(会话 session_01V7WetGmnfoXNn8cLieKKmx):PR #5786 ACCEPT,附一条小指令,完成后待 CI 绿放行。

    已读全量 diff。要点:

    1. A 案实现合格且硬性验收全数满足:42 条真秘密矩阵(camelCase/snake/SCREAMING/kebab/连写/复合复数)仍脱敏;14 条症状面(含裁决点名的 keys/keyword/tokens/monkey)不再脱敏;默认表与 spec schema 默认值未动。
    2. 两个边角的取舍我认可:连写救援用显式限定词表(31 词、只认后缀)而非「以 key 结尾」——正确识别了任何区分 apikey/monkey 的规则都必须编码词法知识,且方向比 B 案保守(漏一个限定词只回到词边界基线);复数按「复合命中、裸词放行」,裸 passwords/secrets 的推论后果与 tokenCount 词义残留都钉成测试并点名属维护者面,不越权收窄。
    3. 额外价值:证实了一个活的 in-tree 命中(dispatcher-plugin.ts 的 keyedBy 当年改名躲子串仍被吃),本 PR 后该限流日志首次真正可读——issue 的「无 in-tree 命中」前提被推翻,修复的实际收益上调。
    4. pin 改判符合裁决授权且翻到正面断言(visibleIf 逐字出现 + issues 结构相等),比「没产出所以过了」强;模块 docblock 第二条理由(有界性)正确保留。
    5. 反向验证 17 红全落症状面、42 绿矩阵不动,方向先判后跑;「多词 pattern 转红」如实标注为净增覆盖。

    小指令(裁决你的请示项):thrown-cause-diagnostics.ts 模块 docblock 第一条 bullet(仍描述子串脱敏旧机制)就地订正——一句话改动、纯注释,文件面扩展由本裁决背书;追加一次提交即可,PR 正文的 ⚠️ 段落改为「已按 PM 裁决订正」。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions