fix: don't scan or serve ebook symlinks that lead outside the library #40
Loading…
Reference in a new issue
No description provided.
Delete branch "jtk/ebook-symlinks"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #27.
Problem: the scanner uses
Path.wildcardandFile.regular?, which follow symlinks. A link inside a library pointing at e.g./etc/passwd(named*.epub) became an ebook record, and/ebooks/:id/downloadserved it to any signed-in user. A directory link to somewhere else on the server worked the same way.Fix:
FuzzyCatalog.Ebooks.SafePath:realpath/1resolves every symlink in a path (relative and absolute targets, directory links too, gives up after 40 links like Linux), andinside?/2checks the resolved path is the resolved root or under it (solibrary2/isn't insidelibrary/).Tests:
safe_path_test.exs(8: plain, file link out, relative..escape, dir link out, inside link, symlinked root, prefix sibling, link loop), a scan test (outside link skipped, inside alias kept, warning logged), and 3 download tests (outside link refused, inside link served, no-library link refused).mix precommit: 499 passed.Not covered: TOCTOU between the check and
send_download(someone with write access to the library could race it). Libraries are admin-configured, so that's left as is.Review from Nye: APPROVE at
d9ee8fd(posted by Upper Wolfjaw; Nye's token is read-only)CI green. realpath resolves one component at a time from an already-resolved prefix; relative targets expand against the link's own dir; dir links and a symlinked root work; loops stop after 40 links and return false. The prefix check adds a trailing '/', so a sibling like /lib2 doesn't match /lib (tested), and a root of '/' works. Library deletes nilify ebooks.library_id, so get_library! can't raise for a missing library.
Non-blocking lows (filed separately): (1) Path.expand(target, current) expands a leading '~' in a link target to $HOME, while POSIX treats it literally; (2) check-then-send TOCTOU in EbookController.download (needs write access to the library). FYI: after a library is deleted, its ebooks under a symlinked mount won't download because of the no-library "no symlinks" rule; acceptable.