# Knott audit
## 0.1.20 terminal output follow-up
The output review closed gaps in escape-sequence parsing and extended the
sanitizer to diagnostics and direct PKGBUILD terminal display. Complete ESC and
C1 control strings are removed, including OSC clipboard/hyperlink payloads,
graphics strings, and charset selectors. Regression cases cover malformed and
truncated sequences. Hidden directional formatting and selected invisible
separators are rendered visibly while ordinary multilingual text is preserved.
Search output now colors package fields after sanitization and wraps descriptions
to terminal columns. Info fields use hanging indentation, and progress avoids
wrapping or emitting cursor controls on dumb/tiny terminals. Piped search output
retains its compact layout. Dry-run previews quote shell arguments and visibly
represent embedded tabs/newlines.
Validation: 95 tests pass (82 unit and 13 CLI integration tests), including real
pseudo-terminal tests for color controls, terminal width, progress suppression,
source redirection, and hostile metadata. Formatting and Clippy with warnings
denied pass. The updated lockfile has zero RustSec vulnerabilities or advisory
warnings with the database retrieved on 2026-09-09.
The parser follows the control families described by the
[xterm control-sequence reference](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html).
Column measurement uses [unicode-width 0.2.2](https://docs.rs/unicode-width/0.2.2/unicode_width/).
Terminal fonts and Unicode versions can still differ in glyph width; long
unbroken identifiers are deliberately preserved. Interactive child processes
inherit the terminal and their output bypasses Knott's sanitizer. Redirected
PKGBUILD source also intentionally bypasses display sanitization.
## 0.1.19 package-operation audit
This audit covered CLI dispatch, subprocess arguments and privilege handling,
AUR metadata, dependency planning, version comparison, installation recovery,
local state, mirrors, and release packaging.
### Findings addressed
| Dependencies | The old HTTP/TLS stack and anyhow lock entries matched RustSec advisories | Update reqwest to 0.12.28 and anyhow to a patched release; remove the old h2 and rustls-pemfile dependencies |
| Install options | Unsupported options such as `--root`, `-Sw`, and `--downloadonly` could be discarded | Reject unsupported sync options and conflicting actions before invoking tools |
| Forwarded operations | Removal did not acquire privileges; helper flags reached pacman | Preserve pacman arguments, strip helper flags, use sudo for writes, and honor dry-run |
| Recovery dry-run | Saved options reset dry-run and recovery wrote durable state | Use in-memory journals for previews; abort/repair/resume previews preserve state |
| Interrupted planning | A failure after repo installation could resume as a completed transaction and lose remaining AUR work | Persist whether the full plan exists and the phase preceding failure; restart unfinished planning |
| Upgrade targets | Supplying a target with `-Syu` omitted other AUR updates | Include AUR updates with explicit targets; keep installed source variants during upgrades |
| Reinstallation | Current AUR versions were skipped even without `--needed` | Honor explicit reinstall requests and rebuild missing artifacts after interrupted installation |
| Package artifacts | All split/debug artifacts were installed | Inspect archive metadata with pacman and select only packages in the plan |
| Install reasons | AUR dependencies became explicit packages | Record dependency reasons and retry that step during recovery; preserve existing reasons |
| AUR metadata | Responses were unbounded and package bases could contain paths | Bound decompressed RPC bodies to 8 MiB, validate response counts and safe package identities |
| AUR cache | Requests larger than the cache lost early results | Keep per-request results separately and cache missing names |
| Providers | Search metadata could omit dependency fields; unversioned providers satisfied version constraints | Fetch full provider info and require an explicit provided version for versioned dependencies |
| Repository resolution | Only exact repo names were recognized | Ask pacman to resolve groups, providers, and version constraints; retain dependency constraints |
| Build order | Sorting split packages individually could build a base before its external dependencies | Sort complete package bases with an iterative dependency graph |
| Versions | Prereleases and missing pkgrel were compared incorrectly | Correct prerelease/separator ordering and ignore pkgrel unless both versions specify it |
| Menu | Hyphenated names were ignored and very large ranges could hang | Fall back to package-name lookup and clamp ranges before iterating |
| Confirmation | EOF accepted the default affirmative answer | EOF cancels installs and leaves interrupted transactions untouched |
| State | Schema and IDs were unchecked; lock location changed with runtime environment | Validate schema/IDs, cap state at 16 MiB, create private files exclusively, use a stable lock path |
| Mirrors and keys | One HTTPS line was enough to accept other active directives; reflector was bootstrapped using a partial upgrade | Validate every active line, use a private temporary directory, bound file reads, and install reflector during a full upgrade |
| Error reporting | Failed local DB/foreign-package queries could look like no updates; partial info lookup could exit successfully | Propagate operational errors and return failure for missing info targets |
`--no-prefer-bin` provides an explicit source-package choice. The documented
minimum Rust version remains 1.87. CI checks it separately from stable Rust.
### Verification
The regression suite passed all 81 tests (73 unit tests and 8 executable tests).
Executable tests use isolated directories, a loopback HTTP service, and fake
pacman/sudo/git/makepkg programs. They do not install or remove system packages.
Tests that exercise makepkg intentionally skip under root, matching Knott's
root refusal; the local audit ran as an ordinary user.
The version comparator was also compared with native libalpm across 576 pairs
covering epochs, releases, prereleases, separators, and numeric segments, with
no differences. Pacman's actual read-only resolution was checked with satisfied
and unsatisfied version constraints. The expected semantics are documented in
[pacman(8)](https://man.archlinux.org/man/pacman.8.en) and
[vercmp(8)](https://man.archlinux.org/man/vercmp.8.en).
Dependency updates address the affected locked versions identified by RustSec,
including [h2 RUSTSEC-2026-0258](https://rustsec.org/advisories/RUSTSEC-2026-0258.html),
[rustls-webpki RUSTSEC-2026-0104](https://rustsec.org/advisories/RUSTSEC-2026-0104.html),
and [anyhow RUSTSEC-2026-0190](https://rustsec.org/advisories/RUSTSEC-2026-0190.html).
The previous lockfile matched four vulnerability advisories, plus unmaintained
and unsoundness warnings. The updated lockfile has zero matching vulnerability
advisories and zero advisory warnings under cargo-audit 0.22.2 with the RustSec
database retrieved on 2026-09-09. Advisory matching does not establish
exploitability through Knott; several advisories affect optional functionality
that Knott does not use.
Release checks include formatting, Clippy with warnings denied, the regression
suite, `cargo package`, and an installation under `/tmp/knott-install-check`.
The release binary's version, help, and diagnostics are smoke-tested.
### Scope and remaining limitations
This is a source audit with controlled subprocess tests, not a live system
upgrade or a power-loss test. Pacman and makepkg remain responsible for package
signature verification, file conflicts, scriptlets, and actual system changes.
AUR PKGBUILDs are executable user-maintained code; Knott does not sandbox or
review that code. Inspect sources before building unfamiliar packages.
Recovery journals describe intended work, not a rollback snapshot. AUR metadata
or git sources can change between planning and building; revisions and artifact
hashes are not pinned in the journal. Unresolved dependencies still produce a
warning and are left for makepkg to check. Forwarded pacman operations and the
key/mirror workflow hold the Knott lock but do not have per-step recovery journals.