@openclaw/fs-safe 0.18.2 → 0.20.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 +47 -0
- package/README.md +15 -5
- package/dist/advanced.d.ts +2 -0
- package/dist/advanced.js +1 -0
- package/dist/archive-durability.js +1 -1
- package/dist/archive-merge.js +1 -1
- package/dist/archive-plan.d.ts +2 -7
- package/dist/archive-read.js +9 -18
- package/dist/archive-staging.js +3 -1
- package/dist/archive-zip-entry.d.ts +11 -11
- package/dist/archive-zip-entry.js +3 -35
- package/dist/archive-zip-integrity.d.ts +2 -2
- package/dist/archive-zip-integrity.js +2 -12
- package/dist/archive-zip-loader.d.ts +7 -3
- package/dist/archive-zip-loader.js +10 -9
- package/dist/archive-zip-preflight.d.ts +2 -1
- package/dist/archive-zip-preflight.js +16 -7
- package/dist/archive.js +17 -16
- package/dist/directory-receipt.js +5 -7
- package/dist/effective-uid.js +1 -4
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js +3 -2
- package/dist/file-lock-sync-root-held.d.ts +4 -11
- package/dist/file-lock-sync-root-held.js +1 -4
- package/dist/file-lock-sync-root-io.d.ts +1 -4
- package/dist/file-lock-sync-root.d.ts +2 -4
- package/dist/file-store-boundary.d.ts +3 -7
- package/dist/file-store-boundary.js +7 -10
- package/dist/file-store-prune.js +3 -2
- package/dist/file-store.d.ts +4 -7
- package/dist/file-store.js +19 -21
- package/dist/guest-native-python.js +23 -31
- package/dist/guest.js +21 -12
- package/dist/json-document-store.d.ts +4 -9
- package/dist/json-durable-queue.js +2 -6
- package/dist/local-file-access.js +2 -5
- package/dist/local-file-descriptor.d.ts +2 -5
- package/dist/local-roots.d.ts +2 -7
- package/dist/move-path-cleanup.js +4 -4
- package/dist/native-binding.d.ts +18 -14
- package/dist/native-staged-symlink.d.ts +13 -0
- package/dist/native-staged-symlink.js +303 -0
- package/dist/owner-dacl-batch-worker.d.ts +1 -0
- package/dist/owner-dacl-batch-worker.js +54 -0
- package/dist/owner-dacl-batch.d.ts +5 -0
- package/dist/owner-dacl-batch.js +64 -0
- package/dist/owner-dacl.d.ts +2 -0
- package/dist/owner-dacl.js +3 -0
- package/dist/path.js +17 -1
- package/dist/permission-exec.js +3 -6
- package/dist/permissions-public.d.ts +1 -0
- package/dist/permissions-public.js +1 -0
- package/dist/pinned-mutation-admission.d.ts +0 -1
- package/dist/pinned-mutation-shared-route.d.ts +2 -8
- package/dist/pinned-open.d.ts +0 -1
- package/dist/pinned-open.js +1 -2
- package/dist/publish-copy-stage.js +4 -0
- package/dist/read-opened-file.d.ts +2 -5
- package/dist/regular-file.js +3 -3
- package/dist/replace-file-copy-fallback.d.ts +1 -2
- package/dist/root-context.js +3 -2
- package/dist/root-impl.js +0 -3
- package/dist/root-move-noreplace.d.ts +2 -7
- package/dist/root-observed-path.d.ts +0 -1
- package/dist/root-observed-path.js +0 -3
- package/dist/root-path-observation.d.ts +4 -11
- package/dist/root-path.js +7 -10
- package/dist/root-paths.d.ts +2 -6
- package/dist/root-remove-identity.d.ts +1 -3
- package/dist/root-walk.js +8 -3
- package/dist/root-write-admission.js +1 -6
- package/dist/root-write-complete-parent.d.ts +2 -0
- package/dist/root-write-complete-parent.js +1 -1
- package/dist/safe-path-segment.d.ts +1 -0
- package/dist/safe-path-segment.js +8 -2
- package/dist/secret-file.d.ts +6 -2
- package/dist/secret-file.js +1 -0
- package/dist/secure-file-windows.js +1 -5
- package/dist/secure-file.js +3 -2
- package/dist/sidecar-lock-admission-parser.d.ts +1 -2
- package/dist/sidecar-lock-handle.d.ts +2 -8
- package/dist/sidecar-lock-policy.d.ts +2 -7
- package/dist/sidecar-lock-stale-admission.d.ts +1 -5
- package/dist/sidecar-lock.js +5 -3
- package/dist/staged-symlink-types.d.ts +49 -0
- package/dist/staged-symlink-types.js +1 -0
- package/dist/symlink-parents.js +58 -7
- package/dist/temp-target.js +4 -2
- package/dist/temp-workspace-owner.js +4 -9
- package/dist/test-hooks.d.ts +1 -1
- package/dist/text-atomic.d.ts +2 -1
- package/dist/text-atomic.js +2 -0
- package/dist/trash.js +27 -1
- package/dist/walk.d.ts +2 -5
- package/dist/windows-owner.d.ts +0 -1
- package/dist/windows-owner.js +0 -1
- package/dist/windows-security-bridge.cs +6 -4
- package/dist/windows-security-bridge.ps1 +78 -3
- package/dist/windows-security-command.d.ts +8 -0
- package/dist/windows-security-command.js +66 -15
- package/dist/windows-security-facts.d.ts +4 -0
- package/dist/windows-security-facts.js +6 -2
- package/docs/advanced.md +3 -2
- package/docs/archive.md +10 -0
- package/docs/atomic.md +11 -3
- package/docs/contributing.md +30 -0
- package/docs/copy.md +2 -0
- package/docs/file-store.md +5 -0
- package/docs/guest.md +7 -1
- package/docs/install.md +28 -0
- package/docs/native-helper.md +11 -4
- package/docs/native.md +45 -1
- package/docs/permissions.md +66 -0
- package/docs/public-api.md +7 -1
- package/docs/root.md +10 -1
- package/docs/secret-file.md +10 -0
- package/docs/security-model.md +4 -1
- package/docs/sidecar-lock.md +2 -0
- package/docs/staged-symlink.md +123 -0
- package/docs/store.md +3 -1
- package/docs/testing.md +28 -0
- package/docs/walk.md +12 -0
- package/docs/writing.md +21 -0
- package/package.json +9 -9
package/docs/testing.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Testing
|
|
2
2
|
|
|
3
|
+
## Linux openat2 fallback
|
|
4
|
+
|
|
5
|
+
Build the host addon and package first. The test hook is cached with the native
|
|
6
|
+
capability probe; set it before starting the process, rather than changing it
|
|
7
|
+
between tests in one process:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm native:build
|
|
11
|
+
pnpm build
|
|
12
|
+
FS_SAFE_TEST_NO_OPENAT2=1 FS_SAFE_NATIVE_MODE=require pnpm test test/linux-openat2-fallback.test.ts test/root-move-noreplace.test.ts test/root-move-native-integration.test.ts test/native-write-containment.test.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
On Linux, the seccomp harness also exercises the real syscall failure without
|
|
16
|
+
the environment hook. It needs a C compiler and permission to install an
|
|
17
|
+
unprivileged seccomp filter; it affects only its child process:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cc test/fixtures/deny-openat2.c -o /tmp/fs-safe-deny-openat2
|
|
21
|
+
/tmp/fs-safe-deny-openat2 ENOSYS node test/fixtures/linux-openat2-fallback.mjs "$PWD/native/fs-safe-native.linux-x64-gnu.node"
|
|
22
|
+
/tmp/fs-safe-deny-openat2 EPERM node test/fixtures/linux-openat2-fallback.mjs "$PWD/native/fs-safe-native.linux-x64-gnu.node"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Use the matching native artifact filename on other Linux architectures/libcs.
|
|
26
|
+
The fixture proves nested moves, collisions, read/write, traversal, symlink and
|
|
27
|
+
hardlink rejection, cached selection, and `helper-unavailable` for strict
|
|
28
|
+
bounded cleanup. Bounded-cleanup success tests require real `openat2`; run the
|
|
29
|
+
full suite with the environment hook unset.
|
|
30
|
+
|
|
3
31
|
`@openclaw/fs-safe/test-hooks` exposes test-only injection points. Registration
|
|
4
32
|
is allowed only when `process.env.NODE_ENV === "test"` or
|
|
5
33
|
`process.env.VITEST === "true"`; registering a non-empty hook set elsewhere
|
package/docs/walk.md
CHANGED
|
@@ -113,6 +113,18 @@ typed `FsSafeError("too-large")` instead.
|
|
|
113
113
|
|
|
114
114
|
For followed symlinks, both `kind` and `size` describe the resolved target.
|
|
115
115
|
|
|
116
|
+
The caller's starting path retains Root home shorthand: `~` and `~/dir` expand
|
|
117
|
+
the home directory when iteration starts and must resolve inside the Root.
|
|
118
|
+
Home-started walks report actual Root-relative paths, such as `home/dir/file`,
|
|
119
|
+
rather than `~/dir/file`. Use `./~/dir` to start at a literal `~` directory.
|
|
120
|
+
An alias within a home-started path is reported under its admitted canonical
|
|
121
|
+
target; ordinary non-home starting aliases retain their caller-supplied spelling.
|
|
122
|
+
|
|
123
|
+
Entry names remain literal filesystem data, including a directory named `~`
|
|
124
|
+
and its descendants. To reuse an entry path in another Root method without
|
|
125
|
+
home-directory expansion, prefix it with `./`, as in
|
|
126
|
+
`capability.open("./" + entry.relativePath)`.
|
|
127
|
+
|
|
116
128
|
The default `order: "sorted"` visits each directory's names in lexicographic
|
|
117
129
|
order before descending depth first. It reads and sorts all names in each
|
|
118
130
|
visited directory. With `maxEntries`, it prepares small metadata batches capped
|
package/docs/writing.md
CHANGED
|
@@ -211,6 +211,11 @@ can establish them; native disposal can retain them inside a `SuppressedError`
|
|
|
211
211
|
cause. Preserve those details when handling errors: a rejection can follow
|
|
212
212
|
complete publication, and an indeterminate link or native rename must preserve names for
|
|
213
213
|
recovery. A cleanup or close failure also retains the original operation failure.
|
|
214
|
+
The JavaScript fallback checks the destination again immediately before attempting
|
|
215
|
+
publication. A collision observed there leaves publication unattempted and cleans
|
|
216
|
+
the owned stage. A later collision or other error from the link call remains
|
|
217
|
+
indeterminate, including `EEXIST`; the error code alone does not prove that the
|
|
218
|
+
filesystem left both names unchanged.
|
|
214
219
|
No later verification, mode, or synchronization failure authorizes deleting an
|
|
215
220
|
already published complete destination. See [receipt meanings](staged-file.md).
|
|
216
221
|
|
|
@@ -301,6 +306,12 @@ type RootWriteJsonOptions = RootWriteOptions & {
|
|
|
301
306
|
|
|
302
307
|
Open in append mode, write, sync the file handle, and close. Honors `mkdir` for the parent directory and syncs the parent directory when the append creates the file. `durable: false` skips both syncs. Pass `prependNewlineIfNeeded: true` to insert a `\n` if the file does not already end in one.
|
|
303
308
|
|
|
309
|
+
`mode` selects the creation mode, defaulting to `0o600` when neither the call nor
|
|
310
|
+
the Root supplies it. On POSIX, the process umask can further restrict that mode;
|
|
311
|
+
for example, `mode: 0o640` with umask `0o077` creates a `0o600` file. Existing
|
|
312
|
+
files are not chmodded, even when an explicit `mode` is supplied. Empty appends
|
|
313
|
+
use the same creation rules.
|
|
314
|
+
|
|
304
315
|
```ts
|
|
305
316
|
await fs.append("logs/today.log", `[${ts}] ${line}\n`);
|
|
306
317
|
await fs.append("notes/scratch.md", "* new bullet", { prependNewlineIfNeeded: true });
|
|
@@ -348,6 +359,11 @@ replacing rename. After dispatch it rechecks both parent identities, so a
|
|
|
348
359
|
post-operation rejection can mean the no-replace rename completed. Directory
|
|
349
360
|
moves continue to require `overwrite: true`.
|
|
350
361
|
|
|
362
|
+
Linux without `openat2` uses the [guarded native parent walk](native.md#linux-without-openat2).
|
|
363
|
+
The move still uses `renameat2(RENAME_NOREPLACE)` and preserves collisions;
|
|
364
|
+
parent resolution reports the documented `best-effort` containment class.
|
|
365
|
+
Disabling the addon still makes no-clobber moves unavailable.
|
|
366
|
+
|
|
351
367
|
Both selected canonical endpoints are admitted inside the retained Root after
|
|
352
368
|
native parent admission. With `mutationSymlinks: "reject"`, both full operation
|
|
353
369
|
paths are rechecked after the live mutation-authority callback and before
|
|
@@ -522,6 +538,11 @@ destination — there is no atomic-rename step. For exclusive publication of a
|
|
|
522
538
|
complete stream, use [`create()`](#streamed-creation). For streamed replacement,
|
|
523
539
|
the [`atomic`](atomic.md) helpers provide a staged writer.
|
|
524
540
|
|
|
541
|
+
For all three write modes, `mode` only selects new-file creation permissions,
|
|
542
|
+
defaulting to `0o600` when neither the call nor the Root supplies it. POSIX
|
|
543
|
+
permissions remain subject to the process umask; existing files are not chmodded.
|
|
544
|
+
The returned numeric `stat` records the admitted descriptor before caller writes.
|
|
545
|
+
|
|
525
546
|
On POSIX, existing-target opens use `O_NONBLOCK` as an admission safeguard so
|
|
526
547
|
a no-reader FIFO cannot stall regular-file validation. This does not change
|
|
527
548
|
ordinary regular-file write semantics. `replace` and `update` remain write-only
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -169,18 +169,18 @@
|
|
|
169
169
|
"archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
|
|
170
170
|
},
|
|
171
171
|
"optionalDependencies": {
|
|
172
|
-
"@openclaw/fs-safe-darwin-arm64": "0.
|
|
173
|
-
"@openclaw/fs-safe-darwin-x64": "0.
|
|
174
|
-
"@openclaw/fs-safe-linux-arm64-gnu": "0.
|
|
175
|
-
"@openclaw/fs-safe-linux-arm64-musl": "0.
|
|
176
|
-
"@openclaw/fs-safe-linux-x64-gnu": "0.
|
|
177
|
-
"@openclaw/fs-safe-linux-x64-musl": "0.
|
|
178
|
-
"@openclaw/fs-safe-win32-x64-msvc": "0.
|
|
172
|
+
"@openclaw/fs-safe-darwin-arm64": "0.20.0",
|
|
173
|
+
"@openclaw/fs-safe-darwin-x64": "0.20.0",
|
|
174
|
+
"@openclaw/fs-safe-linux-arm64-gnu": "0.20.0",
|
|
175
|
+
"@openclaw/fs-safe-linux-arm64-musl": "0.20.0",
|
|
176
|
+
"@openclaw/fs-safe-linux-x64-gnu": "0.20.0",
|
|
177
|
+
"@openclaw/fs-safe-linux-x64-musl": "0.20.0",
|
|
178
|
+
"@openclaw/fs-safe-win32-x64-msvc": "0.20.0",
|
|
179
179
|
"jszip": "^3.10.2"
|
|
180
180
|
},
|
|
181
181
|
"devDependencies": {
|
|
182
182
|
"@emnapi/runtime": "2.0.0-alpha.5",
|
|
183
|
-
"@napi-rs/cli": "3.10.
|
|
183
|
+
"@napi-rs/cli": "3.10.5",
|
|
184
184
|
"@types/node": "^26.6.1",
|
|
185
185
|
"@vitest/coverage-v8": "5.0.1",
|
|
186
186
|
"fast-check": "^4.10.1",
|