@openclaw/fs-safe 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -0
- package/README.md +15 -8
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +32 -11
- package/dist/advanced.d.ts +3 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +3 -1
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +4 -3
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +13 -9
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +6 -2
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +15 -2
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +7 -19
- package/dist/archive-plan.js +1 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +55 -46
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +43 -10
- package/dist/archive-tar-inspect.d.ts.map +1 -1
- package/dist/archive-tar-inspect.js +4 -1
- package/dist/archive-zip-admission.d.ts +1 -1
- package/dist/archive-zip-admission.d.ts.map +1 -1
- package/dist/archive-zip-admission.js +2 -2
- package/dist/archive-zip-directory.d.ts +3 -0
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +13 -2
- package/dist/archive-zip-loader.d.ts +3 -2
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +30 -4
- package/dist/archive-zip-manifest.d.ts +5 -0
- package/dist/archive-zip-manifest.d.ts.map +1 -0
- package/dist/archive-zip-manifest.js +22 -0
- package/dist/archive-zip-names.d.ts +6 -1
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +50 -17
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +3 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/bounded-read.js +2 -2
- package/dist/darwin-acl.d.ts +4 -0
- package/dist/darwin-acl.d.ts.map +1 -0
- package/dist/darwin-acl.js +24 -0
- package/dist/deny-mutations.d.ts.map +1 -1
- package/dist/deny-mutations.js +8 -2
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +5 -3
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +67 -23
- package/dist/directory-entry-path.d.ts +3 -0
- package/dist/directory-entry-path.d.ts.map +1 -0
- package/dist/directory-entry-path.js +21 -0
- package/dist/directory-guard.d.ts +17 -1
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +134 -48
- package/dist/directory-mode-node.d.ts +12 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +102 -4
- package/dist/effective-uid.d.ts +2 -0
- package/dist/effective-uid.d.ts.map +1 -0
- package/dist/effective-uid.js +25 -0
- package/dist/file-handle-transfer.d.ts.map +1 -1
- package/dist/file-handle-transfer.js +98 -27
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +12 -2
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +36 -6
- package/dist/file-lock.d.ts.map +1 -1
- package/dist/file-lock.js +29 -9
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +6 -2
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +20 -65
- package/dist/file-store-copy-source.d.ts +5 -0
- package/dist/file-store-copy-source.d.ts.map +1 -0
- package/dist/file-store-copy-source.js +31 -0
- package/dist/file-store-path.d.ts.map +1 -1
- package/dist/file-store-path.js +4 -1
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +9 -1
- package/dist/file-store-sync-directory.d.ts +16 -0
- package/dist/file-store-sync-directory.d.ts.map +1 -0
- package/dist/file-store-sync-directory.js +349 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +11 -28
- package/dist/filename.d.ts +1 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +85 -23
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +3 -2
- package/dist/guarded-mkdir.d.ts +10 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +217 -33
- package/dist/guest.d.ts.map +1 -1
- package/dist/guest.js +16 -5
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +73 -10
- package/dist/install-path.d.ts +6 -0
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +67 -19
- package/dist/json-durable-queue-ownership.d.ts +8 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +134 -43
- package/dist/json-durable-queue-paths.d.ts +11 -0
- package/dist/json-durable-queue-paths.d.ts.map +1 -0
- package/dist/json-durable-queue-paths.js +42 -0
- package/dist/json-durable-queue-read.d.ts +6 -0
- package/dist/json-durable-queue-read.d.ts.map +1 -0
- package/dist/json-durable-queue-read.js +61 -0
- package/dist/json-durable-queue.d.ts +1 -1
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +83 -134
- package/dist/json-store.d.ts.map +1 -1
- package/dist/json-store.js +5 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +54 -22
- package/dist/local-file-access.d.ts.map +1 -1
- package/dist/local-file-access.js +4 -0
- package/dist/local-file-descriptor.d.ts +17 -0
- package/dist/local-file-descriptor.d.ts.map +1 -0
- package/dist/local-file-descriptor.js +84 -0
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +35 -7
- package/dist/move-path-cleanup.d.ts +2 -0
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +44 -18
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +31 -6
- package/dist/native-binding.d.ts +21 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-directory-observation.d.ts +17 -0
- package/dist/native-directory-observation.d.ts.map +1 -0
- package/dist/native-directory-observation.js +37 -0
- package/dist/native-parent-admission.d.ts +31 -0
- package/dist/native-parent-admission.d.ts.map +1 -0
- package/dist/native-parent-admission.js +124 -0
- package/dist/native-pinned-write-windows.d.ts +0 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +0 -3
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +309 -40
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +15 -5
- package/dist/overwrite-file-handle.d.ts.map +1 -1
- package/dist/overwrite-file-handle.js +5 -1
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +2 -0
- package/dist/path-policy.d.ts.map +1 -1
- package/dist/path-policy.js +7 -2
- package/dist/path-prefix.d.ts +7 -0
- package/dist/path-prefix.d.ts.map +1 -0
- package/dist/path-prefix.js +82 -0
- package/dist/path-scope-lexical.d.ts +14 -0
- package/dist/path-scope-lexical.d.ts.map +1 -0
- package/dist/path-scope-lexical.js +37 -0
- package/dist/path-segment-route.d.ts +7 -0
- package/dist/path-segment-route.d.ts.map +1 -0
- package/dist/path-segment-route.js +24 -0
- package/dist/path-suffix-aliases.d.ts +10 -0
- package/dist/path-suffix-aliases.d.ts.map +1 -0
- package/dist/path-suffix-aliases.js +386 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +35 -6
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +14 -3
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +37 -8
- package/dist/pinned-mutation-admission.d.ts +25 -0
- package/dist/pinned-mutation-admission.d.ts.map +1 -0
- package/dist/pinned-mutation-admission.js +425 -0
- package/dist/pinned-mutation-observation.d.ts +34 -0
- package/dist/pinned-mutation-observation.d.ts.map +1 -0
- package/dist/pinned-mutation-observation.js +142 -0
- package/dist/pinned-mutation-shared-route.d.ts +24 -0
- package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
- package/dist/pinned-mutation-shared-route.js +70 -0
- package/dist/pinned-open.d.ts +6 -0
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +28 -10
- package/dist/pinned-write-types.d.ts +75 -0
- package/dist/pinned-write-types.d.ts.map +1 -0
- package/dist/pinned-write-types.js +1 -0
- package/dist/pinned-write.d.ts +7 -33
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +152 -17
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +2 -0
- package/dist/private-producer-handoff.d.ts +16 -0
- package/dist/private-producer-handoff.d.ts.map +1 -0
- package/dist/private-producer-handoff.js +272 -0
- package/dist/private-temp-workspace.d.ts +2 -39
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +183 -77
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +33 -29
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +56 -45
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +12 -5
- package/dist/replace-file-temp-owner.d.ts +1 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +12 -1
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +24 -24
- package/dist/root-boundary.d.ts +29 -0
- package/dist/root-boundary.d.ts.map +1 -0
- package/dist/root-boundary.js +182 -0
- package/dist/root-context.d.ts +12 -1
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +89 -16
- package/dist/root-directory-creation.d.ts +13 -0
- package/dist/root-directory-creation.d.ts.map +1 -0
- package/dist/root-directory-creation.js +212 -0
- package/dist/root-directory-list.d.ts +16 -4
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +182 -33
- package/dist/root-directory.d.ts +23 -0
- package/dist/root-directory.d.ts.map +1 -0
- package/dist/root-directory.js +141 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +12 -1
- package/dist/root-file-final-admission.d.ts +21 -0
- package/dist/root-file-final-admission.d.ts.map +1 -0
- package/dist/root-file-final-admission.js +83 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +103 -24
- package/dist/root-impl.d.ts +1 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +316 -325
- package/dist/root-move-noreplace.d.ts +14 -0
- package/dist/root-move-noreplace.d.ts.map +1 -0
- package/dist/root-move-noreplace.js +202 -0
- package/dist/root-move-preflight.d.ts +8 -0
- package/dist/root-move-preflight.d.ts.map +1 -0
- package/dist/root-move-preflight.js +16 -0
- package/dist/root-observed-path.d.ts +9 -0
- package/dist/root-observed-path.d.ts.map +1 -0
- package/dist/root-observed-path.js +95 -0
- package/dist/root-path-errors.d.ts +12 -0
- package/dist/root-path-errors.d.ts.map +1 -0
- package/dist/root-path-errors.js +13 -0
- package/dist/root-path-existing.d.ts +2 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +63 -22
- package/dist/root-path-observation.d.ts +63 -0
- package/dist/root-path-observation.d.ts.map +1 -0
- package/dist/root-path-observation.js +180 -0
- package/dist/root-path-stat.d.ts +5 -0
- package/dist/root-path-stat.d.ts.map +1 -0
- package/dist/root-path-stat.js +101 -0
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +23 -4
- package/dist/root-path.d.ts +13 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +263 -73
- package/dist/root-paths-lexical.d.ts +9 -0
- package/dist/root-paths-lexical.d.ts.map +1 -0
- package/dist/root-paths-lexical.js +22 -0
- package/dist/root-paths.d.ts +3 -29
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +89 -178
- package/dist/root-read-admission.d.ts +26 -0
- package/dist/root-read-admission.d.ts.map +1 -0
- package/dist/root-read-admission.js +94 -0
- package/dist/root-remove-identity.d.ts +15 -0
- package/dist/root-remove-identity.d.ts.map +1 -0
- package/dist/root-remove-identity.js +89 -0
- package/dist/root-remove-receipt.d.ts +17 -0
- package/dist/root-remove-receipt.d.ts.map +1 -0
- package/dist/root-remove-receipt.js +37 -0
- package/dist/root-remove.d.ts +2 -1
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +172 -10
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +2 -1
- package/dist/root-write-admission.d.ts +68 -0
- package/dist/root-write-admission.d.ts.map +1 -0
- package/dist/root-write-admission.js +322 -0
- package/dist/root-write-compatibility.d.ts +13 -0
- package/dist/root-write-compatibility.d.ts.map +1 -0
- package/dist/root-write-compatibility.js +74 -0
- package/dist/root-write-complete-parent.d.ts +42 -0
- package/dist/root-write-complete-parent.d.ts.map +1 -0
- package/dist/root-write-complete-parent.js +195 -0
- package/dist/root-write-mode.d.ts +2 -0
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +20 -7
- package/dist/root-write-publication.d.ts +24 -0
- package/dist/root-write-publication.d.ts.map +1 -0
- package/dist/root-write-publication.js +85 -0
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +18 -7
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +3 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +62 -24
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +18 -13
- package/dist/secret-read-policy.d.ts.map +1 -1
- package/dist/secret-read-policy.js +5 -1
- package/dist/secure-file-windows.d.ts +10 -0
- package/dist/secure-file-windows.d.ts.map +1 -0
- package/dist/secure-file-windows.js +186 -0
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +98 -10
- package/dist/secure-temp-dir.d.ts +6 -3
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +121 -101
- package/dist/secure-temp-repair.d.ts +35 -0
- package/dist/secure-temp-repair.d.ts.map +1 -0
- package/dist/secure-temp-repair.js +106 -0
- package/dist/sibling-staged-file.d.ts +3 -1
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +181 -50
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +27 -12
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +45 -15
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +4 -2
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -3
- package/dist/staged-directory.d.ts +14 -5
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +54 -3
- package/dist/standalone-publication-path.d.ts +2 -0
- package/dist/standalone-publication-path.d.ts.map +1 -0
- package/dist/standalone-publication-path.js +6 -0
- package/dist/stat-observation.d.ts +11 -0
- package/dist/stat-observation.d.ts.map +1 -0
- package/dist/stat-observation.js +64 -0
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/strict-file-identity.js +39 -2
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +7 -2
- package/dist/temp-target.d.ts +2 -0
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +133 -4
- package/dist/temp-workspace-admission.d.ts +22 -0
- package/dist/temp-workspace-admission.d.ts.map +1 -0
- package/dist/temp-workspace-admission.js +374 -0
- package/dist/temp-workspace-child-admission.d.ts +13 -0
- package/dist/temp-workspace-child-admission.d.ts.map +1 -0
- package/dist/temp-workspace-child-admission.js +95 -0
- package/dist/temp-workspace-descriptor.d.ts +48 -0
- package/dist/temp-workspace-descriptor.d.ts.map +1 -0
- package/dist/temp-workspace-descriptor.js +363 -0
- package/dist/temp-workspace-identity.d.ts +15 -0
- package/dist/temp-workspace-identity.d.ts.map +1 -0
- package/dist/temp-workspace-identity.js +41 -0
- package/dist/temp-workspace-owner.d.ts +7 -6
- package/dist/temp-workspace-owner.d.ts.map +1 -1
- package/dist/temp-workspace-owner.js +121 -61
- package/dist/temp-workspace-permissions.d.ts +4 -0
- package/dist/temp-workspace-permissions.d.ts.map +1 -0
- package/dist/temp-workspace-permissions.js +32 -0
- package/dist/temp-workspace-types.d.ts +40 -0
- package/dist/temp-workspace-types.d.ts.map +1 -0
- package/dist/temp-workspace-types.js +1 -0
- package/dist/temp.d.ts +1 -1
- package/dist/temp.d.ts.map +1 -1
- package/dist/temp.js +1 -1
- package/dist/test-hooks.d.ts +7 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/text-atomic.d.ts.map +1 -1
- package/dist/text-atomic.js +3 -1
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +54 -8
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +20 -16
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +5 -0
- package/dist/windows-path-alias.d.ts +39 -0
- package/dist/windows-path-alias.d.ts.map +1 -0
- package/dist/windows-path-alias.js +153 -0
- package/docs/advanced.md +28 -1
- package/docs/archive.md +29 -2
- package/docs/atomic.md +27 -3
- package/docs/contributing.md +4 -1
- package/docs/copy.md +4 -1
- package/docs/durability.md +18 -2
- package/docs/errors.md +14 -6
- package/docs/file-store.md +30 -3
- package/docs/filename.md +21 -7
- package/docs/guest.md +5 -0
- package/docs/install-path.md +61 -15
- package/docs/install.md +12 -8
- package/docs/json-store.md +5 -1
- package/docs/json.md +11 -0
- package/docs/mutation-policy-proof.md +65 -0
- package/docs/native-helper.md +28 -9
- package/docs/native.md +46 -10
- package/docs/output.md +33 -11
- package/docs/path-prefix.md +64 -0
- package/docs/path-suffix-aliases.md +159 -0
- package/docs/path.md +12 -1
- package/docs/permissions.md +26 -1
- package/docs/private-file-store.md +14 -0
- package/docs/public-api.md +14 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +8 -1
- package/docs/root.md +21 -4
- package/docs/secret-file.md +3 -0
- package/docs/secure-file.md +21 -16
- package/docs/security-model.md +76 -9
- package/docs/sidecar-lock.md +28 -1
- package/docs/store.md +27 -2
- package/docs/temp.md +262 -34
- package/docs/test-hooks.md +14 -0
- package/docs/writing.md +38 -8
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,125 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.13.0 - 2026-09-16
|
|
6
|
+
|
|
7
|
+
### Highlights
|
|
8
|
+
|
|
9
|
+
- **Stronger protection against filesystem replacement races:** bind Root reads, metadata, writes, moves, and cleanup to exact file and directory identities; use atomic native rename for no-clobber moves.
|
|
10
|
+
- **Less overhead in guarded writes and archives:** avoid repeated path and mutation-policy work on eligible routes, reuse validated archive paths, and size native result buffers to bounded member sizes.
|
|
11
|
+
- **Safer Windows paths and writes:** reject NTFS stream and directory-index aliases before filesystem access, and retain destination handles through compatibility writes and verification.
|
|
12
|
+
- **New tools for prospective paths:** add `resolvePathPrefixSync()` and `probePathSuffixAliasesSync()` for existing-prefix traversal and bounded observations of missing suffix aliases.
|
|
13
|
+
- **Opt-in bounded temporary-file cleanup:** add retained-directory cleanup ownership to `tempFile()` and strengthen temporary-workspace admission and permission repair.
|
|
14
|
+
|
|
15
|
+
### Compatibility and upgrade notes
|
|
16
|
+
|
|
17
|
+
- **No-clobber `Root.move()` now requires usable native support.** With `overwrite` omitted or `false`, native-off mode, missing bindings, or unavailable safe parent/rename support fail with `helper-unavailable`; there is no JavaScript check-and-replace fallback. Keep the matching native package installed for these moves. Directory moves still require `overwrite: true`, which retains guarded JavaScript support; use it only when replacement is intended. Post-operation verification can report an error after a rename has completed.
|
|
18
|
+
- Windows alternate-data-stream and directory-index aliases now reject across guarded APIs before I/O. Supported rooted and extended-drive paths retain their handling, as do released drive-relative forms in trusted-path atomic, store/queue, move, publication, and lock APIs. Existing Root destination restrictions remain distinct; ordinary colon-bearing POSIX names keep their established rules.
|
|
19
|
+
- POSIX secure-file ownership checks now use the process's effective user ID and fail closed when required identity facts are unavailable. Public `Stats` values remain numeric; exact writable-file verification uses private bigint receipts.
|
|
20
|
+
- `tempFile({ cleanupSafety: "require-bounded" })` is opt-in and requires an existing supplied root plus supported retained-parent cleanup. Unsupported admission fails before child creation. `"compatible"` remains the default; cleanup callbacks and error-suppression behavior are preserved. Bounded cleanup retains the documented [POSIX final-entry unlink limitation](https://github.com/openclaw/fs-safe/blob/v0.13.0/docs/temp.md).
|
|
21
|
+
- The two new path helpers are observations, not permission to access a path. Prefix resolution is read-only but is not a pinned or consistent snapshot. Suffix probing can create and remove directories under an approved writable parent, returns `undefined` when unresolved, and may preserve artifacts whose ownership cannot be verified; its budgets limit work counts and path sizes, not elapsed time.
|
|
22
|
+
|
|
23
|
+
### Root boundaries and mutation performance
|
|
24
|
+
|
|
25
|
+
- Fence Root content reads, metadata observations, directory entries, and low-level file opens with exact captured-root, parent, and final-path identities. Admit supported native Windows root spellings without relaxing symlink or replacement rejection.
|
|
26
|
+
- Apply `denyMutations` and mutation-symlink policy to the actual retained parent used by `Root.write`, `Root.create`, `Root.copyIn`, `Root.openWritable`, `Root.append`, and `Root.mkdir`, including Windows buffer fallbacks. Admit missing components before creating them, and re-authorize followed final-link destinations before open, truncation, staging, and publication.
|
|
27
|
+
- Reuse operation-local policy observations, canonical parent guards, post-create receipts, and immutable suffix offsets for eligible Node.js mutation paths. This avoids repeated resolver work and depth-quadratic segment copies; collisions, callbacks, aliases, ambiguous routes, incomplete identity observations, and final open/publication/cleanup checks retain full admission.
|
|
28
|
+
- Bound repeated resolver work during deep missing-parent creation on eligible Windows and POSIX JavaScript paths. Advance only through owned directory-creation receipts, and let `Root.mkdir` reuse an exact existing-parent proof for its single missing child.
|
|
29
|
+
- Reuse an already admitted parent guard for private producer Roots while preserving isolated handoff, including legacy Windows delete-pending and read-only restoration behavior.
|
|
30
|
+
- Bind nonrecursive removal to its admitted parent and revalidate native move endpoints and explicit parent-symlink rejection before dispatch. Recheck copied-directory identities after child cleanup so concurrent replacements are preserved.
|
|
31
|
+
|
|
32
|
+
### Credentials, permissions, and temporary files
|
|
33
|
+
|
|
34
|
+
- Snapshot secure-read, secret-reader, traversal, and borrowed-transfer policies before asynchronous work, including size/link limits, trusted directories, and selected environment values. In-flight admission can no longer be relaxed by changing shared options.
|
|
35
|
+
- Preserve single-read pathname and authority accessors on Node.js 22, along with callback receivers, cancellation signals, and progress callbacks across JSON, durable directory/queue, sibling-staging, and borrowed-handle operations. Windows temp admission remains active with platform adapters, and canonical prefixes are checked before inspection.
|
|
36
|
+
- Bind secure temporary-directory and synchronous store-directory mode repairs to no-follow descriptors and exact current identities, including concurrent mode changes during the matching-mode fast path.
|
|
37
|
+
- Admit temporary-workspace roots and children before use, preserve existing permissions, and bind initialization and cleanup to retained identities. Opt-in bounded temporary-file admission closes descriptors on failure.
|
|
38
|
+
- Retain Windows destination descriptors through buffered compatibility-write locking, content verification, mode changes, sync, and final identity checks.
|
|
39
|
+
- Validate descriptor-bound macOS clone ACLs before admission and publication, including absent ACLs. Once payload bytes exist, normalization or security failures are terminal `EIO` errors rather than a reason to retry with an ordinary copy.
|
|
40
|
+
|
|
41
|
+
### Archive safety and performance
|
|
42
|
+
|
|
43
|
+
- Bind physical ZIP entry names and file/directory kinds to decoder metadata before callbacks or member selection, rejecting ambiguous interpretations consistently.
|
|
44
|
+
- Stop zstd and bzip2 compressed-input refills after cancellation while preserving decoded-output limits and complete archive validation.
|
|
45
|
+
- Reuse canonical archive paths when no components are stripped and avoid temporary ZIP collision collections, retaining validation/filter order and checks of both raw and Unicode identities.
|
|
46
|
+
- Reserve native archive result capacity from bounded member-size hints so small ZIP and compressed TAR reads do not allocate the caller's larger byte budget.
|
|
47
|
+
|
|
48
|
+
### Path helpers and reliability
|
|
49
|
+
|
|
50
|
+
- Add [`resolvePathPrefixSync()`](https://github.com/openclaw/fs-safe/blob/v0.13.0/docs/path-prefix.md) under `@openclaw/fs-safe/advanced` to traverse existing path prefixes while retaining the raw missing suffix, preserving dangling aliases and propagating ambiguous resolution failures.
|
|
51
|
+
- Add [`probePathSuffixAliasesSync()`](https://github.com/openclaw/fs-safe/blob/v0.13.0/docs/path-suffix-aliases.md) under `@openclaw/fs-safe/advanced`, with fixed path, depth, creation, and observation budgets and conservative identity-checked cleanup.
|
|
52
|
+
- Sanitize fallback filenames and validate completed sibling callback names before producers run, retaining length bounds and Windows device-name rejection. Reuse Windows namespace admission for unchanged ordinary rooted-drive paths while keeping full checks for relative, normalized, malformed, namespaced, and colon-bearing forms.
|
|
53
|
+
- Pin asynchronous relative sidecar-lock paths and roots to their entry-time working directory through acquisition, reclaim, verification, and release. Close synchronous lock descriptors after metadata inspection failures while preserving the original error and unverified sidecar; retry vanished admitted successors under the existing identity checks and budgets.
|
|
54
|
+
- Report actionable guarded-write errno diagnostics while preserving error codes, categories, original causes, and already-classified failures.
|
|
55
|
+
- Tolerate concurrent guest parent-directory creation only after descriptor-relative no-follow admission; file and symlink competitors remain rejected. Thanks @vincentkoc.
|
|
56
|
+
- Show only the moon icon in the docs light-theme picker and only the sun icon in dark mode.
|
|
57
|
+
|
|
58
|
+
### Validation and tooling
|
|
59
|
+
|
|
60
|
+
- Strengthen exact-identity, rounded-inode, Windows recovery, mutation-policy, and package-consumer regressions, with explicit test-hook provenance and bounded fixture lifetimes. Queue stress fixtures retain and drain outstanding writers before cleanup.
|
|
61
|
+
- Separate manual method-audit harnesses from immutable candidate and baseline checkouts, validate dispatch inputs before fan-out, and support Node 22/24 plus rebuild and same-artifact controls. Versioned benchmark receipts bind source, harness, dependencies, artifacts, native addons, and runner metadata; they detect accidental drift and are not a sandbox for hostile code.
|
|
62
|
+
- Expand benchmark coverage for filename sanitizers, all 17 profiles, shared mutation admission, temporary-workspace modes, and existing/missing roots at depths 4/8/32. Keep Windows-specific rows and observed-preservation claims distinct from JavaScript operation counts.
|
|
63
|
+
- Isolate split-credential proof runners, authenticate official Node archives before checkout, and use private, identity-checked stages and tools. Fixtures remain traversable by both dropped identities while published receipts stay in a separately admitted container; the seven behavioral cases are unchanged.
|
|
64
|
+
- Update development tooling to `@napi-rs/cli` 3.9.1, fast-check 4.10.0, and Vite 8.3.0 with verified registry integrity.
|
|
65
|
+
|
|
66
|
+
## 0.12.0 - 2026-09-15
|
|
67
|
+
|
|
68
|
+
### Highlights
|
|
69
|
+
|
|
70
|
+
- **Faster archive workloads:** reuse validated ZIP names, batch native plain-TAR reads, and reduce gzip validation and payload-buffer allocations while preserving complete archive checks.
|
|
71
|
+
- **Less overhead in everyday filesystem work:** reduce repeated path resolution, directory-walk work, small-hash allocations, Linux copy buffers, and lock-manager setup.
|
|
72
|
+
- **Stronger Windows file boundaries:** verify secure-file permissions against the descriptor supplying the bytes, retain handles through private-directory creation and cleanup, and check directory identity when Root containment relies on case folding.
|
|
73
|
+
- **Safer queue recovery and cleanup:** prevent stale migrations from replacing newer queue entries, preserve files refreshed during expiry pruning, and keep existing destinations intact when guest symlink moves fail.
|
|
74
|
+
- **Versioned install identifiers:** add `safePathSegmentHashedV2`, which hashes every trimmed ID into a fixed lowercase directory segment and avoids the deterministic aliases of the legacy readable encoder.
|
|
75
|
+
|
|
76
|
+
### Compatibility and upgrade notes
|
|
77
|
+
|
|
78
|
+
- Windows `readSecureFile()` now requires the matching current native package for descriptor-bound owner and DACL checks. Missing or stale helpers, remote handles, denied access, and unsupported or incomplete permission facts fail closed before reading; there is no pathname ACL-command fallback. Standalone permission-reporting APIs retain their documented fallbacks.
|
|
79
|
+
- Windows private-directory creation rejects explicit `.` and `..` components and components ending in spaces or periods before filesystem operations. Directory association requires the complete file identity; unsupported identity classes fail closed.
|
|
80
|
+
- Windows native descriptor operations require the host runtime's paired libuv descriptor bridge. Missing or partial bridges reject with `ENOTSUP` instead of interpreting descriptors as raw handles or using another runtime's descriptor table.
|
|
81
|
+
- `safePathSegmentHashedV2` is opt-in. Legacy encoder outputs and the default encoder remain unchanged. Switching to V2 changes existing paths: use a new base directory or explicitly migrate after verifying recorded IDs, and do not silently fall back to legacy paths.
|
|
82
|
+
|
|
83
|
+
### Archive performance and reliability
|
|
84
|
+
|
|
85
|
+
- Reuse identical local and central ZIP name validation within one entry, including matching complete Unicode Path metadata. Differing fields and shared backing memory retain independent checks; CRC, traversal, collision, and interpretation validation remain intact.
|
|
86
|
+
- Reuse raw archive path segments during validation, preserving platform rules, normalized component limits, and error ordering.
|
|
87
|
+
- Speed up native plain-TAR inspection and extraction with bounded file read-ahead, retaining complete framing, payload, trailer, and cancellation checks at parser boundaries.
|
|
88
|
+
- Fill one owned result buffer when reading admitted gzip TAR payloads in JavaScript, reducing retained decoded buffers while preserving complete validation and independent returned bytes.
|
|
89
|
+
- Validate gzip container padding with bounded buffer comparisons and one lazily allocated reusable zero window, preserving complete suffix checks, short-read handling, and cancellation cadence.
|
|
90
|
+
- Destroy and join ZIP decoder sources before rejecting bounded member reads, preventing abandoned decoders and retained archive buffers after byte-limit failures while preserving error classifications on Node 22 and newer.
|
|
91
|
+
|
|
92
|
+
### Paths, reads, and copying
|
|
93
|
+
|
|
94
|
+
- Reuse normalized absolute POSIX roots within bulk lexical path resolution and avoid per-segment drive-letter validation allocations, preserving path spellings, validation order, and errors.
|
|
95
|
+
- Reuse resolved Windows drive paths and descendant comparisons, with existing namespace, UNC, and colon-bearing component handling retained. Classify Windows device names without intermediate segment arrays.
|
|
96
|
+
- Reuse lexical directory prefixes while walking, preserving traversal order, followed-link spelling, budgets, callback behavior, and directory checks.
|
|
97
|
+
- Size small JavaScript SHA-256 scratch buffers to the file and byte budget, growing when size hints are stale while preserving complete reads, overflow detection, cancellation, and borrowed-descriptor ownership.
|
|
98
|
+
- Complete positive short reads from virtual files that report size zero instead of returning a truncated prefix. Byte-limit overflow detection and borrowed-descriptor cursor behavior remain intact.
|
|
99
|
+
- Confirm EOF when Linux `copy_file_range` reports zero after partial progress; resume guarded byte copying when readable data remains instead of publishing a truncated copy.
|
|
100
|
+
- Size Linux native byte-copy buffers from existing source-size hints, bounded between 4 KiB and 1 MiB, retaining read-to-EOF behavior, sparse output, and borrowed descriptors.
|
|
101
|
+
|
|
102
|
+
### Windows security and generated names
|
|
103
|
+
|
|
104
|
+
- Bind secure-file owner and DACL verification to the open descriptor and compare its native volume serial and file-index projection with Node's bigint receipt before reading.
|
|
105
|
+
- Retain parent and created-directory handles through private-directory creation, protected-DACL validation, complete identity association, and failure cleanup, preserving concurrent pathname replacements.
|
|
106
|
+
- Require differently cased Root prefixes to match the Root directory's exact identity, then continue under its trusted spelling. Exact-case paths retain their lexical fast path.
|
|
107
|
+
- Prevent filename truncation and temp-file sanitization from producing Windows reserved-device aliases. Invalid completed sibling callback components reject before hooks or producers run.
|
|
108
|
+
|
|
109
|
+
### Queues, locks, and cleanup
|
|
110
|
+
|
|
111
|
+
- Bind durable-queue migrations to their pinned processing generation, rejecting stale callbacks after acknowledgement, quarantine, or replacement. Release the verified read pin at Windows publication so migrations can replace their target.
|
|
112
|
+
- Resync resumed processing claims under the transfer lock so conditional migrations cannot bypass a failed publication sync on retry.
|
|
113
|
+
- Recheck current file type and modification time immediately before expiry pruning removes a file, preserving fresh replacements and timestamp refreshes without requiring read access.
|
|
114
|
+
- Stage guest cross-device symlink moves privately before atomic replacement, preserving the existing destination and source link when creation or publication fails.
|
|
115
|
+
- Avoid rescanning held locks during repeated lock-manager construction, and initialize legacy reference counts during acquisition and release so nested handles retain the outer lock.
|
|
116
|
+
|
|
117
|
+
### Validation
|
|
118
|
+
|
|
119
|
+
- Expand representative method-audit coverage to 572 workloads across JavaScript/native modes on Linux, macOS, and Windows, including 1,000-entry walks, larger TAR inventories, and small synchronous hashes, with explicit platform exclusions.
|
|
120
|
+
- Allow focused method audits with explicit native mode and balanced A-B-B-A or B-A-A-B measurement ordering.
|
|
121
|
+
- Extend walker coverage for lexical aliases and callback mutation. Keep cancellation fixture setup outside the operation deadline and drain fixture writes before cleanup on slow or failing filesystems.
|
|
122
|
+
- Give the Windows slow physical-package-copy sidecar proof separate setup, operation, child, and teardown budgets with bounded failure diagnostics, preserving ordinary helper deadlines.
|
|
123
|
+
|
|
5
124
|
## 0.11.0 - 2026-09-14
|
|
6
125
|
|
|
7
126
|
### Highlights
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ This is a **library-level guardrail**, not OS-level isolation. It does not repla
|
|
|
57
57
|
pnpm add @openclaw/fs-safe
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Node 22 or newer. Core root/path/json/temp helpers avoid framework dependencies. With all optional dependencies omitted, public subpaths remain safe to import and
|
|
60
|
+
Node 22 or newer. Core root/path/json/temp helpers avoid framework dependencies. With all optional dependencies omitted, public subpaths remain safe to import and fallback-capable operations work in `auto` or `off`. Native-only features, including no-clobber `Root.move()`, remain unavailable and fail with `helper-unavailable`. TAR/gzip fallback uses the bundled WASM build of the same Rust parser as native and works with optional dependencies omitted. ZIP fallback still needs optional `jszip`. See the [0.6 migration guide](docs/migrating-to-0.6.md).
|
|
61
61
|
|
|
62
62
|
Bun 1.4.2 is also supported with the [Bun runtime requirements](docs/install.md#bun-runtime), including the matching Rust addon on macOS and Linux. JIT-disabled Bun works too.
|
|
63
63
|
|
|
@@ -164,7 +164,7 @@ are written, support `maxBytes` and `signal`, and recheck mutation authority
|
|
|
164
164
|
before writes and publication. See [streamed creation](docs/writing.md#streamed-creation)
|
|
165
165
|
for producer ownership and cancellation semantics.
|
|
166
166
|
|
|
167
|
-
`write()` replaces file contents by default; pass `{ overwrite: false }` or use `create()` when an existing file should be an error. `move()` defaults to no clobber because it can otherwise delete an unrelated target while also consuming the source. Pass `{ overwrite: true }` when replacing the target is intended.
|
|
167
|
+
`write()` replaces file contents by default; pass `{ overwrite: false }` or use `create()` when an existing file should be an error. `move()` defaults to no clobber because it can otherwise delete an unrelated target while also consuming the source. No-clobber moves require the native helper so the collision decision and rename are one descriptor-relative operation; they fail with `helper-unavailable` rather than falling back to a replacing rename. Pass `{ overwrite: true }` when replacing the target is intended.
|
|
168
168
|
|
|
169
169
|
Mutating methods accept `assertBeforeMutation: () => void` for live lease or
|
|
170
170
|
cancellation checks immediately before filesystem dispatch. Root defaults and
|
|
@@ -233,7 +233,7 @@ const locked = await root("/srv/workspace", {
|
|
|
233
233
|
await locked.write(".env", "token"); // FsSafeError code "denied-path"
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
-
`stat()`, `exists()`, `list()`, and `entries()`
|
|
236
|
+
`stat()`, `exists()`, `list()`, and `entries()` check the exact selected objects while collecting their advisory results, but they cannot pin a later operation to the same filesystem object. Use `read()`, `open()`, `write()`, `create()`, `copyIn()`, `move()`, or `remove()` for operation-local identity checks, and inspect `containment` when the platform distinction matters.
|
|
237
237
|
|
|
238
238
|
## Subpaths
|
|
239
239
|
|
|
@@ -260,7 +260,7 @@ contract. Low-level helpers that OpenClaw needs to compose higher-level APIs are
|
|
|
260
260
|
| `@openclaw/fs-safe/walk` | budget-bounded directory walking with symlink policy, filters, and truncation accounting; not root-bounded |
|
|
261
261
|
| `@openclaw/fs-safe/copy` | directory copying with `clone: "auto"`, `"always"`, or `"never"`; native APFS, Btrfs, ReFS, XFS, and ZFS cloning, portable byte copying, and clone metadata; see [directory copying](docs/copy.md) |
|
|
262
262
|
| `@openclaw/fs-safe/archive` | policy-driven ZIP/TAR extraction, clamp/filter policy, metadata/path-depth limits, native gzip/zstd/bzip2, and bounded entry reads |
|
|
263
|
-
| `@openclaw/fs-safe/advanced` | lower-level composition helpers such as path scopes, root-file open, bounded descriptor reads, [borrowed-handle copying](docs/copy.md#borrowed-filehandle-transfers), [exact directory identity](docs/directory-identity.md), [case probing](docs/path-case.md), [in-place writes](docs/in-place-write.md), install
|
|
263
|
+
| `@openclaw/fs-safe/advanced` | lower-level composition helpers such as path scopes, root-file open, bounded descriptor reads, [borrowed-handle copying](docs/copy.md#borrowed-filehandle-transfers), [exact directory identity](docs/directory-identity.md), [case probing](docs/path-case.md), [suffix-alias probing](docs/path-suffix-aliases.md), [in-place writes](docs/in-place-write.md), [versioned install-ID encoding](docs/install-path.md#safepathsegmenthashedv2), filename sanitizing, temp-file targets, sibling-temp writes, local-root readers, regular-file helpers, `pathExists`, and `withTimeout`; less stable than focused public subpaths |
|
|
264
264
|
| `@openclaw/fs-safe/errors` | `FsSafeError`, closed codes/categories, causes, and operation-specific details receipts |
|
|
265
265
|
| `@openclaw/fs-safe/types` | shared types: `DirEntry`, `PathStat`, … |
|
|
266
266
|
| `@openclaw/fs-safe/test-hooks` | hooks the test suite uses to inject races; registration requires `NODE_ENV=test` or `VITEST=true` |
|
|
@@ -391,8 +391,8 @@ const store = files.json("settings.json", { lock: true });
|
|
|
391
391
|
await store.updateOr({ enabled: false }, (current) => ({ ...current, enabled: true }));
|
|
392
392
|
```
|
|
393
393
|
|
|
394
|
-
`jsonStore({ filePath })` is the
|
|
395
|
-
primitive.
|
|
394
|
+
`jsonStore({ filePath })` is the single-path convenience wrapper for the same
|
|
395
|
+
primitive and exposes its resolved absolute path.
|
|
396
396
|
|
|
397
397
|
Use `update()` when missing state is part of your model; use `updateOr()` for
|
|
398
398
|
the common merge-into-defaults case. Standalone helpers use options bags
|
|
@@ -429,7 +429,9 @@ await media.pruneExpired({ ttlMs: 10 * 60 * 1000, recursive: true });
|
|
|
429
429
|
The `store` subpath also includes durable JSON queue helpers for the common
|
|
430
430
|
"one JSON file per work item" pattern: atomic entry writes, pending-entry loads,
|
|
431
431
|
acknowledgement via `.delivered` markers, failed-entry moves, and stale temp
|
|
432
|
-
cleanup.
|
|
432
|
+
cleanup. On Windows, every independently supplied queue path rejects NTFS
|
|
433
|
+
alternate-stream and directory-index namespace spellings before reads, locks,
|
|
434
|
+
or mutations. Retry, dedupe, and transport semantics stay with the caller.
|
|
433
435
|
|
|
434
436
|
`tempWorkspace()` exposes `write()`, `writeText()`, `writeJson()`, `copyIn()`, and `read()` for
|
|
435
437
|
single-file scratch workflows without hand-rolled path joins, plus a `store: FileStore` view of
|
|
@@ -438,6 +440,9 @@ Compatible creation and cleanup remain available without native support.
|
|
|
438
440
|
Set `cleanupSafety: "require-bounded"` to require collision-safe quarantine and
|
|
439
441
|
descriptor-bounded recursive cleanup before creating a child. See the
|
|
440
442
|
[temp workspace contract](docs/temp.md).
|
|
443
|
+
On POSIX, bounded cleanup requires owner read and search in the final `dirMode`
|
|
444
|
+
(`0o500`); restrictive modes select compatible fallback or reject `require-bounded`
|
|
445
|
+
before child creation.
|
|
441
446
|
Linux bounded cleanup requires the exact `openat2`/`RESOLVE_NO_XDEV` capability
|
|
442
447
|
at runtime; compatible mode falls back when unavailable, while `require-bounded`
|
|
443
448
|
rejects before child creation.
|
|
@@ -460,7 +465,9 @@ Use `readSecureFile()` when the caller gives you an absolute credential path
|
|
|
460
465
|
instead of a root-relative workspace path. It opens the file first, validates the
|
|
461
466
|
same handle it will read from, checks trusted directories, owner, POSIX mode or
|
|
462
467
|
Windows ACLs, hardlink count, size, and optional timeout, then reads through the
|
|
463
|
-
pinned handle.
|
|
468
|
+
pinned handle. On Windows, both the bytes and the owner/DACL facts come from that
|
|
469
|
+
handle; secure reads require the matching current native package and do not fall
|
|
470
|
+
back to a pathname ACL command.
|
|
464
471
|
|
|
465
472
|
```ts
|
|
466
473
|
import { readSecureFile } from "@openclaw/fs-safe/secure-file";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"absolute-path.d.ts","sourceRoot":"","sources":["../src/absolute-path.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"absolute-path.d.ts","sourceRoot":"","sources":["../src/absolute-path.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAUhE,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAU5D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,GAAG;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAmL7D,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAchE;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGnF;AA4BD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,6BAA6B,CAAC,CA+ExC;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBzF;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,oBAAoB,CAAC,CAmB/B;AAED,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,4BAA4B,CAAC,CA2BvC"}
|
package/dist/absolute-path.js
CHANGED
|
@@ -6,6 +6,7 @@ import { FsSafeError } from "./errors.js";
|
|
|
6
6
|
import { pathExists } from "./fs.js";
|
|
7
7
|
import { realpathSync } from "./realpath.js";
|
|
8
8
|
import { resolveRootPath } from "./root-path.js";
|
|
9
|
+
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
9
10
|
function resolveSymlinkPolicy(policy) {
|
|
10
11
|
if (policy === undefined)
|
|
11
12
|
return "reject";
|
|
@@ -68,7 +69,7 @@ async function directoryGuardFailure(err, dir, scopeLabel) {
|
|
|
68
69
|
return { ok: false, code: err.code, error: err };
|
|
69
70
|
}
|
|
70
71
|
try {
|
|
71
|
-
const stat = fsSync.lstatSync(dir);
|
|
72
|
+
const stat = fsSync.lstatSync(pathForWindowsFilesystem(dir));
|
|
72
73
|
const failure = classifyExistingDirectorySegment(stat, scopeLabel);
|
|
73
74
|
if (failure) {
|
|
74
75
|
return failure;
|
|
@@ -88,7 +89,7 @@ async function resolveTrustedDirectoryPrefix(targetPath, scopeLabel) {
|
|
|
88
89
|
let current = root;
|
|
89
90
|
let currentStat;
|
|
90
91
|
try {
|
|
91
|
-
currentStat = fsSync.lstatSync(current);
|
|
92
|
+
currentStat = fsSync.lstatSync(pathForWindowsFilesystem(current));
|
|
92
93
|
}
|
|
93
94
|
catch (err) {
|
|
94
95
|
const failure = classifyDirectoryLookupError(err, scopeLabel);
|
|
@@ -112,7 +113,7 @@ async function resolveTrustedDirectoryPrefix(targetPath, scopeLabel) {
|
|
|
112
113
|
}
|
|
113
114
|
const next = path.join(current, segment);
|
|
114
115
|
try {
|
|
115
|
-
const nextStat = fsSync.lstatSync(next);
|
|
116
|
+
const nextStat = fsSync.lstatSync(pathForWindowsFilesystem(next));
|
|
116
117
|
const segmentFailure = classifyExistingDirectorySegment(nextStat, scopeLabel);
|
|
117
118
|
if (segmentFailure) {
|
|
118
119
|
return segmentFailure;
|
|
@@ -145,19 +146,28 @@ export function assertAbsolutePathInput(filePath) {
|
|
|
145
146
|
if (filePath.includes("\0")) {
|
|
146
147
|
throw new FsSafeError("invalid-path", "path must not contain NUL bytes");
|
|
147
148
|
}
|
|
149
|
+
assertNoWindowsPathAlias(filePath);
|
|
148
150
|
if (!path.isAbsolute(filePath)) {
|
|
149
151
|
throw new FsSafeError("invalid-path", "path must be absolute");
|
|
150
152
|
}
|
|
151
|
-
|
|
153
|
+
const normalized = path.normalize(filePath);
|
|
154
|
+
assertNoWindowsPathAlias(normalized);
|
|
155
|
+
return normalized;
|
|
152
156
|
}
|
|
153
157
|
export async function findExistingAncestor(filePath) {
|
|
158
|
+
assertNoWindowsPathAlias(filePath);
|
|
154
159
|
return (await findExistingAncestorWithStat(filePath))?.path ?? null;
|
|
155
160
|
}
|
|
156
161
|
async function findExistingAncestorWithStat(filePath) {
|
|
157
|
-
|
|
162
|
+
assertNoWindowsPathAlias(filePath);
|
|
163
|
+
let current = resolvePathPreservingWindowsRoot(filePath);
|
|
164
|
+
assertNoWindowsPathAlias(current);
|
|
158
165
|
while (true) {
|
|
159
166
|
try {
|
|
160
|
-
return {
|
|
167
|
+
return {
|
|
168
|
+
path: current,
|
|
169
|
+
stat: fsSync.lstatSync(pathForWindowsFilesystem(current)),
|
|
170
|
+
};
|
|
161
171
|
}
|
|
162
172
|
catch (err) {
|
|
163
173
|
if (err.code !== "ENOENT") {
|
|
@@ -246,26 +256,37 @@ export async function ensureAbsoluteDirectory(dirPath, options = {}) {
|
|
|
246
256
|
return { ok: true, path: targetPath };
|
|
247
257
|
}
|
|
248
258
|
export async function canonicalPathFromExistingAncestor(filePath) {
|
|
259
|
+
assertNoWindowsPathAlias(filePath);
|
|
249
260
|
const ancestor = await findExistingAncestor(filePath);
|
|
250
261
|
if (!ancestor) {
|
|
251
|
-
|
|
262
|
+
const resolved = resolvePathPreservingWindowsRoot(filePath);
|
|
263
|
+
assertNoWindowsPathAlias(resolved);
|
|
264
|
+
return resolved;
|
|
252
265
|
}
|
|
253
266
|
let canonicalAncestor = ancestor;
|
|
267
|
+
let resolvedAncestor;
|
|
254
268
|
try {
|
|
255
|
-
|
|
269
|
+
resolvedAncestor = realpathSync.native(pathForWindowsFilesystem(ancestor));
|
|
256
270
|
}
|
|
257
271
|
catch {
|
|
258
272
|
// Keep lexical path when the existing ancestor cannot be canonicalized.
|
|
259
273
|
}
|
|
274
|
+
if (resolvedAncestor !== undefined) {
|
|
275
|
+
assertNoWindowsPathAlias(resolvedAncestor);
|
|
276
|
+
canonicalAncestor = resolvedAncestor;
|
|
277
|
+
}
|
|
260
278
|
const relative = path.relative(ancestor, filePath);
|
|
261
|
-
|
|
279
|
+
const canonicalPath = relative ? path.join(canonicalAncestor, relative) : canonicalAncestor;
|
|
280
|
+
assertNoWindowsPathAlias(canonicalPath);
|
|
281
|
+
return canonicalPath;
|
|
262
282
|
}
|
|
263
283
|
export async function resolveAbsolutePathForRead(filePath, options = {}) {
|
|
264
284
|
const symlinks = resolveSymlinkPolicy(options.symlinks);
|
|
265
285
|
const normalized = assertAbsolutePathInput(filePath);
|
|
266
286
|
let canonicalPath;
|
|
267
287
|
try {
|
|
268
|
-
canonicalPath = realpathSync.native(normalized);
|
|
288
|
+
canonicalPath = realpathSync.native(pathForWindowsFilesystem(normalized));
|
|
289
|
+
assertNoWindowsPathAlias(canonicalPath);
|
|
269
290
|
}
|
|
270
291
|
catch (err) {
|
|
271
292
|
if (err.code === "ENOENT") {
|
|
@@ -282,7 +303,7 @@ export async function resolveAbsolutePathForWrite(filePath, options = {}) {
|
|
|
282
303
|
const symlinks = resolveSymlinkPolicy(options.symlinks);
|
|
283
304
|
const normalized = assertAbsolutePathInput(filePath);
|
|
284
305
|
const parentDir = path.dirname(normalized);
|
|
285
|
-
const parentExists = await pathExists(parentDir);
|
|
306
|
+
const parentExists = await pathExists(pathForWindowsFilesystem(parentDir));
|
|
286
307
|
if (symlinks === "reject") {
|
|
287
308
|
const filesystemRoot = path.parse(normalized).root;
|
|
288
309
|
await resolveRootPath({
|
package/dist/advanced.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { createAsyncLock } from "./async-lock.js";
|
|
|
2
2
|
export { copyFileHandle, type CopyFileHandleOptions } from "./file-handle-transfer.js";
|
|
3
3
|
export { overwriteFileHandle, type OverwriteFileHandleOptions } from "./overwrite-file-handle.js";
|
|
4
4
|
export { probePathCaseInsensitiveSync, type ProbePathCaseOptions } from "./path-case.js";
|
|
5
|
+
export { resolvePathPrefixSync, type ResolvedPathPrefix } from "./path-prefix.js";
|
|
6
|
+
export { probePathSuffixAliasesSync, type ProbePathSuffixAliasesOptions } from "./path-suffix-aliases.js";
|
|
5
7
|
export { readDirectoryIdentity, assertDirectoryIdentitySync, type DirectoryIdentity, } from "./directory-guard.js";
|
|
6
8
|
export { stageFileInDirectory, type StagedFile, type StagedFileReceipt, type PublishedFileReceipt, type StagedFilePublication, type StagedFileCleanupReceipt, type StagedFileFailureDetails, } from "./native-staged-file.js";
|
|
7
9
|
export { readFileDescriptorBounded, readFileDescriptorBoundedSync, readFileHandleBounded, } from "./bounded-read.js";
|
|
@@ -19,7 +21,7 @@ export { assertNoHardlinkedFinalPath, assertNoPathAliasEscape, PATH_ALIAS_POLICI
|
|
|
19
21
|
export { openRootFile, openRootFileSync, canUseRootFileOpen, matchRootFileOpenFailure, type OpenRootFileParams, type OpenRootFileSyncParams, type RootFileOpenFailure, type RootFileOpenFailureReason, type RootFileOpenResult, } from "./root-file.js";
|
|
20
22
|
export { ROOT_PATH_ALIAS_POLICIES, resolvePathViaExistingAncestorSync, resolveRootPath, resolveRootPathSync, type ResolvedRootPath, type RootPathAliasPolicy, } from "./root-path.js";
|
|
21
23
|
export { ensureDirectoryWithinRoot, pathScope, resolveExistingPathsWithinRoot, resolvePathWithinRoot, resolvePathsWithinRoot, resolveStrictExistingPathsWithinRoot, resolveWritablePathWithinRoot, type PathScope, type PathScopeOptions, type PathScopeResolveOptions, } from "./root-paths.js";
|
|
22
|
-
export { safeDirName, safePathSegmentHashed, resolveSafeInstallDir, assertCanonicalPathWithinBase, } from "./install-path.js";
|
|
24
|
+
export { safeDirName, safePathSegmentHashed, safePathSegmentHashedV2, resolveSafeInstallDir, assertCanonicalPathWithinBase, } from "./install-path.js";
|
|
23
25
|
export { assertNoSymlinkParents, assertNoSymlinkParentsSync, type AssertNoSymlinkParentsOptions, } from "./symlink-parents.js";
|
|
24
26
|
export { movePathToTrash, type MovePathToTrashOptions } from "./trash.js";
|
|
25
27
|
export { withTimeout } from "./timing.js";
|
package/dist/advanced.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"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"}
|
package/dist/advanced.js
CHANGED
|
@@ -5,6 +5,8 @@ export { createAsyncLock } from "./async-lock.js";
|
|
|
5
5
|
export { copyFileHandle } from "./file-handle-transfer.js";
|
|
6
6
|
export { overwriteFileHandle } from "./overwrite-file-handle.js";
|
|
7
7
|
export { probePathCaseInsensitiveSync } from "./path-case.js";
|
|
8
|
+
export { resolvePathPrefixSync } from "./path-prefix.js";
|
|
9
|
+
export { probePathSuffixAliasesSync } from "./path-suffix-aliases.js";
|
|
8
10
|
export { readDirectoryIdentity, assertDirectoryIdentitySync, } from "./directory-guard.js";
|
|
9
11
|
export { stageFileInDirectory, } from "./native-staged-file.js";
|
|
10
12
|
export { readFileDescriptorBounded, readFileDescriptorBoundedSync, readFileHandleBounded, } from "./bounded-read.js";
|
|
@@ -22,7 +24,7 @@ export { assertNoHardlinkedFinalPath, assertNoPathAliasEscape, PATH_ALIAS_POLICI
|
|
|
22
24
|
export { openRootFile, openRootFileSync, canUseRootFileOpen, matchRootFileOpenFailure, } from "./root-file.js";
|
|
23
25
|
export { ROOT_PATH_ALIAS_POLICIES, resolvePathViaExistingAncestorSync, resolveRootPath, resolveRootPathSync, } from "./root-path.js";
|
|
24
26
|
export { ensureDirectoryWithinRoot, pathScope, resolveExistingPathsWithinRoot, resolvePathWithinRoot, resolvePathsWithinRoot, resolveStrictExistingPathsWithinRoot, resolveWritablePathWithinRoot, } from "./root-paths.js";
|
|
25
|
-
export { safeDirName, safePathSegmentHashed, resolveSafeInstallDir, assertCanonicalPathWithinBase, } from "./install-path.js";
|
|
27
|
+
export { safeDirName, safePathSegmentHashed, safePathSegmentHashedV2, resolveSafeInstallDir, assertCanonicalPathWithinBase, } from "./install-path.js";
|
|
26
28
|
export { assertNoSymlinkParents, assertNoSymlinkParentsSync, } from "./symlink-parents.js";
|
|
27
29
|
export { movePathToTrash } from "./trash.js";
|
|
28
30
|
export { withTimeout } from "./timing.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-entry.d.ts","sourceRoot":"","sources":["../src/archive-entry.ts"],"names":[],"mappings":"AAOA,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC,IAAI,
|
|
1
|
+
{"version":3,"file":"archive-entry.d.ts","sourceRoot":"","sources":["../src/archive-entry.ts"],"names":[],"mappings":"AAOA,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC,IAAI,CAgEN;AAGD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc1F;AAED,wBAAgB,8BAA8B,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAgBlG;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAWT"}
|
package/dist/archive-entry.js
CHANGED
|
@@ -21,12 +21,13 @@ export function validateArchiveEntryPath(entryPath, params) {
|
|
|
21
21
|
throw new ArchiveSecurityError("entry-path", `archive entry contains a NUL byte: ${formatErrorDetail(entryPath)}`);
|
|
22
22
|
}
|
|
23
23
|
const slashNormalized = normalizeArchiveEntryPath(entryPath);
|
|
24
|
+
const rawSegments = slashNormalized.split("/");
|
|
24
25
|
if (process.platform === "win32" &&
|
|
25
|
-
|
|
26
|
+
rawSegments.some((segment) => segment.includes(":"))) {
|
|
26
27
|
throw new ArchiveSecurityError("entry-path", `archive entry uses a Windows alternate data stream path: ${formatErrorDetail(entryPath)}`);
|
|
27
28
|
}
|
|
28
29
|
if (process.platform === "win32" &&
|
|
29
|
-
|
|
30
|
+
rawSegments.some((segment) => isWindowsReservedDeviceName(segment))) {
|
|
30
31
|
throw new ArchiveSecurityError("entry-path", `archive entry uses a reserved device path: ${formatErrorDetail(entryPath)}`);
|
|
31
32
|
}
|
|
32
33
|
const normalized = path.posix.normalize(slashNormalized);
|
|
@@ -40,7 +41,7 @@ export function validateArchiveEntryPath(entryPath, params) {
|
|
|
40
41
|
if (path.posix.isAbsolute(normalized) || normalized.startsWith("//")) {
|
|
41
42
|
throw new ArchiveSecurityError("entry-path", `archive entry is absolute: ${formatErrorDetail(entryPath)}`);
|
|
42
43
|
}
|
|
43
|
-
if (
|
|
44
|
+
if (rawSegments.includes("..")) {
|
|
44
45
|
throw new ArchiveSecurityError("entry-path", `archive entry contains a parent segment: ${formatErrorDetail(entryPath)}`);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-gzip-tail.d.ts","sourceRoot":"","sources":["../src/archive-gzip-tail.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-gzip-tail.d.ts","sourceRoot":"","sources":["../src/archive-gzip-tail.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAaxC,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAEvD;AAQD,gFAAgF;AAChF,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CASjH;AAED;8EAC8E;AAC9E,qBAAa,SAAU,SAAQ,QAAQ;IAIzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,WAAW,CAAqB;IAExC,YAA6B,OAAO,EAAE,MAAM,EAE3C;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAcxG;IAEQ,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAE9D;IAEQ,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAGpF;CACF;AAED;6EAC6E;AAC7E,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBvH"}
|
|
@@ -2,6 +2,14 @@ import fsSync from "node:fs";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import { Writable } from "node:stream";
|
|
4
4
|
import { ArchiveFormatError } from "./archive-errors.js";
|
|
5
|
+
const PADDING_WINDOW_BYTES = 65536;
|
|
6
|
+
let zeroPadding;
|
|
7
|
+
function assertZeroPadding(bytes) {
|
|
8
|
+
zeroPadding ??= Buffer.alloc(PADDING_WINDOW_BYTES);
|
|
9
|
+
if (!bytes.equals(zeroPadding.subarray(0, bytes.length))) {
|
|
10
|
+
throw new ArchiveFormatError("nonzero gzip container padding");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
5
13
|
export function isGzipBuffer(input) {
|
|
6
14
|
return input[0] === 31 && input[1] === 139;
|
|
7
15
|
}
|
|
@@ -14,12 +22,10 @@ function assertConsumedBoundary(consumed, size) {
|
|
|
14
22
|
export async function validateGzipBufferTail(input, consumed, signal) {
|
|
15
23
|
signal?.throwIfAborted();
|
|
16
24
|
assertConsumedBoundary(consumed, input.length);
|
|
17
|
-
for (let position = consumed; position < input.length; position +=
|
|
25
|
+
for (let position = consumed; position < input.length; position += PADDING_WINDOW_BYTES) {
|
|
18
26
|
signal?.throwIfAborted();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
if (position + 65536 < input.length)
|
|
27
|
+
assertZeroPadding(input.subarray(position, position + PADDING_WINDOW_BYTES));
|
|
28
|
+
if (position + PADDING_WINDOW_BYTES < input.length)
|
|
23
29
|
await new Promise(resolve => setImmediate(resolve));
|
|
24
30
|
}
|
|
25
31
|
signal?.throwIfAborted();
|
|
@@ -75,7 +81,7 @@ export async function validateGzipContainerTail(filePath, consumed, signal) {
|
|
|
75
81
|
assertConsumedBoundary(consumed, size);
|
|
76
82
|
if (consumed === size)
|
|
77
83
|
return;
|
|
78
|
-
const buffer = Buffer.allocUnsafe(
|
|
84
|
+
const buffer = Buffer.allocUnsafe(PADDING_WINDOW_BYTES);
|
|
79
85
|
let position = consumed;
|
|
80
86
|
while (position < size) {
|
|
81
87
|
signal?.throwIfAborted();
|
|
@@ -83,9 +89,7 @@ export async function validateGzipContainerTail(filePath, consumed, signal) {
|
|
|
83
89
|
signal?.throwIfAborted();
|
|
84
90
|
if (bytesRead === 0)
|
|
85
91
|
throw new ArchiveFormatError("truncated gzip container padding");
|
|
86
|
-
|
|
87
|
-
throw new ArchiveFormatError("nonzero gzip container padding");
|
|
88
|
-
}
|
|
92
|
+
assertZeroPadding(buffer.subarray(0, bytesRead));
|
|
89
93
|
position += bytesRead;
|
|
90
94
|
}
|
|
91
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-input.d.ts","sourceRoot":"","sources":["../src/archive-input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-input.d.ts","sourceRoot":"","sources":["../src/archive-input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;AAO7B,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAM/E,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,kBAAkB,CAAC;CAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CAchB;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,4BAA4B,CAAC;IACrC,QAAQ,EAAE,kBAAkB,CAAC;CAC9B,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwE7B"}
|
package/dist/archive-input.js
CHANGED
|
@@ -6,6 +6,7 @@ import { FsSafeError } from "./errors.js";
|
|
|
6
6
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
7
7
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
8
8
|
import { tempFile } from "./temp-target.js";
|
|
9
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
9
10
|
async function closeFileHandle(handle) {
|
|
10
11
|
if (handle)
|
|
11
12
|
await handle.close().catch(() => undefined);
|
|
@@ -23,11 +24,14 @@ export async function writeFileHandleFully(params) {
|
|
|
23
24
|
}
|
|
24
25
|
export async function stageArchiveFileForExtraction(params) {
|
|
25
26
|
params.deadline.check();
|
|
26
|
-
const
|
|
27
|
+
const archivePath = params.archivePath;
|
|
28
|
+
assertNoWindowsPathAlias(archivePath);
|
|
29
|
+
const sourcePath = path.resolve(archivePath);
|
|
30
|
+
assertNoWindowsPathAlias(sourcePath);
|
|
27
31
|
const initialStat = await inspectFileIdentity(async () => {
|
|
28
32
|
const stat = fsSync.lstatSync(sourcePath, { bigint: true });
|
|
29
33
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
30
|
-
throw new Error(`archive is not a regular file: ${
|
|
34
|
+
throw new Error(`archive is not a regular file: ${archivePath}`);
|
|
31
35
|
}
|
|
32
36
|
return stat;
|
|
33
37
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-merge.d.ts","sourceRoot":"","sources":["../src/archive-merge.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAClG,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-merge.d.ts","sourceRoot":"","sources":["../src/archive-merge.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAClG,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAc9B,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACjG,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,CAAC;AACF,KAAK,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG;IACtE,gBAAgB,EAAE,qBAAqB,CAAC;CACzC,CAAC;AAEF,wBAAsB,kCAAkC,CACtD,MAAM,EAAE,kBAAkB,GAAG;IAAE,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9F,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAS1F"}
|
package/dist/archive-merge.js
CHANGED
|
@@ -14,12 +14,25 @@ import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
|
14
14
|
import { onCopyPublication } from "./copy-publication.js";
|
|
15
15
|
import { syncFileBestEffortSync } from "./file-sync.js";
|
|
16
16
|
import { finalizeArchivePublication } from "./archive-durability.js";
|
|
17
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
17
18
|
export async function mergePlannedArchiveIntoDestination(params) {
|
|
19
|
+
assertMergePathInputs(params);
|
|
18
20
|
await mergeTree(params, params.entries, params.durable === true);
|
|
19
21
|
}
|
|
20
22
|
export async function mergeExtractedTreeIntoDestination(params) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
+
const sourceDir = params.sourceDir;
|
|
24
|
+
const destinationDir = params.destinationDir;
|
|
25
|
+
const destinationRealDir = params.destinationRealDir;
|
|
26
|
+
assertNoWindowsPathAlias(sourceDir);
|
|
27
|
+
assertNoWindowsPathAlias(destinationDir);
|
|
28
|
+
assertNoWindowsPathAlias(destinationRealDir);
|
|
29
|
+
const destinationGuard = await createDirectoryIdentityGuard(destinationRealDir);
|
|
30
|
+
await mergeTree({ sourceDir, deadline: params.deadline, destinationGuard });
|
|
31
|
+
}
|
|
32
|
+
function assertMergePathInputs(params) {
|
|
33
|
+
assertNoWindowsPathAlias(params.sourceDir);
|
|
34
|
+
assertNoWindowsPathAlias(params.destinationGuard.dir);
|
|
35
|
+
assertNoWindowsPathAlias(params.destinationGuard.realPath);
|
|
23
36
|
}
|
|
24
37
|
async function mergeTree(params, publication, durable = true) {
|
|
25
38
|
const publishedFiles = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-native.d.ts","sourceRoot":"","sources":["../src/archive-native.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"archive-native.d.ts","sourceRoot":"","sources":["../src/archive-native.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAOlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAYnE;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACrC,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;CAClD,GAAG,OAAO,CAAC,IAAI,CAAC,CAkFhB"}
|