@openclaw/fs-safe 0.15.0 → 0.17.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 +86 -0
- package/README.md +36 -7
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +2 -8
- package/dist/advanced.d.ts +3 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +3 -0
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +22 -23
- package/dist/archive-kind.d.ts +0 -1
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +5 -17
- package/dist/archive-merge.d.ts +1 -0
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +4 -4
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +1 -0
- package/dist/archive-options.d.ts +2 -0
- package/dist/archive-options.d.ts.map +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +6 -7
- package/dist/archive-tar-stream.d.ts +3 -0
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +56 -37
- package/dist/archive-tar-wasm.d.ts +16 -4
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +134 -34
- package/dist/archive-zip-count.d.ts.map +1 -1
- package/dist/archive-zip-count.js +21 -1
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +23 -1
- package/dist/archive-zip-loader.d.ts +2 -0
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +7 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +7 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +14 -9
- package/dist/byte-view.d.ts +3 -0
- package/dist/byte-view.d.ts.map +1 -0
- package/dist/byte-view.js +13 -0
- package/dist/clone-metadata.d.ts +1 -0
- package/dist/clone-metadata.d.ts.map +1 -1
- package/dist/clone-metadata.js +6 -2
- package/dist/create-directory.d.ts +20 -0
- package/dist/create-directory.d.ts.map +1 -0
- package/dist/create-directory.js +130 -0
- package/dist/create-file-async.d.ts +7 -0
- package/dist/create-file-async.d.ts.map +1 -0
- package/dist/create-file-async.js +121 -0
- package/dist/create-file.d.ts +8 -0
- package/dist/create-file.d.ts.map +1 -0
- package/dist/create-file.js +190 -0
- package/dist/create-owned-file.d.ts +8 -0
- package/dist/create-owned-file.d.ts.map +1 -0
- package/dist/create-owned-file.js +16 -0
- package/dist/create.d.ts +4 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +2 -0
- package/dist/creation-darwin.d.ts +6 -0
- package/dist/creation-darwin.d.ts.map +1 -0
- package/dist/creation-darwin.js +70 -0
- package/dist/creation-file-state.d.ts +19 -0
- package/dist/creation-file-state.d.ts.map +1 -0
- package/dist/creation-file-state.js +118 -0
- package/dist/creation-path.d.ts +21 -0
- package/dist/creation-path.d.ts.map +1 -0
- package/dist/creation-path.js +71 -0
- package/dist/creation-permissions.d.ts +19 -0
- package/dist/creation-permissions.d.ts.map +1 -0
- package/dist/creation-permissions.js +125 -0
- package/dist/directory-durability.d.ts +7 -7
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +22 -80
- package/dist/directory-guard.d.ts +3 -0
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-mode-node.d.ts +2 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +8 -0
- package/dist/directory-receipt.d.ts +24 -0
- package/dist/directory-receipt.d.ts.map +1 -0
- package/dist/directory-receipt.js +123 -0
- package/dist/file-cleanup.d.ts +20 -0
- package/dist/file-cleanup.d.ts.map +1 -0
- package/dist/file-cleanup.js +81 -0
- package/dist/file-contents.d.ts +6 -0
- package/dist/file-contents.d.ts.map +1 -0
- package/dist/file-contents.js +40 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +16 -4
- package/dist/file-lock-sync-root-acquire.d.ts.map +1 -1
- package/dist/file-lock-sync-root-acquire.js +3 -0
- package/dist/file-lock-sync-root-held.d.ts +1 -2
- package/dist/file-lock-sync-root-held.d.ts.map +1 -1
- package/dist/file-lock-sync-root-held.js +7 -5
- package/dist/file-lock-sync-stale-admission.d.ts.map +1 -1
- package/dist/file-lock-sync-stale-admission.js +3 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +8 -11
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +2 -6
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +3 -9
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +2 -5
- package/dist/file-store.js +3 -3
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +27 -19
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +2 -5
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +2 -6
- package/dist/json-durable-queue-paths.d.ts.map +1 -1
- package/dist/json-durable-queue-paths.js +2 -24
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +10 -9
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +32 -75
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +19 -21
- package/dist/move-path-cleanup.d.ts +5 -19
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +57 -21
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +63 -40
- package/dist/native-binding.d.ts +11 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-fallback-warning.d.ts +4 -0
- package/dist/native-fallback-warning.d.ts.map +1 -0
- package/dist/native-fallback-warning.js +11 -0
- package/dist/native-operations.d.ts +0 -2
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +0 -24
- package/dist/native-parent-admission.d.ts +2 -0
- package/dist/native-parent-admission.d.ts.map +1 -1
- package/dist/native-parent-admission.js +3 -2
- package/dist/native-pinned-write-windows.d.ts +1 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +173 -28
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +19 -3
- package/dist/native-policy-parent-windows.d.ts.map +1 -1
- package/dist/native-policy-parent-windows.js +15 -6
- package/dist/native-staged-file.d.ts +5 -3
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +90 -40
- package/dist/native.js +2 -2
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +11 -2
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +10 -4
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +2 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +3 -17
- package/dist/pinned-write-input.d.ts +4 -0
- package/dist/pinned-write-input.d.ts.map +1 -0
- package/dist/pinned-write-input.js +35 -0
- package/dist/pinned-write-mode.d.ts +5 -0
- package/dist/pinned-write-mode.d.ts.map +1 -0
- package/dist/pinned-write-mode.js +31 -0
- package/dist/pinned-write-staged.d.ts +6 -0
- package/dist/pinned-write-staged.d.ts.map +1 -0
- package/dist/pinned-write-staged.js +186 -0
- package/dist/pinned-write-types.d.ts +3 -0
- package/dist/pinned-write-types.d.ts.map +1 -1
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +41 -147
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +18 -4
- package/dist/private-producer-handoff-sync.d.ts +14 -0
- package/dist/private-producer-handoff-sync.d.ts.map +1 -0
- package/dist/private-producer-handoff-sync.js +114 -0
- package/dist/private-producer-handoff.d.ts +22 -4
- package/dist/private-producer-handoff.d.ts.map +1 -1
- package/dist/private-producer-handoff.js +140 -77
- package/dist/publish-copy-stage.d.ts +2 -1
- package/dist/publish-copy-stage.d.ts.map +1 -1
- package/dist/publish-copy-stage.js +16 -7
- package/dist/publish-file.d.ts +2 -2
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +58 -98
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +35 -44
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +28 -26
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +13 -22
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +10 -16
- package/dist/replace-file-temp-owner.d.ts +0 -7
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +9 -60
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +9 -13
- package/dist/root-create-input.d.ts +2 -1
- package/dist/root-create-input.d.ts.map +1 -1
- package/dist/root-create-input.js +13 -4
- package/dist/root-directory-creation.d.ts +3 -3
- package/dist/root-directory-creation.d.ts.map +1 -1
- package/dist/root-directory-creation.js +15 -3
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +20 -3
- package/dist/root-file-final-admission.d.ts +1 -1
- package/dist/root-file-final-admission.d.ts.map +1 -1
- package/dist/root-file-final-admission.js +5 -2
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +3 -2
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +78 -27
- package/dist/root-move-noreplace.d.ts +2 -0
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +22 -13
- package/dist/root-options.d.ts +12 -4
- package/dist/root-options.d.ts.map +1 -1
- package/dist/root-path-stat.d.ts.map +1 -1
- package/dist/root-path-stat.js +59 -7
- package/dist/root-read-admission.d.ts.map +1 -1
- package/dist/root-read-admission.js +7 -2
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +15 -1
- package/dist/root-write-publication.js +1 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +1 -0
- package/dist/secure-file-windows.d.ts +6 -0
- package/dist/secure-file-windows.d.ts.map +1 -1
- package/dist/secure-file-windows.js +34 -117
- package/dist/secure-file.js +2 -2
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +4 -6
- package/dist/sidecar-lock-handle.d.ts +3 -0
- package/dist/sidecar-lock-handle.d.ts.map +1 -1
- package/dist/sidecar-lock-handle.js +6 -0
- package/dist/sidecar-lock-reclaim.d.ts +1 -1
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +11 -8
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +2 -1
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +3 -5
- package/dist/staged-directory.d.ts +2 -2
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +6 -6
- package/dist/staged-file-settlement.d.ts +17 -0
- package/dist/staged-file-settlement.d.ts.map +1 -0
- package/dist/staged-file-settlement.js +57 -0
- package/dist/strict-file-identity.d.ts +1 -1
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/strict-file-identity.js +9 -9
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +2 -27
- package/dist/temp-workspace-owner.js +4 -4
- package/dist/unicode-path.d.ts.map +1 -1
- package/dist/unicode-path.js +3 -0
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +4 -2
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +2 -1
- package/dist/windows-security-bridge.cs +336 -0
- package/dist/windows-security-bridge.ps1 +15 -0
- package/dist/windows-security-command.d.ts +26 -0
- package/dist/windows-security-command.d.ts.map +1 -0
- package/dist/windows-security-command.js +363 -0
- package/dist/windows-security-facts.d.ts +6 -0
- package/dist/windows-security-facts.d.ts.map +1 -0
- package/dist/windows-security-facts.js +108 -0
- package/dist/write-file-handle.d.ts +7 -0
- package/dist/write-file-handle.d.ts.map +1 -1
- package/dist/write-file-handle.js +23 -0
- package/dist/write-open-flags.d.ts.map +1 -1
- package/dist/write-open-flags.js +1 -8
- package/dist/write-queue.d.ts.map +1 -1
- package/dist/write-queue.js +1 -4
- package/docs/advanced.md +71 -2
- package/docs/archive.md +102 -39
- package/docs/atomic.md +29 -5
- package/docs/config.md +6 -2
- package/docs/contributing.md +48 -4
- package/docs/copy.md +2 -0
- package/docs/creation.md +132 -0
- package/docs/durability.md +59 -0
- package/docs/file-contents.md +68 -0
- package/docs/install.md +31 -7
- package/docs/json.md +5 -4
- package/docs/local-roots.md +2 -0
- package/docs/migrating-to-0.5.md +15 -6
- package/docs/migrating-to-0.6.md +9 -4
- package/docs/mutation-policy-proof.md +5 -3
- package/docs/native-helper.md +22 -9
- package/docs/native.md +47 -15
- package/docs/path.md +4 -4
- package/docs/permissions.md +37 -14
- package/docs/public-api.md +5 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +2 -2
- package/docs/regular-file.md +3 -0
- package/docs/root.md +43 -0
- package/docs/secret-file.md +11 -2
- package/docs/secure-file.md +9 -4
- package/docs/sidecar-lock.md +14 -5
- package/docs/staged-file.md +9 -3
- package/docs/store.md +3 -1
- package/docs/temp.md +4 -1
- package/docs/types.md +18 -2
- package/docs/walk.md +7 -0
- package/docs/writing.md +80 -7
- package/package.json +18 -15
|
@@ -50,19 +50,22 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
50
50
|
var e = new Error(message);
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
|
+
import { assertDarwinCreationAcl } from "./creation-darwin.js";
|
|
54
|
+
import { assertPrivateCreationFile } from "./creation-file-state.js";
|
|
53
55
|
import { requireNativeBinding } from "./native.js";
|
|
54
56
|
import { syncFileBestEffortSync } from "./file-sync.js";
|
|
55
57
|
import { randomUUID } from "node:crypto";
|
|
56
|
-
import fs from "node:fs";
|
|
58
|
+
import fs, {} from "node:fs";
|
|
57
59
|
import path from "node:path";
|
|
58
60
|
import { FsSafeError } from "./errors.js";
|
|
59
|
-
import { MutationAuthorityError } from "./mutation-authority.js";
|
|
61
|
+
import { assertSynchronousCallbackResult, MutationAuthorityError } from "./mutation-authority.js";
|
|
60
62
|
import { captureNativeFdClose } from "./native-binding.js";
|
|
61
|
-
import {
|
|
63
|
+
import { writePinnedInput } from "./pinned-write-input.js";
|
|
62
64
|
import { assertNativeCopyCompleted, createNativeCopyFile } from "./copy-file-input.js";
|
|
63
65
|
import { assertStagedDirectoryCurrent, openStagedDirectory } from "./staged-directory.js";
|
|
64
66
|
import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
|
|
65
67
|
import { classifyNativeRenameFailure } from "./native-rename-outcome.js";
|
|
68
|
+
import { createStagedFileReceipt, stagedFileFailure as failure } from "./staged-file-settlement.js";
|
|
66
69
|
export function assertNativeStaging(binding) {
|
|
67
70
|
if ([
|
|
68
71
|
binding.closeOwnedFd,
|
|
@@ -82,12 +85,6 @@ function assertBasename(name, portable) {
|
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
const NOT_PUBLISHED = Object.freeze({ status: "not-published" });
|
|
85
|
-
function failure(error, details) {
|
|
86
|
-
const code = error instanceof FsSafeError
|
|
87
|
-
? error.code
|
|
88
|
-
: error?.code === "EEXIST" ? "already-exists" : "helper-failed";
|
|
89
|
-
return new FsSafeError(code, `staged file ${details.phase} failed`, { cause: error, details });
|
|
90
|
-
}
|
|
91
88
|
class NativeStagedFile {
|
|
92
89
|
#binding;
|
|
93
90
|
#closeFd;
|
|
@@ -97,12 +94,15 @@ class NativeStagedFile {
|
|
|
97
94
|
#portableNames;
|
|
98
95
|
#publishedMode;
|
|
99
96
|
#sync;
|
|
97
|
+
#strictFileSync;
|
|
98
|
+
#private;
|
|
99
|
+
#verifyMode;
|
|
100
100
|
#assertBeforeMutation;
|
|
101
101
|
#name;
|
|
102
102
|
#state = { status: "open", publication: NOT_PUBLISHED };
|
|
103
103
|
#receipt;
|
|
104
104
|
#rejectFinalSymlink = false;
|
|
105
|
-
constructor(binding, parentFd, closeParentFd, directory, portableNames, publishedMode, sync, assertBeforeMutation, name = `.fs-safe-${randomUUID()}.tmp
|
|
105
|
+
constructor(binding, parentFd, closeParentFd, directory, portableNames, publishedMode, sync, assertBeforeMutation, name = `.fs-safe-${randomUUID()}.tmp`, strictFileSync = false, permissionPolicy) {
|
|
106
106
|
assertBasename(name, portableNames);
|
|
107
107
|
this.#name = name;
|
|
108
108
|
this.#closeFd = captureNativeFdClose(binding);
|
|
@@ -113,16 +113,19 @@ class NativeStagedFile {
|
|
|
113
113
|
this.#portableNames = portableNames;
|
|
114
114
|
this.#publishedMode = publishedMode;
|
|
115
115
|
this.#sync = sync;
|
|
116
|
+
this.#strictFileSync = strictFileSync;
|
|
117
|
+
this.#private = permissionPolicy === "private-creation";
|
|
118
|
+
this.#verifyMode = permissionPolicy !== undefined;
|
|
116
119
|
this.#assertBeforeMutation = assertBeforeMutation;
|
|
117
120
|
}
|
|
118
121
|
// Public staging supplies a Node parent; pinned writes supply a native parent.
|
|
119
122
|
// The supplied closer owns parentFd even on construction or preparation failure.
|
|
120
123
|
static async create(binding, parentFd, closeParentFd, directory, input, mode, maxBytes,
|
|
121
124
|
// Public staging uses portable names; existing POSIX writes accept literal names.
|
|
122
|
-
portableNames = true, sync = true, assertBeforeMutation, exclusiveBasename) {
|
|
125
|
+
portableNames = true, sync = true, assertBeforeMutation, exclusiveBasename, strictFileSync = false, permissionPolicy) {
|
|
123
126
|
let staged;
|
|
124
127
|
try {
|
|
125
|
-
staged = new NativeStagedFile(binding, parentFd, closeParentFd, directory, portableNames, mode, sync, assertBeforeMutation, exclusiveBasename);
|
|
128
|
+
staged = new NativeStagedFile(binding, parentFd, closeParentFd, directory, portableNames, mode, sync, assertBeforeMutation, exclusiveBasename, strictFileSync, permissionPolicy);
|
|
126
129
|
}
|
|
127
130
|
catch (error) {
|
|
128
131
|
try {
|
|
@@ -142,19 +145,29 @@ class NativeStagedFile {
|
|
|
142
145
|
const exclusive = params.overwrite === false && params.input.kind === "buffer" && params.input.stageBeforePublish === false;
|
|
143
146
|
// This owner never escapes. Only the internal verifier borrows its fd;
|
|
144
147
|
// public descriptor methods remain await-free and cannot race disposal.
|
|
145
|
-
const staged = __addDisposableResource(env_1, await NativeStagedFile.create(binding, parentFd, closeParentFd, directory, params.input, params.mode, params.maxBytes, false, params.sync, params.assertBeforeMutation, exclusive ? params.basename : undefined), true);
|
|
148
|
+
const staged = __addDisposableResource(env_1, await NativeStagedFile.create(binding, parentFd, closeParentFd, directory, params.input, params.mode, params.maxBytes, false, params.sync, params.assertBeforeMutation, exclusive ? params.basename : undefined, params.strictFileSync, params.private ? "private-creation" : params.verifyPosixMode === true ? "mode-only" : undefined), true);
|
|
146
149
|
staged.#rejectFinalSymlink = params.rejectFinalSymlink === true;
|
|
147
150
|
if (params.input.kind === "file")
|
|
148
151
|
await params.input.verifySource();
|
|
149
152
|
if (exclusive) {
|
|
150
153
|
staged.#assertCurrent();
|
|
151
154
|
params.assertBeforeMutation?.();
|
|
155
|
+
if (staged.#verifyMode)
|
|
156
|
+
staged.#assertCurrent();
|
|
152
157
|
}
|
|
153
158
|
const published = exclusive
|
|
154
159
|
? staged.#completePublication(params.basename, false, params.onPublished)
|
|
155
160
|
: await staged.publish(params.basename, { overwrite: params.overwrite !== false }, params.onPublished);
|
|
156
161
|
const identity = published.staged.identity;
|
|
157
|
-
|
|
162
|
+
try {
|
|
163
|
+
await params.verifyPublished?.(staged.#file(), identity, parentGuard);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (params.overwrite === false && params.input.kind !== "file" && params.input.stageBeforePublish === true) {
|
|
167
|
+
throw failure(error, { phase: "publish", publication: published });
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
158
171
|
return { dev: identity.dev, ino: identity.ino };
|
|
159
172
|
}
|
|
160
173
|
catch (e_1) {
|
|
@@ -186,13 +199,14 @@ class NativeStagedFile {
|
|
|
186
199
|
}
|
|
187
200
|
return state.fileFd;
|
|
188
201
|
}
|
|
189
|
-
#assertNamed(name) {
|
|
202
|
+
#assertNamed(name, expectedMode) {
|
|
190
203
|
const fd = this.#file();
|
|
191
204
|
const stat = this.#binding.stagedFileMatches(this.#parentFd, name, fd)
|
|
192
205
|
? fs.fstatSync(fd, { bigint: true }) : undefined;
|
|
193
206
|
if (!stat || stat.nlink !== 1n) {
|
|
194
207
|
throw new FsSafeError("path-mismatch", "staged entry no longer names the exclusive created file");
|
|
195
208
|
}
|
|
209
|
+
this.#assertPermissions(fd, stat, expectedMode);
|
|
196
210
|
return Number(stat.mode & 4095n);
|
|
197
211
|
}
|
|
198
212
|
#assertCurrent() {
|
|
@@ -200,7 +214,17 @@ class NativeStagedFile {
|
|
|
200
214
|
throw new FsSafeError("helper-failed", "staged file publication has already been attempted");
|
|
201
215
|
}
|
|
202
216
|
assertStagedDirectoryCurrent(this.#directory);
|
|
203
|
-
this.#assertNamed(this.#name);
|
|
217
|
+
this.#assertNamed(this.#name, 0o600);
|
|
218
|
+
}
|
|
219
|
+
#assertPermissions(fd, stat, expectedMode) {
|
|
220
|
+
if (this.#private)
|
|
221
|
+
assertPrivateCreationFile(stat, fd);
|
|
222
|
+
if (this.#verifyMode && Number(stat.mode & 4095n) !== expectedMode) {
|
|
223
|
+
throw new FsSafeError("insecure-permissions", "filesystem did not enforce the staged file mode");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
#assertStagePermissions(fd) {
|
|
227
|
+
this.#assertPermissions(fd, fs.fstatSync(fd, { bigint: true }), 0o600);
|
|
204
228
|
}
|
|
205
229
|
async #prepare(input, maxBytes) {
|
|
206
230
|
try {
|
|
@@ -208,44 +232,63 @@ class NativeStagedFile {
|
|
|
208
232
|
if (!Number.isInteger(mode) || mode < 0 || mode > 0o7777) {
|
|
209
233
|
throw new FsSafeError("invalid-path", "invalid staged file mode");
|
|
210
234
|
}
|
|
235
|
+
const fileSignal = input.kind === "file" && this.#verifyMode ? input.signal : undefined;
|
|
236
|
+
if (input.kind === "file" && this.#verifyMode) {
|
|
237
|
+
fileSignal?.throwIfAborted();
|
|
238
|
+
if (input.clone === "always") {
|
|
239
|
+
throw new FsSafeError("helper-unavailable", "required cloning cannot verify staged permissions before copying");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
211
242
|
assertStagedDirectoryCurrent(this.#directory);
|
|
212
243
|
// The exclusive open performs no fallible post-open checks. Store its fd
|
|
213
244
|
// before every subsequent operation, including the first metadata read.
|
|
214
245
|
const state = this.#open();
|
|
246
|
+
if (this.#private)
|
|
247
|
+
assertDarwinCreationAcl(this.#parentFd, "file");
|
|
215
248
|
this.#assertBeforeMutation?.();
|
|
216
|
-
|
|
249
|
+
if (this.#private)
|
|
250
|
+
assertDarwinCreationAcl(this.#parentFd, "file");
|
|
251
|
+
// Native copying populates its new file before returning its descriptor.
|
|
252
|
+
const copied = input.kind === "file" && !this.#verifyMode
|
|
217
253
|
? await createNativeCopyFile(this.#binding, input, this.#parentFd, this.#name, maxBytes, false)
|
|
218
254
|
: undefined;
|
|
219
255
|
state.fileFd = copied?.fd;
|
|
220
256
|
if (state.fileFd === undefined) {
|
|
221
257
|
this.#assertBeforeMutation?.();
|
|
258
|
+
if (this.#private)
|
|
259
|
+
assertDarwinCreationAcl(this.#parentFd, "file");
|
|
222
260
|
state.fileFd = this.#binding.createStagedFile(this.#parentFd, this.#name);
|
|
223
261
|
}
|
|
224
262
|
const fd = state.fileFd;
|
|
225
263
|
if (input.kind === "file")
|
|
226
264
|
assertNativeCopyCompleted(input, copied);
|
|
227
|
-
this.#assertBeforeMutation
|
|
265
|
+
const assertBeforeChmod = this.#assertBeforeMutation;
|
|
266
|
+
const beforeChmodResult = assertBeforeChmod?.();
|
|
267
|
+
if (this.#private) {
|
|
268
|
+
assertSynchronousCallbackResult(beforeChmodResult, "assertBeforeMutation");
|
|
269
|
+
assertPrivateCreationFile(fs.fstatSync(fd, { bigint: true }), fd);
|
|
270
|
+
}
|
|
228
271
|
fs.fchmodSync(fd, 0o600);
|
|
272
|
+
// Some filesystems report successful chmod without enforcing its mode.
|
|
273
|
+
if (this.#verifyMode)
|
|
274
|
+
this.#assertStagePermissions(fd);
|
|
275
|
+
const assertBeforeMutation = this.#verifyMode ? () => {
|
|
276
|
+
assertSynchronousCallbackResult(this.#assertBeforeMutation?.(), "assertBeforeMutation");
|
|
277
|
+
fileSignal?.throwIfAborted();
|
|
278
|
+
this.#assertStagePermissions(fd);
|
|
279
|
+
} : this.#assertBeforeMutation;
|
|
229
280
|
if (!copied)
|
|
230
|
-
await
|
|
231
|
-
if (this.#
|
|
232
|
-
|
|
281
|
+
await writePinnedInput(fd, input, maxBytes, assertBeforeMutation);
|
|
282
|
+
if (this.#verifyMode)
|
|
283
|
+
this.#assertStagePermissions(fd);
|
|
284
|
+
if (this.#sync) {
|
|
285
|
+
if (this.#strictFileSync)
|
|
286
|
+
fs.fsyncSync(fd);
|
|
287
|
+
else
|
|
288
|
+
syncFileBestEffortSync(fd);
|
|
289
|
+
}
|
|
233
290
|
const stat = fs.fstatSync(fd, { bigint: true });
|
|
234
|
-
this.#receipt =
|
|
235
|
-
directory: this.#directory,
|
|
236
|
-
temporaryBasename: this.#name,
|
|
237
|
-
identity: Object.freeze({
|
|
238
|
-
dev: stat.dev,
|
|
239
|
-
ino: stat.ino,
|
|
240
|
-
mode: Number(stat.mode & 4095n),
|
|
241
|
-
nlink: stat.nlink,
|
|
242
|
-
size: stat.size,
|
|
243
|
-
uid: Number(stat.uid),
|
|
244
|
-
gid: Number(stat.gid),
|
|
245
|
-
mtimeNs: stat.mtimeNs,
|
|
246
|
-
ctimeNs: stat.ctimeNs,
|
|
247
|
-
}),
|
|
248
|
-
});
|
|
291
|
+
this.#receipt = createStagedFileReceipt(this.#directory, this.#name, stat);
|
|
249
292
|
this.#assertCurrent();
|
|
250
293
|
}
|
|
251
294
|
catch (error) {
|
|
@@ -275,6 +318,10 @@ class NativeStagedFile {
|
|
|
275
318
|
this.#assertCurrent();
|
|
276
319
|
assertFinalSymlinkRejected(path.join(this.#directory.realPath, basename), this.#rejectFinalSymlink);
|
|
277
320
|
this.#assertBeforeMutation?.();
|
|
321
|
+
if (this.#assertBeforeMutation) {
|
|
322
|
+
this.#assertCurrent();
|
|
323
|
+
assertFinalSymlinkRejected(path.join(this.#directory.realPath, basename), this.#rejectFinalSymlink);
|
|
324
|
+
}
|
|
278
325
|
try {
|
|
279
326
|
if (overwrite) {
|
|
280
327
|
this.#binding.renameReplace(this.#parentFd, this.#name, this.#parentFd, basename);
|
|
@@ -307,7 +354,7 @@ class NativeStagedFile {
|
|
|
307
354
|
const receipt = Object.freeze({ status: "published", staged: this.receipt, basename, overwrite });
|
|
308
355
|
this.#open().publication = receipt;
|
|
309
356
|
onPublished?.(receipt.staged.identity);
|
|
310
|
-
const stagedMode = this.#assertNamed(basename);
|
|
357
|
+
const stagedMode = this.#assertNamed(basename, 0o600);
|
|
311
358
|
assertStagedDirectoryCurrent(this.#directory);
|
|
312
359
|
// Keep contents private until the name passes its identity fence. Mode
|
|
313
360
|
// changes use the owned fd, including for final mode 000.
|
|
@@ -317,11 +364,14 @@ class NativeStagedFile {
|
|
|
317
364
|
// Content was synced during preparation. A lost chmod leaves 0600, no wider
|
|
318
365
|
// than modes retaining owner rw; restrictive modes still need a durable correction.
|
|
319
366
|
if (this.#sync && ((this.#publishedMode & 0o600) !== 0o600 || (stagedMode & ~this.#publishedMode) !== 0)) {
|
|
320
|
-
|
|
367
|
+
if (this.#strictFileSync)
|
|
368
|
+
fs.fsyncSync(fd);
|
|
369
|
+
else
|
|
370
|
+
syncFileBestEffortSync(fd);
|
|
321
371
|
}
|
|
322
372
|
if (this.#sync)
|
|
323
373
|
syncFileBestEffortSync(this.#parentFd);
|
|
324
|
-
this.#assertNamed(basename);
|
|
374
|
+
this.#assertNamed(basename, this.#publishedMode);
|
|
325
375
|
assertStagedDirectoryCurrent(this.#directory);
|
|
326
376
|
return receipt;
|
|
327
377
|
}
|
|
@@ -397,5 +447,5 @@ export async function stageFileInDirectory(options) {
|
|
|
397
447
|
const input = { kind: "buffer", data: Buffer.from(options.content) };
|
|
398
448
|
const mode = options.mode ?? 0o600;
|
|
399
449
|
const parent = openStagedDirectory(options.directory);
|
|
400
|
-
return await createNativeStage(binding, parent.fd, fs.closeSync, parent.receipt, input, mode);
|
|
450
|
+
return await createNativeStage(binding, parent.fd, fs.closeSync, parent.receipt, input, mode, undefined, true, true, undefined, undefined, false, "mode-only");
|
|
401
451
|
}
|
package/dist/native.js
CHANGED
|
@@ -22,7 +22,7 @@ function isMuslFromReport() {
|
|
|
22
22
|
return true;
|
|
23
23
|
}
|
|
24
24
|
catch {
|
|
25
|
-
// Continue with
|
|
25
|
+
// Continue with ELF and filesystem inspection.
|
|
26
26
|
}
|
|
27
27
|
return undefined;
|
|
28
28
|
}
|
|
@@ -114,7 +114,7 @@ function isMuslFromElfInterpreter() {
|
|
|
114
114
|
function isMusl() {
|
|
115
115
|
if (process.platform !== "linux")
|
|
116
116
|
return false;
|
|
117
|
-
for (const detector of [isMuslFromReport,
|
|
117
|
+
for (const detector of [isMuslFromReport, isMuslFromElfInterpreter, isMuslFromFilesystem]) {
|
|
118
118
|
const result = detector();
|
|
119
119
|
if (result !== undefined)
|
|
120
120
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opened-realpath.d.ts","sourceRoot":"","sources":["../src/opened-realpath.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"opened-realpath.d.ts","sourceRoot":"","sources":["../src/opened-realpath.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAK7E,wBAAsB,kCAAkC,CACtD,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,MAAM,EACV,UAAU,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACxC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC;AACpD,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,WAAW,CAAA;CAAE,CAAC,CAAC"}
|
package/dist/opened-realpath.js
CHANGED
|
@@ -5,9 +5,18 @@ import { openedPathResolutionError } from "./opened-file-failure.js";
|
|
|
5
5
|
import { sameFileIdentity } from "./file-identity.js";
|
|
6
6
|
import { isNotFoundPathError } from "./path.js";
|
|
7
7
|
import { realpathSync } from "./realpath.js";
|
|
8
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
8
9
|
export async function resolveOpenedFileRealPathForHandle(handle, ioPath) {
|
|
9
|
-
const
|
|
10
|
-
|
|
10
|
+
const fd = handle.fd;
|
|
11
|
+
const identity = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }));
|
|
12
|
+
const resolved = await resolveOpenedFileRealPathForFd(fd, identity, ioPath);
|
|
13
|
+
let first = resolved.stat;
|
|
14
|
+
inspectFileIdentitySync(() => {
|
|
15
|
+
const current = first ?? fsSync.statSync(resolved.realPath, { bigint: true });
|
|
16
|
+
first = undefined;
|
|
17
|
+
return current;
|
|
18
|
+
}, identity);
|
|
19
|
+
return resolved.realPath;
|
|
11
20
|
}
|
|
12
21
|
export async function resolveOpenedFileRealPathForFd(fd, handleStat, ioPath) {
|
|
13
22
|
const statOptions = typeof handleStat.dev === "bigint" || typeof handleStat.ino === "bigint"
|
package/dist/owner-dacl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACnC,GACD;IACE,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAmCvE"}
|
package/dist/owner-dacl.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { FsSafeError } from "./errors.js";
|
|
2
2
|
import { getNativeBinding } from "./native.js";
|
|
3
|
+
import { getFsSafeNativeConfig } from "./native-config.js";
|
|
4
|
+
import { warnNativeFallback } from "./native-fallback-warning.js";
|
|
3
5
|
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
6
|
+
import { readWindowsSecurityFactsCommand } from "./windows-security-command.js";
|
|
4
7
|
export function readOwnerAndDacl(targetPath) {
|
|
5
8
|
if (process.platform !== "win32") {
|
|
6
9
|
return { status: "unsupported-platform", platform: process.platform };
|
|
7
10
|
}
|
|
8
11
|
assertNoWindowsPathAlias(targetPath, "filesystem", "owner and DACL path uses a Windows filesystem namespace alias");
|
|
9
12
|
const native = getNativeBinding();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const inspect = native?.readOwnerAndDacl;
|
|
14
|
+
if (typeof inspect !== "function") {
|
|
15
|
+
if (getFsSafeNativeConfig().mode === "require") {
|
|
16
|
+
throw new FsSafeError("helper-unavailable", "Windows owner and DACL facts require an up-to-date native helper");
|
|
17
|
+
}
|
|
18
|
+
warnNativeFallback("windows-owner-dacl", "Windows owner and DACL inspection uses a slower built-in system command.");
|
|
13
19
|
}
|
|
14
|
-
const facts =
|
|
20
|
+
const facts = typeof inspect === "function" ? inspect.call(native, targetPath) : readWindowsSecurityFactsCommand(targetPath);
|
|
15
21
|
return {
|
|
16
22
|
status: "supported",
|
|
17
23
|
ownerSid: facts.ownerSid,
|
package/dist/path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAczB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,kBAAkB,CAAC;AAM1B,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWvE;AAaD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAI1E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,SAA6B,GAAG,IAAI,CAIjG;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CA0ClE;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAExE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAa/F;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAChE,OAAO,CAsBT;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,CAMhE;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAczB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,kBAAkB,CAAC;AAM1B,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWvE;AAaD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAI1E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,SAA6B,GAAG,IAAI,CAIjG;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CA0ClE;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAExE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAa/F;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAChE,OAAO,CAsBT;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,CAMhE;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CASrF"}
|
package/dist/path.js
CHANGED
|
@@ -160,7 +160,8 @@ export function splitSafeRelativePath(relativePath) {
|
|
|
160
160
|
export function resolveSafeRelativePath(rootDir, relativePath) {
|
|
161
161
|
assertNoWindowsPathAlias(rootDir, "filesystem", "root dir uses a Windows filesystem namespace alias");
|
|
162
162
|
const root = resolvePathPreservingWindowsRoot(rootDir);
|
|
163
|
-
|
|
163
|
+
splitSafeRelativePath(relativePath);
|
|
164
|
+
const target = resolvePathFromBasePreservingWindowsRoot(root, relativePath);
|
|
164
165
|
if (!isPathInside(root, target)) {
|
|
165
166
|
throw new FsSafeError("outside-workspace", "relative path escapes root");
|
|
166
167
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAKlC,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;IAC5C,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,wBAAwB,CAAC;IACvC,sFAAsF;IACtF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA8BF,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAK1E;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,sBAAsB,GAC5B,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
|
package/dist/permissions.js
CHANGED
|
@@ -39,23 +39,9 @@ export async function inspectPathPermissions(targetPath, opts) {
|
|
|
39
39
|
const admissionPlatform = process.platform === "win32"
|
|
40
40
|
? process.platform
|
|
41
41
|
: (opts?.platform ?? process.platform);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
ok: false,
|
|
46
|
-
isSymlink: false,
|
|
47
|
-
isDir: false,
|
|
48
|
-
mode: null,
|
|
49
|
-
bits: null,
|
|
50
|
-
source: "unknown",
|
|
51
|
-
worldWritable: false,
|
|
52
|
-
groupWritable: false,
|
|
53
|
-
worldReadable: false,
|
|
54
|
-
groupReadable: false,
|
|
55
|
-
error: st.error,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
const st = await safeStatAdmitted(targetPath);
|
|
42
|
+
const st = hasWindowsPathAlias(targetPath, "filesystem", admissionPlatform)
|
|
43
|
+
? failedSafeStat("Path uses a Windows filesystem namespace alias")
|
|
44
|
+
: await safeStatAdmitted(targetPath);
|
|
59
45
|
if (!st.ok) {
|
|
60
46
|
return {
|
|
61
47
|
ok: false,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FileHandle } from "node:fs/promises";
|
|
2
|
+
import type { PinnedWriteInput } from "./pinned-write-types.js";
|
|
3
|
+
export declare function writePinnedInput(target: FileHandle | number, input: PinnedWriteInput, maxBytes?: number, assertBeforeMutation?: () => void): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=pinned-write-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinned-write-input.d.ts","sourceRoot":"","sources":["../src/pinned-write-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAWhE,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,KAAK,EAAE,gBAAgB,EACvB,QAAQ,CAAC,EAAE,MAAM,EACjB,oBAAoB,CAAC,EAAE,MAAM,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CAoBf"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { types } from "node:util";
|
|
2
|
+
import { snapshotByteView } from "./byte-view.js";
|
|
3
|
+
import { writeCopyFileToFd } from "./copy-file-input.js";
|
|
4
|
+
import { FsSafeError } from "./errors.js";
|
|
5
|
+
import { writeAllToFile } from "./write-file-handle.js";
|
|
6
|
+
const isUint8Array = types.isUint8Array;
|
|
7
|
+
function streamChunkBytes(chunk) {
|
|
8
|
+
if (Buffer.isBuffer(chunk))
|
|
9
|
+
return chunk;
|
|
10
|
+
if (!isUint8Array(chunk))
|
|
11
|
+
return Buffer.from(chunk);
|
|
12
|
+
return snapshotByteView(chunk);
|
|
13
|
+
}
|
|
14
|
+
export async function writePinnedInput(target, input, maxBytes, assertBeforeMutation) {
|
|
15
|
+
if (input.kind === "file") {
|
|
16
|
+
await writeCopyFileToFd(typeof target === "number" ? target : target.fd, input, maxBytes, assertBeforeMutation);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let bytes = 0;
|
|
20
|
+
const write = async (data) => {
|
|
21
|
+
bytes += data.byteLength;
|
|
22
|
+
if (maxBytes !== undefined && bytes > maxBytes) {
|
|
23
|
+
throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
24
|
+
}
|
|
25
|
+
await writeAllToFile(target, data, { assertBeforeMutation });
|
|
26
|
+
};
|
|
27
|
+
if (input.kind === "buffer") {
|
|
28
|
+
await write(typeof input.data === "string" ? Buffer.from(input.data, input.encoding ?? "utf8") : input.data);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
for await (const chunk of input.stream) {
|
|
32
|
+
await write(streamChunkBytes(chunk));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FileHandle } from "node:fs/promises";
|
|
2
|
+
export declare function assertPinnedWriteMode(fd: number, mode: number, privateCreation?: boolean): void;
|
|
3
|
+
export declare function preparePinnedWriteMode(handle: FileHandle, createdMode: number, assertBeforeMutation?: () => void, privateCreation?: boolean): Promise<(() => void) | undefined>;
|
|
4
|
+
export declare function pinnedWriteModeAssertion(fd: number, mode: number, assertBeforeMutation?: () => void, privateCreation?: boolean): () => void;
|
|
5
|
+
//# sourceMappingURL=pinned-write-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinned-write-mode.d.ts","sourceRoot":"","sources":["../src/pinned-write-mode.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,UAAQ,GAAG,IAAI,CAO7F;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,EACnB,oBAAoB,CAAC,EAAE,MAAM,IAAI,EACjC,eAAe,UAAQ,GACtB,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC,CAQnC;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,oBAAoB,CAAC,EAAE,MAAM,IAAI,EACjC,eAAe,UAAQ,GACtB,MAAM,IAAI,CAKZ"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { FsSafeError } from "./errors.js";
|
|
3
|
+
import { assertSynchronousCallbackResult } from "./mutation-authority.js";
|
|
4
|
+
import { assertPrivateCreationFile } from "./creation-file-state.js";
|
|
5
|
+
export function assertPinnedWriteMode(fd, mode, privateCreation = false) {
|
|
6
|
+
if (process.platform === "win32")
|
|
7
|
+
return;
|
|
8
|
+
const stat = fs.fstatSync(fd, { bigint: true });
|
|
9
|
+
if (privateCreation)
|
|
10
|
+
assertPrivateCreationFile(stat, fd);
|
|
11
|
+
if (Number(stat.mode & 4095n) !== mode) {
|
|
12
|
+
throw new FsSafeError("insecure-permissions", "filesystem did not enforce the requested file mode");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function preparePinnedWriteMode(handle, createdMode, assertBeforeMutation, privateCreation = false) {
|
|
16
|
+
if (process.platform === "win32")
|
|
17
|
+
return assertBeforeMutation;
|
|
18
|
+
assertSynchronousCallbackResult(assertBeforeMutation?.(), "assertBeforeMutation");
|
|
19
|
+
if (createdMode !== 0o600) {
|
|
20
|
+
if (privateCreation)
|
|
21
|
+
assertPrivateCreationFile(fs.fstatSync(handle.fd, { bigint: true }), handle.fd);
|
|
22
|
+
await handle.chmod(0o600);
|
|
23
|
+
}
|
|
24
|
+
return pinnedWriteModeAssertion(handle.fd, 0o600, assertBeforeMutation, privateCreation);
|
|
25
|
+
}
|
|
26
|
+
export function pinnedWriteModeAssertion(fd, mode, assertBeforeMutation, privateCreation = false) {
|
|
27
|
+
return () => {
|
|
28
|
+
assertSynchronousCallbackResult(assertBeforeMutation?.(), "assertBeforeMutation");
|
|
29
|
+
assertPinnedWriteMode(fd, mode, privateCreation);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
import type { AsyncDirectoryGuard } from "./directory-guard.js";
|
|
3
|
+
import { type FileIdentityStat } from "./file-identity.js";
|
|
4
|
+
import type { PinnedWriteParams } from "./pinned-write-types.js";
|
|
5
|
+
export declare function runPinnedStagedWrite(params: PinnedWriteParams, parentPath: string, parentGuard: AsyncDirectoryGuard<BigIntStats>): Promise<FileIdentityStat>;
|
|
6
|
+
//# sourceMappingURL=pinned-write-staged.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinned-write-staged.d.ts","sourceRoot":"","sources":["../src/pinned-write-staged.ts"],"names":[],"mappings":"AACA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAMnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhE,OAAO,EAA+B,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAKxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AASjE,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,mBAAmB,CAAC,WAAW,CAAC,GAC5C,OAAO,CAAC,gBAAgB,CAAC,CAqJ3B"}
|