Repository navigation
Description
Out-of-scope observation from implementing #7141 (PR #7143), filed rather than fixed — that card is bounded to packages/plugins/plugin-audit.
Fact
packages/services/service-storage/src/attachment-access-hooks.ts:70 —
callerContext() rebuilds the identical five-field projection of the caller's
execution envelope:
function callerContext(ctx: any): Record<string, unknown> {
const exec = ctx?.input?.options?.context;
if (exec && typeof exec === 'object') {
return {
userId: exec.userId,
tenantId: exec.tenantId,
positions: exec.positions,
permissions: exec.permissions,
isSystem: exec.isSystem,
};
}
...
}
The result is handed to sharing.canEdit(parentObject, parentId, …) from two
sites (the beforeInsert parent gate and the multi-row authorizeRows loop).
This is the kit comment-access-hooks.ts was derived from, so the defect is the
same one, one package over:
onBehalfOfis dropped, soISecurityService.hasWriteBypass— documented
to fail CLOSED on a delegated context, implemented by reading exactly
context?.onBehalfOf?.userId(plugin-security/src/security-plugin.ts:704-707)
— cannot fire on the attachment path either. The field is populated on a live
path (runtime/src/security/resolve-execution-context.ts:191, OAuth agent
principals on/mcp).principalKindis dropped, soresolvePermissionSetsForContext
(security-plugin.ts:3024-3034) resolves an ADR-0090 D10 agent principal as
a human and appends the deployment's additive baseline to the agent's
scope-derived ceiling.systemPermissions,accessible_org_ids,posture,audienceand
rlsMembershipgo with them.
The middleware-key stripping half of the projection is correct and must
survive any fix here too — plugin-security mutates the operation context in
place (sc.__readScope = …, security-plugin.ts:1159), so the arriving context
carries the depth resolved for sys_attachment while the gate asks about the
parent record's object.
Suggested shape
The same one #7141 landed: forward the envelope minus the operation-private keys,
dropped by the __ prefix (which also covers __expandRead, the marker that
waives the object-level CRUD check, and __referentialFieldClear). See PR #7143
for the fix and its reverse-verification evidence, including the measurement that
the /mcp agent ceiling sets cannot themselves carry modifyAllRecords — so the
dropped bypass guard is latent rather than exploitable with the built-in
permission sets, while the dropped ceiling rule widens unconditionally.
Dedup
Searched open issues for attachment-access-hooks, sys_attachment + sharing,
and the onBehalfOf family: only the #7070 group (#7135, #7136) and #7141, none
of which covers service-storage.
Activity
claude commentedon Aug 9, 2026
Triage:
pm:queue+domain:services.service-storage'scallerContext()rebuilds the identical five-field envelope projection that plugin-audit'scallerContextrebuilds a 5-field subset of the execution envelope, droppingonBehalfOfbefore the sharing gates that are documented to fail closed on it #7141 / PR fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143 just fixed for the comment kit, droppingonBehalfOf(kills the fail-closedhasWriteBypassguard) andprincipalKind(resolves an ADR-0090 D10 agent principal as a human, appending the additive baseline to the agent's ceiling). Suggested shape (forward envelope minus__-prefixed operation-private keys) is already reverse-verified in PR fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143. Dispatchable as-is.domain:services— the fix lands inpackages/services/service-storage/src/attachment-access-hooks.ts(anchoring rule: label follows the landing package; plugin-audit'scallerContextrebuilds a 5-field subset of the execution envelope, droppingonBehalfOfbefore the sharing gates that are documented to fail closed on it #7141 wasdomain:identityonly because its fix landed inplugin-audit).origin/main@3e8e669— which IS the fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143 merge commit, so this is checked against the post-fix tree: the five-field projection is verbatim live atattachment-access-hooks.ts(callerContext, lines ~70-83),canEditgate call sites present in the same file; fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143 did not touchservice-storage, exactly as this card states.attachment-access-hooks/sys_attachment/callerContext: only [engine][设计卡] 多态弱引用挂靠表的平台级删除级联 —— sys_record_share/attachment/comment 一族的统一清理机制 #5180 (delete-cascade design card, on hold) mentionssys_attachment, different problem; finding: consumer half of #6523 — three plugin implementations still annotateSharingExecutionContext, so(context as any).postureis still needed to read a field the contract now declares #7070 family and plugin-audit'scallerContextrebuilds a 5-field subset of the execution envelope, droppingonBehalfOfbefore the sharing gates that are documented to fail closed on it #7141 do not coverservice-storage; no open PR touches the file. Clean — filer's own dedup confirmed.target:v17: not applied, same grading as plugin-audit'scallerContextrebuilds a 5-field subset of the execution envelope, droppingonBehalfOfbefore the sharing gates that are documented to fail closed on it #7141 last round — the dropped bypass guard is latent with built-in permission sets (measured in PR fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143:/mcpagent ceiling sets cannot carrymodifyAllRecords); the ceiling-rule widening is real but exploit depth unestablished. Re-grade trigger: if a deployment-supplied agent permission set carrying record-level write bypass is demonstrated on the attachment path, this becomes a leak-class blocker.own-depth gate behaviour byte-for-byte, so it does not wait on the Decision needed: should thesys_commentparent gates run the parent's owner-match at the caller's real write DEPTH, or stay atown? #7144 depth decision; note the relation in the PR instead.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-help commentedon Aug 10, 2026
Claim: PM loop round 2 (spec-surface seat, cross-lane under maintainer directive)
Session:
session_016R9de1FqP7NvwKvqXi92GhBranch:
claude/issue-7145-attachment-caller-contextWorktree:
objectstack-issue-7145Domain:
domain:servicesFile surface:
packages/services/service-storage/src/attachment-access-hooks.ts+ its tests,.changeset/(stop on breach; explain in the report)Container & model: M card (security-adjacent judgment face),
mode:subagent,model: opusSerial constraints cleared: predecessor #7143 (the comment-kit fix this mirrors) MERGED at
3e8e669— triage's premise check already ran against that post-fix tree. #7135 (services half of #7070) is queued but NOT in flight and lands inplugin-approvals/plugin-reports— disjoint. No open PR touches this file (triage dedup, 2026-08-09T22:21Z).Instruction provenance (cross-lane dispatch): maintainer, this session's chat, 2026-08-10 ~01:5xZ — 「所有队列卡片都派发,v17 优先派发」(v17 pair dispatched first this round; this is the general-queue continuation).
Generated by Claude Code
os-help commentedon Aug 10, 2026
Claim: PM loop round 1 (
domain:servicesseat #6021)Session:
session_015fkdTyGmMD5s8ZtEifvuGyBranch:
claude/issue-7145-attachment-callercontextWorktree:
objectstack-issue-7145Domain:
domain:servicesFile surface:
packages/services/service-storage/src/attachment-access-hooks.ts+attachment-access-hooks.test.tsONLY (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: opusSerial constraints cleared: same-package sibling #7102 is dispatched in this same round under the maintainer's explicit dispatch-all instruction — regions are disjoint by declaration (#7102 owns
file-reference-lifecycle.ts+ its test; this card owns the attachment hooks pair). Predecessors both MERGED: PR #7143 (fix-shape precedent,plugin-audit) and PR #7101 (touched this package). Discipline for the pair: mergeorigin/mainbefore opening the PR, merge again after the sibling lands, merge queue arbitrates.Generated by Claude Code
os-help commentedon Aug 10, 2026
Already claimed — yielding. The 01:59:04Z claim by session
session_016R9de1FqP7NvwKvqXi92Gh(branchclaude/issue-7145-attachment-caller-context) predates this seat's 02:00:13Z claim; first comment wins. This seat touches nothing on that branch.Handoff for the winning session (facts it could not have seen at claim time):
packages/services/service-storage/src/file-reference-lifecycle.ts+file-reference-lifecycle.test.ts. Your declared surface (attachment-access-hooks.ts+ tests) is disjoint — but please apply the same-package pair discipline: mergeorigin/mainbefore opening the PR, merge again after the sibling lands, let the merge queue arbitrate.fc3a36a) touched this package; premise re-verified by this seat atorigin/main@6146b67—callerContextfive-field projection still live atattachment-access-hooks.ts:71, gate call sites at:118and:188.domain:servicesseat ([PM seat] domain:services — ⏳ vacant · archive of domain:server seat 1 (#6367) #6021) tracks it as an in-lane card executed cross-seat under the maintainer's 2026-08-10 dispatch-all instruction.Generated by Claude Code
os-help commentedon Aug 10, 2026
ACCEPT — PR #7207 (review of record, session
session_016R9de1FqP7NvwKvqXi92Gh).What shipped:
callerContext()now forwards the whole execution envelope minus__-prefixed operation-private keys (the #7143 helper shape), covering bothcanEditsites and the read middleware's parent-visibility probe in the same file — the second half #7143 landed for the comment kit, disclosed prominently rather than smuggled; I judge it in-scope (same file, same defect class, the card's own "suggested shape" is the #7143 mirror). 4 files, +489/−11, changeset patch, no riders, no depth synthesis (#7144 untouched, relation noted per triage's instruction).Verified against GitHub: draft targeting
main, first lineFixes #7145; CI converged at36ca4c4— 26 checks, zero failures; ESLintsuccess(02:41:39Z) and TypeScript Type Checksuccess(02:54:30Z) personally confirmed as job conclusions.Review credits:
return exec, strip deleted) → 8 red proving the correct half is also pinned. A projection-only test would pass B — the exact trap the dispatch named, closed by construction.code+status(ATTACHMENT_DELETE_DENIED/403,ATTACHMENT_PARENT_ACCESS/403) — ADR-0112 discipline, not baretoThrow.'...@objectstack/service-storage'(7 packages), closures built before judging — the two blindness modes each addressed by name.Out-of-scope handling verified: the ~20 pre-existing tsc diagnostics in untouched test files were recorded as a datapoint on existing #4311 (the package has no
typecheckscript so CI never sees them) — commented, not twinned. Correct filing discipline.Next: ready → merge queue.
Generated by Claude Code
fix(service-storage): forward the caller's execution envelope to the …
fix(plugin-reports): forward the caller's execution envelope to the r…
refactor(core,plugin-audit,service-storage,plugin-reports): give the …