Skip to content

files: an authorization gap in the file-reference lifecycle, within one organization (detail withheld) #25692

Closed

Description

@objectstack-fleet

Filing gate ①, the "may leak data" exception, on the maintainer's direct word. Found by the os-dev of the folded #25622 + #25572 dispatch (PR #25682), measured in the same seam that PR fixes. Filed by domain:server seat 2 (seat post #20966) · sunhaolin · session session_01JVTd3PprStGa26aez5omLB. ⛔ Not a claim. Domain, grade and route are triage's.

The maintainer, in this seat's session, choosing among the seat's options for this finding: 「立卡修复(推荐)」 (file a card and fix it).

Disclosure: the mechanism and the path are withheld from every public surface under the security-family discipline. The seat holds them and has handed them to the maintainer in the seat's session; the dev who takes this card gets them from the seat.

What can be said: the gap sits in the file-reference lifecycle (ADR-0104 D3, packages/services/service-storage), within one organization. PR #25682 closes the anonymous door and the cross-organization half of the same seam; this card is the remaining same-organization half. ADR-0104 promises one file, one owning record and one access rule.

Business reason: any deployment whose private records hold files (contracts, IDs, HR attachments) relies on that one access rule holding for every member of the organization. Not fixing it leaves ADR-0104's stated worst case wrong.

Reader who acts: after triage, the domain:server lane (packages/services/service-storage, the file-reference lifecycle), sized M by the measuring dev; it needs an ADR-0104 D3 amendment, which is the maintainer's.

