fix: repo-wide correctness, security & filesystem-safety hardening pass (v3.2.0)#92
Merged
Mikola Lysenko (mikolalysenko) merged 2 commits intoMay 29, 2026
Merged
Conversation
…ss (v3.2.0) Reviewed every source file in both crates line by line, fixed the bugs found, and added regression tests throughout. Highlights: Security - patch/package.rs: path-traversal via validate-before-normalize (package//etc/passwd escaped the package tree) - patch/diff.rs: clamp unbounded Vec preallocation from untrusted bsdiff target-size header (OOM/abort on a hostile delta) - vex/verify.rs: omit zero-file patches instead of emitting an evidence-free not_affected attestation Filesystem safety / atomicity / rollback - apply: DirWriteGuard for read-only dirs, chown-before-chmod to keep setuid/setgid, parent-dir fsync after rename - cow: atomic rename-over symlink (no pre-unlink), stage cleanup - rollback: delegate to hardened apply_file_patch; AlreadyOriginal before blob check; read-only-dir new-file delete - file_hash/git_sha256: open-once + fstat (TOCTOU), regular-file guard, size/body mismatch detection - cargo/nuget sidecars: hardened writes/deletes in read-only caches - cleanup_blobs: symlink-tolerant, accurate counts - apply_lock: classify genuine flock errors as Io, clamp timeout sleep Crawlers (on-disk layout & metadata) - composer v-prefix + malformed-entry tolerance + on-disk check - go cache-at-root, version case-encoding, GOPATH list, module directive - npm symlink following + nested-recursion guard - nuget global-cache version casing - python macOS framework layout + dist-info dir-name fallback - deno macOS cache path, XDG_CACHE_HOME, empty DENO_DIR - maven XML-comment stripping + skip-section depth - cargo TOML header tolerance + dir-name version split - shared utils/fs::entry_is_dir follows symlinks API client, commands & misc - proxy-url override on binary downloads; deterministic org/title/batch flag; case-insensitive hash compare - USER_AGENT + telemetry version track CARGO_PKG_VERSION (was 1.0.0) - apply release-variant NotFound spurious-failure fix - get/scan/remove char-safe truncation (UTF-8 panic) - setup/repair honest non-zero exit codes + telemetry - rollback no-op miscount; unlock released-snapshot; vex qualified PURLs - package.json non-object/dedup/glob/key-order (preserve_order) - json_envelope status invariant + oldUuid; list ordering; fuzzy_match tie-break; lock_cli sub-second timeout; vex schema/product fixes Updated stale repair/python_crawler e2e expectations to the corrected contracts. Bumped version to 3.2.0 and added the scripts/study-crates.ts audit harness used to drive the review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wenxin Jiang (Wenxin-Jiang)
approved these changes
May 29, 2026
`File::open` on a directory fails outright on Windows (different OS error kind), whereas on Unix it opens and the is_file() guard rejects it with InvalidInput. The production code rejects directories on both platforms; only pin the specific InvalidInput kind off-Windows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
A repo-wide review where every source file in both crates was read line by line, the bugs found were fixed, and regression tests were added throughout. The audit harness that drove it is included (
scripts/study-crates.ts); per-file write-ups are in the (gitignored)study-output/SUMMARY.md.Net: 90 source files touched, ~10k lines added (mostly tests). Version bumped 3.1.0 → 3.2.0 (minor) across Cargo / npm / PyPI manifests.
Security
patch/package.rs): validated the raw tar path but wrote thepackage/-stripped one, sopackage//etc/passwdescaped the package tree. Now validates the normalized path actually written.patch/diff.rs): the bsdiff target-size field (never validated by qbsdiff) fedVec::with_capacity; a tiny hostile delta could abort the process. Clamped to 64 MiB.vex/verify.rs): zero-file patches reportedapplied; now omitted asno_files.Filesystem safety / atomicity / rollback
apply:DirWriteGuardfor read-only dirs (Go cache0o555), chown-before-chmod to preserve setuid/setgid, parent-dirfsyncafter rename.cow: atomic rename-over-symlink (no destructive pre-unlink), stage cleanup on every error arm.rollback: now delegates to the hardenedapply_file_patch;AlreadyOriginalchecked before the blob lookup; read-only-dir new-file delete.file_hash/git_sha256: open-once + fstat (closes a TOCTOU), regular-file guard, streamed size/body mismatch detection.cleanup_blobs: symlink-tolerant, accurate "checked" count.apply_lock: genuineflockerrors surface asIo(notHeld); sleep clamped to remaining budget.Crawlers (on-disk layout & metadata)
Composer
v-prefix + malformed-entry tolerance; Go cache-at-root / version case-encoding /GOPATHlist /moduledirective; npm symlink following + nested-recursion guard; NuGet global-cache version casing; Python macOS framework layout +.dist-infodir-name fallback; Deno macOS cache path /XDG_CACHE_HOME/ emptyDENO_DIR; Maven XML-comment stripping + skip-section depth; Cargo TOML header tolerance + dir-name version split; sharedutils/fs::entry_is_dirfollows symlinks.API client, commands & misc
USER_AGENT+ telemetry version now trackCARGO_PKG_VERSION(were stale1.0/1.0.0).applyrelease-variantNotFoundspurious-failure fix;get/scan/removechar-safe truncation (UTF-8 panic);setup/repairhonest non-zero exit codes + failure telemetry;rollbackno-op miscount;unlockreleased-snapshot;vexqualified PyPI/Gem/Maven PURL resolution.package.json: non-object root/scripts no-panic, workspace dedup, bare/deep glob support, inline-comment stripping, top-level key-order preservation (preserve_order).listordering, case-insensitivefuzzy_matchtie-break,json_envelopestatus invariant +oldUuid,lock_clisub-second timeout, VEX schema/product fixes.Tests
repair_with_blob_404_marks_failure_in_summary→ now asserts exit1(repair correctly fails on partial download failure).crawler_python_e2emissing/malformed-METADATA cases → now assert the dir-name fallback recovers the package (and a genuinely unparseable name still skips).--features cargo.Notes
study-output/(raw session logs + SUMMARY) is not committed; added to.gitignore. The audit harness scripts are committed for reuse in future studies.🤖 Generated with Claude Code