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
1.6 KiB
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 |