fix: deep-scan findings + pre-merge review gate - #31
Merged
Conversation
Five user-visible defects from the prior deep scan: - File extraction (`x` save, content viewer) on multi-GB images no longer materialises every layer blob in memory at once. The lazy blob loader is bounded by one blob (the one currently being scanned) and includes an explicit per-blob size cap so a malformed archive declaring a petabyte layer cannot trigger runaway allocation. - `x` save now writes via temp-file + rename rather than truncating in place. A process kill, OOM, or power loss mid-write cannot leave a truncated file at the user's chosen path; the target either holds the complete pre-write content or the complete new content. The save resolves symlinks before writing, applies the process umask, and fsyncs the parent directory on POSIX so the rename survives power loss. - Opening a large source file in the viewer no longer freezes the TUI while Chroma tokenises. The highlight pass moved off the input goroutine; the file shows immediately in plain text and the coloured version swaps in when ready. - Filenames with CJK or wide-emoji characters no longer overflow the Waste panel's path column; truncation measures display columns rather than rune count. Pre-merge review (5 finder angles + verifier + sweep, per CLAUDE.md PR review discipline rule #1) surfaced three additional CONFIRMED defects introduced by the fixes themselves — all addressed: - atomicWriteFile bypassed the process umask (tmp.Chmod set the literal bits). Now applies umask via a build-tagged helper so the final mode matches the prior os.WriteFile semantics on POSIX. - atomicWriteFile destroyed symlinks (rename replaced the link with a regular file). Now resolves the link first and renames onto the resolved target, so saving over a symlink updates the link's target rather than the link itself. - TestReadSingleBlobFromSpool_OversizedHeaderRejected leaked a file descriptor and on Windows blocked t.TempDir cleanup; added defer tmp.Close().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses 5 defects from the prior deep scan, plus 5 additional CONFIRMED issues surfaced by the pre-merge code-review gate (per
CLAUDE.mdPR review discipline rule #1).Original deep-scan findings (all 5 fixed)
xsave, content viewer) replaced eagerloadLayerTars(full map of blobs in memory) with a lazyblobLoaderclosure. Peak heap is now bounded by one layer blob plus an explicit per-blob size cap (16 GiB) — malformed archives declaring petabyte layers can no longer trigger runaway allocation. (image/extractor.go, image/archive.go)x savepreviously usedos.WriteFile, leaving truncated files on process kill / OOM / power loss. Now writes via temp-file + rename in the target's directory; the file at the chosen path always holds either the complete pre-write content or the complete new content.tea.Cmd. The plain-text view appears immediately; the coloured version swaps in when ready, gated byrequestIDso stale highlights from interrupted opens are discarded.truncateLeftnow measures display columns vialipgloss.Widthrather than rune count.MaxLayerBlobSize(16 GiB) ceiling on each layer body read, preventing runaway allocation from a hostile or malformed manifest.Pre-merge review gate findings (3 CONFIRMED + 2 PLAUSIBLE-promoted, all fixed)
The
code-reviewskill (5 finder angles + 1-vote verifier + sweep) was run on the working-tree diff before commit. Five blocking findings:tmp.Chmod(perm)applied literal mode bits. New build-taggedapplyUmaskhelper round-trips throughsyscall.Umaskon POSIX and is a no-op on Windows, restoringos.WriteFilesemantics.os.Renamereplaced the link with a regular file. Nowfilepath.EvalSymlinksresolves first and the rename targets the resolved path, so saving over a symlink updates the link's target rather than the link itself.os.IsNotExistonly. Permission-denied / loop-detection errors now surface to the caller rather than silently writing at the unresolved path.fsync(parentDir)after rename, ext4 default mount options can lose the rename. Added build-taggedsyncDir(POSIXSyncon the directory fd, Windows no-op since NTFS journals MFT updates).TestReadSingleBlobFromSpool_OversizedHeaderRejectednever closed its temp*os.File; on Windows this blockedt.TempDircleanup. Addeddefer tmp.Close().Triage of remaining PLAUSIBLE findings (accepted with rationale)
syscall.Umaskis process-global; concurrent file creation in this codebase only opens 0o600 files, which are unaffected by the round-trip.os.WriteFile'sO_TRUNCfollow had the same class of risk.io.ReadAllcap allows a 16 GiB allocation by design (perMaxLayerBlobSizedoc: "permissive enough for production ML images").scanBlobIndexduplicate-name overwrite — adversarial-only, malformed archive corner.REFUTED: claimed O(N²) re-scan in
walkBackForFile—tar.Reader.Next()usesSeekwhen the underlying reader isio.Seeker(*os.Filequalifies), so per-call header scans are header-only seeks, not body re-reads.Test plan
go build ./...clean (Windows dev machine)go vet ./...cleangopls checkclean across touched filesGOOS=linux,GOOS=darwin,GOOS=windowsgo test ./...passes on Linux runnerx saveagainst nginx:latest; verify file contents matchx saveover a symlink; verify the symlink target is updated, link preserved