Skip to content

fs-safe 0.7.0

Choose a tag to compare

@github-actions github-actions released this 31 Aug 16:30
· 8 commits to main since this release
v0.7.0
054b488

Highlights

  • Safer reads and writes: extend exact identity checks and retained-descriptor publication to reject substituted inputs and detect publication mismatches, including rounded-equal Windows file IDs.
  • Opt into bounded temp cleanup: cleanupSafety: "require-bounded" prevents recursive traversal of substituted workspace trees, while the compatible default remains available without native support. See the cleanup guarantees and POSIX limits.
  • Harden untrusted archive handling: reject hidden path aliases, ambiguous TAR metadata, corrupt gzip streams, and excessive decoded data before publication; align filtering and entry reads across JavaScript and native backends.
  • Preserve queued work and durability: generation-bound claims protect newer same-ID entries from acknowledgement or quarantine, and queue transitions and synchronous store writes sync their filesystem changes.
  • Make lock handoffs and timeouts reliable: recover from ownership records unlinked during contention, surface release failures, and finish in-flight archive publication or rollback before returning a timeout.

Compatibility and upgrade notes

  • Claim queued entries before processing. Call loadJsonDurableQueueEntry() or the batch loader before acknowledgement, rather than pairing a direct read with ackJsonDurableQueueEntry(). A pending entry without a processing claim now rejects; missing entries remain idempotent no-ops. Queue and failed directories must share a filesystem with hardlink support for quarantine. See the queue recovery contract.
  • Handle the new cleanup result. TempWorkspaceCleanupResult adds "indeterminate" for cleanup whose safe completion cannot be established. Exhaustive result handlers must accept it; do not treat it as successful removal or blindly delete retained artifacts.
  • Archive filters receive canonical pre-strip paths. entryFilter now sees normalized separators, dot components, and effective metadata names rather than alternate spellings of the same entry. Update filters that match raw aliases; invalid onFiltered values reject before extraction rather than enabling skipping.
  • Validate limits and handle surfaced errors. Root, FileStore, secure/secret/regular reads, durable queues, and external output require maxBytes to be a non-negative safe integer or positive Infinity; zero is enforced and undefined retains configured defaults. Lock retry/deadline numbers are validated. Handle propagated release, failed-acquire cleanup, and durability failures; exhaustive archive-limit handlers must accept archive-decoded-size-exceeds-limit.

File safety and temp cleanup

  • Verify regular-file reads, root-file adapters, archive staging, Root.copyIn() sources, and durable JSON queue reads with lossless bigint identities. Reject replacements whose IDs round to the same JavaScript number and unresolved Windows identities before reading bytes; public numeric Stats receipts remain unchanged.
  • Verify async and sync regular-file append targets against exact pre-open, descriptor, and current-path identities, rejecting rounded-equal replacements and persistent unknown Windows identities before chmod or append.
  • Retain async and sync atomic replacement descriptors across beforeRename, retries, copy fallback, and publication. Reject substituted, non-regular, or hardlinked stages and preserve unowned cleanup paths. replaceFileAtomic() and its sync variant add renameIdentity: "verify-content-with-lock" for rename-unstable FUSE mounts: an explicit weaker identity contract for cooperating writers, not protection against same-authority actors ignoring the lock.
  • Pin callback-produced sibling temps through mode application, optional fsync, rename, and publication checks. Preserve unverified paths and producer modes, retain disabled sync defaults and best-effort file/directory chmod, and admit read-only descriptors unless writeSiblingTempFile requests file sync.
  • Apply writeJsonSync() best-effort file-mode tightening only through a reopened, single-link regular descriptor matching the staged bigint identity, so a swap cannot chmod an unrelated file.
  • Add native handle-relative temp workspace removal with collision-safe quarantine, enumerated-child identity checks, mount-crossing rejection, and symlink/reparse leaf removal without target traversal. Windows deletes exact opened handles; POSIX retains a documented final name-based race bounded to one substituted leaf or empty directory entry, never recursive traversal of a substituted nonempty tree.
  • Require usable Linux openat2 with RESOLVE_NO_XDEV at runtime for bounded cleanup. Compatible mode falls back when unavailable; cleanupSafety: "require-bounded" rejects before creating a child.
  • Open attacker-raceable secure, secret, archive, queue, publication, fallback, and lock-file read paths nonblocking on POSIX, so a FIFO substitution cannot stall admission, deadlines, or cleanup verification.

Archive extraction and reads

  • Prevent archive path aliases from bypassing excluded subtrees: pass canonical pre-strip paths to entryFilter across JavaScript/native ZIP, TAR, gzip, zstd, and bzip2, while retaining raw-path validation, stripping, collision checks, and filter rejection policy.
  • Reject ambiguous TAR EOF framing, unsafe numeric sizes, and bodies on raw directory/link entries before parsing. Enforce an absolute decoded-byte ceiling through physical EOF, including metadata and zero padding, before extraction publication or entry-read success; report archive-decoded-size-exceeds-limit on overflow.
  • Bound retained TAR manifests with a shared budget capped at 64 MiB, independent of compressed input size. Validate checksums, strict fixed-field UTF-8, NFC/NFD component lengths, GNU effective trailing separators, and linkname/type consistency before metadata processing or caller policy.
  • Validate GNU long-name/link bodies on both backends before parsing: reject malformed UTF-8/NUL structure, repeated or dangling metadata, mixed PAX/GNU chains, and unsafe effective names, while preserving original bytes and valid L+K pairs.
  • Apply ordered path, count, strip, depth, collision, and filter policy even to TAR records parsers would ignore (V, A, I, M, and unknown typeflags). Safely omit accepted unsupported records, validate raw names hidden by metadata or NUL terminators, and align native device/FIFO filtering and GNUDumpDir handling with JavaScript.
  • Stop starting archive destination mutations at the timeout boundary and wait for any already-running destination mutation and rollback before rejecting. Non-mutating work retains prompt deadlines; publication cannot continue after the timeout is reported.
  • Match readArchiveEntry() to extraction's validated canonical pre-strip paths and effective metadata names across all supported formats/backends. Preserve directory, link, collision, integrity, and byte-limit checks; read maxBytes remains a requested-entry budget, separate from archive-wide decoding and metadata limits.
  • Charge TAR maxEntryBytes and maxExtractedBytes only to entries accepted after strip/filter policy, not skipped or fully stripped members. Raw framing, logical entry counts, metadata, and absolute decoded-byte limits still apply to the whole archive.
  • Accept highly compressible gzip TARs within fs-safe's explicit limits consistently across backends by disabling node-tar's extra 1000x ratio threshold only after complete decoded-byte admission. Archive, decoded, entry, output, and deadline limits remain enforced.
  • Preserve large finite TAR limits such as Number.MAX_VALUE in native auto/require modes through shared internal metadata/decoded-byte and u32 entry-count clamping before backend selection; retain high-level payload budgets and reject malformed direct native limits.
  • Update native gzip/DEFLATE decoding to flate2 1.1.10 and miniz_oxide 0.9.1, including upstream incomplete-stream fixes. Truncated bodies, missing trailers, and checksum failures reject before extraction publishes files or an entry read returns bytes.

Durable queues, stores, and locks

  • Claim durable queue generations under a fail-closed cross-process lock using no-replace hardlinks and recoverable source retirement. Acknowledgement and quarantine preserve newer same-ID replacements, and quarantine collisions preserve existing failed-entry evidence.
  • Fsync affected directories for queue creation, claims, acknowledgement, quarantine, marker cleanup, and retirement; propagate durability failures. Synchronous file-store writes now fsync temps before rename and parent directories after publication.
  • Retry Root-backed sidecar acquisition when the owner unlinks its record after the waiter opens it, but only after the same Root capability proves absence. Replacements remain fail-closed; generic Root.open() behavior is unchanged.
  • Propagate asynchronous sidecar release and failed-acquire deletion failures, preserve paired errors, and retain failed release state for a safe retry through the same handle or manager drain.
  • Clamp synchronous lock backoff to the remaining finite deadline instead of overshooting the timeout or blocking forever.

Published packages

Package Registry tarball Verified integrity Provenance
@openclaw/fs-safe-linux-x64-gnu@0.7.0 tgz sha512-lTn5h0lkJjL1yiOxh+kgY2L4JDAXo2fNHXXZqxKACQdhB1+YFcc8YyUPTjK2b8aWlsgpRwfq/7JJ1eDHRbDcrg== verified attestation
@openclaw/fs-safe-linux-x64-musl@0.7.0 tgz sha512-e/LcF75zEQzg9yII5jv7WBZ3w7dNGBVpiOxlbm1dpy5XX+ap8pjsqWC0c0UGJdIbwWQInzKpb4/B0mwED/obYw== verified attestation
@openclaw/fs-safe-linux-arm64-gnu@0.7.0 tgz sha512-3UZDqtYJSMMN4Rid68EuTmCYjHyGHilQ7BFfm+ZKGloJoJLEvXoaAbDAqsazvWjrQKecfxlXJTC9qbLa5RYJEg== verified attestation
@openclaw/fs-safe-linux-arm64-musl@0.7.0 tgz sha512-rX1m2ft84KOb61/EhJmkiC0hqH4QCd1knQFcDU8tiA49sbiXsghNfSXRd2K07GlA7spXtqQfSX3ZvMh4f7JhBw== verified attestation
@openclaw/fs-safe-darwin-x64@0.7.0 tgz sha512-LS89eU+x7Kq0kugOR1HLsdVcuTNwCeMU+UQoWZUsWbWg9XTtw5ohOTU7bqewPNVGmB0cdihPfdorKc5wZgLTVw== verified attestation
@openclaw/fs-safe-darwin-arm64@0.7.0 tgz sha512-11z1Tv1ZVa31M+aoC56EX/DUACVuq4GiYSOpbv/ZDqwFPCdy+N/Vi2x/hcwCLjhb6UOsMWRsMsIAMjQAR2qHmA== verified attestation
@openclaw/fs-safe-win32-x64-msvc@0.7.0 tgz sha512-FiVNgckcHBuRov6vRytRjx8AdRBPxl56qZ8Dr0aAaAxfo+yhSJCGPmhnpgqUirBxkKQyrABIrP3LdIyscJMgWA== verified attestation
@openclaw/fs-safe@0.7.0 tgz sha512-i+0a4yQYa4ThUahaSLMledG99AL38XdAn8q/ppVMr49zr0dsOO+1gf8Jz6jgI0ggdcq6U4IhgsNunACsSymjyA== verified attestation

Release proof: GitHub Actions run.