@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/dist/archive-staging.js
CHANGED
|
@@ -14,6 +14,7 @@ import { mkdirPathComponentsWithGuards } from "./guarded-mkdir.js";
|
|
|
14
14
|
import { expandRelativePathWithHome } from "./root-context.js";
|
|
15
15
|
import { resolveRootPath } from "./root-path.js";
|
|
16
16
|
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
17
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
17
18
|
import { realpathSync } from "./realpath.js";
|
|
18
19
|
const ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK = "archive entry traverses symlink in destination";
|
|
19
20
|
const ARCHIVE_STAGING_MODE = 0o700;
|
|
@@ -25,8 +26,11 @@ function symlinkTraversalError(originalPath) {
|
|
|
25
26
|
return new ArchiveSecurityError("destination-symlink-traversal", `${ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK}: ${formatErrorDetail(originalPath)}`);
|
|
26
27
|
}
|
|
27
28
|
export async function createDirectoryIdentityGuard(dir) {
|
|
29
|
+
assertNoWindowsPathAlias(dir);
|
|
28
30
|
try {
|
|
29
|
-
|
|
31
|
+
const guard = await createAsyncDirectoryGuard(dir, { bigint: true });
|
|
32
|
+
assertNoWindowsPathAlias(guard.realPath);
|
|
33
|
+
return guard;
|
|
30
34
|
}
|
|
31
35
|
catch (err) {
|
|
32
36
|
if (err instanceof FsSafeError && err.code === "not-file") {
|
|
@@ -47,6 +51,7 @@ export async function assertDirectoryIdentityGuard(guard) {
|
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
export async function prepareArchiveDestinationGuard(destDir) {
|
|
54
|
+
assertNoWindowsPathAlias(destDir);
|
|
50
55
|
let stat;
|
|
51
56
|
try {
|
|
52
57
|
stat = inspectFileIdentitySync(() => {
|
|
@@ -69,6 +74,7 @@ export async function prepareArchiveDestinationGuard(destDir) {
|
|
|
69
74
|
throw err;
|
|
70
75
|
}
|
|
71
76
|
const realPath = realpathSync.native(destDir);
|
|
77
|
+
assertNoWindowsPathAlias(realPath);
|
|
72
78
|
const guard = { dir: destDir, realPath, stat };
|
|
73
79
|
try {
|
|
74
80
|
inspectFileIdentitySync(() => fsSync.statSync(realPath, { bigint: true }), stat);
|
|
@@ -106,9 +112,14 @@ async function assertNoSymlinkTraversal(params) {
|
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
114
|
export async function assertResolvedInsideDestination(params) {
|
|
115
|
+
const destinationRealDir = params.destinationRealDir;
|
|
116
|
+
const targetPath = params.targetPath;
|
|
117
|
+
const originalPath = params.originalPath;
|
|
118
|
+
assertNoWindowsPathAlias(destinationRealDir);
|
|
119
|
+
assertNoWindowsPathAlias(targetPath);
|
|
109
120
|
let resolved;
|
|
110
121
|
try {
|
|
111
|
-
resolved = realpathSync.native(
|
|
122
|
+
resolved = realpathSync.native(targetPath);
|
|
112
123
|
}
|
|
113
124
|
catch (err) {
|
|
114
125
|
if (isNotFoundPathError(err)) {
|
|
@@ -116,8 +127,9 @@ export async function assertResolvedInsideDestination(params) {
|
|
|
116
127
|
}
|
|
117
128
|
throw err;
|
|
118
129
|
}
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
assertNoWindowsPathAlias(resolved);
|
|
131
|
+
if (!isPathInside(destinationRealDir, resolved)) {
|
|
132
|
+
throw symlinkTraversalError(originalPath);
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
135
|
async function mkdirArchiveOutput(params) {
|
|
@@ -131,14 +143,28 @@ async function mkdirArchiveOutput(params) {
|
|
|
131
143
|
throw error;
|
|
132
144
|
}
|
|
133
145
|
}
|
|
146
|
+
function ownArchiveOutputPathParams(params) {
|
|
147
|
+
const deadline = params.deadline;
|
|
148
|
+
checkExtractionDeadline(deadline);
|
|
149
|
+
const destinationDir = params.destinationDir;
|
|
150
|
+
const destinationRealDir = params.destinationRealDir;
|
|
151
|
+
const relPath = params.relPath;
|
|
152
|
+
const outPath = params.outPath;
|
|
153
|
+
const originalPath = params.originalPath;
|
|
154
|
+
const isDirectory = params.isDirectory;
|
|
155
|
+
assertNoWindowsPathAlias(destinationDir);
|
|
156
|
+
assertNoWindowsPathAlias(destinationRealDir);
|
|
157
|
+
assertNoWindowsPathAlias(relPath, "relative");
|
|
158
|
+
assertNoWindowsPathAlias(outPath);
|
|
159
|
+
return { destinationDir, destinationRealDir, relPath, outPath, originalPath, isDirectory, deadline };
|
|
160
|
+
}
|
|
134
161
|
export async function prepareArchiveOutputPath(params) {
|
|
135
|
-
await prepareOutputPath(params);
|
|
162
|
+
await prepareOutputPath(ownArchiveOutputPathParams(params));
|
|
136
163
|
}
|
|
137
164
|
export async function preparePrivateArchiveOutputPath(params, assertGuards, destinationGuard) {
|
|
138
|
-
await prepareOutputPath(params, assertGuards, true, destinationGuard);
|
|
165
|
+
await prepareOutputPath(ownArchiveOutputPathParams(params), assertGuards, true, destinationGuard);
|
|
139
166
|
}
|
|
140
167
|
async function prepareOutputPath(params, assertGuards, privateWorkingMode = false, existingDestinationGuard) {
|
|
141
|
-
checkExtractionDeadline(params.deadline);
|
|
142
168
|
const targetRoot = privateWorkingMode ? {
|
|
143
169
|
async mkdir(relativePath) {
|
|
144
170
|
// Retain Root.mkdir's strict alias admission before the shared traversal,
|
|
@@ -231,6 +257,7 @@ async function prepareOutputPath(params, assertGuards, privateWorkingMode = fals
|
|
|
231
257
|
checkExtractionDeadline(params.deadline);
|
|
232
258
|
}
|
|
233
259
|
function assertSafeArchiveStagingPrefix(prefix) {
|
|
260
|
+
assertNoWindowsPathAlias(prefix, "relative");
|
|
234
261
|
if (!prefix ||
|
|
235
262
|
prefix === "." ||
|
|
236
263
|
prefix === ".." ||
|
|
@@ -242,16 +269,22 @@ function assertSafeArchiveStagingPrefix(prefix) {
|
|
|
242
269
|
return prefix;
|
|
243
270
|
}
|
|
244
271
|
export async function withStagedArchiveDestination(params) {
|
|
272
|
+
const destinationRealDir = params.destinationRealDir;
|
|
273
|
+
assertNoWindowsPathAlias(destinationRealDir);
|
|
245
274
|
const stagingRoot = resolveSecureTempRoot({
|
|
246
275
|
fallbackPrefix: "fs-safe-archive",
|
|
247
276
|
unsafeFallbackLabel: "archive staging temp dir",
|
|
248
277
|
warn: () => undefined,
|
|
249
278
|
});
|
|
250
|
-
|
|
279
|
+
assertNoWindowsPathAlias(stagingRoot);
|
|
280
|
+
if (isPathInside(destinationRealDir, stagingRoot)) {
|
|
251
281
|
throw new Error(`archive staging root must be outside destination: ${stagingRoot}`);
|
|
252
282
|
}
|
|
253
|
-
const
|
|
254
|
-
const
|
|
283
|
+
const stagingDirPrefix = params.stagingDirPrefix;
|
|
284
|
+
const stagingPrefix = assertSafeArchiveStagingPrefix(stagingDirPrefix ?? "fs-safe-archive-");
|
|
285
|
+
const stagingPathPrefix = path.join(stagingRoot, stagingPrefix);
|
|
286
|
+
assertNoWindowsPathAlias(stagingPathPrefix);
|
|
287
|
+
const stagingDir = await fs.mkdtemp(stagingPathPrefix);
|
|
255
288
|
const stagingGuard = await createDirectoryIdentityGuard(stagingDir);
|
|
256
289
|
try {
|
|
257
290
|
await fs.chmod(stagingDir, ARCHIVE_STAGING_MODE).catch(() => undefined);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar-inspect.d.ts","sourceRoot":"","sources":["../src/archive-tar-inspect.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-tar-inspect.d.ts","sourceRoot":"","sources":["../src/archive-tar-inspect.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAQrF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,qBAAqB,EAC/D,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;AAE3F,2FAA2F;AAC3F,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA6C/G"}
|
|
@@ -8,14 +8,17 @@ import { resolveArchiveFilteredEntryPolicy } from "./archive-policy.js";
|
|
|
8
8
|
import { createTarEntryPlanner } from "./archive-tar.js";
|
|
9
9
|
import { inspectTar } from "./archive-tar-stream.js";
|
|
10
10
|
import { getNativeBinding } from "./native.js";
|
|
11
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
11
12
|
/** Complete TAR/gzip admission and zero-strip extraction policy, without output writes. */
|
|
12
13
|
export async function inspectTarArchive(params) {
|
|
14
|
+
const archivePath = params.archivePath;
|
|
13
15
|
const onFiltered = resolveArchiveFilteredEntryPolicy(params.onFiltered);
|
|
14
16
|
const limits = resolveExtractLimits(params.limits);
|
|
15
17
|
const tarLimits = resolveTarMeterLimits(limits);
|
|
16
18
|
const native = getNativeBinding();
|
|
19
|
+
assertNoWindowsPathAlias(archivePath, "filesystem", "archive source uses a Windows filesystem namespace alias");
|
|
17
20
|
return await withExtractionDeadline(params.timeoutMs, "inspect tar", async (deadline) => {
|
|
18
|
-
const staged = await stageArchiveFileForExtraction({ archivePath
|
|
21
|
+
const staged = await stageArchiveFileForExtraction({ archivePath, limits, deadline });
|
|
19
22
|
try {
|
|
20
23
|
// Staging closes descriptors asynchronously; expiry there must not start a decoder.
|
|
21
24
|
deadline.check();
|
|
@@ -2,7 +2,7 @@ import type { ExtractionDeadline } from "./archive-deadline.js";
|
|
|
2
2
|
import { type ResolvedArchiveExtractLimits } from "./archive-limits.js";
|
|
3
3
|
import { type ZipDirectoryEntry } from "./archive-zip-directory.js";
|
|
4
4
|
/** Admit physical records before a decoder can normalize or collapse names. */
|
|
5
|
-
export declare function admitZipBuffer(input: Uint8Array, limits: ResolvedArchiveExtractLimits): number;
|
|
5
|
+
export declare function admitZipBuffer(input: Uint8Array, limits: ResolvedArchiveExtractLimits, onEntry?: (entry: ZipDirectoryEntry) => void): number;
|
|
6
6
|
/** The extraction input is private and immutable; read bounded metadata windows. */
|
|
7
7
|
export declare function admitZipFile(archivePath: string, limits: ResolvedArchiveExtractLimits, deadline: ExtractionDeadline, onEntry?: (entry: ZipDirectoryEntry) => void): Promise<number>;
|
|
8
8
|
//# sourceMappingURL=archive-zip-admission.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-admission.d.ts","sourceRoot":"","sources":["../src/archive-zip-admission.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAA+C,KAAK,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAErH,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAUtF,+EAA+E;AAC/E,wBAAgB,cAAc,
|
|
1
|
+
{"version":3,"file":"archive-zip-admission.d.ts","sourceRoot":"","sources":["../src/archive-zip-admission.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAA+C,KAAK,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAErH,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAUtF,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,4BAA4B,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GACpG,MAAM,CAUR;AAMD,oFAAoF;AACpF,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,4BAA4B,EAAE,QAAQ,EAAE,kBAAkB,EACvF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC3C,OAAO,CAAC,MAAM,CAAC,CA8DjB"}
|
|
@@ -12,10 +12,10 @@ function checkSize(size, limits) {
|
|
|
12
12
|
zipFormat("invalid archive size");
|
|
13
13
|
}
|
|
14
14
|
/** Admit physical records before a decoder can normalize or collapse names. */
|
|
15
|
-
export function admitZipBuffer(input, limits) {
|
|
15
|
+
export function admitZipBuffer(input, limits, onEntry) {
|
|
16
16
|
checkSize(input.byteLength, limits);
|
|
17
17
|
const buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
18
|
-
const scan = scanZipDirectory(buffer.length, limits);
|
|
18
|
+
const scan = scanZipDirectory(buffer.length, limits, onEntry);
|
|
19
19
|
let step = scan.next();
|
|
20
20
|
while (!step.done) {
|
|
21
21
|
const { offset, length } = step.value;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ResolvedArchiveExtractLimits } from "./archive-limits.js";
|
|
2
|
+
import type { ArchiveEntryKind } from "./archive-policy.js";
|
|
2
3
|
export type ZipRead = {
|
|
3
4
|
offset: number;
|
|
4
5
|
length: number;
|
|
@@ -10,6 +11,8 @@ export type ZipDirectoryEntry = {
|
|
|
10
11
|
externalAttributes: number;
|
|
11
12
|
size: number;
|
|
12
13
|
path?: string;
|
|
14
|
+
portableKey: string;
|
|
15
|
+
kind: ArchiveEntryKind;
|
|
13
16
|
};
|
|
14
17
|
export declare function scanZipDirectory(size: number, limits: ResolvedArchiveExtractLimits, onEntry?: (entry: ZipDirectoryEntry) => void): ZipScan;
|
|
15
18
|
//# sourceMappingURL=archive-zip-directory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-directory.d.ts","sourceRoot":"","sources":["../src/archive-zip-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-zip-directory.d.ts","sourceRoot":"","sources":["../src/archive-zip-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AA4IF,wBAAiB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,4BAA4B,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC/F,OAAO,CAmET"}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { assertArchiveEntryCountWithinLimit, } from "./archive-limits.js";
|
|
2
2
|
import { admitZipNames, zipExtraFields, zipFormat, zipUInt64 } from "./archive-zip-names.js";
|
|
3
|
+
function entryKind(attributes, directory) {
|
|
4
|
+
const type = (attributes >>> 16) & 0o170000;
|
|
5
|
+
// High-word symlinks remain links regardless of the creator or directory bits.
|
|
6
|
+
if (type === 0o120000)
|
|
7
|
+
return "symlink";
|
|
8
|
+
if ((attributes & 0x10) || type === 0o040000 || directory)
|
|
9
|
+
return "directory";
|
|
10
|
+
// Preserve the native decoder's rejection of unrecognized link-like types.
|
|
11
|
+
return (type & 0o120000) === 0o120000 ? "other" : "file";
|
|
12
|
+
}
|
|
3
13
|
function* read(offset, length, bound) {
|
|
4
14
|
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0 || length > bound - offset) {
|
|
5
15
|
zipFormat("record outside archive bounds");
|
|
@@ -193,7 +203,7 @@ export function* scanZipDirectory(size, limits, onEntry) {
|
|
|
193
203
|
const localExtraLength = local.readUInt16LE(28);
|
|
194
204
|
const localNames = yield* read(localAt + 30, localNameLength + localExtraLength, directory.start);
|
|
195
205
|
const localExtra = zipExtraFields(localNames.subarray(localNameLength));
|
|
196
|
-
const
|
|
206
|
+
const admittedName = admitZipNames({ central: centralName, local: localNames.subarray(0, localNameLength), flags, centralExtra, localExtra, seen });
|
|
197
207
|
const localValues = wideValues(local, localExtra, false);
|
|
198
208
|
const crc = central.readUInt32LE(16);
|
|
199
209
|
if (!(flags & 8) && (local.readUInt32LE(14) !== crc || localValues.compressed !== values.compressed || localValues.uncompressed !== values.uncompressed)) {
|
|
@@ -213,7 +223,8 @@ export function* scanZipDirectory(size, limits, onEntry) {
|
|
|
213
223
|
spans.push({ start: localAt, end: dataEnd });
|
|
214
224
|
onEntry?.({
|
|
215
225
|
index: count - 1, creatorSystem: central[5], externalAttributes: central.readUInt32LE(38),
|
|
216
|
-
size: values.uncompressed, path:
|
|
226
|
+
size: values.uncompressed, path: admittedName.path, portableKey: admittedName.portableKey,
|
|
227
|
+
kind: entryKind(central.readUInt32LE(38), admittedName.directory),
|
|
217
228
|
});
|
|
218
229
|
at = next;
|
|
219
230
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { ZipDirectoryEntry } from "./archive-zip-directory.js";
|
|
1
2
|
export type ZipArchiveWithFiles = {
|
|
2
3
|
files: Record<string, unknown>;
|
|
3
4
|
};
|
|
4
|
-
/** Internal: the caller has admitted these unchanged bytes and their
|
|
5
|
-
export declare function loadAdmittedZipArchive(buffer: Buffer | Uint8Array,
|
|
5
|
+
/** Internal: the caller has admitted these unchanged bytes and their metadata. */
|
|
6
|
+
export declare function loadAdmittedZipArchive(buffer: Buffer | Uint8Array, admitted: ZipDirectoryEntry[]): Promise<ZipArchiveWithFiles>;
|
|
6
7
|
//# sourceMappingURL=archive-zip-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-loader.d.ts","sourceRoot":"","sources":["../src/archive-zip-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"archive-zip-loader.d.ts","sourceRoot":"","sources":["../src/archive-zip-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAMF,kFAAkF;AAClF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,GAAG,UAAU,EAC3B,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA6C9B"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
2
2
|
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { isZipSymlinkEntry } from "./archive-zip-entry.js";
|
|
4
|
+
import { zipPathKey } from "./archive-zip-names.js";
|
|
5
|
+
/** Internal: the caller has admitted these unchanged bytes and their metadata. */
|
|
6
|
+
export async function loadAdmittedZipArchive(buffer, admitted) {
|
|
5
7
|
const JSZip = await importOptionalJsZip();
|
|
6
8
|
let archive;
|
|
7
9
|
try {
|
|
@@ -11,11 +13,35 @@ export async function loadAdmittedZipArchive(buffer, entryCount) {
|
|
|
11
13
|
throw new ArchiveFormatError(`invalid ZIP archive: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
|
|
12
14
|
}
|
|
13
15
|
const names = Object.keys(archive.files);
|
|
14
|
-
if (names.length !==
|
|
16
|
+
if (names.length !== admitted.length) {
|
|
15
17
|
throw new ArchiveSecurityError("entry-path", "zip archive contains duplicate or colliding entry names");
|
|
16
18
|
}
|
|
17
|
-
|
|
19
|
+
// Object key order is not central-directory order (numeric names reorder).
|
|
20
|
+
// Use the admitted portable interpretation, including legacy UTF-8 decoding.
|
|
21
|
+
const physicalByPath = new Map();
|
|
22
|
+
for (const entry of admitted) {
|
|
23
|
+
physicalByPath.set(entry.portableKey, entry);
|
|
24
|
+
}
|
|
25
|
+
for (const name of names) {
|
|
18
26
|
validateArchiveEntryPath(name);
|
|
27
|
+
const key = zipPathKey(name);
|
|
28
|
+
const physical = physicalByPath.get(key);
|
|
29
|
+
const entry = archive.files[name];
|
|
30
|
+
const entryName = entry.name;
|
|
31
|
+
let entryKey = key;
|
|
32
|
+
if (entryName !== name) {
|
|
33
|
+
validateArchiveEntryPath(entryName);
|
|
34
|
+
entryKey = zipPathKey(entryName);
|
|
35
|
+
}
|
|
36
|
+
const kind = isZipSymlinkEntry(entry) ? "symlink" : entry.dir ? "directory" : "file";
|
|
37
|
+
if (!physical || entryKey !== key || kind !== physical.kind) {
|
|
38
|
+
throw new ArchiveFormatError("ZIP decoder disagrees with admitted directory metadata");
|
|
39
|
+
}
|
|
40
|
+
physicalByPath.delete(key);
|
|
41
|
+
}
|
|
42
|
+
if (physicalByPath.size) {
|
|
43
|
+
throw new ArchiveFormatError("ZIP decoder disagrees with admitted directory metadata");
|
|
44
|
+
}
|
|
19
45
|
return archive;
|
|
20
46
|
}
|
|
21
47
|
async function importOptionalJsZip() {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { NativeArchiveEntry } from "./native-binding.js";
|
|
2
|
+
import type { ZipDirectoryEntry } from "./archive-zip-directory.js";
|
|
3
|
+
/** Associate all native entries with physical admission before exposing any. */
|
|
4
|
+
export declare function validateNativeZipManifest(manifest: NativeArchiveEntry[], admitted: ZipDirectoryEntry[]): void;
|
|
5
|
+
//# sourceMappingURL=archive-zip-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-zip-manifest.d.ts","sourceRoot":"","sources":["../src/archive-zip-manifest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,gFAAgF;AAChF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,kBAAkB,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAC5D,IAAI,CAkBN"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
2
|
+
import { stripArchivePath } from "./archive-entry.js";
|
|
3
|
+
/** Associate all native entries with physical admission before exposing any. */
|
|
4
|
+
export function validateNativeZipManifest(manifest, admitted) {
|
|
5
|
+
if (manifest.length !== admitted.length) {
|
|
6
|
+
throw new ArchiveSecurityError("entry-path", "zip decoder collapsed entry names");
|
|
7
|
+
}
|
|
8
|
+
for (const [ordinal, entry] of manifest.entries()) {
|
|
9
|
+
const physical = admitted[ordinal];
|
|
10
|
+
const physicalPath = physical?.path;
|
|
11
|
+
const pathMatches = physicalPath === undefined || entry.path === physicalPath ||
|
|
12
|
+
stripArchivePath(entry.path, 0) === stripArchivePath(physicalPath, 0);
|
|
13
|
+
// Native indices are physical central-directory ordinals. Legacy name
|
|
14
|
+
// decoding remains native-selected; compare names when admission knows them.
|
|
15
|
+
if (!physical || physical.index !== ordinal || entry.index !== ordinal ||
|
|
16
|
+
entry.size !== physical.size || entry.kind !== physical.kind ||
|
|
17
|
+
!pathMatches ||
|
|
18
|
+
(physical.creatorSystem === 3 && entry.mode !== physical.externalAttributes >>> 16)) {
|
|
19
|
+
throw new ArchiveFormatError("ZIP decoder disagrees with admitted directory metadata");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare function zipFormat(message: string): never;
|
|
2
2
|
export declare function zipUInt64(bytes: Buffer, offset: number): number;
|
|
3
3
|
export declare function zipExtraFields(bytes: Buffer): Map<number, Buffer>;
|
|
4
|
+
export declare function zipPathKey(name: string): string;
|
|
4
5
|
export declare function admitZipNames(params: {
|
|
5
6
|
central: Buffer;
|
|
6
7
|
local: Buffer;
|
|
@@ -8,5 +9,9 @@ export declare function admitZipNames(params: {
|
|
|
8
9
|
centralExtra: Map<number, Buffer>;
|
|
9
10
|
localExtra: Map<number, Buffer>;
|
|
10
11
|
seen: Set<string>;
|
|
11
|
-
}):
|
|
12
|
+
}): {
|
|
13
|
+
path?: string;
|
|
14
|
+
portableKey: string;
|
|
15
|
+
directory: boolean;
|
|
16
|
+
};
|
|
12
17
|
//# sourceMappingURL=archive-zip-names.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-names.d.ts","sourceRoot":"","sources":["../src/archive-zip-names.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAEhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjE;
|
|
1
|
+
{"version":3,"file":"archive-zip-names.d.ts","sourceRoot":"","sources":["../src/archive-zip-names.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAEhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjE;AAUD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAoBD,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAiE7D"}
|
|
@@ -41,7 +41,10 @@ function utf8(bytes) {
|
|
|
41
41
|
return zipFormat("invalid UTF-8 name");
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
// Canonical identity only; this does not replace raw path validation.
|
|
45
|
+
export function zipPathKey(name) {
|
|
46
|
+
if (name && name !== "." && !name.includes("/") && !name.includes("\\"))
|
|
47
|
+
return name;
|
|
45
48
|
return stripArchivePath(name, 0) ?? "";
|
|
46
49
|
}
|
|
47
50
|
function originalName(name, rawName, flags) {
|
|
@@ -87,31 +90,49 @@ export function admitZipNames(params) {
|
|
|
87
90
|
(centralField === localField ||
|
|
88
91
|
(centralField !== undefined && localField !== undefined && centralField.equals(localField)));
|
|
89
92
|
const localUnicode = sameName && sameUnicode ? centralUnicode : unicodeName(local, localExtra);
|
|
90
|
-
const centralKey =
|
|
91
|
-
if (!sameName && centralKey !==
|
|
93
|
+
const centralKey = zipPathKey(centralRaw);
|
|
94
|
+
if (!sameName && centralKey !== zipPathKey(localRaw)) {
|
|
92
95
|
zipFormat("central and local names disagree");
|
|
93
96
|
}
|
|
94
97
|
const interpretations = [centralUtf8, localUtf8, centralUnicode, localUnicode].filter((value) => value !== undefined);
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
// Canonical paths erase terminal separators. Preserve their kind meaning in
|
|
99
|
+
// every interpretation, including Unicode overrides of legacy-encoded names.
|
|
100
|
+
const directory = /[/\\]$/.test(centralRaw);
|
|
101
|
+
if (/[/\\]$/.test(localRaw) !== directory ||
|
|
102
|
+
interpretations.some((value) => /[/\\]$/.test(value) !== directory)) {
|
|
103
|
+
zipFormat("conflicting terminal directory markers");
|
|
104
|
+
}
|
|
105
|
+
const interpretationKey = interpretations.length ? zipPathKey(interpretations[0]) : undefined;
|
|
106
|
+
if (interpretations.some((value) => value !== interpretations[0] && zipPathKey(value) !== interpretationKey)) {
|
|
97
107
|
zipFormat("conflicting Unicode name interpretations");
|
|
98
108
|
}
|
|
99
109
|
// JSZip checks the central Unicode field against the local name. A slash-only
|
|
100
110
|
// spelling difference must not make one decoder ignore a meaningful override.
|
|
101
111
|
if (centralUnicode && !central.equals(local) &&
|
|
102
|
-
|
|
112
|
+
zipPathKey(centralUnicode) !== zipPathKey(local.toString("utf8"))) {
|
|
103
113
|
zipFormat("Unicode override disagrees with local decoder name");
|
|
104
114
|
}
|
|
105
|
-
if (!centralUnicode && localUnicode &&
|
|
115
|
+
if (!centralUnicode && localUnicode && zipPathKey(localUnicode) !== zipPathKey(local.toString("utf8"))) {
|
|
106
116
|
zipFormat("local-only Unicode override changes the name");
|
|
107
117
|
}
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
118
|
+
const unicodeKey = centralUnicode === undefined
|
|
119
|
+
? undefined : zipPathKey(Buffer.from(centralUnicode).toString("latin1"));
|
|
120
|
+
if (seen.has(centralKey) ||
|
|
121
|
+
(unicodeKey !== undefined && unicodeKey !== centralKey && seen.has(unicodeKey))) {
|
|
112
122
|
throw new ArchiveSecurityError("entry-path", "zip archive contains duplicate or colliding entry names");
|
|
113
123
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
seen.add(centralKey);
|
|
125
|
+
if (unicodeKey !== undefined && unicodeKey !== centralKey)
|
|
126
|
+
seen.add(unicodeKey);
|
|
127
|
+
const path = centralUnicode ?? centralUtf8 ?? (central.every((byte) => byte < 128) ? central.toString("ascii") : undefined);
|
|
128
|
+
const portablePath = centralUnicode ?? localUtf8 ?? (sameName && path !== undefined ? path : local.toString("utf8"));
|
|
129
|
+
// Reuse the admitted interpretation, never the Latin-1 Unicode collision key.
|
|
130
|
+
// Unflagged legacy names can decode differently and need their own key.
|
|
131
|
+
const portableKey = portablePath === centralRaw ? centralKey
|
|
132
|
+
: interpretations.includes(portablePath) ? interpretationKey : zipPathKey(portablePath);
|
|
133
|
+
return {
|
|
134
|
+
path,
|
|
135
|
+
portableKey,
|
|
136
|
+
directory,
|
|
137
|
+
};
|
|
117
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-preflight.d.ts","sourceRoot":"","sources":["../src/archive-zip-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-zip-preflight.d.ts","sourceRoot":"","sources":["../src/archive-zip-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE3F,OAAO,EAAE,iCAAiC,EAAE,MAAM,wBAAwB,CAAC;AAC3E,YAAY,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,GAAG,UAAU,EAC3B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAQ9B"}
|
|
@@ -7,6 +7,7 @@ export async function loadZipArchiveWithPreflight(buffer, limits) {
|
|
|
7
7
|
if (buffer.byteLength > resolvedLimits.maxArchiveBytes) {
|
|
8
8
|
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ARCHIVE_SIZE_EXCEEDS_LIMIT);
|
|
9
9
|
}
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const entries = [];
|
|
11
|
+
admitZipBuffer(buffer, resolvedLimits, entry => { entries.push(entry); });
|
|
12
|
+
return await loadAdmittedZipArchive(buffer, entries);
|
|
12
13
|
}
|
package/dist/archive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAiNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDjF"}
|
package/dist/archive.js
CHANGED
|
@@ -22,6 +22,7 @@ import { stageArchiveFileForExtraction } from "./archive-input.js";
|
|
|
22
22
|
import { getNativeBinding } from "./native.js";
|
|
23
23
|
import { resolveArchiveFilteredEntryPolicy, shouldExtractArchiveEntry, } from "./archive-policy.js";
|
|
24
24
|
import { writeSiblingTempFile } from "./sibling-temp.js";
|
|
25
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
25
26
|
import { realpathSync } from "./realpath.js";
|
|
26
27
|
export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
|
|
27
28
|
export { resolveArchiveKind, resolvePackedRootDir } from "./archive-kind.js";
|
|
@@ -51,7 +52,7 @@ function resolveZipOutputPath(params) {
|
|
|
51
52
|
const canonicalPath = stripArchivePath(params.entryPath, 0);
|
|
52
53
|
if (!canonicalPath)
|
|
53
54
|
return null;
|
|
54
|
-
const relPath = stripArchivePath(canonicalPath, params.strip);
|
|
55
|
+
const relPath = params.strip === 0 ? canonicalPath : stripArchivePath(canonicalPath, params.strip);
|
|
55
56
|
if (!relPath) {
|
|
56
57
|
return null;
|
|
57
58
|
}
|
|
@@ -203,19 +204,23 @@ async function extractZip(params) {
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
export async function extractArchive(params) {
|
|
207
|
+
const archivePath = params.archivePath;
|
|
208
|
+
const destDir = params.destDir;
|
|
206
209
|
const onFiltered = resolveArchiveFilteredEntryPolicy(params.onFiltered);
|
|
207
|
-
const kind = params.kind ?? resolveArchiveKind(
|
|
210
|
+
const kind = params.kind ?? resolveArchiveKind(archivePath);
|
|
208
211
|
if (!kind) {
|
|
209
|
-
throw new Error(`unsupported archive: ${
|
|
212
|
+
throw new Error(`unsupported archive: ${archivePath}`);
|
|
210
213
|
}
|
|
211
214
|
const label = kind === "zip" ? "extract zip" : "extract tar";
|
|
212
215
|
const limits = resolveExtractLimits(params.limits);
|
|
213
216
|
const tarLimits = resolveTarMeterLimits(limits);
|
|
214
217
|
const native = getNativeBinding();
|
|
218
|
+
assertNoWindowsPathAlias(archivePath, "filesystem", "archive source uses a Windows filesystem namespace alias");
|
|
219
|
+
assertNoWindowsPathAlias(destDir, "filesystem", "archive destination uses a Windows filesystem namespace alias");
|
|
215
220
|
// Read the declared public fields before private adapters copy options: class
|
|
216
221
|
// getters and inherited policy must survive identically on every backend.
|
|
217
222
|
const options = {
|
|
218
|
-
archivePath
|
|
223
|
+
archivePath, destDir,
|
|
219
224
|
durable: params.durable,
|
|
220
225
|
stripComponents: params.stripComponents, limits,
|
|
221
226
|
entryModes: params.entryModes, entryFilter: params.entryFilter, onFiltered,
|
|
@@ -2,7 +2,7 @@ import { type BigIntStats } from "node:fs";
|
|
|
2
2
|
import type { FileHandle } from "node:fs/promises";
|
|
3
3
|
import { type CopyCloneMode } from "./copy-policy.js";
|
|
4
4
|
import { type NativeBinding } from "./native.js";
|
|
5
|
-
import type
|
|
5
|
+
import { type NativeFileCopyResult } from "./native-binding.js";
|
|
6
6
|
export type CopyFileInput = {
|
|
7
7
|
kind: "file";
|
|
8
8
|
handle: FileHandle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-file-input.d.ts","sourceRoot":"","sources":["../src/copy-file-input.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"copy-file-input.d.ts","sourceRoot":"","sources":["../src/copy-file-input.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAwB,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAItF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,aAAa,CAM5E;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAChD,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,aAAa,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,oBAAoB,CAAC,EAAE,MAAM,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CA+B3C;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAWnG"}
|
package/dist/copy-file-input.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs, {} from "node:fs";
|
|
|
2
2
|
import { resolveCopyCloneMode } from "./copy-policy.js";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
4
|
import { getNativeBinding } from "./native.js";
|
|
5
|
+
import { captureNativeFdClose } from "./native-binding.js";
|
|
5
6
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
6
7
|
import { transferFileHandle } from "./file-handle-transfer.js";
|
|
7
8
|
export function resolveFileCopyCloneMode(mode) {
|
|
@@ -34,6 +35,7 @@ export async function createNativeCopyFile(native, input, parentFd, basename, ma
|
|
|
34
35
|
}
|
|
35
36
|
return undefined;
|
|
36
37
|
}
|
|
38
|
+
captureNativeFdClose(native);
|
|
37
39
|
const nativeSignal = input.signal ? AbortSignal.any([input.signal]) : undefined;
|
|
38
40
|
try {
|
|
39
41
|
// The caller adopts this descriptor before observing a later cancellation.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NativeDarwinAclFacts } from "./native-binding.js";
|
|
2
|
+
/** Internal descriptor facts only; callers own identity, lifetime, and ACL policy. */
|
|
3
|
+
export declare function inspectDarwinAcl(fd: number): NativeDarwinAclFacts;
|
|
4
|
+
//# sourceMappingURL=darwin-acl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"darwin-acl.d.ts","sourceRoot":"","sources":["../src/darwin-acl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,CAmBjE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
2
|
+
import { getNativeBinding } from "./native.js";
|
|
3
|
+
/** Internal descriptor facts only; callers own identity, lifetime, and ACL policy. */
|
|
4
|
+
export function inspectDarwinAcl(fd) {
|
|
5
|
+
if (!Number.isInteger(fd) || fd < 0 || fd > 0x7fff_ffff) {
|
|
6
|
+
throw new FsSafeError("permission-unverified", "Darwin ACL inspection requires a valid descriptor");
|
|
7
|
+
}
|
|
8
|
+
const native = getNativeBinding();
|
|
9
|
+
if (typeof native?.inspectDarwinAcl !== "function") {
|
|
10
|
+
throw new FsSafeError("helper-unavailable", "Darwin ACL inspection requires the matching native capability");
|
|
11
|
+
}
|
|
12
|
+
let facts;
|
|
13
|
+
try {
|
|
14
|
+
facts = native.inspectDarwinAcl(fd);
|
|
15
|
+
}
|
|
16
|
+
catch (cause) {
|
|
17
|
+
throw new FsSafeError("permission-unverified", "Darwin descriptor ACL could not be inspected", { cause });
|
|
18
|
+
}
|
|
19
|
+
const state = facts && typeof facts === "object" && "state" in facts ? facts.state : undefined;
|
|
20
|
+
if (state !== "absent" && state !== "empty" && state !== "present") {
|
|
21
|
+
throw new FsSafeError("permission-unverified", "Darwin ACL inspection returned invalid facts");
|
|
22
|
+
}
|
|
23
|
+
return { state };
|
|
24
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deny-mutations.d.ts","sourceRoot":"","sources":["../src/deny-mutations.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deny-mutations.d.ts","sourceRoot":"","sources":["../src/deny-mutations.ts"],"names":[],"mappings":"AASA,wBAAsB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAQ1F;AAMD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAsBF,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,kBAAkB,GAAG,SAAS,EAC7C,UAAU,EAAE,kBAAkB,GAAG,SAAS,GACzC,kBAAkB,GAAG,SAAS,CAWhC"}
|