Skip to content

[P2 security] No CSRF token on require_local writes, including the /promote airlock #22

Description

@marsrobertson

Summary

Local-only writes have no CSRF token. Locality is inferred from the absence of a header that browsers never set cross-origin, so a page in the operator's browser can POST JSON to these routes using a simple-request content type, and the request is indistinguishable from a deliberate operator action.

Evidence

Unprotected local-only writes:

  • fleet/bin/fleet.py:1086/api/signatures/judge, rewrites the whole signatures file (fleet.py:1112).
  • fleet/bin/fleet.py:1057/api/convene, subprocess.Popen at fleet.py:1074.
  • Every require_local cockpit write: approvals approve/revoke (main.py:556, 595), handoffs (main.py:655), touch (main.py:672), horizons (main.py:718), triage (main.py:1130), promote (main.py:1211).

Already protected, and the model to follow: /api/kill (fleet.py:1151) and /api/paste-image (fleet.py:1132) require KILL_TOKEN in the body. That is what the token is actually good for — /api/kill-token (fleet.py:868) hands it to any same-origin page, so it is a CSRF token, not an authN credential.

Impact

Includes /api/signals/{id}/promote, which is the human airlock crossing from inbox.json into life.json. A CSRF against it means a stranger's signal is promoted without the operator ever reading it.

Composes with the stored-XSS issue: same-origin XSS reads the token, so a CSRF token alone is insufficient while that is open. Both need fixing.

Acceptance criteria

  • Local-only writes require KILL_TOKEN, matching /api/kill.
  • Tests cover a cross-origin simple-request POST to at least /api/convene and /api/signals/{id}/promote.

Activity

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions