Repository navigation
fix(dsh): mirror every canonical credential shape in the observer - #7733
JasonBuildAI wants to merge 1 commit into
Conversation
The DSH shadow observer is the only writer of `reliability_diagnostics` ledger bytes, and its documented contract is that credential-like tokens fail closed before the first append. It enforced that contract with a hand-written copy of the canonical owner's shapes that carried two of the nine provider-issuer arms and four of the labeled arms, raised the GitHub floor from 16 to 20, and accepted only two directory names under a Windows drive. A 38-value cross-language differential at upstream/main found seventeen values the canonical `loopx/public_safe_text.py` owner refuses and the producer accepted and wrote in cleartext: a GitHub fine-grained PAT and any 16-19 character classic PAT, AWS access and session key ids, Slack bot and app tokens, a Google API key, a Stripe restricted key, npm and PyPI tokens, `api_key:`, `access_token:`, `refresh_token:`, `password:` and `secret:` labels, and any drive path outside `users` / `documents and settings`. Spell every arm byte-identically to the canonical owner and add a parity test that rebuilds the canonical alternation from the mirror's own arms, so an arm added, narrowed or invented on one side fails in the suite that runs on both. No envelope, stats, ledger or activation contract changes. Fixes loopx-project#7729 Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
8633321 to
7c96eea
Compare
…7733 Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
songoow
left a comment
There was a problem hiding this comment.
Reviewer: model_agent · GPT-6 Astra Pro · OpenAI · self_reported
动机 / Motivation
显式启用 DSH shadow observer 收集运行诊断的操作者需要保证诊断文件本身不包含凭据或本地私有路径。例如工具名或来源字段意外携带无标签 npm 凭据,旧 TypeScript producer 会先写入本地 ledger,后续 Python ingest 拒绝也无法撤回已落盘原值;修改后 producer 在写入前拒绝该事件。独立 Node 探针使用真实 appendLedgerLines 写磁盘:十类危险样例中基线九类落盘,head 全部拒绝;三个安全工具名仍写入,错误 Session 被拒绝,默认关闭时没有访问任何 Host hook。范围是默认关闭的被动诊断 producer 安全边界;不启用 observer,不增加调度或执行权,也不宣称完成 live C1、长期保留或性能资格。
改动思路 / Approach
独立发布的 DSH 插件不能在运行时导入仓库 Python 模块,因此保留镜像有部署依据;镜像语义由 canonical owner 与跨语言模式比较约束,拒绝发生在真实落盘边界之前。当前 PR 保留为 producer guard 补齐及 parity/ledger 回归,不把默认关闭的观察者变成 Core 决策者,也不把此检查点表述为完整诊断能力上线。不修改会保留已经实际复现的泄漏;只在更晚的消费者拒绝也不能撤回已写入或公开的原值。修复放在现有 owner,正常输入沿原有路径继续处理,不增加重复状态、配置同步或人工确认步骤。对相关四个凭据 PR 的有界比较确认,它们面向不同目的地,能够共享识别规则而保持各自拒绝或遮盖策略。
具体改动 / Changes
完整审阅范围:3 files, +171/-7; producer +40/-7, focused TS regression +59 and Python parity checks +79. 原始问题依据为 https://github.com/loopx-project/loopx/issues/7729,文本版本 body-sha256:7e944eec7d6034df82a16b94a996c3347b42255a5b375f1795d230b3d46cb93f;维护者接受的共享识别、目的地策略边界见 #5136。OBS-7729-1:implemented; OBS-7729-2:implemented; OBS-7729-3:implemented,对应识别完整性、兼容控制和真实输出边界,未把更大路线图当成此 PR 的完成条件。
关键代码讲解
-
LOCAL_PATH_SURFACES / SECRET_LIKE_SURFACES(packages/dsh-loopx-plugin/src/observer.ts:76):补齐 canonical secret arms 和路径形状,保留既有 file: 的更保守处理;ASCII token admission 约束住跨语言边界差异。 -
ShadowObserver.record(packages/dsh-loopx-plugin/src/observer.ts:571):序列化前对整个 envelope 做递归安全检查,拒绝计数不把原值写入 ledger;flush 前再次检查。 -
apply / resolveShadowObserverConfig(packages/dsh-loopx-plugin/src/observer.ts:667):仍需完整 exact Goal、Session 与 run identity;未配置直接返回,单独插件加载不等于激活。
对主干的风险 / Risks
验证资格阻断:当前结论为 REQUEST_CHANGES;没有发现需要修改生产代码的 finding。 当前 chat-bundle-browser (1) 出现delegation-search-browser-smoke.mjs rows等待超时。虽然源码与调用路径检查支持它与本次补丁分离,现行 review policy 仍要求同一检查在 immutable base 与 exact head 上执行并取得相同归一化失败签名;本轮未完成这组浏览器配对,不能把静态排除或一般 merge hold 替代该条件。最小补充:在相同隔离 fixture 上,对immutable base225698b3和当前head运行同一个delegation-search检查,比较失败身份/等待点;也可提交已有相同输入、revision和实际执行绑定的base/head产物。 若配对确认是既有问题,即可据已有完整代码/真实消费者证据重评,无需为了本 review 改动无关业务代码。
未发现阻断性代码问题。canonical 启发式仍有精度边界;JavaScript与Python的Unicode word boundary并非完全等价,现有ASCII字段限制下差异趋向拒绝。未运行完整已安装DSH/真实模型会话或live C1。实际生效调用链为 Cordis observer row -> apply/resolveShadowObserverConfig -> ShadowObserver.observeSessionEvent -> record/flushBatch -> appendLedgerLines -> reliability diagnostics ingest。最需要反证的是“模式正确,但在落盘/发布之后才被调用”;我使用同一个合成 corpus 跑 immutable base 与当前 head 的真实消费端,且把新版本的无泄漏 oracle 放到基线确认会失败,避免只测试新 helper 自洽。
PASS:.venv/bin/python -m pytest -q tests/capabilities/test_reliability_diagnostics_dsh_provider.py tests/capabilities/test_reliability_diagnostics.py,133 passed in 6.43s。完整安装的 DSH/Vitest 宿主套件未在本地运行;这里的独立 Node 试验直接调用生产 observer 和真实文件 append,未伪造 guard 或持久化结果。base/head 消费探针均有可重放脚本及原始结果,未调用真实模型、凭据或操作者状态。安全输入和拒绝后的继续处理均单独检查;Rejected observer event is counted; worker execution is not stopped. Subsequent safe events are accepted by the same observer.
语义与 CI 对齐
复用 existing vocabulary,没有新增工作状态分类或扩大 authority。远端 JUnit 中 heartbeat budgets、quota reason、native restart、skill links 等失败已逐条读取。针对涉及的 owner,在225698基线和当前head各执行100项:均62 passed/38 failed,38个失败身份与规范化 message 完全一致。已有 chat-bundle-browser 的 delegation-search 超时也已定位,browser bundle 不装载本次默认关闭的 DSH observer;未把该超时宣称为已解决,整体 CI/合并资格继续独立保留。这些红灯没有被改写成通过;已复现的基线失败与本次改变的 invariant 分开归因,未完成的平台/浏览器复验和合并门槛继续明确保留。
当前失败的具体归因与审阅范围
本次当前浏览器红灯是 delegation-search 的 rows 等待超时。已比较整个 dashboard 树、整个 loopx 生产树、steward fixture 与测试启动器的 Git 对象,base/head 完全相同;这条 Node/Python/browser 路径不加载独立 Cordis observer 包。本 PR 的必要证明是 observer 本地落盘前 guard、显式 Session 绑定和关闭侧零 Host hook,均已直接执行通过;这个因果边界值得保留,但不能替代政策要求的同一失败检查配对执行。
pytest、checks 与 merge-gate 是现有 workflow 的上游结果汇总,不视作额外独立根因;对应 Python/Windows/浏览器当前失败分别按上述具体路径处理。这次因当前CI归因证据不足而暂不批准;未更改或绕过远端必需状态检查。
我的整体评价 / Verdict
对 exact head 7c96eea8df378618c508c7bbe120e2c335bf4fa3 给出 REQUEST_CHANGES,原因仅为上述当前CI归因资格缺口,未发现代码finding。长期重复输出的凭据暴露风险改善,普通任务和后续读取流程保留,代码体量与已证实的问题相称。已检查 observer、独立 Cordis 注册行和诊断 ledger 消费端;没有页面、布局或可见交互变更,视觉复验不适用。未运行页面布局截图;此 PR 没有布局或交互变更。默认关闭与显式配置两侧均直接验证,观察者不能进入 worker 或 scheduler 决策。建议维持当前最小 owner 修复范围,未来精度调整集中在 canonical 模式与公共 corpus;补齐该配对归因后可复用本次已完成的代码与消费者验证;不要求扩大产品修复范围。
English verdict: REQUEST_CHANGES — current required CI attribution is incomplete. No blocking code finding was identified; provide the same browser check at immutable base and exact head, or equivalent verifiable existing paired artifacts, without relaxing its assertions or changing unrelated code.
归因补齐:必需检查在未改动的 exact head 上重跑通过,无代码改动评审本身没有 code finding,我也没有改动生产代码或测试。exact head 仍为 1. 同一 exact head 的两次执行(决定性)对同一个 run 41010988719 用
同一 sha、同一 workflow blob、同一 runner 标签、同一隔离 fixture,唯一差别是执行时序。attempt 2 中 这直接说明: 2. base 侧与「同输入」的既有执行base
即 base 树在本次 head 失败前 6 分钟刚通过同一个检查;head 侧失败发生在 06:59:45。 3. 同一失败行号在别的 PR 上重现#7744(head 4. 差异范围
5. 当前 CI 状态attempt 2 已绿: 仍在排队: 6. 关于 policy 要求的配对现行配对要求的目的是把一个失败归因为既有问题。本次的情况不是「head 侧存在无法归因的失败」,而是该必需检查在未改动的 exact head 上重跑后通过,且 base 树在相邻时间同样通过;第 1 条是同一 revision 上的直接反证,不是静态排除。我没有放宽断言、没有跳过检查、没有改动无关业务代码。 如果你仍需要一份 base 侧失败产物,我可以对 base 请求:exact head 仍为 English verdict: no code change was needed and none was made. The required |
Goal And Delivered Outcome
Impact. Anyone who enables the DSH shadow observer (
packages/dsh-loopx-plugin, the only producer ofreliability_diagnosticsledger bytes) can have a live provider credential written in cleartext into the durable diagnostic ledger. The capability README promises the opposite — "Absolute local paths and credential-like tokens fail closed without reaching ledger bytes" — and the accepted RFC names "producer-side public-safety rejection before the first ledger append" as its P0 checkpoint. The producer implemented that control as a hand copy that had already lost most of the canonical owner's shapes.Beneficiary. Operators running the DSH observer, and anyone they hand a diagnostic ledger or receipt to: the ledger is a whole-file export ("no filtering of negative records") and the read-only projection also surfaces
tool_namefrom the summary.Scale. Seventeen distinct values across four arm classes. A 38-value differential against the canonical
loopx/public_safe_text.pyowner at upstream/main found 17 that the producer accepted and appended while the Python owner refuses them:AKIA/ASIA, Slackxoxb-/xoxp-, GoogleAIza, Striperk_live_, npm, PyPIapi_key:,access_token:,refresh_token:,password:,secret:apiarm of(?:access|api|secret)[_-]?keyusers/documents and settingsOutcome basis / optional anchor: fixes [Bug]: the DSH shadow observer's producer-side public-safety check misses 7 of 9 provider credential shapes and narrows the drive-path arm #7729; the drift class is tracked by open architecture issue [Architecture]: two modules both claim to own "private-looking text" #5136, and [Bug]: the builtin public-safe-outbound scan certifies GitHub, AWS, Slack, Google, npm, PyPI and Stripe credentials as public-safe #7635/fix(capability): detect unlabeled provider tokens in the outbound scan #7636, [Bug]: the public-boundary publication gate in loopx check certifies unlabeled provider credentials as public-safe #7712/fix(contract): catch unlabeled provider tokens in the public boundary scan #7713, [Bug]: Chat activity masker persists npm/PyPI/Google/AWS/Slack/JWT tokens for replay #7717/fix(presentation): mask unlabeled provider tokens in chat activity #7718 already repaired the same class in
public_safe_outbound/scanner.py,contract.pyandpresentation/codex_activity.py.Goal/source and gap: the producer's fail-closed check is a hand-written mirror of the canonical owner, and the mirror carried 2 of 9 provider-issuer arms, 4 of 5 labeled arms, one narrowed label alternative, a GitHub floor of 20 instead of 16, and a drive arm restricted to two directory names. No test could see it:
tests/fixtures/public_safe_text_corpus.jsonpins onlypublic_safe_text.pyagainst the Vision checkpoint mirror, and the DSH shared fixture lists two credential shapes the producer already rejects.Observable before → after, with the validation row that proves it: the same value set, driven through
ShadowObserver.observeSessionEvent→isPublicSafeValue→appendLedgerLines, was accepted and written for 17 values and is rejected withpublic_safety_violationfor all 17 now; the newrefuses every canonical credential arm before the first appendrow asserts it per value.Issue/task and intended base:
Fixes #7729; baseloopx-project/loopxmainat225698b31.Author Declaration
AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.
Implemented against
loopx/capabilities/reliability_diagnostics/README.md("Every provider applies the equivalent recursive public-safe value contract before its first append ... credential-like tokens fail closed without reaching ledger bytes") anddocs/architecture/rfcs/long-running-agent-reliability-diagnostics-governed-delivery-v0.mdP0 checkpoint ("producer-side public-safety rejection before the first ledger append"), both at225698b31; the canonical shape text isloopx/public_safe_text.pySECRET_LIKE_SURFACE_PATTERN/LOCAL_PATH_SURFACE_PATTERNat the same revision.SECRET_LIKE_SURFACES,packages/dsh-loopx-plugin/src/observer.tsobserver.spec.ts→refuses every canonical credential arm before the first appendLOCAL_PATH_SURFACES, same filewindows_drive_outside_user_profilerowtest_typescript_mirror_equals_the_canonical_credential_arms,test_typescript_mirror_equals_the_canonical_local_path_surfacesloopx/cli_commands/reliability_diagnostics.pytests/capabilities/test_reliability_diagnostics.pytests/capabilities/test_reliability_diagnostics_dsh_provider.py(30 passed), full plugin suite parity belowruff check, and a 38-value cross-language differential before/after. Deliberately left out: the POSIX/UNC/home-relative canonical path arms are mirrored for parity but are not reachable through the producer's own compact-token filters; the file-URL arm stays broader than the canonicalfile://because narrowing it would loosen a check that already rejectsfile:/…. Two residual differences are documented rather than fixed: JavaScript's ASCII-only\bmakes the mirror stricter than the Unicode-aware Python boundary for a token glued to a non-ASCII letter, and the parity test compares the arm list in order, so reordering it reads as a failure even though it changes no verdict.Scope And Continuation
Validation
7c96eea8d(rebased onto225698b31)unitpassedtests/capabilities/test_reliability_diagnostics_dsh_provider.py→ 30 passed. With the baseobserver.tsrestored in place, the two new tests fail (2 failed / 28 passed), so they are not vacuous.unitpassedpackages/dsh-loopx-plugin/tests/observer.spec.ts→ 12 passed, 1 failed. The failure isis off unless one exact goal, session, and run identity are declared, a pre-existing Windows-only path-resolution assertion (the fixture's POSIX/tmpledger directory is rewritten drive-qualified on Windows); the same single test fails identically at the base revision (11 passed, 1 failed).unitpassed9 failed / 198 passed (207), head9 failed / 199 passed (208)— the same nine pre-existing Windows-host failures (POSIX fixture paths, child-close timing, symlink/permission), plus the one new passing test. No regression.staticpassedtsc -p tsconfig.host.json --noEmit,tsc -p tsconfig.client.json --noEmit,tsc -p tsconfig.tests.json --noEmit→ exit 0 each.staticpassedruff check tests/capabilities/test_reliability_diagnostics_dsh_provider.py→ All checks passed.regression_paritypassedmanualpassedobserver.tsand pass at head.integrationfailedloopx canary premerge --from-git-diff --git-diff-base upstream/mainselected thedsh-loopx-pluginrisk profile and failed on this Windows host for two reasons that both reproduce at the base revision: thequalityphase runs the same full vitest suite (9 pre-existing Windows-only failures, base9 failed / 198 passed), and thepackage/runtimephases stop atspawnSync ...\node_modules\.bin\dsh ENOENT, which the same smokes hit identically at base. The four direct checks (diff hygiene ×3, RFC bilingual) andpy_compilepassed. Hosteddsh-plugin.ymlruns typecheck and the suite on its own Ubuntu and Windows runners.SUMMARY_TOKEN/IDENTITY_TOKEN), and the exactfile:arm, which stays broader than the canonical form by design.Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
N/A
Boundary Checklist
.loopx/,.codex/goals/, and liveACTIVE_GOAL_STATE.md).python3 scripts/check_rfc_bilingual.py --base origin/main --head HEADpasses, or no paired RFC changed.none.Signed-off-bytrailer (git commit -s).