@openclaw/fs-safe 0.5.1 → 0.5.3
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 +19 -6
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +20 -12
- package/dist/archive-entry.d.ts +1 -0
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +37 -6
- package/dist/archive-errors.d.ts +1 -0
- package/dist/archive-errors.d.ts.map +1 -1
- package/dist/archive-errors.js +4 -0
- package/dist/archive-input.d.ts +7 -0
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +11 -1
- package/dist/archive-limits.js +1 -1
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +12 -6
- package/dist/archive-policy.d.ts.map +1 -1
- package/dist/archive-policy.js +2 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +62 -37
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +3 -2
- package/dist/archive-tar-meta.d.ts.map +1 -1
- package/dist/archive-tar-meta.js +15 -1
- package/dist/archive-tar-runtime.d.ts +1 -0
- package/dist/archive-tar-runtime.d.ts.map +1 -1
- package/dist/archive-tar-runtime.js +11 -0
- package/dist/archive-tar.d.ts.map +1 -1
- package/dist/archive-tar.js +8 -4
- package/dist/archive-zip-entry.d.ts +7 -1
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-entry.js +11 -1
- package/dist/archive-zip-integrity.d.ts +5 -0
- package/dist/archive-zip-integrity.d.ts.map +1 -0
- package/dist/archive-zip-integrity.js +54 -0
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +12 -1
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -5
- package/dist/bounded-read-stream.d.ts.map +1 -1
- package/dist/bounded-read-stream.js +13 -2
- package/dist/deny-mutations.d.ts +1 -0
- package/dist/deny-mutations.d.ts.map +1 -1
- package/dist/deny-mutations.js +6 -39
- package/dist/device-path.d.ts +1 -0
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +6 -6
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +5 -4
- package/dist/error-detail.d.ts +2 -0
- package/dist/error-detail.d.ts.map +1 -0
- package/dist/error-detail.js +4 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +4 -1
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +88 -35
- package/dist/file-store-boundary.d.ts +6 -0
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +20 -5
- package/dist/file-store-sync-write.d.ts +9 -0
- package/dist/file-store-sync-write.d.ts.map +1 -0
- package/dist/file-store-sync-write.js +97 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +62 -139
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +27 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +5 -6
- package/dist/install-path.js +1 -1
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +19 -13
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +1 -3
- package/dist/local-file-access.d.ts +3 -2
- package/dist/local-file-access.d.ts.map +1 -1
- package/dist/local-file-access.js +12 -6
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +35 -30
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +51 -12
- package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
- package/dist/native/darwin-x64/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
- package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
- package/dist/native-binding.d.ts +1 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +14 -1
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +44 -17
- package/dist/output-sibling.d.ts.map +1 -1
- package/dist/output-sibling.js +6 -5
- package/dist/path-policy.d.ts.map +1 -1
- package/dist/path-policy.js +1 -7
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +4 -0
- package/dist/permission-exec.d.ts +6 -0
- package/dist/permission-exec.d.ts.map +1 -0
- package/dist/permission-exec.js +28 -0
- package/dist/permissions-windows.d.ts +56 -0
- package/dist/permissions-windows.d.ts.map +1 -0
- package/dist/permissions-windows.js +285 -0
- package/dist/permissions.d.ts +3 -46
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +17 -328
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +21 -4
- package/dist/pinned-write.js +2 -1
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +45 -4
- package/dist/read-error.d.ts +2 -0
- package/dist/read-error.d.ts.map +1 -0
- package/dist/read-error.js +11 -0
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +21 -2
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +10 -1
- package/dist/replace-file-copy-fallback.d.ts +3 -0
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +18 -11
- package/dist/replace-file-descriptor.d.ts +33 -0
- package/dist/replace-file-descriptor.d.ts.map +1 -0
- package/dist/replace-file-descriptor.js +96 -0
- package/dist/replace-file.d.ts +9 -2
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +64 -72
- package/dist/root-context.d.ts +8 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +43 -2
- package/dist/root-errors.d.ts +7 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +46 -1
- package/dist/root-file.d.ts +1 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +2 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +216 -169
- package/dist/root-path-symlink.d.ts +3 -0
- package/dist/root-path-symlink.d.ts.map +1 -0
- package/dist/root-path-symlink.js +35 -0
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +129 -190
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +15 -0
- package/dist/safe-path-segment.d.ts +2 -0
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +12 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +29 -6
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +5 -1
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +6 -1
- package/dist/short-path.d.ts +2 -0
- package/dist/short-path.d.ts.map +1 -0
- package/dist/short-path.js +7 -0
- package/dist/sidecar-lock-acquire.d.ts +30 -0
- package/dist/sidecar-lock-acquire.d.ts.map +1 -0
- package/dist/sidecar-lock-acquire.js +260 -0
- package/dist/sidecar-lock-policy.d.ts +3 -0
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +20 -3
- package/dist/sidecar-lock-reclaim.d.ts +5 -1
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +59 -9
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +27 -226
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +9 -8
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +15 -4
- package/dist/test-hooks.d.ts +1 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/timing.d.ts +2 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +9 -0
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +10 -1
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +15 -0
- package/docs/advanced.md +7 -1
- package/docs/archive.md +27 -20
- package/docs/assets/readme-banner.jpg +0 -0
- package/docs/atomic.md +48 -19
- package/docs/errors.md +34 -8
- package/docs/file-store.md +14 -1
- package/docs/filename.md +17 -16
- package/docs/index.md +1 -0
- package/docs/install-path.md +9 -6
- package/docs/json.md +8 -2
- package/docs/local-roots.md +79 -103
- package/docs/native-helper.md +11 -5
- package/docs/native.md +2 -2
- package/docs/output.md +8 -3
- package/docs/path-scope.md +4 -1
- package/docs/path.md +7 -3
- package/docs/permissions.md +8 -0
- package/docs/public-api.md +109 -0
- package/docs/quickstart.md +6 -2
- package/docs/reading.md +13 -10
- package/docs/root.md +26 -4
- package/docs/secret-file.md +21 -4
- package/docs/security-model.md +21 -4
- package/docs/sidecar-lock.md +15 -5
- package/docs/temp.md +42 -5
- package/docs/test-hooks.md +2 -0
- package/docs/types.md +32 -15
- package/docs/writing.md +25 -12
- package/package.json +15 -4
- package/dist/archive-file-io.d.ts +0 -9
- package/dist/archive-file-io.d.ts.map +0 -1
- package/dist/archive-file-io.js +0 -11
- package/dist/file-store-source.d.ts +0 -5
- package/dist/file-store-source.d.ts.map +0 -1
- package/dist/file-store-source.js +0 -30
- package/dist/file-url.d.ts +0 -2
- package/dist/file-url.d.ts.map +0 -1
- package/dist/file-url.js +0 -1
- package/dist/path-stat.d.ts +0 -4
- package/dist/path-stat.d.ts.map +0 -1
- package/dist/path-stat.js +0 -15
- package/dist/windows-permissions-native.d.ts +0 -9
- package/dist/windows-permissions-native.d.ts.map +0 -1
- package/dist/windows-permissions-native.js +0 -31
package/dist/local-roots.js
CHANGED
|
@@ -2,11 +2,11 @@ import fsSync from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
4
|
import { expandHomePrefix, resolveUserPath } from "./home-dir.js";
|
|
5
|
-
import { safeFileURLToPath } from "./local-file-access.js";
|
|
5
|
+
import { isFileUrl, safeFileURLToPath } from "./local-file-access.js";
|
|
6
6
|
import { isPathInside } from "./path.js";
|
|
7
7
|
import { root } from "./root.js";
|
|
8
8
|
function resolveLocalPathInput(input, label) {
|
|
9
|
-
if (input
|
|
9
|
+
if (isFileUrl(input)) {
|
|
10
10
|
try {
|
|
11
11
|
return safeFileURLToPath(input);
|
|
12
12
|
}
|
|
@@ -25,7 +25,7 @@ function resolveLocalRootInput(input, label) {
|
|
|
25
25
|
if (!trimmed) {
|
|
26
26
|
throw new FsSafeError("invalid-path", `${label} entry is required`);
|
|
27
27
|
}
|
|
28
|
-
const resolved = trimmed
|
|
28
|
+
const resolved = isFileUrl(trimmed)
|
|
29
29
|
? resolveLocalPathInput(trimmed, label)
|
|
30
30
|
: expandHomePrefix(trimmed);
|
|
31
31
|
if (resolved.includes("\0")) {
|
|
@@ -41,7 +41,9 @@ function isPathInsideRoot(candidate, rootDir) {
|
|
|
41
41
|
}
|
|
42
42
|
function resolveRootRealSync(rootDir) {
|
|
43
43
|
try {
|
|
44
|
-
|
|
44
|
+
// Configured roots may themselves be symlinks. Follow only this trusted
|
|
45
|
+
// root entry, then use its canonical directory for containment checks.
|
|
46
|
+
const stat = fsSync.statSync(rootDir);
|
|
45
47
|
if (!stat.isDirectory()) {
|
|
46
48
|
return null;
|
|
47
49
|
}
|
|
@@ -101,8 +103,8 @@ function resolveCandidateCanonicalSync(filePath) {
|
|
|
101
103
|
export function resolveLocalPathFromRootsSync(options) {
|
|
102
104
|
const label = options.label ?? "local roots";
|
|
103
105
|
const requestedPath = path.resolve(resolveLocalPathInput(options.filePath, "file path"));
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
const rootDirs = options.roots.map((rootEntry) => resolveLocalRootInput(rootEntry, label));
|
|
107
|
+
for (const rootDir of rootDirs) {
|
|
106
108
|
const rootReal = resolveRootRealSync(rootDir);
|
|
107
109
|
if (!rootReal) {
|
|
108
110
|
continue;
|
|
@@ -129,8 +131,8 @@ export function resolveLocalPathFromRootsSync(options) {
|
|
|
129
131
|
export async function readLocalFileFromRoots(options) {
|
|
130
132
|
const label = options.label ?? "local roots";
|
|
131
133
|
const requestedPath = path.resolve(resolveLocalPathInput(options.filePath, "file path"));
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
const rootDirs = options.roots.map((rootEntry) => resolveLocalRootInput(rootEntry, label));
|
|
135
|
+
for (const rootDir of rootDirs) {
|
|
134
136
|
let scopedRoot;
|
|
135
137
|
try {
|
|
136
138
|
scopedRoot = await root(rootDir);
|
|
@@ -138,29 +140,32 @@ export async function readLocalFileFromRoots(options) {
|
|
|
138
140
|
catch {
|
|
139
141
|
continue;
|
|
140
142
|
}
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
const readOptions = {
|
|
144
|
+
hardlinks: options.hardlinks,
|
|
145
|
+
nonBlockingRead: options.nonBlockingRead,
|
|
146
|
+
symlinks: options.symlinks,
|
|
147
|
+
};
|
|
148
|
+
// Leave maxBytes absent when the caller omits it so Root's own default
|
|
149
|
+
// cap remains in force instead of being overwritten by undefined.
|
|
150
|
+
if (options.maxBytes !== undefined) {
|
|
151
|
+
readOptions.maxBytes = options.maxBytes;
|
|
152
|
+
}
|
|
153
|
+
// A trusted root symlink has two valid spellings. Preserve the caller's
|
|
154
|
+
// lexical spelling when possible so Root.read() still enforces its
|
|
155
|
+
// symlink policy, while also accepting a path expressed below rootReal.
|
|
156
|
+
const relativePaths = [scopedRoot.rootDir, scopedRoot.rootReal]
|
|
157
|
+
.filter((rootPath, index, roots) => roots.indexOf(rootPath) === index)
|
|
158
|
+
.filter((rootPath) => isPathInside(rootPath, requestedPath))
|
|
159
|
+
.map((rootPath) => path.relative(rootPath, requestedPath))
|
|
160
|
+
.filter(Boolean);
|
|
161
|
+
for (const relativePath of relativePaths) {
|
|
162
|
+
try {
|
|
163
|
+
const result = await scopedRoot.read(relativePath, readOptions);
|
|
164
|
+
return { ...result, root: scopedRoot.rootReal };
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Try the canonical spelling before moving to the next configured root.
|
|
158
168
|
}
|
|
159
|
-
const result = await scopedRoot.read(relativePath, readOptions);
|
|
160
|
-
return { ...result, root: scopedRoot.rootReal };
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
continue;
|
|
164
169
|
}
|
|
165
170
|
}
|
|
166
171
|
return null;
|
package/dist/move-path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,+BAA+B,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,sBAAsB,GAAG,cAAc,GAAG,uBAAuB,CAAC;AAEvE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,GAAG,SAAS,CASpC;AA6VD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CA2Df"}
|
package/dist/move-path.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { guardedRename } from "./guarded-mutation.js";
|
|
7
|
+
import { registerTempPathForExit } from "./temp-cleanup.js";
|
|
7
8
|
export function moveCopyFallbackReasonForRenameError(error, platform = process.platform) {
|
|
8
9
|
const code = error?.code;
|
|
9
10
|
if (code === "EXDEV") {
|
|
@@ -102,6 +103,21 @@ function regularReadFlags() {
|
|
|
102
103
|
? fsConstants.O_NONBLOCK
|
|
103
104
|
: 0));
|
|
104
105
|
}
|
|
106
|
+
async function chmodDirectoryPinned(directoryPath, mode) {
|
|
107
|
+
if (process.platform === "win32") {
|
|
108
|
+
// Node cannot portably open a Windows directory for descriptor-bound
|
|
109
|
+
// chmod. POSIX modes are not enforced there, so do not fall back to a
|
|
110
|
+
// pathname operation that could follow a replacement symlink.
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const handle = await fs.open(directoryPath, fsConstants.O_RDONLY | fsConstants.O_DIRECTORY | fsConstants.O_NOFOLLOW);
|
|
114
|
+
try {
|
|
115
|
+
await handle.chmod(mode);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await handle.close();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
105
121
|
async function writeAll(handle, buffer, bytesRead) {
|
|
106
122
|
let offset = 0;
|
|
107
123
|
while (offset < bytesRead) {
|
|
@@ -141,20 +157,20 @@ async function copyRegularFilePinned(params) {
|
|
|
141
157
|
}
|
|
142
158
|
await writeAll(destinationHandle, scratch, bytesRead);
|
|
143
159
|
}
|
|
160
|
+
// Re-check the opened source before the staged tree can be committed. If
|
|
161
|
+
// it changed while we copied, the caller should retry the move.
|
|
162
|
+
const finalSourceStat = await sourceHandle.stat();
|
|
163
|
+
if (params.rejectHardlinks && finalSourceStat.nlink > 1) {
|
|
164
|
+
throw hardlinkedSourceError(params.from);
|
|
165
|
+
}
|
|
166
|
+
if (!sameIdentity(params.identity, entryIdentity(finalSourceStat))) {
|
|
167
|
+
throw sourceChangedError(params.from);
|
|
168
|
+
}
|
|
169
|
+
await destinationHandle.chmod(modeBits(params.mode));
|
|
144
170
|
}
|
|
145
171
|
finally {
|
|
146
172
|
await destinationHandle.close();
|
|
147
173
|
}
|
|
148
|
-
// Re-check the opened handle before the staged tree can be committed. If
|
|
149
|
-
// the source changed while we copied, the caller should retry the move.
|
|
150
|
-
const finalSourceStat = await sourceHandle.stat();
|
|
151
|
-
if (params.rejectHardlinks && finalSourceStat.nlink > 1) {
|
|
152
|
-
throw hardlinkedSourceError(params.from);
|
|
153
|
-
}
|
|
154
|
-
if (!sameIdentity(params.identity, entryIdentity(finalSourceStat))) {
|
|
155
|
-
throw sourceChangedError(params.from);
|
|
156
|
-
}
|
|
157
|
-
await fs.chmod(params.to, modeBits(params.mode)).catch(() => undefined);
|
|
158
174
|
}
|
|
159
175
|
catch (error) {
|
|
160
176
|
if (destinationCreated) {
|
|
@@ -198,7 +214,7 @@ async function copyEntryWithManifest(from, to, options) {
|
|
|
198
214
|
await assertSourceStillMatches(from, identity);
|
|
199
215
|
// mkdir() honors process umask. Restore the source mode before commit so
|
|
200
216
|
// EXDEV fallback preserves directory permissions like fs.cp did.
|
|
201
|
-
await
|
|
217
|
+
await chmodDirectoryPinned(to, modeBits(sourceStat.mode));
|
|
202
218
|
return { ...identity, children, kind: "directory" };
|
|
203
219
|
}
|
|
204
220
|
if (!sourceStat.isFile()) {
|
|
@@ -284,19 +300,42 @@ export async function movePathWithCopyFallback(options) {
|
|
|
284
300
|
await assertCopyDestinationOutsideSource(sourcePath, targetPath);
|
|
285
301
|
const targetDir = path.dirname(targetPath);
|
|
286
302
|
const staged = path.join(targetDir, `.fs-safe-move-${process.pid}-${randomUUID()}.tmp`);
|
|
303
|
+
const unregisterStaged = registerTempPathForExit(staged, { recursive: true });
|
|
304
|
+
let stagedCommitted = false;
|
|
287
305
|
try {
|
|
288
306
|
const manifest = await copyEntryWithManifest(sourcePath, staged, {
|
|
289
307
|
sourceHardlinks: options.sourceHardlinks ?? "allow",
|
|
290
308
|
...(rejectHardlinks ? { budget: { discovered: 1 } } : {}),
|
|
291
309
|
});
|
|
310
|
+
unregisterStaged.setIdentity(await fs.lstat(staged));
|
|
292
311
|
await assertCopyDestinationOutsideSource(sourcePath, targetPath);
|
|
293
312
|
await guardedRename({ from: staged, to: targetPath });
|
|
313
|
+
stagedCommitted = true;
|
|
314
|
+
unregisterStaged();
|
|
294
315
|
const cleanupResult = await cleanupCopiedEntry(sourcePath, manifest);
|
|
295
316
|
if (cleanupResult === "stale") {
|
|
296
317
|
throw sourceChangedError(sourcePath);
|
|
297
318
|
}
|
|
298
319
|
}
|
|
299
320
|
finally {
|
|
300
|
-
|
|
321
|
+
if (!stagedCommitted) {
|
|
322
|
+
try {
|
|
323
|
+
const stagedIdentity = await fs.lstat(staged);
|
|
324
|
+
if (!stagedIdentity.isSymbolicLink())
|
|
325
|
+
unregisterStaged.setIdentity(stagedIdentity);
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
if (error.code === "ENOENT") {
|
|
329
|
+
unregisterStaged();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
await fs.rm(staged, { recursive: true, force: true });
|
|
334
|
+
unregisterStaged();
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
// Keep the identity-bound exit cleanup registered for a later retry.
|
|
338
|
+
}
|
|
339
|
+
}
|
|
301
340
|
}
|
|
302
341
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/native-binding.d.ts
CHANGED
|
@@ -74,6 +74,7 @@ export interface NativeBinding {
|
|
|
74
74
|
readArchiveEntryNative(path: string, kind: string, requested: string, maxBytes: number, maxEntries: number, maxMetaEntryBytes: number, signal: AbortSignal): Promise<Buffer>;
|
|
75
75
|
readOwnerAndDacl(path: string): NativeWindowsSecurityFacts;
|
|
76
76
|
renameNoReplace(sourceRootFd: number, sourceRelPath: string, targetRootFd: number, targetRelPath: string): void;
|
|
77
|
+
renameReplace(sourceRootFd: number, sourceRelPath: string, targetRootFd: number, targetRelPath: string): void;
|
|
77
78
|
sha256File(fd: number): Promise<NativeFileHash>;
|
|
78
79
|
}
|
|
79
80
|
//# sourceMappingURL=native-binding.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-binding.d.ts","sourceRoot":"","sources":["../src/native-binding.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"native-binding.d.ts","sourceRoot":"","sources":["../src/native-binding.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-operations.d.ts","sourceRoot":"","sources":["../src/native-operations.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAG7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E,CAAC;AA0CF,wBAAgB,oCAAoC,CAAC,MAAM,EAAE;IAC3D,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,GAAG,IAAI,CAsBP;AAED,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"native-operations.d.ts","sourceRoot":"","sources":["../src/native-operations.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAG7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E,CAAC;AA0CF,wBAAgB,oCAAoC,CAAC,MAAM,EAAE;IAC3D,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,GAAG,IAAI,CAsBP;AAED,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAyDvC;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAQzD;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D"}
|
|
@@ -72,7 +72,20 @@ export async function createNativeExclusiveFile(targetPath, mode) {
|
|
|
72
72
|
let fd;
|
|
73
73
|
let created;
|
|
74
74
|
try {
|
|
75
|
-
|
|
75
|
+
let opened;
|
|
76
|
+
try {
|
|
77
|
+
opened = binding.openBeneath(parent.fd, basename, nativeOpenFlags(fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL));
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
// The parent open above and every post-create operation remain untagged.
|
|
81
|
+
// Only this exclusive-open failure has enough provenance for a caller to
|
|
82
|
+
// classify a Windows lock-file denial without swallowing setup failures.
|
|
83
|
+
const openError = error;
|
|
84
|
+
if (process.platform === "win32" && openError.code === "EPERM") {
|
|
85
|
+
openError.path = targetPath;
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
76
89
|
fd = opened.fd;
|
|
77
90
|
fsSync.fchmodSync(fd, mode);
|
|
78
91
|
created = fsSync.fstatSync(fd);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-pinned-write.d.ts","sourceRoot":"","sources":["../src/native-pinned-write.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAM3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAiD7E,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"native-pinned-write.d.ts","sourceRoot":"","sources":["../src/native-pinned-write.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAM3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAiD7E,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAsH3B"}
|
|
@@ -9,23 +9,22 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
9
9
|
throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
async function
|
|
12
|
+
async function writeNativeInput(fd, input, maxBytes) {
|
|
13
13
|
if (input.kind === "buffer") {
|
|
14
14
|
const data = typeof input.data === "string"
|
|
15
15
|
? Buffer.from(input.data, input.encoding ?? "utf8")
|
|
16
16
|
: Buffer.from(input.data);
|
|
17
17
|
assertWithinMaxBytes(data.byteLength, maxBytes);
|
|
18
|
-
|
|
18
|
+
writeNativeFd(fd, data);
|
|
19
|
+
return;
|
|
19
20
|
}
|
|
20
|
-
const chunks = [];
|
|
21
21
|
let bytes = 0;
|
|
22
22
|
for await (const chunk of input.stream) {
|
|
23
23
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
24
24
|
bytes += buffer.byteLength;
|
|
25
25
|
assertWithinMaxBytes(bytes, maxBytes);
|
|
26
|
-
|
|
26
|
+
writeNativeFd(fd, buffer);
|
|
27
27
|
}
|
|
28
|
-
return Buffer.concat(chunks, bytes);
|
|
29
28
|
}
|
|
30
29
|
function nativeOpenFlags(flags) {
|
|
31
30
|
const closeOnExec = fsSync.constants.O_CLOEXEC;
|
|
@@ -37,7 +36,6 @@ function sameNativeIdentity(left, right) {
|
|
|
37
36
|
return left.dev === right.dev && left.ino === right.ino;
|
|
38
37
|
}
|
|
39
38
|
export async function runPinnedWriteNative(binding, params) {
|
|
40
|
-
const data = await inputToBuffer(params.input, params.maxBytes);
|
|
41
39
|
const root = await fs.open(params.rootPath, fsSync.constants.O_RDONLY |
|
|
42
40
|
(typeof fsSync.constants.O_DIRECTORY === "number" ? fsSync.constants.O_DIRECTORY : 0));
|
|
43
41
|
let parentFd;
|
|
@@ -67,27 +65,56 @@ export async function runPinnedWriteNative(binding, params) {
|
|
|
67
65
|
!sameNativeIdentity(parentPathStat, parentIdentity)) {
|
|
68
66
|
throw new FsSafeError("path-mismatch", "native write parent changed during resolution");
|
|
69
67
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
if (params.overwrite === false) {
|
|
69
|
+
try {
|
|
70
|
+
await fs.lstat(path.join(parentPath, params.basename));
|
|
71
|
+
throw Object.assign(new Error("destination already exists"), { code: "EEXIST" });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error.code !== "ENOENT") {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
tempFd = binding.openBeneath(parentFd, tempName, nativeOpenFlags(fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL)).fd;
|
|
80
|
-
fsSync.fchmodSync(tempFd, params.mode || 0o600);
|
|
81
|
-
writeNativeFd(tempFd, data);
|
|
82
|
-
syncNativeFileBestEffort(tempFd);
|
|
83
80
|
tempIdentity = fsSync.fstatSync(tempFd);
|
|
84
|
-
binding
|
|
81
|
+
// Creation is requested at 0600 in the binding, but a restrictive umask
|
|
82
|
+
// can remove owner access. Keep the unpublished inode private and
|
|
83
|
+
// reopenable until the published name has been identity-fenced.
|
|
84
|
+
fsSync.fchmodSync(tempFd, 0o600);
|
|
85
|
+
await writeNativeInput(tempFd, params.input, params.maxBytes);
|
|
86
|
+
syncNativeFileBestEffort(tempFd);
|
|
87
|
+
if (params.overwrite === false) {
|
|
88
|
+
binding.renameNoReplace(parentFd, tempName, parentFd, params.basename);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
binding.renameReplace(parentFd, tempName, parentFd, params.basename);
|
|
92
|
+
}
|
|
85
93
|
renamed = true;
|
|
86
94
|
targetFd = binding.openBeneath(parentFd, params.basename, nativeOpenFlags(fsSync.constants.O_RDONLY)).fd;
|
|
87
95
|
const targetIdentity = binding.fstatIdentity(targetFd);
|
|
88
96
|
if (!targetIdentity.isFile || !sameNativeIdentity(tempIdentity, targetIdentity)) {
|
|
89
97
|
throw new FsSafeError("path-mismatch", "native write target changed after rename");
|
|
90
98
|
}
|
|
99
|
+
// Native exclusive creation starts at 0600. Apply the requested mode only
|
|
100
|
+
// after reopening and fencing the published name, both so mode 000 stays
|
|
101
|
+
// verifiable and so broader modes are never exposed before that fence.
|
|
102
|
+
try {
|
|
103
|
+
fsSync.fchmodSync(targetFd, params.mode);
|
|
104
|
+
syncNativeFileBestEffort(targetFd);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
fsSync.closeSync(targetFd);
|
|
108
|
+
targetFd = undefined;
|
|
109
|
+
removeNativeCreatedFileIfStillPinned({
|
|
110
|
+
binding,
|
|
111
|
+
parentPath,
|
|
112
|
+
parentFd,
|
|
113
|
+
basename: params.basename,
|
|
114
|
+
created: tempIdentity,
|
|
115
|
+
});
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
91
118
|
syncNativeFileBestEffort(parentFd);
|
|
92
119
|
return { dev: targetIdentity.dev, ino: targetIdentity.ino };
|
|
93
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-sibling.d.ts","sourceRoot":"","sources":["../src/output-sibling.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"output-sibling.d.ts","sourceRoot":"","sources":["../src/output-sibling.ts"],"names":[],"mappings":"AA+FA,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,MAAM,EAAE;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,CAAC,CAAC,CA8Cb"}
|
package/dist/output-sibling.js
CHANGED
|
@@ -66,11 +66,10 @@ async function cleanupTempPath(tempPath, identity) {
|
|
|
66
66
|
if (!identity || (!current.isSymbolicLink() && sameFileIdentity(current, identity))) {
|
|
67
67
|
await fs.rm(tempPath, { force: true });
|
|
68
68
|
}
|
|
69
|
+
return true;
|
|
69
70
|
}
|
|
70
71
|
catch (error) {
|
|
71
|
-
|
|
72
|
-
// Cleanup is best-effort; the randomized path remains inside the writable boundary.
|
|
73
|
-
}
|
|
72
|
+
return error.code === "ENOENT";
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
export async function writeExternalFileViaSibling(params) {
|
|
@@ -114,9 +113,11 @@ export async function writeExternalFileViaSibling(params) {
|
|
|
114
113
|
return result;
|
|
115
114
|
}
|
|
116
115
|
finally {
|
|
116
|
+
let cleanupComplete = renamed;
|
|
117
117
|
if (!renamed) {
|
|
118
|
-
await cleanupTempPath(tempPath, stagedIdentity);
|
|
118
|
+
cleanupComplete = await cleanupTempPath(tempPath, stagedIdentity);
|
|
119
119
|
}
|
|
120
|
-
|
|
120
|
+
if (cleanupComplete)
|
|
121
|
+
unregisterTempPath();
|
|
121
122
|
}
|
|
122
123
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-policy.d.ts","sourceRoot":"","sources":["../src/path-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"path-policy.d.ts","sourceRoot":"","sources":["../src/path-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AAIxB,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD,eAAO,MAAM,mBAAmB;;;;;;;;;CAA2B,CAAC;AAE5D,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhB;AAED,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB"}
|
package/dist/path-policy.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath, } from "./root-path.js";
|
|
4
3
|
import { isNotFoundPathError } from "./path.js";
|
|
4
|
+
import { shortPath } from "./short-path.js";
|
|
5
5
|
export const PATH_ALIAS_POLICIES = ROOT_PATH_ALIAS_POLICIES;
|
|
6
6
|
export async function assertNoPathAliasEscape(params) {
|
|
7
7
|
const resolved = await resolveRootPath({
|
|
@@ -42,9 +42,3 @@ export async function assertNoHardlinkedFinalPath(params) {
|
|
|
42
42
|
throw new Error(`Hardlinked path is not allowed under ${params.boundaryLabel} (${shortPath(params.root)}): ${shortPath(params.filePath)}`);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
function shortPath(value) {
|
|
46
|
-
if (value.startsWith(os.homedir())) {
|
|
47
|
-
return `~${value.slice(os.homedir().length)}`;
|
|
48
|
-
}
|
|
49
|
-
return value;
|
|
50
|
-
}
|
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;
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAMzB,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;AAED,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,CA4BlE;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,CAUT;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,CAMhE;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAOrF"}
|
package/dist/path.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { isDriveRelativePath } from "./safe-path-segment.js";
|
|
4
5
|
export { assertNoUnsafeDeviceReadPath, isUnsafeDeviceReadPath, matchUnsafeDeviceReadPath, } from "./device-path.js";
|
|
5
6
|
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
|
|
6
7
|
const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
|
|
@@ -119,6 +120,9 @@ export function splitSafeRelativePath(relativePath) {
|
|
|
119
120
|
if (segment === "..") {
|
|
120
121
|
throw new FsSafeError("invalid-path", "relative path must not contain '..'");
|
|
121
122
|
}
|
|
123
|
+
if (isDriveRelativePath(segment)) {
|
|
124
|
+
throw new FsSafeError("invalid-path", "relative path must not contain a drive letter");
|
|
125
|
+
}
|
|
122
126
|
}
|
|
123
127
|
return segments;
|
|
124
128
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-exec.d.ts","sourceRoot":"","sources":["../src/permission-exec.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,kCAAkC,QAAS,CAAC;AAEzD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,SAAqC,GAC7C,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB7C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
export const DEFAULT_PERMISSION_EXEC_TIMEOUT_MS = 30_000;
|
|
5
|
+
export async function executePermissionCommand(command, args, timeoutMs = DEFAULT_PERMISSION_EXEC_TIMEOUT_MS) {
|
|
6
|
+
try {
|
|
7
|
+
return (await execFileAsync(command, args, {
|
|
8
|
+
encoding: "utf8",
|
|
9
|
+
windowsHide: true,
|
|
10
|
+
maxBuffer: 1024 * 1024,
|
|
11
|
+
timeout: timeoutMs,
|
|
12
|
+
killSignal: "SIGKILL",
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
if (err &&
|
|
17
|
+
typeof err === "object" &&
|
|
18
|
+
"killed" in err &&
|
|
19
|
+
err.killed === true &&
|
|
20
|
+
"signal" in err &&
|
|
21
|
+
err.signal === "SIGKILL") {
|
|
22
|
+
throw new Error(`Windows permission inspection timed out after ${timeoutMs}ms`, {
|
|
23
|
+
cause: err,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { PermissionCheck, PermissionCheckOptions, SafeStatResult } from "./permissions.js";
|
|
2
|
+
export type PermissionExec = (command: string, args: string[]) => Promise<{
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}>;
|
|
6
|
+
export type WindowsAclEntry = {
|
|
7
|
+
principal: string;
|
|
8
|
+
/** Canonical principal SID when resolved from Windows. */
|
|
9
|
+
sid?: string;
|
|
10
|
+
rights: string[];
|
|
11
|
+
rawRights: string;
|
|
12
|
+
canRead: boolean;
|
|
13
|
+
canWrite: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type WindowsAclSummary = {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
entries: WindowsAclEntry[];
|
|
18
|
+
untrustedWorld: WindowsAclEntry[];
|
|
19
|
+
untrustedGroup: WindowsAclEntry[];
|
|
20
|
+
trusted: WindowsAclEntry[];
|
|
21
|
+
error?: string;
|
|
22
|
+
};
|
|
23
|
+
export type WindowsUserInfoProvider = () => {
|
|
24
|
+
username?: string | null;
|
|
25
|
+
};
|
|
26
|
+
export type IcaclsResetCommandOptions = {
|
|
27
|
+
isDir: boolean;
|
|
28
|
+
env?: NodeJS.ProcessEnv;
|
|
29
|
+
userInfo?: WindowsUserInfoProvider;
|
|
30
|
+
};
|
|
31
|
+
export declare function inspectWindowsPermissions(params: {
|
|
32
|
+
targetPath: string;
|
|
33
|
+
stat: SafeStatResult;
|
|
34
|
+
effectiveIsDir: boolean;
|
|
35
|
+
effectiveMode: number | null;
|
|
36
|
+
bits: number | null;
|
|
37
|
+
opts?: PermissionCheckOptions;
|
|
38
|
+
}): Promise<PermissionCheck>;
|
|
39
|
+
export declare function resolveWindowsUserPrincipal(env?: NodeJS.ProcessEnv, userInfo?: WindowsUserInfoProvider): string | null;
|
|
40
|
+
export declare function parseIcaclsOutput(output: string, targetPath: string): WindowsAclEntry[];
|
|
41
|
+
export declare function summarizeWindowsAcl(entries: WindowsAclEntry[], env?: NodeJS.ProcessEnv): Pick<WindowsAclSummary, "trusted" | "untrustedWorld" | "untrustedGroup">;
|
|
42
|
+
export declare function inspectWindowsAcl(targetPath: string, opts?: {
|
|
43
|
+
env?: NodeJS.ProcessEnv;
|
|
44
|
+
exec?: PermissionExec;
|
|
45
|
+
currentUserSid?: string;
|
|
46
|
+
principalSids?: Record<string, string>;
|
|
47
|
+
principalTranslationFailed?: boolean;
|
|
48
|
+
}): Promise<WindowsAclSummary>;
|
|
49
|
+
export declare function formatWindowsAclSummary(summary: WindowsAclSummary): string;
|
|
50
|
+
export declare function formatIcaclsResetCommand(targetPath: string, opts: IcaclsResetCommandOptions): string;
|
|
51
|
+
export declare function createIcaclsResetCommand(targetPath: string, opts: IcaclsResetCommandOptions): {
|
|
52
|
+
command: string;
|
|
53
|
+
args: string[];
|
|
54
|
+
display: string;
|
|
55
|
+
} | null;
|
|
56
|
+
//# sourceMappingURL=permissions-windows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-windows.d.ts","sourceRoot":"","sources":["../src/permissions-windows.ts"],"names":[],"mappings":"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"}
|