@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
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import fs, {} from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { isPathInside, isPathRelativeEscape } from "./path.js";
|
|
5
|
+
import { realpathSync } from "./realpath.js";
|
|
6
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
7
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
8
|
+
import { createSuppressedError } from "./suppressed-error.js";
|
|
9
|
+
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
10
|
+
function changedStoreDirectory(messagePrefix, cause) {
|
|
11
|
+
return new FsSafeError("outside-workspace", `${messagePrefix} directory escapes root`, { cause });
|
|
12
|
+
}
|
|
13
|
+
function storeDirectoryRealPath(dir) {
|
|
14
|
+
const realPath = realpathSync(pathForWindowsFilesystem(dir));
|
|
15
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
16
|
+
return realPath;
|
|
17
|
+
}
|
|
18
|
+
function inspectStoreDirectory(params) {
|
|
19
|
+
assertNoWindowsPathAlias(params.dir, "filesystem");
|
|
20
|
+
return inspectFileIdentitySync(() => {
|
|
21
|
+
const stat = fs.lstatSync(pathForWindowsFilesystem(params.dir), { bigint: true });
|
|
22
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
23
|
+
const subject = params.root ? "root" : "directory component";
|
|
24
|
+
throw new FsSafeError("not-file", `${params.messagePrefix} ${subject} must be a directory: ${params.dir}`);
|
|
25
|
+
}
|
|
26
|
+
return stat;
|
|
27
|
+
}, params.expected);
|
|
28
|
+
}
|
|
29
|
+
function observeStoreDirectory(params) {
|
|
30
|
+
return {
|
|
31
|
+
dir: params.dir,
|
|
32
|
+
stat: inspectStoreDirectory(params),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function inspectReceiptCurrent(receipt, messagePrefix, root) {
|
|
36
|
+
try {
|
|
37
|
+
const stat = inspectStoreDirectory({
|
|
38
|
+
dir: receipt.dir,
|
|
39
|
+
expected: receipt.stat,
|
|
40
|
+
messagePrefix,
|
|
41
|
+
root,
|
|
42
|
+
});
|
|
43
|
+
if (stat.dev !== receipt.stat.dev || stat.ino !== receipt.stat.ino) {
|
|
44
|
+
throw changedStoreDirectory(messagePrefix);
|
|
45
|
+
}
|
|
46
|
+
return stat;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof FsSafeError && error.code === "outside-workspace")
|
|
50
|
+
throw error;
|
|
51
|
+
throw changedStoreDirectory(messagePrefix, error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function assertRootCurrent(rootReceipt, rootReal, messagePrefix) {
|
|
55
|
+
const stat = inspectReceiptCurrent(rootReceipt, messagePrefix, true);
|
|
56
|
+
try {
|
|
57
|
+
if (storeDirectoryRealPath(rootReceipt.dir) !== rootReal) {
|
|
58
|
+
throw changedStoreDirectory(messagePrefix);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof FsSafeError && error.code === "outside-workspace")
|
|
63
|
+
throw error;
|
|
64
|
+
throw changedStoreDirectory(messagePrefix, error);
|
|
65
|
+
}
|
|
66
|
+
return stat;
|
|
67
|
+
}
|
|
68
|
+
function assertAdmittedEdgeCurrent(receipts, rootReal, messagePrefix) {
|
|
69
|
+
const rootReceipt = receipts[0];
|
|
70
|
+
const targetIndex = receipts.length - 1;
|
|
71
|
+
const targetReceipt = receipts[targetIndex];
|
|
72
|
+
if (!rootReceipt || !targetReceipt) {
|
|
73
|
+
throw new FsSafeError("helper-failed", "store directory receipt is missing");
|
|
74
|
+
}
|
|
75
|
+
const rootStat = assertRootCurrent(rootReceipt, rootReal, messagePrefix);
|
|
76
|
+
if (targetIndex === 0)
|
|
77
|
+
return rootStat;
|
|
78
|
+
const parent = receipts[targetIndex - 1];
|
|
79
|
+
if (!parent)
|
|
80
|
+
throw new FsSafeError("helper-failed", "store directory receipt is missing");
|
|
81
|
+
if (targetIndex > 1)
|
|
82
|
+
inspectReceiptCurrent(parent, messagePrefix, false);
|
|
83
|
+
const targetStat = inspectReceiptCurrent(targetReceipt, messagePrefix, false);
|
|
84
|
+
if (path.dirname(targetReceipt.dir) !== parent.dir) {
|
|
85
|
+
throw changedStoreDirectory(messagePrefix);
|
|
86
|
+
}
|
|
87
|
+
return targetStat;
|
|
88
|
+
}
|
|
89
|
+
function assertModeTargetCurrent(receipts, rootReal, messagePrefix) {
|
|
90
|
+
const target = receipts.at(-1);
|
|
91
|
+
if (!target)
|
|
92
|
+
throw new FsSafeError("helper-failed", "store directory receipt is missing");
|
|
93
|
+
const stat = assertAdmittedEdgeCurrent(receipts, rootReal, messagePrefix);
|
|
94
|
+
let targetReal;
|
|
95
|
+
try {
|
|
96
|
+
targetReal = storeDirectoryRealPath(target.dir);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw changedStoreDirectory(messagePrefix, error);
|
|
100
|
+
}
|
|
101
|
+
if (!isPathInside(rootReal, targetReal))
|
|
102
|
+
throw changedStoreDirectory(messagePrefix);
|
|
103
|
+
assertAdmittedEdgeCurrent(receipts, rootReal, messagePrefix);
|
|
104
|
+
return stat;
|
|
105
|
+
}
|
|
106
|
+
function assertReceiptChain(receipts, rootReal, messagePrefix) {
|
|
107
|
+
const rootReceipt = receipts[0];
|
|
108
|
+
const finalReceipt = receipts.at(-1);
|
|
109
|
+
if (!rootReceipt || !finalReceipt) {
|
|
110
|
+
throw new FsSafeError("helper-failed", "store directory receipt is missing");
|
|
111
|
+
}
|
|
112
|
+
assertRootCurrent(rootReceipt, rootReal, messagePrefix);
|
|
113
|
+
let finalStat = rootReceipt.stat;
|
|
114
|
+
for (const [index, receipt] of receipts.entries()) {
|
|
115
|
+
if (index === 0)
|
|
116
|
+
continue;
|
|
117
|
+
finalStat = inspectReceiptCurrent(receipt, messagePrefix, false);
|
|
118
|
+
}
|
|
119
|
+
let finalReal;
|
|
120
|
+
try {
|
|
121
|
+
finalReal = storeDirectoryRealPath(finalReceipt.dir);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw changedStoreDirectory(messagePrefix, error);
|
|
125
|
+
}
|
|
126
|
+
if (!isPathInside(rootReal, finalReal))
|
|
127
|
+
throw changedStoreDirectory(messagePrefix);
|
|
128
|
+
assertRootCurrent(rootReceipt, rootReal, messagePrefix);
|
|
129
|
+
if (finalReceipt !== rootReceipt) {
|
|
130
|
+
finalStat = inspectReceiptCurrent(finalReceipt, messagePrefix, false);
|
|
131
|
+
}
|
|
132
|
+
return { finalReal, finalStat };
|
|
133
|
+
}
|
|
134
|
+
function directoryOpenFlags() {
|
|
135
|
+
const { O_DIRECTORY, O_NOFOLLOW, O_NONBLOCK, O_RDONLY } = fs.constants;
|
|
136
|
+
if ([O_DIRECTORY, O_NOFOLLOW, O_NONBLOCK, O_RDONLY]
|
|
137
|
+
.some((flag) => typeof flag !== "number")) {
|
|
138
|
+
throw new FsSafeError("helper-unavailable", "no-follow directory mode descriptors are unavailable");
|
|
139
|
+
}
|
|
140
|
+
return O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_NONBLOCK;
|
|
141
|
+
}
|
|
142
|
+
function directorySearchFlags() {
|
|
143
|
+
// Linux O_PATH descriptors cannot be fchmoded, and /proc/self/fd would
|
|
144
|
+
// reintroduce a pathname mutation. Darwin O_SEARCH remains descriptor-bound.
|
|
145
|
+
if (process.platform !== "darwin" ||
|
|
146
|
+
(process.arch !== "x64" && process.arch !== "arm64"))
|
|
147
|
+
return undefined;
|
|
148
|
+
const { O_DIRECTORY, O_NOFOLLOW, O_NONBLOCK } = fs.constants;
|
|
149
|
+
if ([O_DIRECTORY, O_NOFOLLOW, O_NONBLOCK].some((flag) => typeof flag !== "number")) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
// Darwin SDK O_SEARCH = O_EXEC (0x40000000) | O_DIRECTORY on supported Node hosts.
|
|
153
|
+
return 0x40000000 | O_DIRECTORY | O_NOFOLLOW | O_NONBLOCK;
|
|
154
|
+
}
|
|
155
|
+
function hasErrorCode(error, code) {
|
|
156
|
+
if (error && typeof error === "object") {
|
|
157
|
+
if (error.code === code)
|
|
158
|
+
return true;
|
|
159
|
+
const combined = error;
|
|
160
|
+
return (combined.error !== error && hasErrorCode(combined.error, code)) ||
|
|
161
|
+
(combined.suppressed !== error && hasErrorCode(combined.suppressed, code));
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
function openStoreDirectoryForMode(params) {
|
|
166
|
+
let openError;
|
|
167
|
+
try {
|
|
168
|
+
return fs.openSync(pathForWindowsFilesystem(params.receipt.dir), directoryOpenFlags());
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
openError = error;
|
|
172
|
+
}
|
|
173
|
+
const searchFlags = hasErrorCode(openError, "EACCES") ? directorySearchFlags() : undefined;
|
|
174
|
+
if (searchFlags !== undefined) {
|
|
175
|
+
try {
|
|
176
|
+
return fs.openSync(pathForWindowsFilesystem(params.receipt.dir), searchFlags);
|
|
177
|
+
}
|
|
178
|
+
catch (searchError) {
|
|
179
|
+
openError = createSuppressedError(searchError, openError, "ordinary and search-only directory opens both failed");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
assertModeTargetCurrent(params.receipts, params.rootReal, params.messagePrefix);
|
|
184
|
+
}
|
|
185
|
+
catch (boundaryError) {
|
|
186
|
+
throw changedStoreDirectory(params.messagePrefix, createSuppressedError(boundaryError, openError, "directory acquisition and boundary revalidation both failed"));
|
|
187
|
+
}
|
|
188
|
+
if (hasErrorCode(openError, "EACCES")) {
|
|
189
|
+
throw new FsSafeError("permission-unverified", `${params.messagePrefix} directory cannot be safely mode-repaired through a Node descriptor`, { cause: openError });
|
|
190
|
+
}
|
|
191
|
+
throw openError;
|
|
192
|
+
}
|
|
193
|
+
function requestedDirectoryMode(mode) {
|
|
194
|
+
return BigInt(mode & 0o7777);
|
|
195
|
+
}
|
|
196
|
+
function inspectOpenedStoreDirectory(descriptor, receipt, messagePrefix) {
|
|
197
|
+
try {
|
|
198
|
+
const stat = inspectFileIdentitySync(() => fs.fstatSync(descriptor, { bigint: true }), receipt.stat);
|
|
199
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
200
|
+
throw changedStoreDirectory(messagePrefix);
|
|
201
|
+
}
|
|
202
|
+
return stat;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
if (error instanceof FsSafeError && error.code === "outside-workspace")
|
|
206
|
+
throw error;
|
|
207
|
+
throw changedStoreDirectory(messagePrefix, error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function finalizeDirectoryMode(params) {
|
|
211
|
+
const receipt = params.receipts.at(-1);
|
|
212
|
+
if (!receipt)
|
|
213
|
+
throw new FsSafeError("helper-failed", "store directory receipt is missing");
|
|
214
|
+
const requestedMode = requestedDirectoryMode(params.mode);
|
|
215
|
+
if ((receipt.stat.mode & 4095n) === requestedMode || process.platform === "win32") {
|
|
216
|
+
const current = assertAdmittedEdgeCurrent(params.receipts, params.rootReal, params.messagePrefix);
|
|
217
|
+
// Identity can remain stable while permissions change after the first receipt.
|
|
218
|
+
if (process.platform === "win32" || (current.mode & 4095n) === requestedMode) {
|
|
219
|
+
return { ...receipt, stat: current };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Repair owns the stronger pre/post mode-target fence below, including an
|
|
223
|
+
// admission check on acquisition failure; no earlier admission is needed.
|
|
224
|
+
const descriptor = openStoreDirectoryForMode({
|
|
225
|
+
receipt,
|
|
226
|
+
receipts: params.receipts,
|
|
227
|
+
rootReal: params.rootReal,
|
|
228
|
+
messagePrefix: params.messagePrefix,
|
|
229
|
+
});
|
|
230
|
+
let finalStat;
|
|
231
|
+
let operationFailed = false;
|
|
232
|
+
let operationError;
|
|
233
|
+
try {
|
|
234
|
+
const opened = inspectOpenedStoreDirectory(descriptor, receipt, params.messagePrefix);
|
|
235
|
+
assertModeTargetCurrent(params.receipts, params.rootReal, params.messagePrefix);
|
|
236
|
+
if ((opened.mode & 4095n) !== requestedMode) {
|
|
237
|
+
fs.fchmodSync(descriptor, Number(requestedMode));
|
|
238
|
+
}
|
|
239
|
+
const finalized = inspectOpenedStoreDirectory(descriptor, receipt, params.messagePrefix);
|
|
240
|
+
const pathname = assertModeTargetCurrent(params.receipts, params.rootReal, params.messagePrefix);
|
|
241
|
+
if ((finalized.mode & 4095n) !== requestedMode ||
|
|
242
|
+
(pathname.mode & 4095n) !== requestedMode) {
|
|
243
|
+
throw new FsSafeError("insecure-permissions", `${params.messagePrefix} directory mode could not be finalized`);
|
|
244
|
+
}
|
|
245
|
+
finalStat = pathname;
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
operationFailed = true;
|
|
249
|
+
operationError = error;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
fs.closeSync(descriptor);
|
|
253
|
+
}
|
|
254
|
+
catch (closeError) {
|
|
255
|
+
if (operationFailed) {
|
|
256
|
+
throw createSuppressedError(closeError, operationError, "store directory finalization and close both failed");
|
|
257
|
+
}
|
|
258
|
+
throw closeError;
|
|
259
|
+
}
|
|
260
|
+
if (operationFailed)
|
|
261
|
+
throw operationError;
|
|
262
|
+
if (!finalStat)
|
|
263
|
+
throw new FsSafeError("helper-failed", "store directory mode receipt is missing");
|
|
264
|
+
return { ...receipt, stat: finalStat };
|
|
265
|
+
}
|
|
266
|
+
export function ensureSyncStoreDirectory(params) {
|
|
267
|
+
assertNoWindowsPathAlias(params.rootDir, "filesystem", "store root uses a Windows filesystem namespace alias");
|
|
268
|
+
assertNoWindowsPathAlias(params.targetDir, "filesystem", "store path uses a Windows filesystem namespace alias");
|
|
269
|
+
const rootDir = resolvePathPreservingWindowsRoot(params.rootDir);
|
|
270
|
+
const dir = resolvePathPreservingWindowsRoot(params.targetDir);
|
|
271
|
+
assertNoWindowsPathAlias(rootDir, "filesystem", "store root uses a Windows filesystem namespace alias");
|
|
272
|
+
assertNoWindowsPathAlias(dir, "filesystem", "store path uses a Windows filesystem namespace alias");
|
|
273
|
+
const relative = path.relative(rootDir, dir);
|
|
274
|
+
if (isPathRelativeEscape(relative)) {
|
|
275
|
+
throw new FsSafeError("outside-workspace", "file path escapes store root");
|
|
276
|
+
}
|
|
277
|
+
fs.mkdirSync(recursiveMkdirPath(pathForWindowsFilesystem(rootDir)), { recursive: true, mode: params.mode });
|
|
278
|
+
const receipts = [observeStoreDirectory({
|
|
279
|
+
dir: rootDir,
|
|
280
|
+
messagePrefix: params.messagePrefix,
|
|
281
|
+
root: true,
|
|
282
|
+
})];
|
|
283
|
+
const rootReal = storeDirectoryRealPath(rootDir);
|
|
284
|
+
receipts[0] = finalizeDirectoryMode({
|
|
285
|
+
receipts,
|
|
286
|
+
rootReal,
|
|
287
|
+
mode: params.mode,
|
|
288
|
+
messagePrefix: params.messagePrefix,
|
|
289
|
+
});
|
|
290
|
+
let current = rootDir;
|
|
291
|
+
for (const segment of relative.split(path.sep).filter(Boolean)) {
|
|
292
|
+
current = path.join(current, segment);
|
|
293
|
+
let receipt;
|
|
294
|
+
try {
|
|
295
|
+
receipt = observeStoreDirectory({
|
|
296
|
+
dir: current,
|
|
297
|
+
messagePrefix: params.messagePrefix,
|
|
298
|
+
root: false,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (error.code !== "ENOENT")
|
|
303
|
+
throw error;
|
|
304
|
+
fs.mkdirSync(pathForWindowsFilesystem(current), { mode: params.mode });
|
|
305
|
+
receipt = observeStoreDirectory({
|
|
306
|
+
dir: current,
|
|
307
|
+
messagePrefix: params.messagePrefix,
|
|
308
|
+
root: false,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
receipts.push(receipt);
|
|
312
|
+
receipts[receipts.length - 1] = finalizeDirectoryMode({
|
|
313
|
+
receipts,
|
|
314
|
+
rootReal,
|
|
315
|
+
mode: params.mode,
|
|
316
|
+
messagePrefix: params.messagePrefix,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
const finalChain = assertReceiptChain(receipts, rootReal, params.messagePrefix);
|
|
320
|
+
return {
|
|
321
|
+
dir,
|
|
322
|
+
realPath: finalChain.finalReal,
|
|
323
|
+
exactStat: finalChain.finalStat,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
export function assertSyncStoreDirectoryReceipt(receipt) {
|
|
327
|
+
try {
|
|
328
|
+
assertNoWindowsPathAlias(receipt.realPath, "filesystem");
|
|
329
|
+
const stat = inspectStoreDirectory({
|
|
330
|
+
dir: receipt.dir,
|
|
331
|
+
expected: receipt.exactStat,
|
|
332
|
+
messagePrefix: "store",
|
|
333
|
+
root: false,
|
|
334
|
+
});
|
|
335
|
+
if (stat.dev !== receipt.exactStat.dev || stat.ino !== receipt.exactStat.ino ||
|
|
336
|
+
storeDirectoryRealPath(receipt.dir) !== receipt.realPath) {
|
|
337
|
+
throw new FsSafeError("path-mismatch", "store directory changed during write");
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
if (error instanceof FsSafeError && error.code === "path-mismatch" &&
|
|
342
|
+
error.message !== "store directory changed during write") {
|
|
343
|
+
throw new FsSafeError("path-mismatch", "store directory changed during write", {
|
|
344
|
+
cause: error,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
}
|
package/dist/file-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAM5C,OAAO,EAA4B,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAQnE,OAAO,EAAmB,KAAK,oBAAoB,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAItG,OAAO,EAAQ,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,IAAI,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"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"}
|
package/dist/file-store.js
CHANGED
|
@@ -15,9 +15,10 @@ import { isNotFoundPathError } from "./path.js";
|
|
|
15
15
|
import { throwFsSafeReadError } from "./root-errors.js";
|
|
16
16
|
import { root } from "./root.js";
|
|
17
17
|
import { DEFAULT_ROOT_MAX_BYTES } from "./root-impl.js";
|
|
18
|
-
import { readRegularFile } from "./regular-file.js";
|
|
19
18
|
import { matchRootFileOpenFailure, openRootFileSync } from "./root-file.js";
|
|
20
19
|
import { writeSecretFileAtomic } from "./secret-file.js";
|
|
20
|
+
import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
21
|
+
import { readFileStoreCopySource } from "./file-store-copy-source.js";
|
|
21
22
|
function isNotFound(error) {
|
|
22
23
|
return error instanceof FsSafeError ? error.code === "not-found" : isNotFoundPathError(error);
|
|
23
24
|
}
|
|
@@ -49,34 +50,10 @@ function handleSyncStoreReadOpenFailure(opened) {
|
|
|
49
50
|
},
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
|
-
async function readFileStoreCopySource(params) {
|
|
53
|
-
const sourceStat = syncFs.lstatSync(params.sourcePath);
|
|
54
|
-
if (sourceStat.isSymbolicLink() || !sourceStat.isFile()) {
|
|
55
|
-
throw new FsSafeError("not-file", "source path is not a file");
|
|
56
|
-
}
|
|
57
|
-
assertFileStoreMaxBytes(sourceStat.size, params.maxBytes);
|
|
58
|
-
try {
|
|
59
|
-
return (await readRegularFile({ filePath: params.sourcePath, maxBytes: params.maxBytes }))
|
|
60
|
-
.buffer;
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
64
|
-
if (message.includes("regular file") || message.includes("not a regular file")) {
|
|
65
|
-
throw new FsSafeError("not-file", "source path is not a file", {
|
|
66
|
-
cause: error instanceof Error ? error : undefined,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
if (params.maxBytes !== undefined && message.includes(`exceeds ${params.maxBytes} bytes`)) {
|
|
70
|
-
throw new FsSafeError("too-large", `file exceeds maximum size of ${params.maxBytes} bytes`, {
|
|
71
|
-
cause: error instanceof Error ? error : undefined,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
53
|
async function copyIntoRoot(params) {
|
|
78
54
|
const relativePath = assertRelativePath(params.relativePath);
|
|
79
55
|
const destination = resolveStorePath(params.rootDir, relativePath);
|
|
56
|
+
assertNoWindowsPathAlias(params.sourcePath, "filesystem", "source path uses a Windows filesystem namespace alias");
|
|
80
57
|
const sourceStat = syncFs.lstatSync(params.sourcePath);
|
|
81
58
|
if (sourceStat.isSymbolicLink() || !sourceStat.isFile()) {
|
|
82
59
|
throw new FsSafeError("not-file", "source path is not a file");
|
|
@@ -98,7 +75,10 @@ async function copyIntoRoot(params) {
|
|
|
98
75
|
return destination;
|
|
99
76
|
}
|
|
100
77
|
export function fileStore(options) {
|
|
101
|
-
const
|
|
78
|
+
const rootDirInput = options.rootDir;
|
|
79
|
+
assertNoWindowsPathAlias(rootDirInput, "filesystem", "store root uses a Windows filesystem namespace alias");
|
|
80
|
+
const rootDir = resolvePathPreservingWindowsRoot(rootDirInput);
|
|
81
|
+
assertNoWindowsPathAlias(rootDir, "filesystem", "store root uses a Windows filesystem namespace alias");
|
|
102
82
|
const privateMode = options.private ?? false;
|
|
103
83
|
const dirMode = options.dirMode ?? 0o700;
|
|
104
84
|
const mode = options.mode ?? 0o600;
|
|
@@ -284,7 +264,10 @@ export function fileStore(options) {
|
|
|
284
264
|
};
|
|
285
265
|
}
|
|
286
266
|
export function fileStoreSync(options) {
|
|
287
|
-
const
|
|
267
|
+
const rootDirInput = options.rootDir;
|
|
268
|
+
assertNoWindowsPathAlias(rootDirInput, "filesystem", "store root uses a Windows filesystem namespace alias");
|
|
269
|
+
const rootDir = resolvePathPreservingWindowsRoot(rootDirInput);
|
|
270
|
+
assertNoWindowsPathAlias(rootDir, "filesystem", "store root uses a Windows filesystem namespace alias");
|
|
288
271
|
const privateMode = options.private ?? false;
|
|
289
272
|
const dirMode = options.dirMode ?? 0o700;
|
|
290
273
|
const mode = options.mode ?? 0o600;
|
package/dist/filename.d.ts
CHANGED
package/dist/filename.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filename.d.ts","sourceRoot":"","sources":["../src/filename.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filename.d.ts","sourceRoot":"","sources":["../src/filename.ts"],"names":[],"mappings":"AA0BA,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAexE;AA+CD,yFAAyF;AACzF,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CA8BT;AAiCD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAMxF"}
|
package/dist/filename.js
CHANGED
|
@@ -2,8 +2,15 @@ import path from "node:path";
|
|
|
2
2
|
import { WINDOWS_RESERVED_DEVICE_NAMES } from "./device-path.js";
|
|
3
3
|
import { maxNormalizedUtf8Bytes } from "./unicode-path.js";
|
|
4
4
|
const INVALID_FILE_NAME_CHARACTERS = /[\u0000-\u001f\u007f-\u009f<>:"/\\|?*]/g;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const HAS_INVALID_FILE_NAME_CHARACTER = /[\u0000-\u001f\u007f-\u009f<>:"/\\|?*]/;
|
|
6
|
+
const HAS_RETAINED_FILE_NAME_CHARACTER = /[^\u0000-\u001f\u007f-\u009f<>:"/\\|?*\s]/;
|
|
7
|
+
function canStartWindowsDeviceName(character) {
|
|
8
|
+
const folded = character | 0x20;
|
|
9
|
+
return folded === 0x61 || folded === 0x63 || folded === 0x6c ||
|
|
10
|
+
folded === 0x6e || folded === 0x70;
|
|
11
|
+
}
|
|
12
|
+
function windowsDeviceStemEnd(value, stemEnd) {
|
|
13
|
+
let end = stemEnd;
|
|
7
14
|
while (end > 0) {
|
|
8
15
|
const character = value.charCodeAt(end - 1);
|
|
9
16
|
if (character !== 0x20 && character !== 0x2e) {
|
|
@@ -11,22 +18,61 @@ function trimWindowsIgnoredSuffix(value) {
|
|
|
11
18
|
}
|
|
12
19
|
end -= 1;
|
|
13
20
|
}
|
|
14
|
-
return end
|
|
21
|
+
return end;
|
|
15
22
|
}
|
|
16
|
-
function suffixWindowsReservedDeviceName(fileName) {
|
|
23
|
+
export function suffixWindowsReservedDeviceName(fileName) {
|
|
24
|
+
if (!canStartWindowsDeviceName(fileName.charCodeAt(0))) {
|
|
25
|
+
return fileName;
|
|
26
|
+
}
|
|
17
27
|
const extensionIndex = fileName.indexOf(".");
|
|
18
28
|
const baseNameEnd = extensionIndex < 0 ? fileName.length : extensionIndex;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
const deviceBaseNameEnd = windowsDeviceStemEnd(fileName, baseNameEnd);
|
|
30
|
+
if (deviceBaseNameEnd === 0 ||
|
|
31
|
+
deviceBaseNameEnd > 7 ||
|
|
32
|
+
!WINDOWS_RESERVED_DEVICE_NAMES.has(fileName.slice(0, deviceBaseNameEnd).toUpperCase())) {
|
|
22
33
|
return fileName;
|
|
23
34
|
}
|
|
24
|
-
return `${
|
|
35
|
+
return `${fileName.slice(0, baseNameEnd)}_${fileName.slice(baseNameEnd)}`;
|
|
25
36
|
}
|
|
26
37
|
const PORTABLE_FILE_NAME_BYTES = 255;
|
|
38
|
+
const SANITIZED_FILE_NAME_CODE_UNITS = 200;
|
|
39
|
+
const SAFE_FALLBACK_FILE_NAME = "file";
|
|
40
|
+
/**
|
|
41
|
+
* Uses native string and regexp operations to recognize the full sanitizer's
|
|
42
|
+
* bounded fixed points. The separate non-global regexp keeps this predicate
|
|
43
|
+
* stateless, and the authoritative device-name transform remains the final
|
|
44
|
+
* admission check.
|
|
45
|
+
*/
|
|
46
|
+
function isBoundedSanitizedFileName(fileName, trimmedFileName) {
|
|
47
|
+
return typeof fileName === "string" &&
|
|
48
|
+
fileName.length > 0 &&
|
|
49
|
+
fileName.length <= SANITIZED_FILE_NAME_CODE_UNITS &&
|
|
50
|
+
fileName !== "." &&
|
|
51
|
+
fileName !== ".." &&
|
|
52
|
+
trimmedFileName === fileName &&
|
|
53
|
+
!HAS_INVALID_FILE_NAME_CHARACTER.test(fileName) &&
|
|
54
|
+
suffixWindowsReservedDeviceName(fileName) === fileName;
|
|
55
|
+
}
|
|
56
|
+
function hasWindowsDrivePrefix(value) {
|
|
57
|
+
if (value.length < 2 || value.charCodeAt(1) !== 0x3a)
|
|
58
|
+
return false;
|
|
59
|
+
const firstCodeUnit = value.charCodeAt(0);
|
|
60
|
+
return (firstCodeUnit >= 0x41 && firstCodeUnit <= 0x5a) ||
|
|
61
|
+
(firstCodeUnit >= 0x61 && firstCodeUnit <= 0x7a);
|
|
62
|
+
}
|
|
27
63
|
function normalizedFileNameBytes(value) {
|
|
28
64
|
return maxNormalizedUtf8Bytes(value, true);
|
|
29
65
|
}
|
|
66
|
+
function truncateCodeUnitsWithoutSplittingSurrogate(value, limit) {
|
|
67
|
+
if (value.length <= limit)
|
|
68
|
+
return value;
|
|
69
|
+
let truncated = value.slice(0, limit);
|
|
70
|
+
const trailingCodeUnit = truncated.charCodeAt(truncated.length - 1);
|
|
71
|
+
if (trailingCodeUnit >= 0xd800 && trailingCodeUnit <= 0xdbff) {
|
|
72
|
+
truncated = truncated.slice(0, -1);
|
|
73
|
+
}
|
|
74
|
+
return truncated;
|
|
75
|
+
}
|
|
30
76
|
/** Keeps short names exact and trims only the filename tail of a composite temp name. */
|
|
31
77
|
export function fitFileNameToPortableComponent(params) {
|
|
32
78
|
const complete = `${params.prefix}${params.fileName}${params.suffix}`;
|
|
@@ -59,24 +105,40 @@ export function fitFileNameToPortableComponent(params) {
|
|
|
59
105
|
}
|
|
60
106
|
return `${codePoints.slice(0, low).join("")}${tailSuffix}`;
|
|
61
107
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
108
|
+
function sanitizeFileNameCandidate(fileName) {
|
|
109
|
+
if (typeof fileName !== "string")
|
|
110
|
+
return undefined;
|
|
111
|
+
const trimmed = fileName.trim();
|
|
112
|
+
if (!trimmed)
|
|
113
|
+
return undefined;
|
|
114
|
+
if (isBoundedSanitizedFileName(fileName, trimmed))
|
|
115
|
+
return fileName;
|
|
116
|
+
// Basename selection cannot manufacture a character, and the complete
|
|
117
|
+
// sanitizer removes this same invalid set before its final trim.
|
|
118
|
+
if (!HAS_RETAINED_FILE_NAME_CHARACTER.test(trimmed))
|
|
119
|
+
return undefined;
|
|
120
|
+
let base = trimmed;
|
|
121
|
+
if (base.includes("/"))
|
|
122
|
+
base = path.posix.basename(base);
|
|
123
|
+
if (base.includes("\\") || hasWindowsDrivePrefix(base)) {
|
|
124
|
+
base = path.win32.basename(base);
|
|
66
125
|
}
|
|
67
|
-
let base = path.posix.basename(trimmed);
|
|
68
|
-
base = path.win32.basename(base);
|
|
69
126
|
base = base.replace(INVALID_FILE_NAME_CHARACTERS, "").trim();
|
|
70
127
|
if (!base || base === "." || base === "..") {
|
|
71
|
-
return
|
|
128
|
+
return undefined;
|
|
72
129
|
}
|
|
73
|
-
base =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
130
|
+
base = truncateCodeUnitsWithoutSplittingSurrogate(base, SANITIZED_FILE_NAME_CODE_UNITS);
|
|
131
|
+
let safeBase = suffixWindowsReservedDeviceName(base);
|
|
132
|
+
if (safeBase.length > SANITIZED_FILE_NAME_CODE_UNITS) {
|
|
133
|
+
// The safety suffix is the final invariant. Shorten the unsuffixed tail so
|
|
134
|
+
// truncation cannot turn a padded reserved stem back into a device name.
|
|
135
|
+
base = truncateCodeUnitsWithoutSplittingSurrogate(base, SANITIZED_FILE_NAME_CODE_UNITS - 1);
|
|
136
|
+
safeBase = suffixWindowsReservedDeviceName(base);
|
|
80
137
|
}
|
|
81
|
-
return
|
|
138
|
+
return safeBase;
|
|
139
|
+
}
|
|
140
|
+
export function sanitizeUntrustedFileName(fileName, fallbackName) {
|
|
141
|
+
return (sanitizeFileNameCandidate(fileName) ??
|
|
142
|
+
sanitizeFileNameCandidate(fallbackName) ??
|
|
143
|
+
SAFE_FALLBACK_FILE_NAME);
|
|
82
144
|
}
|
package/dist/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOxD"}
|
package/dist/fs.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { pathForWindowsFilesystem } from "./windows-path-alias.js";
|
|
2
3
|
/**
|
|
3
4
|
* Returns true when `fs.stat()` can stat the path.
|
|
4
5
|
*
|
|
@@ -7,7 +8,7 @@ import fs from "node:fs";
|
|
|
7
8
|
*/
|
|
8
9
|
export async function pathExists(filePath) {
|
|
9
10
|
try {
|
|
10
|
-
fs.statSync(filePath);
|
|
11
|
+
fs.statSync(pathForWindowsFilesystem(filePath));
|
|
11
12
|
return true;
|
|
12
13
|
}
|
|
13
14
|
catch {
|
|
@@ -20,7 +21,7 @@ export async function pathExists(filePath) {
|
|
|
20
21
|
*/
|
|
21
22
|
export function pathExistsSync(filePath) {
|
|
22
23
|
try {
|
|
23
|
-
fs.statSync(filePath);
|
|
24
|
+
fs.statSync(pathForWindowsFilesystem(filePath));
|
|
24
25
|
return true;
|
|
25
26
|
}
|
|
26
27
|
catch {
|
package/dist/guarded-mkdir.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type RootBoundaryIdentity } from "./root-boundary.js";
|
|
2
|
+
import { type MutationDirectoryObservation } from "./pinned-mutation-observation.js";
|
|
3
|
+
import type { PinnedCreatedDirectoryReceipt, PinnedMutationAdmissionReceipt, PinnedMutationAuthorizationToken } from "./pinned-write.js";
|
|
1
4
|
/**
|
|
2
5
|
* Creates each missing path component from `rootReal` down to `targetPath`,
|
|
3
6
|
* guarding every step. Returns the real (symlink-resolved) path of the final
|
|
@@ -8,8 +11,15 @@ export declare function mkdirPathComponentsWithGuards(params: {
|
|
|
8
11
|
rootReal: string;
|
|
9
12
|
targetPath: string;
|
|
10
13
|
beforeComponent?: (componentPath: string) => Promise<void> | void;
|
|
14
|
+
beforeCreateComponent?: (componentPath: string, prospectiveTargetPath: string, retainedTargetPath: string | undefined, parent: MutationDirectoryObservation) => Promise<PinnedMutationAdmissionReceipt | undefined> | PinnedMutationAdmissionReceipt | undefined;
|
|
15
|
+
beforeUseComponent?: (componentPath: string, prospectiveTargetPath: string, retainedTargetPath: string | undefined) => Promise<void> | void;
|
|
16
|
+
afterCreateComponent?: (receipt: PinnedCreatedDirectoryReceipt) => PinnedMutationAuthorizationToken | undefined;
|
|
11
17
|
assertBeforeMutation?: () => void;
|
|
12
18
|
mode?: number;
|
|
13
19
|
rejectSymlinks?: boolean;
|
|
20
|
+
revalidateParentAfterBeforeComponent?: boolean;
|
|
21
|
+
synchronousAuthorizationIncludesFence?: boolean;
|
|
22
|
+
retainedTargetPath?: string;
|
|
23
|
+
rootIdentity?: RootBoundaryIdentity;
|
|
14
24
|
}): Promise<string>;
|
|
15
25
|
//# sourceMappingURL=guarded-mkdir.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guarded-mkdir.d.ts","sourceRoot":"","sources":["../src/guarded-mkdir.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"guarded-mkdir.d.ts","sourceRoot":"","sources":["../src/guarded-mkdir.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAA4B,KAAK,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAC/G,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAC9B,gCAAgC,EACjC,MAAM,mBAAmB,CAAC;AAuG3B;;;;;GAKG;AACH,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClE,qBAAqB,CAAC,EAAE,CACtB,aAAa,EAAE,MAAM,EACrB,qBAAqB,EAAE,MAAM,EAC7B,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,MAAM,EAAE,4BAA4B,KACjC,OAAO,CAAC,8BAA8B,GAAG,SAAS,CAAC,GAAG,8BAA8B,GAAG,SAAS,CAAC;IACtG,kBAAkB,CAAC,EAAE,CACnB,aAAa,EAAE,MAAM,EACrB,qBAAqB,EAAE,MAAM,EAC7B,kBAAkB,EAAE,MAAM,GAAG,SAAS,KACnC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,oBAAoB,CAAC,EAAE,CACrB,OAAO,EAAE,6BAA6B,KACnC,gCAAgC,GAAG,SAAS,CAAC;IAClD,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oCAAoC,CAAC,EAAE,OAAO,CAAC;IAC/C,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,oBAAoB,CAAC;CACrC,GAAG,OAAO,CAAC,MAAM,CAAC,CAyMlB"}
|