diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 21:47:06 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 21:47:06 +0200 |
| commit | 79c746ceac46188deababcf5453c7ca37e173a6a (patch) | |
| tree | aaa1df06a356c4d09505b3d9643af67450f85703 | |
| parent | 49ea29a66254cc23265025a912a73c3e5b0c6ae5 (diff) | |
| download | krino-79c746ceac46188deababcf5453c7ca37e173a6a.tar.gz krino-79c746ceac46188deababcf5453c7ca37e173a6a.zip | |
plan 9: docs and changelog describe what 0.0.7 does
| -rw-r--r-- | CHANGELOG.md | 86 | ||||
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | docs/design.md | 38 | ||||
| -rw-r--r-- | man/krino.1 | 19 |
4 files changed, 103 insertions, 45 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index b0296f8..f34c309 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,39 +4,65 @@ ## 0.0.7 — 2026-09-14 -Security and correctness hardening, from a threat model (spec §15.1) and the -tests that hold it. +Security and correctness hardening, from a threat model (spec §15.1), an +independent review, and the tests that hold both. -- File names, paths and tool messages can no longer control the terminal: - control characters, bidirectional controls and invalid UTF-8 are printed - as escapes (`\x1b`, `\u202e`) everywhere krino prints them. -- A placeholder can no longer rename a file to "", "." or "..", or add a - ".." to a destination; such a step is skipped with a reason. +Hostile names and contents +- File names, paths, tool messages, errors, explain traces, JSON plans and + log fields can no longer control or reorder the terminal: control + characters, every bidirectional control, line and paragraph separators + and invalid UTF-8 are printed as escapes (`\x1b`, `\u202e`). +- A placeholder can no longer take a destination outside the directory the + rule names: a capture of "..", "~" or nothing is refused with a reason, + and so is a rename to "", "." or "..". - A file swapped between planning and applying - for a symlink, or for - another file with the same size and modification time - is not acted on. -- Undo refuses a trash entry name that is not a plain name inside the - Trash, and a trashinfo whose path is not absolute. + another file with the same size and modification time - is not acted on; + a step that had to take a free name stops the rest of its chain. +- `on-conflict overwrite` never trashes a directory, or another file of the + same plan; it skips or takes a free name instead. +- A content exclude holds when its content test cannot read the file + (fails closed), so a protected file krino could not read is left alone. +- Extraction tools are only found through absolute PATH entries. + +Apply and undo +- Every step is logged as soon as it has run; SIGHUP is handled like + SIGTERM. A run killed mid-chain can be undone as far as it got. - Undo reverses a file its run renamed and then moved, moved twice, or - moved and then sent to the Trash. Before, undo judged the first step - against the disk as it is now, found its destination empty (the later - step had moved the file on) and refused the whole file as "missing". Found - by the new generated apply-and-undo test. -- Folding maps the capital sharp s (ẞ) to "SS", as the other capitals - already were: "STRAẞE" in a document now matches the keyword "straße". -- Folding replaces invalid UTF-8 with U+FFFD first, so a stray invalid byte - no longer stops the letter after it from losing its accent. -- `krino new` refuses a directory whose path is not valid UTF-8 with that - reason, instead of reporting a broken template. -- The keyword cache is also discarded when normalisation changes, so answers - cached before these fixes are recomputed once. -- Builds need Go 1.25 or newer and use the Go 1.26.8 toolchain, which an - older `go` downloads itself. `golang.org/x/text` is updated to v0.41.0. - This fixes an infinite loop a crafted file's text could cause - (GO-2026-5970) and, through the toolchain, the XML nesting guard - (GO-2026-6088) and the `os/exec` and `os` fixes govulncheck reported. -- Tests: fuzz targets for every decoder of outside data (`make fuzz`), a - generated apply-and-undo round trip (`KRINO_PROPERTY_RUNS`), - enum-completeness tests, `make race` and `make vulncheck`. + moved and then trashed (it refused them before). +- Undo restores a trash entry only while it is still the file the run put + there, re-checks every file just before acting, keeps same-named files + of two directories apart, and leaves no directory behind. +- An undo that stopped part way can be finished by undoing the run again; + plain `krino undo` after an undo does that. +- The log restores a missing final newline, so a crash mid-line no longer + breaks the next run's undo. +- Trash entries skip names left orphaned in the Trash, and long names are + shortened to fit. + +Matching and the keyword cache +- Folding maps the capital sharp s (ẞ) to "SS", and replaces invalid UTF-8 + first so a stray byte no longer stops the next letter losing its accent. +- The cache keys on the extension too (it picks the extractor), and is + discarded when max-read, the Go release or the Unicode tables change; + removed keywords leave it, and an unused cache is removed. + +Command line +- Undo's per-file review behaves like review's (`w` leaves the rest + unreviewed, choices echoed). +- `w` stops krino even when applying fails. +- `--min-age` is refused by commands it does not apply to, and an empty + value is an error; a file dated in the future counts as brand new. +- `krino new` refuses a path that is not valid UTF-8, with that reason. +- A rule name cannot start with "(". +- Builds need Go 1.25 or newer and use the Go 1.26.8 toolchain; + `golang.org/x/text` is v0.41.0 (GO-2026-5970, and through the toolchain + GO-2026-6088 and the `os/exec` and `os` fixes). + +Tests +- Fuzz targets for every decoder of outside data (`make fuzz`), a generated + apply-and-undo round trip comparing files and directories + (`KRINO_PROPERTY_RUNS`), enum-completeness tests, `make race`, + `make vulncheck`, and a dependency gate that checks modules. ## 0.0.6 — 2026-09-14 @@ -215,8 +215,9 @@ rule content only when a private pattern list is configured with `krino log`. A reversal that the world has moved on since (the target is gone, changed, or the original path is occupied again) is refused for that file rather than guessed at. -- `-n` prints the plan and changes nothing, ever — no config is written, no - file is touched, nothing is logged. +- `-n` prints the plan and changes none of your files — no config is + written, nothing is moved, nothing is logged. It may refresh the keyword + cache in `~/.cache/krino`, which only makes the next run faster. ## Status diff --git a/docs/design.md b/docs/design.md index 9d7abb5..5b70183 100644 --- a/docs/design.md +++ b/docs/design.md @@ -678,7 +678,7 @@ krino log [-n N] list recent runs krino undo [RUN] reverse a run (default: the last one) -y apply without asking --n dry run: show the plan, change nothing +-n dry run: show the plan, change no file (the keyword cache may be refreshed) -v also list unmatched, ignored and busy files; full match reasons --json with -n: the plan as JSON (format unstable before 1.0) -c FILE use FILE instead of ~/.config/krino/krino.conf @@ -847,18 +847,31 @@ survives their corruption — a truncated line, a damaged cache, a hand-edited trashinfo — but does not defend against another local process with write access to them, which could already do anything krino can. -What that means, and the tests that hold it (plan 8): +What that means, and the tests that hold it (plans 8 and 9): -- A name never controls the terminal. Every name, path, reason and warning - is printed through `display`, which shows C0 controls, DEL, C1 controls, - Unicode bidirectional embeddings, overrides and isolates, and invalid - UTF-8 as escapes (`\x1b`, `\u202e`). -- A name never redirects a step. A rename whose placeholders produce "", - "." or "..", and a destination whose placeholders add a ".." segment, are - skipped with a reason. +- A name never controls the terminal. Everything printed from a name, path, + reason, warning, error, explain trace, JSON plan or log field is escaped: + C0 controls, DEL, C1 controls, every Unicode bidirectional control + (embeddings, overrides, isolates and the marks), line and paragraph + separators, and invalid UTF-8 (`\x1b`, `\u202e`). All of stderr goes + through the same escaping. Invisible format characters that are not + controls are printed as they are (known limitation). +- A name never redirects a step. A destination with a placeholder must + 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. - 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. + in its place), and, at its planned path, the same inode. A step that had + to land at a free name stops the rest of its chain. Overwrite trashes only + a regular file, never a directory or another file of the same plan. +- Every step is logged as soon as it has run, so a run killed mid-chain can + be undone as far as it got. +- Undo restores a trash entry only while it is still the file the run put + there (size, mtime, recorded path), re-checks every file at execution + time, and can finish an undo that stopped part way. +- An exclude whose content test cannot read the file holds (fails closed). - A file reaches an external tool only as an absolute path, so a name starting with `-` is never read as an option. - A trash entry name from the log must be a plain name inside the Trash, @@ -869,7 +882,10 @@ What that means, and the tests that hold it (plan 8): normalisation. - For generated trees and rules, apply then undo restores every file, and nothing is lost in between (property test, `KRINO_PROPERTY_RUNS`). -- Every value of every enum the output, the log or planning depends on is +- For generated trees and rules, apply then undo also restores every + directory, and the test fails if too few cases apply anything. +- Every value of the enums planning, the log and the summary depend on + (`plan.Kind`, `config.ActionKind`, `config.Conflict`, `scan.Reason`) is handled: tests read the constants from source. - `make vulncheck` checks the standard library and dependencies against the Go vulnerability database; `make race` runs the tests under the race diff --git a/man/krino.1 b/man/krino.1 index fb6731f..246184b 100644 --- a/man/krino.1 +++ b/man/krino.1 @@ -73,7 +73,7 @@ prints the plan and asks how to proceed .Fl y applies the plan without asking; .Fl n -prints it and changes nothing. +prints it and changes no file. If standard input is not a terminal and neither is given, .Nm refuses rather than guess. @@ -84,7 +84,8 @@ Apply the plan without asking. Cannot be combined with .Fl n . .It Fl n -Dry run: print the plan and exit without changing anything. +Dry run: print the plan and exit without changing any file. +It may refresh the keyword cache, which only makes the next run faster. .It Fl v Also list files that were skipped as unmatched, ignored or busy, and show the full reason a test matched or not. @@ -445,6 +446,20 @@ cheaper test in the same condition already decided the rule did not match. is a full trace of every test, not a cost-faithful preview of what .Fl n would actually spend time on. +.Pp +Unicode format characters that are invisible but not controls (a zero-width +space, U+FEFF, a soft hyphen) are printed as they are, so two names can look +the same. +Wrapping counts characters, not terminal columns: a name in a wide script +(CJK) or with many combining marks can run past the terminal's width. +.Pp +A second interrupt exits at once: every step completed so far is logged +and can be undone, but the step in flight when it arrived is not logged. +.Pp +Undo identifies a file by its directory and its path within it. +A file one directory's rules move into another included directory, which +sorts it again in the same run, is two files to undo; the first may then be +refused as missing. .Sh SEE ALSO .Xr krino.conf 5 .Pp |
