Skip to content

feat(privacy): unify redaction and data minimization behind one rule source - #64

Open
yjg-djb wants to merge 2 commits into
helsome:mainfrom
yjg-djb:fix/issue-19-unified-redaction
Open

yjg-djb wants to merge 2 commits into
helsome:mainfrom
yjg-djb:fix/issue-19-unified-redaction

Conversation

@yjg-djb

@yjg-djb yjg-djb commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Consolidates the four scattered redaction implementations into one shared rule source so every outbound boundary — kernel logs, diagnostics bundles, LangSmith telemetry, eval artifacts, IPC errors, report exports — redacts with the same rules.

New unified module: packages/shared/src/privacy

  • redactText — fail-closed string pattern engine (never falls back to the raw payload on internal failure)
  • deepRedact — deep JSON walk driven by shared secret-field-name rules, with cycle/depth guards (cyclic or over-deep nodes become [REDACTED], not a crash or a raw leak)
  • redactError — error message + stack serialization for logs/IPC/diagnostics
  • policy.ts — field-name rules, account-like key shapes, REDACTION_POLICY, telemetry content policy constants

New secret coverage (previously unhandled anywhere)

  • cookies / Set-Cookie / session_token
  • connection-string credentials (postgres://user:pass@…, redis://:pass@…, mongodb+srv://… — scheme and host preserved)
  • webhook secrets & signatures (whsec_, x-hub-signature(-256))
  • private key PEM blocks
  • secrets in URL query strings / fragments (?apikey=…, #token=…)
  • Slack xox, SendGrid SG., GitLab glpat-, npm npm_, Google AIza/ya29. tokens
  • Basic auth alongside Bearer

Boundary fixes

  • ErrorLog.push now redacts message and stack at collection time (stack first lines echo the raw error message)
  • evaluation run error objects and judge judge_error reasons are redacted before persisting to eval artifacts
  • kernel console.error site serializes via redactError
  • legacy entry points (diagnostics/redact, evaluation/redactor, export/privacy, main-process redactSecrets) now delegate to the shared module — API-compatible

Data minimization & safety properties

  • observability fields (run ids, trace ids, tool names, statuses, latencies, timestamps) are preserved — asserted by tests
  • redaction is idempotent — asserted by tests
  • default telemetry content policy documented (minimal/standard(default)/full opt-in), plus how to switch: docs/privacy-redaction.md + docs/privacy-redaction.zh-CN.md

Testing

  • 21 new unit tests in packages/shared/src/privacy/ covering every secret class above, nested tool args/results, HTTP error body echoes, fail-closed cycles/depth, immutability, and observability-field survival
  • ErrorLog gained a collection-time redaction test; existing redaction suites (diagnostics/redact.test.ts, evaluation/redactor.test.ts, export/privacy.test.ts, credentialStore.test.ts) pass unchanged
  • bun run typecheck green across all workspaces; bun test --isolate shows no new failures (5 pre-existing Windows-path/symlink failures reproduce on a clean checkout and are unrelated — CI runs on ubuntu)
  • local run of the CI secret-scan pattern set: clean

Closes #19

@yjg-djb
yjg-djb force-pushed the fix/issue-19-unified-redaction branch from 52cbdb9 to 83db04e Compare September 11, 2026 08:57
@yjg-djb

yjg-djb commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

测试报告(按维护者在 #62 上要求的格式补充)

1. 实际执行的命令

bun install --frozen-lockfile        # 依赖安装(CI 同款)
bun run typecheck                    # 全 workspace 类型检查(CI gate 1)
bun test --isolate                   # 全仓单测(CI gate 2)
bun test packages/shared/src/privacy          # 新增 privacy 模块
bun test packages/shared/src/diagnostics      # 诊断/脱敏边界
bun test packages/shared/src/evaluation       # 评测产物脱敏
# CI 密钥扫描等价命令(pr.yml Secret scan 的 grep 模式原样本地执行)→ No secret patterns found.

2. 关键环境信息

  • OS: Windows 10.0.26200 (win32 x64)
  • Bun: 1.4.2
  • 提交: 83db04e(即本 PR 当前 head)

3. 每组测试 pass/fail 数量

结果
typecheck(core/i18n/shared/ui/electron 全部 workspace) 全部 exit 0
packages/shared/src/privacy(本 PR 新增) 21 pass / 0 fail
packages/shared/src/diagnostics 24 pass / 0 fail
packages/shared/src/evaluation 114 pass / 0 fail
bun test --isolate 全仓 1230 pass / 5 fail / 7 skip(1242 tests / 139 files)

4. 关于 5 个失败:均可在当前 main 复现,与本 PR 无关

在干净 main(git stash 本 PR 全部改动后重跑)上得到完全相同的 5 个失败,均为 Windows 环境专有问题:

  • PiRuntimeAdapter / AgentKernelHost:测试硬编码 POSIX 路径(/tmp/...),Windows 上 path 返回反斜杠;
  • SkillHub V2 blocks symlink escapes:Windows 创建 symlink 需开发者模式/管理员权限(EPERM);
  • score formatting renders short shas and datesformatDate 输出依赖系统 locale(zh-CN Windows 输出 2023年11月14日,断言假定为英文格式);
  • 其一为顺序依赖型(单文件运行通过,仅全量运行触发)。

CI 在 ubuntu 运行,不受影响。如需要我可以另行提一个修复 Windows 测试兼容性的 PR。

5. 针对性脱敏用例结果(新增单测,全部通过)

类别 用例 结果
Authorization Authorization: Bearer … / Basic …,保 scheme 去 token
Cookie cookie:/Set-Cookie:/session_token(含 JSON 形式)
API key sk-/sk-ant-/x-api-key/apiKey/AIza/xox/SG./glpat-/npm_/lsv2_
Connection string postgres://user:pass@hostredis://:pass@mongodb+srv://(保留 scheme+host)
嵌套 tool args/results 深层对象/数组中任意深度的敏感字段名与字符串
error/stack 消息+堆栈首行回显、HTTP 401 body 回显 key
URL query ?apikey=/&access_token=/#token=,benign 参数保留
其他 webhook 签名、PEM 私钥块、幂等性、可观测性字段保留

6. Canary 泄漏检查(等价于 CanaryScanner 的实际执行结果)

将 9 类已知 canary secret(sk- key / Bearer / cookie / 连接串 / URL query / JWT / whsec_ / AKIA / x-api-key)注入各真实边界后序列化输出并扫描,canary 值零残留:

PASS redactText (string engine)
PASS serializeSupportBundle (diagnostics export)
PASS ErrorLog.push (kernel error ring buffer)
PASS EvaluationRedactor (eval artifacts, standard)
PASS redactError + deepRedact (IPC/errors/telemetry)

ALL CANARIES CONTAINED

fail-closed 行为(循环引用 / 超深嵌套 / 内部异常 → [REDACTED],绝不回退原始负载)由 packages/shared/src/privacy/deep-redact.test.ts 覆盖。

@helsome helsome left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这条实现和测试报告已经明显优于重复的 #62#62 已关闭为 superseded),当前只保留 #64 作为 #19 主实现。现有 Bun 1.4.2 / Windows、privacy/diagnostics/evaluation focused tests、全仓 baseline 对照、canary 扫描都有效。

#19 属隐私/secret 出站边界,Issue 自身明确要求:必须走一次 production Agent / Deep Research path,把 canary secret 放进环境变量、tool input/error 或测试上下文,最终扫描 logs、diagnostics、eval artifacts、待发送 telemetry;若 #14 已接入,还要验证 Langfuse 中 0 canary。请补这一次真实运行证据。

另外 #84 刚合入 main,请 rebase 最新 main 后跑基础 CI。除这两项外,不要求扩大 redaction 规则或新增框架。若暂时不做 production-path canary,请把 Closes #19 改成 Refs/Part of #19,作为安全增量合入,Issue 保持 open。

@yjg-djb
yjg-djb force-pushed the fix/issue-19-unified-redaction branch from 52be6d2 to e1fe63a Compare September 12, 2026 03:01
yjg-djb added a commit to yjg-djb/folio that referenced this pull request Sep 12, 2026
@yjg-djb

yjg-djb commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

两项评审意见均已完成:

1. Rebase 最新 main + 基础 CI

已 rebase 到含 #84main @ 4cdb6bd(本 PR head e1fe63a),CI 四项全绿(Typecheck / Focused tests / Full unit tests advisory / Secret scan)。rebase 后本地 focused privacy/diagnostics/evaluation 176 tests 全过。

2. Production-path canary 运行证据

#19 的要求完成了一次真实应用内的 canary 运行——不是单测,而是构建产物 + CDP 驱动渲染进程走真实 IPC production path:

注入面(canary 均带 CANARYLEAK 标记)

注入路径 canary
启动环境变量 ANTHROPIC_API_KEY=sk-CANARYLEAK01…FINAGENT_JUDGE_API_KEY=lsv2_pt_CANARYLEAK05…
生产凭证库 evaluation.setCredential / setLangfuseCredential lsv2_pt_CANARYLEAK05…pk-lf-CANARYLEAK06…sk-lf-CANARYLEAK07…
生产连接配置 connections.setConfig apiKey mk-CANARYLEAK03… + endpoint https://folio_user:CANARYLEAK04dbpass@db.host.internal/api(连接串形)

真实外呼与错误路径

  • testLangfuseConnection真实出站到 cloud.langfuse.com → 401 → 渲染层可见消息为 Langfuse 自身错误文本,0 canary 回显
  • connections.test → 对 canary endpoint 真实网络尝试 → 不可达 → 消息 "Could not reach the data provider"0 canary
  • evaluation.testConnectionbackend: none,无外呼

扫描结果(outbound-facing 产物必须干净)

产物 结果
Support bundle(diagnostics.collect 后经生产 serializeSupportBundle 序列化,即 diagnostics:export 落盘内容,14,296 bytes,含完整错误环) CLEAN
IPC 响应(渲染层收到的全部消息/错误) CLEAN(仅 connections.json 本地配置回显包含 endpoint 本身,见下)
echo 服务器(capture 出站请求) 0 请求命中
evaluation/store.jsonstore/research/runs.json 0 canary
凭证落盘 credentials.json 全部 safeStorage 密文(v1:…),无明文 canary
Langfuse 侧 status() 证实 backend: none / tracingEnabled: false / langfuseTracingEnabled: false——telemetry 出站关闭,无任何 trace 导出,故 Langfuse 中 0 canary

原始产物:bundle.json · canary-log.json(存于 fork 的独立 assets 分支,不进入 PR diff)

一条诚实的观察(不扩大本 PR 范围)

本地 at-rest 配置 connections.json 按本地优先设计原样保存 endpoint(含 userinfo)——它不是出站边界(不出设备、bundle 不含它),但若未来有任何同步/导出场景,建议在持久化或回显 endpoint 时剥离 userinfo。可作为后续 issue 跟进。

基于以上,保留 Closes #19

@helsome helsome left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

补充核对了你提供的 pr64-canary-log.json。构建产物 + CDP + 真实 IPC / Langfuse connection test 这些证据有效,但还没有命中 #19 最后一条显式验收:原始 diagnostics 里 agent.providerId = local、Pi status = unknown,没有一次 production Agent / Deep Research run;store/research/runs.json 的 0 canary 也不能替代“实际运行后扫描”。所以现有 REQUEST_CHANGES 仍保留,但只剩这一点:要么跑一条真实 Agent/Research path,把 canary 放进其 context/tool/error 后扫描所有产物;要么把 Closes #19 收窄为 Refs #19 / Part 1。不需要再扩大 redaction 规则。

yjg-djb added a commit to yjg-djb/folio that referenced this pull request Sep 15, 2026
ProviderConfig 是非 secret 设置通道(凭证属于 OS 级
CredentialStore),setConfig 已做字段白名单,但 endpoint 字段本身
未做 secret 形态检查——URL userinfo(scheme://user:pass@host)是
常见凭证携带形态,会在 connections.json 明文落盘并回显到 UI
(PR helsome#64 production canary 运行实测发现,见该 PR 证据评论)。

- 新增 sanitizeEndpoint:scheme://user:pass@host →
  scheme://[REDACTED]@host,协议/主机/路径保留,目标地址仍可读;
  无 userinfo 的 endpoint 原样通过
- setConfig 持久化前净化(写侧)
- getConfig 惰性净化(读侧),修复存量明文文件,不破坏本地优先
  的无迁移语义
- 单测覆盖:userinfo 密码剥离且落盘无明文、旧文件读侧净化、
  无 userinfo endpoint 行为不变

Closes helsome#93
yjg-djb added a commit to yjg-djb/folio that referenced this pull request Sep 15, 2026
@yjg-djb

yjg-djb commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

按上一轮评审的最后一条要求,补齐了 production Agent run + canary in context 的真实运行证据。

运行方式

构建产物(vite build + build:main,本 PR 分支 e1fe63a)+ 干净 userData,FINAGENT_AGENT_PROVIDER=local,经渲染进程调用生产 IPC kernel.createSession / kernel.startRun(与 UI 输入框完全相同的通道)发起真实 run。用户 prompt 中携带 4 个 canary:

sk-CANARYLEAK08agentcontext123
Authorization: Bearer CANARYLEAK09agentbearer
postgres://analyst:CANARYLEAK10agentpass@db.internal:5432/mkt
cookie: session=CANARYLEAK11agentcookie

(另注入 ANTHROPIC_API_KEY=sk-CANARYLEAK01envkey… 环境变量。)

真实 run 证据

本次运行为完整生产生命周期(非 mock,事件流按序到达渲染层):

run_started → tool_started → tool_completed(get_kline NVDA.US,真实能力调用,
CAPABILITY_FAILED: "Add an API key to connect")→ message_started →
message_delta → message_completed → run_completed

run 到达终态后,kernel 的 evaluation observability(本 PR 强化的 EvaluationRedactor 路径)将记录写入 on-disk store——这是"实际运行后扫描"的对象,而非空文件:

{ "id": "9e63c576…", "status": "completed",
  "answer": "Add an API key to connect",
  "toolCalls": [{ "toolName": "get_kline", "status": "error",
                  "error": { "code": "CAPABILITY_FAILED", } }] }

运行后全产物扫描(canary 零残留)

产物 结果
evaluation/store.json(真实 run 记录:answer + toolCalls + error) CLEAN
store/research/runs.json CLEAN
Diagnostics bundle(运行后经 diagnostics.collect 生产采集,8,681 bytes,含错误环) CLEAN
Telemetry 出站 status() 证实 backend: none / tracingEnabled: false / langfuseTracingEnabled: false——无任何 trace 离机,Langfuse 侧 0 canary

渲染层消息列表中用户自己的 prompt 为明文——本地优先设计下用户本机可见自己的输入,不属于出站边界(该消息不出现在任何上述产物中)。

原始产物:eval store · diagnostics bundle(fork 独立 assets 分支)

综上,#19 的验收路径已完整覆盖:环境变量 / 凭证库 / 连接配置 / Agent context 中的 canary → 真实 kernel run + 真实工具调用与错误 → 运行后全产物扫描 0 泄漏。保留 Closes #19

@helsome
helsome dismissed their stale review September 15, 2026 12:09

作者已补充 production IPC/kernel Agent 生命周期证据,原 review 中“完全没有 production Agent run”的表述已不再准确。按新的真实路径验收标准改用后续 review 说明剩余差距。

@helsome helsome left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这次补充的证据有效,但按当前更严格的“真实路径”验收,我把它归为 PRODUCTION-PATH / LIVE-INTEGRATION,还不是可关闭 #19REAL-E2E

已确认有效的部分:构建产物 + renderer IPC → kernel.startRun → 完整 run 生命周期确实跑通;canary 进入真实用户 context;evaluation store / diagnostics 在实际 run 后扫描为 0 泄漏。这已经解决了上一轮“没有 production run”的问题。

但当前 run 仍是 FINAGENT_AGENT_PROVIDER=localget_klineAdd an API key to connect 处失败,没有真实模型调用,也没有任何外部金融/检索数据成功进入 tool result;同时 Langfuse 为 backend:none / tracing disabled,所以“Langfuse 0 canary”目前是因为没有发送 telemetry,而不是证明启用后的 payload 经过统一 redaction 后仍安全。

若要保留 Closes #19,请补一条真正的 REAL-E2E:

  • 使用真实 Agent/model provider,而不是 local/fake provider;
  • 至少有 1 个实际外部数据源或 retrieval tool 成功返回真实内容,并经过正常 Agent → tool → result → answer/eval/diagnostics 路径;
  • 在该 run 的 context/tool/error 中放 canary,并扫描 logs、diagnostics、eval artifacts、report/debug output;
  • #14 已存在,因此请实际启用 Langfuse/telemetry(可使用专门测试项目/假 canary),证明待发送或已发送 payload中 0 canary,而不是以 tracing disabled 作为通过依据;
  • PR 中给出实际 query、model/provider、tool 名、真实 source/provider、返回摘要/provenance、run id/trace id(敏感 credential 必须脱敏)。

不要求再扩 redaction 规则。如果作者不方便提供真实 provider/model/Langfuse 条件,可以把 Closes #19 收窄为 Refs #19 / Part 1;以当前代码与 production-path canary 证据,我愿意把它作为隐私基础层增量合入,#19 保持 open 等 REAL-E2E 收尾。

另外 #94 已合入 main,请最终 rebase 后确认 endpoint sanitization 与统一 redaction 不重复回退。

将仓库中四套分散的脱敏实现(diagnostics 正则列表、evaluation 字段
级深脱敏、export 账户字段过滤、credentialStore 的 redactSecrets)
整合到共享 privacy 模块,使日志、诊断、telemetry 与评测产物使用
同一套规则。

- 新增 packages/shared/src/privacy:redactText(fail-closed 模式
  引擎)、deepRedact(敏感字段深层遍历,带环/深度保护)、
  redactError(消息+堆栈序列化)、策略常量
- 扩展覆盖:cookie/session token、连接串凭证、webhook secret 与
  签名、私钥 PEM 块、URL query/fragment 中的 secret、Slack/
  SendGrid/GitLab/npm/Google token
- 边界加固:ErrorLog 采集时脱敏消息与堆栈;评测 run 错误与 judge
  失败原因落盘前脱敏;内核 console.error 经 redactError 序列化
- 保留可观测性字段(run id、工具名、状态、时延),新增幂等性与
  fail-closed 测试
- 文档化默认 telemetry 内容策略及 privacy-safe / full-content
  模式切换方式(docs/privacy-redaction*.md)

Closes helsome#19
@yjg-djb
yjg-djb force-pushed the fix/issue-19-unified-redaction branch from e1fe63a to 50e70cf Compare September 16, 2026 00:55
@yjg-djb

yjg-djb commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Rebase 与 #94 兼容性确认

关于 REAL-E2E

理解并认同"真实 provider + 真实外部数据 + 实际启用 telemetry"的验收口径。这需要真实 LLM provider key、外部金融数据源凭证与 Langfuse 测试项目,我会在具备条件时补上(届时按您列的证据清单逐项给出:query / model / tool / 真实 source 摘要 / run id / trace id / payload 扫描)。

在此之前,请先确认是否按您提供的备选路径处理:Closes #19Refs #19 / Part 1(隐私基础层增量合入,#19 保持 open 等 REAL-E2E)。如同意我就更新 PR 描述后请求复审。

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Privacy] Centralize redaction and data minimization for logs, diagnostics, traces, and eval artifacts

2 participants