☰
e2e 报告中的 secret 泄露防护:redaction 脱敏机制源码解读
2026/10/9 1:51:42 网站建设 项目流程

e2e 报告中的 secret 泄露防护:redaction 脱敏机制源码解读

【免费下载链接】e2eNext generation e2e testing framework for web and mobile apps.项目地址: https://gitcode.com/GitHub_Trending/e2e6/e2e

e2e 是一个面向 Web 与移动端的下一代 e2e 测试框架,支持 AI 驱动的智能测试。它的 secret 泄露防护(redaction 脱敏机制)能确保测试报告中绝不出现明文密码与 API 密钥:所有已注册的敏感值都会被替换为<secret:name>标记,即使它们藏在 JSON 转义、URL 编码或流式输出中间。本文带你从源码层面快速理解这套脱敏机制的设计与边界。

🎯 为什么 e2e 测试报告需要 secret 脱敏?

e2e 的运行产物(report.json、失败现场树、AI 模型输入、控制台输出、下载文件)会携带大量应用内容。一旦测试涉及登录、支付等场景,密码或密钥很容易出现在:

  • 测试标题、步骤标签、fill的输入值里;
  • 页面的 URL、无障碍树文本、断言失败信息里;
  • AI agent 接收的屏幕观察中。

如果这些产物不做处理,一份失败报告就等于一份"密码泄露报告"。e2e 的答案是:运行器(runner)侧统一脱敏,敏感值永远以不透明句柄流转。

🔑 第一层:Secret 是不透明句柄,读不到明文

框架把密码和 API 密钥包装成Secret句柄,源码见 packages/e2e/src/secrets.ts:

  • secrets.get('stripe-key')返回的不是字符串,而是一个带品牌标记的对象;
  • 一旦尝试把它当字符串用(模板字符串、String()、JSON.stringify),会直接抛出INVALID_CONFIG错误——配置里只允许持有"名字",不允许持有"值";
  • 密码只能交给三个"汇点":定位器的fill、agent 步骤的参数、声明过 secret 的引擎选项(如web({ basicAuth: { password } }))。

也就是说,测试代码从语法上就无法"打印"出一个密钥。用法参考官方文档 docs/authentication.mdx。

🛡️ 第二层:SecretLedger 台账 + 全编码形态匹配

真正干活的是脱敏台账 packages/e2e/src/internal/redact.ts 中的SecretLedger类。每个会话持有一份,登记该会话可能见过的一切敏感值:

  1. 静态值:配置secrets里写死的字符串,在会话创建时预注册;
  2. provider 动态值:如每次填充时现算的 TOTP 验证码,在resolveSecretValue(见 packages/e2e/src/run/secrecy.ts)里"注册在先、取值在后"——值一诞生就已进入脱敏范围;
  3. 轮换不失效:同一个名字的新旧值都会保留在台账里,防止上一轮验证码残留在截图或文本中泄漏。

匹配的聪明之处在于"多形态正则"(valuePattern函数):它不是简单查找原始字符串,而是为每个值生成一条能同时命中下列写法的模式:

  • JSON 转义(\"、\uXXXX)、HTML 字符引用(&amp;、&#x2f;)、百分号编码(%2f);
  • 任意大小写变体(CSStext-transform藏不住它);
  • 空白折叠后的形态(渲染引擎会把连续空白压成一个空格)。

命中后统一替换为<secret:名字>标记,且标记本身绝不会被二次改写——脱敏两次等于脱敏一次。

⏳ 流式输出怎么办?StreamRedactor 的"扣尾巴"策略

终端输出是一块一块写出来的,密钥可能被拆在两次write中间。StreamRedactor(同一文件,约 676 行处)的解法非常优雅:

  • push(chunk)先拼接缓冲,再调用ledger.holdFrom(text)算出一个安全切点:从该位置往后的文本可能被后续片段补全成一个敏感值或一个标记,于是扣下不输出;
  • 之前的安全部分照常脱敏后输出;
  • flush()在流结束时释放并脱敏剩余部分。

最终效果:分段流式脱敏的结果与整段一次性脱敏完全一致。stdout 和 stderr 各挂一个独立实例,实现见 packages/e2e/src/run/process-output.ts。

📸 第三层:填充过密码就"扣住像素"

脱敏不止针对文本。packages/e2e/src/run/secrecy.ts 中的SecretExposure记录会话的暴露等级:只要密码被真正fill进应用,后续整个尝试周期内——截图工具被禁用、断言与失败截图被省略、模型也收不到像素。因为应用可能把密码渲染在掩码字段之外的任何地方,文本脱敏救不了画面。

⚠️ 脱敏的边界:官方安全模型明示的局限

docs/security.mdx 专门列出了"红action 在哪里停下",值得团队知晓:

  • 值的变形形态(如只露后 4 位)不视为密钥,会放行;
  • 整体 base64 编码、哈希后的值是"另一个字符串",会放行(但下载文件中的 base64 片段会被尝试解码后重写);
  • 视频不做任何像素级脱敏,报告中标注redaction: "incomplete";
  • command.log捕获的是应用进程的原始 stdout,运行器不脱敏——敏感值请别打印到应用标准输出。

📚 关键文件导航

模块路径
脱敏核心(台账 + 正则 + 流式脱敏)packages/e2e/src/internal/redact.ts
不透明句柄与注册表packages/e2e/src/secrets.ts
会话机密状态与暴露等级packages/e2e/src/run/secrecy.ts
流输出逐块脱敏packages/e2e/src/run/process-output.ts
长度下限等配置规则packages/e2e/src/config/secrets.ts
安全模型官方文档docs/security.mdx

一个细节值得玩味:静态密钥短于 6 个字符会直接报INVALID_CONFIG——因为太短的值(PIN、端口号)全局替换会误伤正常文本。脱敏的代价可控,比脱敏的漏网之鱼更值得优先。

【免费下载链接】e2eNext generation e2e testing framework for web and mobile apps.项目地址: https://gitcode.com/GitHub_Trending/e2e6/e2e

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询