Skip to content

Fleet Deck 0.9.1

Choose a tag to compare

@lacion lacion released this 15 Jul 12:28

Security + hardening for the 0.9.0 image-paste feature, from an adversarial review.

Fixed a /tmp symlink-follow. 0.9.0 wrote pastes to a fixed, world-known name in shared /tmp, and the pruner followed a directory symlink planted there — on a multi-user host, a local co-tenant could turn a victim's paste into an arbitrary-file delete/overwrite as the victim. Single-user laptops and single-owner Coder workspaces were never exposed. Pastes now live under FLEETDECK_HOME, which no other user can write, so the symlink can't be planted at all.

Also: a burst of pastes can no longer clobber one another (128-bit names + exclusive write); the paste directory is bounded (keeps the 50 most-recent); oversized bodies are rejected before buffering; and — the most visible change — the feature is no longer silent. A too-large screenshot, an offline daemon, or an unsupported file now shows a status over the pane ("image too large (max 10 MB)", "✓ image added — press Enter to send") instead of doing nothing.

Recommended for anyone on 0.9.0, and required on shared/multi-user hosts.


Fixed

  • Image pastes are written under FLEETDECK_HOME, not a fixed name in shared /tmp. The 0.9.0 paste dir was os.tmpdir()/fleetdeck-pastes — a world-known path in sticky /tmp — and the pruner followed a directory symlink planted there. On a multi-user host a local co-tenant could pre-create that symlink and turn a victim's paste into an arbitrary-file delete/overwrite as the victim's user (and, as a lesser variant, squat the name to break the feature for everyone else on the box). A single-user laptop or a single-owner Coder workspace was never exposed — this bites shared login/build hosts. The directory now lives under FLEETDECK_HOME, which no other user can write, so the symlink cannot be planted at all; the pruner additionally lstats every entry and never follows a symlink, and the dir is refused if it is not a real directory this user owns.
  • A burst of pastes can no longer clobber an earlier one. Filenames were a per-second timestamp plus 24 random bits; names are now a 128-bit randomUUID, and the staging write is exclusive (wx).
  • The paste directory is bounded. Age-pruning (>24 h) only ran on the next paste and had no ceiling; it now also keeps at most the 50 most-recent files.
  • The feature is no longer silent. Every failure path (too-large image, unreachable daemon, unsupported bytes, a pane that lost focus mid-upload) now shows a transient status over the pane instead of only a console warning. Success shows "image added — press Enter to send".
  • Reject an oversized paste by its Content-Length before buffering it; tighten the transport cap to what a 10 MB image actually needs; validate base64 up front instead of letting malformed input decode to garbage.