Files
vdm/daemon/docs/deferrals.md
T
samiandClaude Sonnet 5 ab479e7885 daemon: fs/safepath — the saveDir/filename path-traversal boundary (security)
veloxd is the one process that turns an untrusted string into a
filesystem destination, and via capture.offer that string can come
from a web page. CLAUDE.md §4 and the M1 DoD both name this.

daemon/docs/safepath-adversarial.md is the spec, written before the
code the way EXT did for shouldCapture: 21 rows — .. traversal
(A1/A2), absolute-outside-roots (A3), prefix-match confusion (A4),
symlink-out (A7), TOCTOU on a created tail (A8), NUL/control bytes in
the leaf that CORE's fuzzer hit through Content-Disposition (A9/A10),
degenerate and overlong leaves (A11/A13), overlong dir component
(A14), symlinked root (A16), destination-is-a-file (A17), and the
legitimate cases that must still pass — non-ASCII (A18), redundant "."
(A19), trailing space/dot trimming (A20).

fs/safepath.cpp:
- sanitize_leaf: strip <0x20 and 0x7F, trim ws, strip trailing dots,
  reject ""/"."/".."/contains-'/', cap 255 UTF-8 bytes on a codepoint
  boundary. Mirrors core/src/net/content_disposition.cpp.
- canonicalize_root: expand ~ and realpath each allowedRoots entry
  once, so a symlinked root resolves to its target.
- resolve_target: reject relative saveDir and any ".." component
  lexically; if the dir exists, realpath + component-wise containment
  (a symlink that escapes is caught, one that stays inside passes); if
  a tail is missing, realpath+check the deepest existing ancestor then
  create the tail via an openat/mkdirat O_NOFOLLOW walk and re-derive
  the final path from the fd. Every failure is -32011 with data.path =
  the *original* saveDir (never the resolved path). Residual TOCTOU on
  a pre-existing intermediate dir is documented and closed by CORE's
  O_NOFOLLOW open of the file.

veloxd_fs static lib; veloxd_rpc links it for the download.add wiring
next. Test veloxd.safepath is the adversarial table, on a real temp
tree. ASan+UBSan and TSan clean; 33 daemon/cli tests green.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Upd9WhG9oppieig5nRDLig
2026-09-10 19:37:01 +04:00

1.6 KiB

DAEMON — deferred work, tracked

Things that are deliberately incomplete in daemon/ right now, with why and when they close. Kept here (not buried in commit messages) so the next pass can see them at a glance.

# What Where Why deferred Closes when
D1 Pairing prompt is EnvAutoApprover (needs VELOX_PAIR_AUTO=1) rpc/pairing.hpp, main.cpp A GUI dialog / org.freedesktop.Notifications approver is integration work Build step 7 (systemd + notifications)
D2 download.add-32603, download.probe-32603 rpc/dispatcher.cpp The path boundary (-32011) is built and tested (fs/safepath, daemon/docs/safepath-adversarial.md); still need it wired into the download.add handler with the store behind it, plus the probe path (-32013) which needs the engine download.add glue (dispatcher ↔ store ↔ fs/safepath); probe with the engine link
D3 Stub handlers for everything except session.*, download.list, download.get rpc/dispatcher.cpp No store behind them yet Per method, as the store/scheduler wire in
D4 sched/ is the pure Governor + schedule window only; no Scheduler wiring to store/engine/timer sched/ Engine bodies land in CORE stage 8; Scheduler needs the UUID↔vdm::TaskId map, a store query layer, and a timer After CORE stage 8 lands Engine::start()
D5 event.* fan-out not implemented; session.subscribe accepts and echoes but nothing is emitted rpc/uds_server.cpp, rpc/ws_server.cpp No task state to broadcast until the engine is wired With the callback → event.* projection