fix(plugin-audit): exclude sys_upload_session from audit/activity writes (#5202) - #5215
Merged
Merged
Conversation
…tes (#5202) SKIP_OBJECTS group (2) — ADR-0057 decision 5 "stop the amplifier" — gained sys_job_queue in #5193/#5201; sys_upload_session is the same gap one table over. It declares lifecycle.class: 'transient' and its own object comment settles what the rows are worth: "an upload session is ephemeral state, never business truth" (ADR-0057 / #2970 item 4). Nothing connected that declaration to the exemption list, which is hand-written. The audit writers register for all objects and there is no system-context exemption, so StorageMetadataStore's own writes were mirrored into sys_audit_log AND sys_activity. A chunked upload of N parts costs 1 + N writes — the createSession() insert plus one updateSession() per chunk — then a terminal status update and the row's removal (deleteSession, or the TTL/retention reaper), so 2 × (1 + N) ledger rows for one file, each with its own beforeUpdate snapshot read. Each row was unusually fat too: updateSession() writes the merged FULL record, so the `parts` JSON blob that grows with every chunk rode along in every diff's old_value/new_value. sys_file stays audited on purpose: it declares transient as well, but only to reap tombstones and unfinished uploads — its rows are mostly permanent business truth with real compliance value. Tests pin a completed 8-chunk lifecycle and an aborted-then-reaped one producing zero rows, the skipped snapshot read (with a business-object control), the deliberate non-exemption of sys_file, and that ordinary writes are still audited. Removing the one list entry turns the first three red (22, 12 and 5 unwanted writes respectively). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017MCKJaEomEqg4tvz4SzdNd
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5202
与 #5193 / PR #5201 完全同形:一行清单 + 注释,不做任何横切改造。
改了什么
packages/plugins/plugin-audit/src/audit-writers.ts的SKIP_OBJECTS第 (2) 组(ADR-0057 决策 5 “stop the amplifier”)加入
sys_upload_session,注释写明理由:lifecycle.class: 'transient';never business truth」(ADR-0057 / [attachments] v2 tracking: parent-visibility inheritance, authed downloads, remaining enforce-or-remove #2970 item 4);
lifecycle.class没有任何机械关联,所以声明归声明,豁免归豁免 —— 这是同一个耦合缺失的第二个实例(第一个是 plugin-audit:
sys_job_queue不在 SKIP_OBJECTS 里 —— 每条队列消息的 insert/lease/complete 都写 audit_log + activity 行(兄弟表 sys_job / sys_job_run 都已排除) #5193)。为什么是放大器
审计钩子对全部对象注册,且没有「系统上下文写入不审计」的豁免,所以
StorageMetadataStore自己的写入被当成用户编辑记了下来。一次 N 块的分块上传:createSession()→engine.insertupdateSession()→engine.update(每块一次)updateSession()(completed / failed / expired)deleteSession()或 ADR-0057 的 TTL / retention reaper每次写入同时落一行
sys_audit_log和一行sys_activity,即 2 × (1 + N) 行,外加每次
beforeUpdate的captureBefore快照读。而且每行还特别肥:updateSession()写的是 merged 全量记录,所以那个随块数增长的
partsJSON blob 会挤进每一次 diff 的old_value/new_value。⛔ 没有动
sys_filesys_file也声明了transient,但那只为收墓碑行与未完成上传;其行是 mostly permanentbusiness truth,审计有合规价值 —— #5201 的 dev 已查过并刻意不豁免,本 PR 沿用该判断,
并加了一个测试把这个刻意的不豁免钉住,防止后来者“顺手补全”把
sys_file也塞进去。同样按 issue 要求未做「
SKIP_OBJECTS与lifecycle.class机械关联」的横切改造(独立决策),也未碰
packages/spec/content/docs/releases/。测试
新增 describe 块
audit writers — chunked upload sessions are excluded (#5202, ADR-0057 D5),5 个用例:
断言
created为[](不是“少一点”,是零);fixture 里parts是真的按块增长的,因为那些 diff 载荷的体积正是本单的一半问题。
expired,再由 TTL reaper DELETE,同样归零。beforeUpdate+ 1 次beforeDelete零快照读,同一 harness 上的业务对象对照组证明该断言能失败。
sys_file仍然被审计(刻意不豁免的钉子)。假引擎沿用文件里既有的
makeEngine,未定义delete(),所以不涉及 #5197 的assertEngineDeleteDispatch路由要求。突变检查(删掉清单那一行 → 测试红)
22 = 2 × (1 + 8 + 1 + 1),12 = 2 × (1 + 3 + 1 + 1),5 = 4 次 chunk 快照读 + 1 次删除前快照读
—— 与 issue 正文推算的放大倍数逐位吻合。第 4、5 两个对照用例在突变下仍绿(它们本就该绿)。
恢复后
Changeset
.changeset/audit-skip-sys-upload-session.md,patch(审计台账内容对运维可见),照 #5201 的写法,含运维迁移提示:原先靠
sys_activity看上传活动的,应改读sys_upload_session(进行中状态)与sys_file(实际存下什么的持久记录)。🤖 Generated with Claude Code
https://claude.ai/code/session_017MCKJaEomEqg4tvz4SzdNd
Generated by Claude Code