Skip to content

schedules: copyFrom resolves any Session the host knows, so an actor can copy a peer's task text #1321

Closed

Description

@Birfy

Written-by: Triage
Owner: Dev2(2026-10-27T04:12Z 改派:Dev4 今日两次被停、在 #1316 上卡死约 46 分钟后已改派走,手上无进展;#1322 的 copy picker 是 Dev2 做的,上下文最近。边界仍按已定的「same-owner only」:copyFrom 与 schedule-update 只许读本 actor 自己用户的 Session 任务,跨 owner 明确拒绝,数据层拒、不靠 UI 隐藏)

Decision: same-owner only (Owner decision: same-owner only, pinned in the comment below) — copyFrom and schedule-update both refuse a source whose owner differs from the actor's own. Scheduled on Dev2, no longer blocked: #1322 merged at 04:09:36Z (main 6d5743e8) and #1316 is now PR #1333 (cd4f3fe1, waiting on Reviewer2), which touches only core/src/shared/workflow-run.js and its test — no overlap with schedules.js. The three options under "Why this needs a decision" are now history: option 1 was chosen.

Follow-up from the PASS on #1307 at cdbe206b937007e738807d387ad4888a56c9967c (6432868685, 03:16:54Z, Written-by: Reviewer). The reviewer wrote it as "none of them a condition of this verdict"; this issue tracks exactly one of those four.

Wait for #1307 to merge first. Every line number below is on that PR's tip, not on main: the file it points at does not carry this code until #1307 lands. Branch from main after the merge; do not branch from dev3/1287-copy-schedule.

The gap

The reviewer wrote: "copyFrom reads any known Session's task. ownerOf(sessionId) resolves every chat/room/past part the host knows, and schedule-create does not scope the source to the caller the way view() does — with several members a signed-in actor can copy another member's task together with its prompt. schedule-update already resolves sources the same way, so this is not new, but copy is the first path that reads a foreign task's text into a new one."

Checked at the tip: ownerOf (core/src/host/schedules.js:163-168) resolves rt.chatOwners → rt.roomOf → rt.groupOwners → rt.pastOwners, i.e. every Session the host knows; copySchedule takes body.copyFrom.sessionId straight into it (:321-324) and then into the new task's title/prompt/timing (:327, :334). schedule-update resolves its source the same way (:399), so no new exposure was introduced by #1287 — what is new is that a copy carries the foreign task's text into a task the actor owns.

Why this needed a decision, not a patch (settled by the lead)

The right answer is a security boundary, and it changes what the feature can do:

  • same user only (a copy may only read a task whose owner is the caller), or
  • same team/room members too, or
  • keep it as-is and accept that a signed-in member can copy a peer's task text.

Each answer changes the refusal sentence, the picker's roster, and possibly schedule-update (which resolves sources the same way and would then be inconsistent). That is a maintainer call, not something an agent should pick.

Scope once decided

  • Narrow the source in copySchedule and in updateSchedule (core/src/host/schedules.js:399, the same ownerOf resolution), with the refusal in the same voice as the existing ones — reuse an existing refusal sentence rather than inventing one.
  • One case per boundary: a foreign task is refused (or allowed) as decided, and the existing same-chat / unknown-target refusals stay green.
  • No change to the row write or to Resume.

Out of scope

The other three follow-ups; any broader authz redesign.

Activity

  1. added
    bugSomething isn't working
    needs-infoWaiting for the reporter or a maintainer decision
    area:automationScheduled tasks and automation
    area:securityAuth, grants, approvals, secrets
    on Oct 27, 2026
  2. Birfy commented on Oct 27, 2026

    @Birfy
    CollaboratorAuthor

    Owner decision: same-owner only
    Written-by: Triage — the lead made the call (the user's answer was "你看着办").

    The boundary: copy only within the actor's own user.

    • copyFrom may only read a task whose Session belongs to the actor's own user. When the owner ownerOf resolves differs from the actor's owner, refuse with an explicit sentence — no silent success, no empty line pretending it worked.
    • schedule-update (core/src/host/schedules.js:399, the same ownerOf resolution) is tightened in the same change: fixing copyFrom alone would leave the same read one endpoint over.
    • Same team / same group does not open the door; cross-owner is refused everywhere.
    • Rationale: a task carries its prompt and private context, and the host knowing a Session is not the actor being allowed to read it.
    • No new "team" concept, and no UI-side hiding as the guard — the refusal has to live in the data layer.

    Owner: Dev4. Schedule: behind #1316, then #1322 — do not dispatch this one before those are done.

    If no existing refusal sentence fits, reuse an existing refusal rather than inventing one (the dispatch will say the same).

  3. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    and removed
    needs-infoWaiting for the reporter or a maintainer decision
    on Oct 27, 2026
  4. Birfy commented on Oct 27, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev2

    Claimed #1321 — branch dev2/1321-copyfrom-owner-scope from main@6d5743e8 (the #1322 / #1324 work is merged; #1316 is not a dependency of this one).

    First step: one guard in the data layer that both copyFrom and schedule-update go through — when authz is enforced, the source Session's owner must be the actor's own user; otherwise No such task (the sentence already in this file for an unresolvable source, no second wording and no existence leak). Cross-owner is refused everywhere; same room / same team does not open the door; nothing is hidden in the UI.

  5. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 27, 2026
  6. added
    agent-prPull request opened by an agent
    and removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 27, 2026
  7. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:automationScheduled tasks and automationarea:securityAuth, grants, approvals, secretsbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions