Summary
Layout paths are loaded from the user-editable .canvas/layout.json sidecar and used by Move to Trash without validating the resulting file target. A malformed path such as ../../outside.txt can cause Terrazzo to trash an entry outside the active workspace.
Evidence
src-tauri/src/commands.rs:100-107 validates only the canvas directory passed to move_to_trash.
src-tauri/src/workspace/layout.rs:276-296 joins LayoutItem.path onto that directory and passes the result to trash::delete.
LayoutItem.path is deserialized from a file that the user or another process can edit.
- The v0 exit criteria require that no known path permanently delete a file outside an explicit, correctly scoped filesystem command.
Proposed change
- Treat every sidecar path as untrusted input.
- Require an item path to be one normal child component with no prefix, root, current-directory, or parent-directory components.
- Pass the canonical workspace root into mutation helpers and validate the target boundary immediately before mutation.
- Preserve safe deletion of a symlink entry itself without following its destination.
- Apply the same validation rule to every sidecar-driven filesystem operation.
- Return a structured malformed-sidecar error without mutating any file.
Acceptance criteria
- A layout item containing
../../outside.txt cannot read, open, move, or trash the outside target.
- Absolute paths and multi-component paths in a layout item are rejected safely.
- Trashing an ordinary in-directory file still succeeds.
- Trashing an in-directory symlink removes only the symlink, even when its destination is outside the workspace.
- Tests cover parent traversal, absolute paths, nested paths, ordinary files, broken symlinks, and escaping symlinks.
- No failed validation modifies the sidecar or filesystem.
Summary
Layout paths are loaded from the user-editable
.canvas/layout.jsonsidecar and used by Move to Trash without validating the resulting file target. A malformed path such as../../outside.txtcan cause Terrazzo to trash an entry outside the active workspace.Evidence
src-tauri/src/commands.rs:100-107validates only the canvas directory passed tomove_to_trash.src-tauri/src/workspace/layout.rs:276-296joinsLayoutItem.pathonto that directory and passes the result totrash::delete.LayoutItem.pathis deserialized from a file that the user or another process can edit.Proposed change
Acceptance criteria
../../outside.txtcannot read, open, move, or trash the outside target.