mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
* Security: pipe print jobs to lp instead of a shared temp folder On macOS, Linux and FreeBSD, Print wrote each job to <temp>/pdfcraft-print-<pid>/job-<time>.pdf and passed that name to lp. The folder name is predictable and was made with create_dir_all, which accepts a folder another local user created first in a shared /tmp and follows a symlink planted there; the job was then written with fs::write. That user could read every printed document, or swap the file before lp read it. The folder was also never removed. The job now goes to lp on stdin, so nothing is written to disk. lp_args no longer takes a file. The job is fed on its own thread while lp's output is collected, so neither side can block on a full pipe; if that thread can't be started, lp is stopped and an error returned rather than a panic. When lp refuses a job before reading it (an unknown printer) the user sees lp's message rather than a broken pipe. CUPS ends its reply for a job read from stdin with "(0 file(s))", which reads as if nothing was sent, so that is dropped from the message shown. submit_via takes the spooler command so tests can run this test binary as a stand-in lp on every platform; the new test failed on the old code (it received the temp file as an argument and nothing on stdin). Co-Authored-By: Claude Opus 5.5 <[email protected]> * Security: create a new digital ID file fresh, owner-only Configure New Digital ID > Create picked the first free name with `!p.exists()` and then wrote the .p12 with fs::write. That is check-then-write: a file or link planted at the name between the two is written through. exists() also follows links, so a dangling symlink looked free and the private key was created wherever it pointed. The file got default permissions, readable by other local users on most Unix systems. save_new_id_file now creates the file with create_new, never opening anything already at the name, and with mode 0o600 on Unix. A taken name moves on to the next ("<name> 2.p12", ...), including a folder, which Windows reports as access denied rather than already exists. A failed write removes the partial file. The dangling-link test failed on the old code (the key was written through the link); the hard-link and folder test guards the retry loop, and a Unix-only test checks the 0o600 mode. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Security: never write the --control token through a planted link write_control_file opened the --control path with create + truncate. A symlink or hard link already at that path was followed: its target was truncated and overwritten with the control token before set_permissions ran, so the token could land in a file another user can read, and any file the link pointed at was destroyed. The JSON now goes to a fresh file next to the path, with a random name (create_new, 0o600 on Unix, another name if one is taken, including a folder on Windows), which is then renamed over the path. The rename replaces a link instead of following it, a control file left by an earlier run is still replaced as before, and a reader polling for the file never sees it half-written. A failed write or rename removes the temporary file. The hard-link and symlink tests failed on the old code (the token was written into the linked file); a third test covers replacing a previous run's file, and the owner-only mode on Unix. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Contributors: credit jcl-bit as James Locke My own entry in contributors/people.toml (AGENTS.md, Contributor credits), added in my PR as asked there. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]>