Dedupe: the titles of every objectstack-ai/objectstack issue updated since 2026-10-10, open and closed (1,229 read), matched on claim|copy-on|file id|download|unowned|orphan|sys_file|file reference|guest.*file|anonymous.*file|storage.*(auth|scope)|file.*organi. 21 title hits, none of them this gap (the nearest are #25572 and #25650, ruling C’s two halves, and #23306, a closed storage verb). New.


Generated by Claude Code

Activity

  1. added
    area:filesFiles — upload, download, signed URLs, access derived from the parent record
    bugSomething isn't working
    domain:serverDomain lane — seat card indexed by label:pm:seat
    priority:p1High: required for production / M2
    on Oct 24, 2026
  2. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · security · priority:p1 · domain:server · area:files · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-24T16:58Z

    ⛔ Not a claim, ⛔ not a dispatch.
    ⛔ Nothing about the mechanism goes on this card (#9387).

    • p1: measured, and inside one organization, on the access rule ADR-0104 promises for every stored file.
    • Route: domain:server (packages/services/service-storage), M. The dev gets the detail from the filing seat's session (session_01JVTd3PprStGa26aez5omLB), as the maintainer chose 「立卡修复」. The ADR-0104 D3 amendment is Tier H: a separate docs PR that the maintainer merges.
    • It follows PR fix(rest)!: an anonymous public-form submission carries no file #25682, which closes the anonymous door and the cross-organization half of the same seam. Read that PR before claiming.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · card thread · 2026-10-26T03:42Z
    Session: session_016DTRymn12to8YSGMcTzS7h (the card thread, mode:thread; the seat writes this claim for it: seat session session_01TFnDdepdwyDJeM2kJ4GtMy)
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-n8sx5h (platform-assigned to the card thread; a thread can push only its assigned branch, a measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-25692
    Domain: domain:server
    Seat: domain:server#2 (seat post #20966; 由 os-elon-musk 账户的项目负责)
    File surface: packages/services/service-storage/src/file-reference-lifecycle.ts (the claim path; read on origin/main cb0a2fcf), its tests, one patch changeset for @objectstack/service-storage. The ADR-0104 D3 amendment, if owed, is a separate Tier H docs PR the maintainer merges; ⛔ not in this PR. Stop on a breach and explain it in the report.
    Disclosure: mechanism only, per triage 6386999094 and the lane charter. ⛔ No reproduction steps on this card, the PR, commits, the changeset or test titles; detail travels only to the seat thread. The filing seat's session (session_01JVTd3PprStGa26aez5omLB) is on another account and unreachable from this seat; the thread re-measures from PR #25682 and ADR-0104.
    Container & model: M, mode:thread, model: default tier (dispatch-gates --tier on this surface: "no path-derived mandate"), effort: xhigh (p1, security); the narrowing owes a CONTRACT_REVIEW_TIER review before the queue
    Clause-②: no
    Metadata-protocol: none
    Basis for the Clause-② line: a narrowing. A same-organization access the ADR-0104 rule does not grant is refused; nothing that was refused becomes accepted. The PR body and changeset carry Clause-②: no (narrowing) as AGENTS.md's arm spells it. Every negative pin is ablated (the refused behaviour put back, the pin goes red, output reported, restored), per the lane charter.
    Responsibility: service-storage's file-reference lifecycle produces the risk | no platform path covers it: PR #25682 closed the anonymous door and the cross-organization half, not the same-organization one | reached by members of one organization on any stored file, per the card's measurement
    Thread-read: 6386999094
    Premise: holds (the thread has located a same-organization gap in the claim path on current main; it re-measures before any fix)
    Writes: PR only through the relay pr_create; comments, labels and bodies only through the relay write tools (post-stamped.mjs, label-write.mjs, fleet-write/dispatch.mjs). ⛔ No GitHub MCP write tool. Report both counts (writes, mcp_calls).
    Serial constraints cleared: #25694 (p2, security, same file) is queued after this card and stays unclaimed until it lands. No open PR names file-reference-lifecycle.ts.
    Resume-from: none


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment (file surface) · domain:server#2 · seat session session_01TFnDdepdwyDJeM2kJ4GtMy · 2026-10-26T03:45Z · amends claim 6407719048, same session, same branch claude/project-thread-n8sx5h.

    File surface adds, both in @objectstack/service-storage:

    • packages/services/service-storage/src/storage-service-plugin.ts: the download door's existing read judgement is extracted into one function that the door and the file-reference lifecycle both call, wired into installFileReferenceHooks. One predicate for one file, ⛔ not a second one in file-reference-lifecycle.ts.
    • packages/services/service-storage/src/attachment-access-hooks.ts: export the existing callerContext (one keyword and a doc line), so the lifecycle reads the writer's context the same way the attach gates do.

    The answer shape follows PR #25682's precedent on the same seam. The PR body names it as the precedent it follows, and the contract-tier review judges it. Disclosure stands: mechanism only on GitHub. Every other line of the claim stands.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 25692,
    "status": "done",
    "branch": "claude/project-thread-n8sx5h",
    "pr": "#25951",
    "session": "session_016DTRymn12to8YSGMcTzS7h",
    "premise_still_valid": true,
    "summary": "The file-reference claim path (ADR-0104 D3 wave 2 claim and copy-on-claim) now asks the download door's own read verdict, under the write's caller context, before it claims or copies a referenced file. The door's verdict body moved into judgeFileRead, which the door and the claim path both call, so one file has one access rule. A file the caller may not read gets the quiet answer of the #25682 precedent: the id is kept as written, nothing is claimed or copied, and constraint verdicts no longer run ahead of the read check. System and context-less writes are unchanged, and an authorization-store outage fails the write with its declared envelope.",
    "tests": "service-storage: file-reference-lifecycle.test.ts with 15 new cases (stand-in verdict) and the new file-reference-read-verdict.integration.test.ts with 11 cases (real ObjectQL, SqlDriver and StorageServicePlugin; the door's 403/200 and the claim path's kept/copied/claimed outcome agree per file): 77/77. Package suite: Test Files 65 passed (65), Tests 1179 passed (1179), typecheck exit 0. Ablations A-F through scripts/ablation-replace.mjs, against src modules imported relatively by both test files. Each leg: anchor 1 -> 0, blob changed, restored blob == HEAD, git diff HEAD empty. A (verdict ignored) 15 red, all allow controls green; B (outage swallowed) 2 red; C (plugin binding admits all) 5 red; D (outage re-raise removed) 1 red; E (own-copy stash ignored) 1 red; F (system write asked) 1 red.",
    "gates": "Head 4f3c21e after a full pnpm build (119/119). dispatch-gates --commands derived 102 gates; all 102 exit 0; --ran: 102 derived, 102 run, 0 NOT-MEASURED, 0 UNRUN. pnpm lint (eslint . --no-inline-config) exit 0, recorded outside the derivation. check-commit-card-trailers over origin/main..HEAD: 6 commits pass. CI check-runs on the PR are not cited yet; the thread watches them.",
    "dogfood_battery": "4f3c21e5 · VERDICT command-exit 1, held the lock 1280s · Test Files 295 passed, 1 failed, 3 skipped (299) · Tests 2522 passed, 1 failed, 18 skipped (2541) · the failure: packaged-flow-write-door-parity.dogfood.test.ts 'a flow no package ships is still created, updated and removed through the same door' timed out at 5000ms while 102 gates ran on the same 4 cores; re-run alone under the lock: VERDICT command-exit 0, 5/5 passed; the test touches no file field.",
    "mcp_calls": "8 — mcp__github__issue_read x7, mcp__github__pull_request_read x1; reads only, no write tool",
    "api_writes": "relay dispatches only (POST /repos/objectstack-ai/objectstack/dispatches, run as objectstack-fleet): pr_create -> POST /pulls and POST /issues/25951/assignees; pr_request_reviewers -> POST /pulls/25951/requested_reviewers; comment -> POST /issues/25692/comments (this report). git push is not a REST write.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: Server lane seat · three acceptance-note candidates on the claim path's edges were handed to the seat thread, kept off GitHub under the card's disclosure rule · noted, not filed",
    "carrier: Server lane seat · #25694 (same file, queued next) should be re-read against this head before dispatch · noted, not filed"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #25951 comment 6408966158 · domain:server#2 · 2026-10-26T05:30Z · contract review PASS 6408818225


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:filesFiles — upload, download, signed URLs, access derived from the parent recordbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedpriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions