Skip to content

pull-bootstrap uses the server's min_available_seq as a cursor without validating it #900

Description

@fujibee

pull-bootstrap takes the pull cursor from the server's team snapshot and never checks that it is a sequence, then puts it straight into the request URL.

remote-sync.mjs:3522   let cursor = String(teamSnapshot.min_available_seq ?? "0");
remote-sync.mjs:3540   `/v1/teams/${teamId}/messages?after=${cursor}&limit=${limit}`

publicSnapshot() (remote-sync.mjs:3672) validates only team_id and that server_instance_id is a UUIDv7. Nothing looks at min_available_seq. The validation that does exist — sequence(value.min_available_seq, …) inside validateCapabilities — is on the /v1/capabilities path, which the bootstrap does not take.

So a server that answers /v1/teams/<id> with

{ "min_available_seq": "0&limit=99999" }

gets an extra query parameter into the request the client then makes. Any string the server chooses is appended after after=.

Severity

Low, and worth writing down rather than fixing quietly:

  • the endpoint is one the operator deliberately connected to, so this is a hostile-or-broken server, not a third party
  • the parameters it can reach are this endpoint's own query string
  • the value is also stored — it becomes the cursor the pull loop carries

Why it is not fixed in #895

The obvious fix is to put it through sequence(), which throws. That turns a malformed snapshot from "the pull continues with a strange cursor" into "the pull refuses to start", and that is a behaviour change with its own review — not something to attach to a fix for a different defect.

#895 does constrain what reaches the terminal: the progress line prints the cursor only when it is a canonical sequence, so a control sequence in that value cannot travel into a pasted log. That is an output guard and not a validation — the value still flows into the URL and into the loop unchanged.

Suggested shape

Validate the snapshot where it is read, beside the two checks already there, so the rule lives with its siblings rather than at the use site. Whether a malformed sequence should refuse the pull or fall back to "0" is the decision this issue is asking for.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions