From a65e8a0587d3e5c971bef3062dd0d8a3b5cb53b7 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 17 Sep 2026 14:02:24 +0200 Subject: a symlink in the sorted directory no longer redirects a step Placeholders were already stopped from sending a file out of the directory a rule named. A symlink is a name too, and the directory krino sorts is by the threat model's own premise a place the internet writes into: a link named after a rule's destination sent moves and copies anywhere, and under (on-conflict overwrite) trashed a file OUTSIDE the sorted directory - while the plan showed the in-tree text and the run reported success. A step whose destination passes through a symlink at or below the directory being sorted now fails. A destination the configuration names outside it - ~/docs on another disk - is the user's own arrangement and is followed as before; both cases have a test. End to end, the review's scenario (Out -> ~/secret, overwrite): before: 1 applied, the user's file replaced and trashed after: 0 applied 1 failed, the file untouched, nothing trashed Two bookkeeping bugs in MkdirAllTracked went with it: a dangling symlink read as a missing directory and was then recorded as one krino had created - undo would have unlinked a link krino never made - and a directory created by someone else between the check and the mkdir was recorded the same way. --- docs/design.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/design.md b/docs/design.md index 6299288..e2c3cf6 100644 --- a/docs/design.md +++ b/docs/design.md @@ -924,7 +924,13 @@ What that means, and the tests that hold it (plans 8 and 9): resolve at or under the directory its text names before the first placeholder, so a capture of "..", "~" or nothing cannot move it elsewhere; a rename whose placeholders produce "", "." or ".." is skipped - with a reason. + with a reason. A symlink is a name too: a step whose destination passes + through a symlink **inside the directory being sorted** fails rather than + following it, since anything at all may be written there - by a download, + or an unpacked archive - and the plan the user approved shows only the + text. A destination the configuration itself names outside that + directory, `~/docs` on another disk say, is the user's own arrangement + and is followed as before. - A file is acted on only while it is still the file that was planned: same size and modification time, still a regular file (not a symlink put in its place), and, at its planned path, the same inode. A step that had -- cgit v1.3