@openclaw/fs-safe 0.12.0 → 0.13.1
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 +73 -0
- package/README.md +12 -7
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +32 -11
- package/dist/advanced.d.ts +2 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +2 -0
- 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 +18 -13
- 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 +35 -14
- 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/copy-file-input.d.ts +1 -1
- package/dist/copy-file-input.d.ts.map +1 -1
- package/dist/copy-file-input.js +2 -0
- 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/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 +3 -0
- 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-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.map +1 -1
- package/dist/filename.js +65 -20
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +3 -2
- package/dist/guarded-mkdir.d.ts +8 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +176 -27
- package/dist/guest.d.ts.map +1 -1
- package/dist/guest.js +4 -1
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +73 -10
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +54 -19
- package/dist/json-durable-queue-ownership.d.ts +6 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +89 -42
- 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.map +1 -1
- package/dist/json-durable-queue-read.js +2 -0
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +50 -58
- 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 +17 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-binding.js +8 -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-operations.d.ts.map +1 -1
- package/dist/native-operations.js +6 -4
- package/dist/native-parent-admission.d.ts +32 -0
- package/dist/native-parent-admission.d.ts.map +1 -0
- package/dist/native-parent-admission.js +127 -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 +8 -9
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +314 -42
- package/dist/native-staged-file.d.ts +4 -4
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +20 -10
- package/dist/native.d.ts +1 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +7 -2
- 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.map +1 -1
- package/dist/path-scope-lexical.js +18 -8
- 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 +16 -5
- 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 +151 -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 +38 -32
- 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 +4 -0
- package/dist/root-boundary.d.ts.map +1 -1
- package/dist/root-boundary.js +6 -1
- package/dist/root-context.d.ts +12 -1
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +57 -11
- 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 +180 -39
- 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 +289 -317
- 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-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.map +1 -1
- package/dist/root-path-existing.js +53 -20
- 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 +11 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +215 -53
- 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 -25
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +77 -154
- 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-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-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 +6 -4
- 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.d.ts.map +1 -1
- package/dist/secure-file.js +92 -7
- 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 -2
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +179 -55
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +27 -13
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +44 -14
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +4 -2
- 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 +130 -3
- 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 +9 -6
- 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 +25 -0
- package/docs/archive.md +23 -2
- package/docs/atomic.md +27 -3
- package/docs/copy.md +3 -0
- package/docs/durability.md +14 -0
- package/docs/errors.md +14 -6
- package/docs/file-store.md +24 -3
- package/docs/filename.md +14 -7
- package/docs/install-path.md +2 -2
- package/docs/install.md +8 -7
- 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 +41 -11
- package/docs/native.md +32 -8
- 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 +11 -0
- package/docs/private-file-store.md +14 -0
- package/docs/public-api.md +9 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +8 -1
- package/docs/root.md +18 -3
- package/docs/secret-file.md +3 -0
- package/docs/secure-file.md +6 -2
- package/docs/security-model.md +75 -8
- package/docs/sidecar-lock.md +28 -1
- package/docs/store.md +5 -1
- package/docs/temp.md +260 -36
- 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,79 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.13.1 - 2026-09-16
|
|
6
|
+
|
|
7
|
+
### Highlights
|
|
8
|
+
|
|
9
|
+
- **Correct native cleanup in Node workers:** guarded writes, moves, and staged-file cleanup close native-created descriptors through their originating binding, eliminating unmanaged-descriptor warnings while preserving Node's own descriptor tracking. ([#438](https://github.com/openclaw/fs-safe/pull/438))
|
|
10
|
+
|
|
11
|
+
### Fixes and compatibility
|
|
12
|
+
|
|
13
|
+
- Retain the correct closer through errors and native-mode changes, including staged files whose parent directory was opened by Node. Borrowed handles keep their caller-owned lifetime.
|
|
14
|
+
- On Windows, close exported descriptors through the same host runtime's libuv table that opened them. Descriptor-producing operations require complete native close support before allocation; stale helpers are treated as unavailable under the existing native-mode policy.
|
|
15
|
+
- Add real Worker coverage on Node 22/24 and verify that Windows native close releases both file and directory descriptors. Forced `Worker.terminate()` reclamation remains a [documented limitation](https://github.com/openclaw/fs-safe/blob/v0.13.1/docs/native-helper.md); callers should close retained resources and finish in-flight work before termination.
|
|
16
|
+
|
|
17
|
+
## 0.13.0 - 2026-09-16
|
|
18
|
+
|
|
19
|
+
### Highlights
|
|
20
|
+
|
|
21
|
+
- **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.
|
|
22
|
+
- **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.
|
|
23
|
+
- **Safer Windows paths and writes:** reject NTFS stream and directory-index aliases before filesystem access, and retain destination handles through compatibility writes and verification.
|
|
24
|
+
- **New tools for prospective paths:** add `resolvePathPrefixSync()` and `probePathSuffixAliasesSync()` for existing-prefix traversal and bounded observations of missing suffix aliases.
|
|
25
|
+
- **Opt-in bounded temporary-file cleanup:** add retained-directory cleanup ownership to `tempFile()` and strengthen temporary-workspace admission and permission repair.
|
|
26
|
+
|
|
27
|
+
### Compatibility and upgrade notes
|
|
28
|
+
|
|
29
|
+
- **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.
|
|
30
|
+
- 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.
|
|
31
|
+
- 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.
|
|
32
|
+
- `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).
|
|
33
|
+
- 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.
|
|
34
|
+
|
|
35
|
+
### Root boundaries and mutation performance
|
|
36
|
+
|
|
37
|
+
- 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.
|
|
38
|
+
- 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.
|
|
39
|
+
- 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.
|
|
40
|
+
- 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.
|
|
41
|
+
- Reuse an already admitted parent guard for private producer Roots while preserving isolated handoff, including legacy Windows delete-pending and read-only restoration behavior.
|
|
42
|
+
- 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.
|
|
43
|
+
|
|
44
|
+
### Credentials, permissions, and temporary files
|
|
45
|
+
|
|
46
|
+
- 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.
|
|
47
|
+
- 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.
|
|
48
|
+
- 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.
|
|
49
|
+
- 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.
|
|
50
|
+
- Retain Windows destination descriptors through buffered compatibility-write locking, content verification, mode changes, sync, and final identity checks.
|
|
51
|
+
- 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.
|
|
52
|
+
|
|
53
|
+
### Archive safety and performance
|
|
54
|
+
|
|
55
|
+
- Bind physical ZIP entry names and file/directory kinds to decoder metadata before callbacks or member selection, rejecting ambiguous interpretations consistently.
|
|
56
|
+
- Stop zstd and bzip2 compressed-input refills after cancellation while preserving decoded-output limits and complete archive validation.
|
|
57
|
+
- 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.
|
|
58
|
+
- 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.
|
|
59
|
+
|
|
60
|
+
### Path helpers and reliability
|
|
61
|
+
|
|
62
|
+
- 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.
|
|
63
|
+
- 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.
|
|
64
|
+
- 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.
|
|
65
|
+
- 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.
|
|
66
|
+
- Report actionable guarded-write errno diagnostics while preserving error codes, categories, original causes, and already-classified failures.
|
|
67
|
+
- Tolerate concurrent guest parent-directory creation only after descriptor-relative no-follow admission; file and symlink competitors remain rejected. Thanks @vincentkoc.
|
|
68
|
+
- Show only the moon icon in the docs light-theme picker and only the sun icon in dark mode.
|
|
69
|
+
|
|
70
|
+
### Validation and tooling
|
|
71
|
+
|
|
72
|
+
- 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.
|
|
73
|
+
- 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.
|
|
74
|
+
- 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.
|
|
75
|
+
- 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.
|
|
76
|
+
- Update development tooling to `@napi-rs/cli` 3.9.1, fast-check 4.10.0, and Vite 8.3.0 with verified registry integrity.
|
|
77
|
+
|
|
5
78
|
## 0.12.0 - 2026-09-15
|
|
6
79
|
|
|
7
80
|
### 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), [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 |
|
|
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.
|
|
@@ -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";
|
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";
|
|
@@ -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"}
|
package/dist/archive-native.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { classifyArchiveParserError } from "./archive-parser-errors.js";
|
|
2
2
|
import { constants as fsConstants } from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
|
-
import { ArchiveFormatError,
|
|
5
|
-
import {
|
|
4
|
+
import { ArchiveFormatError, isArchiveFormatErrorMessage, } from "./archive-errors.js";
|
|
5
|
+
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
6
6
|
import { createArchiveEntryPlanner } from "./archive-plan.js";
|
|
7
7
|
import { stageArchiveFileForExtraction } from "./archive-input.js";
|
|
8
8
|
import { assertArchiveEntryCountWithinLimit, } from "./archive-limits.js";
|
|
9
9
|
import { prepareArchiveDestinationGuard, withStagedArchiveDestination, } from "./archive-staging.js";
|
|
10
10
|
import { mergePlannedArchiveIntoDestination } from "./archive-merge.js";
|
|
11
11
|
import { admitZipFile } from "./archive-zip-admission.js";
|
|
12
|
+
import { validateNativeZipManifest } from "./archive-zip-manifest.js";
|
|
12
13
|
export function throwMappedNativeArchiveError(error) {
|
|
13
14
|
if (error instanceof Error) {
|
|
14
15
|
const mapped = classifyArchiveParserError(error.message, { cause: error });
|
|
@@ -32,9 +33,9 @@ export async function extractNativeArchive(params) {
|
|
|
32
33
|
});
|
|
33
34
|
try {
|
|
34
35
|
const zipEntries = [];
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
if (params.kind === "zip") {
|
|
37
|
+
await admitZipFile(stagedArchive.path, limits, params.deadline, (entry) => { zipEntries.push(entry); });
|
|
38
|
+
}
|
|
38
39
|
const destinationGuard = await prepareArchiveDestinationGuard(params.destDir);
|
|
39
40
|
const destinationRealDir = destinationGuard.realPath;
|
|
40
41
|
await withStagedArchiveDestination({
|
|
@@ -47,21 +48,8 @@ export async function extractNativeArchive(params) {
|
|
|
47
48
|
.catch(throwMappedNativeArchiveError);
|
|
48
49
|
params.deadline.check();
|
|
49
50
|
assertArchiveEntryCountWithinLimit(manifest.length, limits);
|
|
50
|
-
if (physicalCount !== undefined && manifest.length !== physicalCount) {
|
|
51
|
-
throw new ArchiveSecurityError("entry-path", "zip decoder collapsed entry names");
|
|
52
|
-
}
|
|
53
51
|
if (params.kind === "zip") {
|
|
54
|
-
|
|
55
|
-
const physical = zipEntries[ordinal];
|
|
56
|
-
// Native ZIP indices are physical central-directory ordinals. Legacy
|
|
57
|
-
// filename decoding remains native-selected; compare names when known.
|
|
58
|
-
if (!physical || physical.index !== ordinal || entry.index !== ordinal ||
|
|
59
|
-
entry.size !== physical.size ||
|
|
60
|
-
(physical.path !== undefined && stripArchivePath(entry.path, 0) !== stripArchivePath(physical.path, 0)) ||
|
|
61
|
-
(physical.creatorSystem === 3 && entry.mode !== physical.externalAttributes >>> 16)) {
|
|
62
|
-
throw new ArchiveFormatError("ZIP decoder disagrees with admitted directory metadata");
|
|
63
|
-
}
|
|
64
|
-
}
|
|
52
|
+
validateNativeZipManifest(manifest, zipEntries);
|
|
65
53
|
}
|
|
66
54
|
// Recheck the native manifest at the shared policy boundary before
|
|
67
55
|
// any caller callback observes an entry.
|
package/dist/archive-plan.js
CHANGED
|
@@ -17,7 +17,7 @@ export function createArchiveEntryPlanner(params, archiveKind) {
|
|
|
17
17
|
const canonicalPath = stripArchivePath(entry.path, 0);
|
|
18
18
|
if (!canonicalPath)
|
|
19
19
|
return null;
|
|
20
|
-
const relPath = stripArchivePath(canonicalPath, strip);
|
|
20
|
+
const relPath = strip === 0 ? canonicalPath : stripArchivePath(canonicalPath, strip);
|
|
21
21
|
if (!relPath)
|
|
22
22
|
return null;
|
|
23
23
|
validateArchiveEntryPath(relPath, { escapeLabel: params.escapeLabel });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-read.d.ts","sourceRoot":"","sources":["../src/archive-read.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAiD,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-read.d.ts","sourceRoot":"","sources":["../src/archive-read.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAiD,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA0OpG,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAAE,GAChD,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
|
package/dist/archive-read.js
CHANGED
|
@@ -19,7 +19,9 @@ import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
|
19
19
|
import { realpathSync } from "./realpath.js";
|
|
20
20
|
import { getNativeBinding } from "./native.js";
|
|
21
21
|
import { admitZipBuffer } from "./archive-zip-admission.js";
|
|
22
|
+
import { validateNativeZipManifest } from "./archive-zip-manifest.js";
|
|
22
23
|
import { resolveExtractLimits, resolveTarMeterLimits } from "./archive-limits.js";
|
|
24
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
23
25
|
const ZIP_UNIX_FILE_TYPE_MASK = 0o170000;
|
|
24
26
|
const ZIP_UNIX_SYMLINK_TYPE = 0o120000;
|
|
25
27
|
function canonicalEntryPath(entryPath) {
|
|
@@ -50,7 +52,9 @@ async function readAdmittedTarPayload(stream, size) {
|
|
|
50
52
|
return result;
|
|
51
53
|
}
|
|
52
54
|
async function readArchiveInput(archivePath) {
|
|
55
|
+
assertNoWindowsPathAlias(archivePath);
|
|
53
56
|
const resolved = realpathSync.native(archivePath);
|
|
57
|
+
assertNoWindowsPathAlias(resolved);
|
|
54
58
|
const before = await inspectFileIdentity(async () => {
|
|
55
59
|
const stat = fsSync.lstatSync(archivePath, { bigint: true });
|
|
56
60
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
@@ -89,8 +93,8 @@ async function readArchiveInput(archivePath) {
|
|
|
89
93
|
await handle.close().catch(() => undefined);
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
|
-
async function readZipEntry(buffer, entryPath, maxBytes,
|
|
93
|
-
const archive = await loadAdmittedZipArchive(buffer,
|
|
96
|
+
async function readZipEntry(buffer, entryPath, maxBytes, admitted) {
|
|
97
|
+
const archive = await loadAdmittedZipArchive(buffer, admitted);
|
|
94
98
|
let entry;
|
|
95
99
|
// JSZip keys retain some aliases and may use Unicode Path metadata. Scan the
|
|
96
100
|
// effective entries once, after raw ZIP admission has rejected collisions.
|
|
@@ -171,10 +175,7 @@ async function readTarEntry(archiveBuffer, entryPath, maxBytes) {
|
|
|
171
175
|
} });
|
|
172
176
|
return result;
|
|
173
177
|
}
|
|
174
|
-
function selectNativeEntry(manifest, requested, displayPath
|
|
175
|
-
if (physicalCount !== undefined && manifest.length !== physicalCount) {
|
|
176
|
-
throw new ArchiveSecurityError("entry-path", "zip decoder collapsed entry names");
|
|
177
|
-
}
|
|
178
|
+
function selectNativeEntry(manifest, requested, displayPath) {
|
|
178
179
|
const seen = new Set();
|
|
179
180
|
let selected;
|
|
180
181
|
for (const entry of manifest) {
|
|
@@ -203,14 +204,17 @@ function throwNativeReadError(error) {
|
|
|
203
204
|
}
|
|
204
205
|
throw error;
|
|
205
206
|
}
|
|
206
|
-
async function readNativeBufferEntry(native, buffer, kind, requested, displayPath, maxBytes,
|
|
207
|
+
async function readNativeBufferEntry(native, buffer, kind, requested, displayPath, maxBytes, zipEntries) {
|
|
207
208
|
try {
|
|
208
209
|
const signal = new AbortController().signal;
|
|
209
210
|
const limits = resolveTarMeterLimits();
|
|
210
211
|
const reader = kind === "zip"
|
|
211
212
|
? await native.openZipBufferNative(buffer, limits, AbortSignal.any([signal]))
|
|
212
213
|
: await native.openTarBufferNative(buffer, kind, limits, AbortSignal.any([signal]));
|
|
213
|
-
const
|
|
214
|
+
const manifest = reader.entries;
|
|
215
|
+
if (kind === "zip")
|
|
216
|
+
validateNativeZipManifest(manifest, zipEntries);
|
|
217
|
+
const selected = selectNativeEntry(manifest, requested, displayPath);
|
|
214
218
|
return await reader.readEntry(selected.index, maxBytes, AbortSignal.any([signal]));
|
|
215
219
|
}
|
|
216
220
|
catch (error) {
|
|
@@ -226,14 +230,15 @@ export async function readArchiveEntry(archivePath, entryPath, options) {
|
|
|
226
230
|
throw new Error(`unsupported archive: ${archivePath}`);
|
|
227
231
|
}
|
|
228
232
|
const requestedEntry = normalizedRequestedEntry(entryPath);
|
|
233
|
+
assertNoWindowsPathAlias(archivePath, "filesystem", "archive source uses a Windows filesystem namespace alias");
|
|
229
234
|
const buffer = await readArchiveInput(archivePath);
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
235
|
+
const zipEntries = [];
|
|
236
|
+
if (kind === "zip")
|
|
237
|
+
admitZipBuffer(buffer, resolveExtractLimits(), entry => { zipEntries.push(entry); });
|
|
233
238
|
const native = getNativeBinding();
|
|
234
239
|
if (native)
|
|
235
|
-
return await readNativeBufferEntry(native, buffer, kind, requestedEntry, entryPath, options.maxBytes,
|
|
240
|
+
return await readNativeBufferEntry(native, buffer, kind, requestedEntry, entryPath, options.maxBytes, zipEntries);
|
|
236
241
|
assertPortableArchiveKind(kind);
|
|
237
|
-
return kind === "zip" ? await readZipEntry(buffer, requestedEntry, options.maxBytes,
|
|
242
|
+
return kind === "zip" ? await readZipEntry(buffer, requestedEntry, options.maxBytes, zipEntries)
|
|
238
243
|
: await readTarEntry(buffer, requestedEntry, options.maxBytes);
|
|
239
244
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-staging.d.ts","sourceRoot":"","sources":["../src/archive-staging.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAGnD,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EAErB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-staging.d.ts","sourceRoot":"","sources":["../src/archive-staging.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAGnD,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EAErB,MAAM,qBAAqB,CAAC;AAiB7B,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;AAMrE,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAS1F,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAY9F;AAED,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9F;AAED,wBAAsB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2CpG;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnF;AA0BD,wBAAsB,+BAA+B,CAAC,MAAM,EAAE;IAC5D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhB;AAiBD,KAAK,uBAAuB,GAAG;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,CAAC;AAkBF,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7F;AAED,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,uBAAuB,EAAE,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACnE,gBAAgB,CAAC,EAAE,qBAAqB,GACvC,OAAO,CAAC,IAAI,CAAC,CAOf;AAkHD,wBAAsB,4BAA4B,CAAC,CAAC,EAAE,MAAM,EAAE;IAC5D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACzC,GAAG,OAAO,CAAC,CAAC,CAAC,CAiCb;AAED,OAAO,EAAE,iCAAiC,EAAE,MAAM,oBAAoB,CAAC;AAEvE,wBAAgB,kCAAkC,CAAC,YAAY,EAAE,MAAM,GAAG,oBAAoB,CAE7F"}
|