@openclaw/fs-safe 0.2.4 → 0.2.5
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 +15 -1
- package/README.md +2 -0
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +12 -1
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +6 -5
- package/dist/json-document-store.d.ts +1 -1
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +5 -3
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +157 -4
- package/dist/json-stringify.d.ts +2 -0
- package/dist/json-stringify.d.ts.map +1 -0
- package/dist/json-stringify.js +7 -0
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +3 -2
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +4 -1
- package/dist/path.js +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +7 -10
- package/dist/pinned-python.d.ts.map +1 -1
- package/dist/pinned-python.js +125 -43
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +6 -2
- package/dist/root-paths.js +25 -3
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +3 -1
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +8 -1
- package/docs/index.md +1 -1
- package/docs/json-store.md +3 -1
- package/package.json +4 -4
- package/dist/archive-utils.d.ts +0 -3
- package/dist/archive-utils.d.ts.map +0 -1
- package/dist/archive-utils.js +0 -26
- package/dist/boundary-file-read.d.ts +0 -44
- package/dist/boundary-file-read.d.ts.map +0 -1
- package/dist/boundary-file-read.js +0 -129
- package/dist/boundary-path.d.ts +0 -39
- package/dist/boundary-path.d.ts.map +0 -1
- package/dist/boundary-path.js +0 -598
- package/dist/fs-pinned-path-helper.d.ts +0 -7
- package/dist/fs-pinned-path-helper.d.ts.map +0 -1
- package/dist/fs-pinned-path-helper.js +0 -182
- package/dist/fs-pinned-write-helper.d.ts +0 -21
- package/dist/fs-pinned-write-helper.d.ts.map +0 -1
- package/dist/fs-pinned-write-helper.js +0 -263
- package/dist/hardlink-guards.d.ts +0 -7
- package/dist/hardlink-guards.d.ts.map +0 -1
- package/dist/hardlink-guards.js +0 -30
- package/dist/install-safe-path.d.ts +0 -20
- package/dist/install-safe-path.d.ts.map +0 -1
- package/dist/install-safe-path.js +0 -94
- package/dist/json-file.d.ts +0 -3
- package/dist/json-file.d.ts.map +0 -1
- package/dist/json-file.js +0 -123
- package/dist/json-files.d.ts +0 -20
- package/dist/json-files.d.ts.map +0 -1
- package/dist/json-files.js +0 -153
- package/dist/path-alias-guards.d.ts +0 -19
- package/dist/path-alias-guards.d.ts.map +0 -1
- package/dist/path-alias-guards.js +0 -21
- package/dist/path-guards.d.ts +0 -7
- package/dist/path-guards.d.ts.map +0 -1
- package/dist/path-guards.js +0 -49
- package/dist/path-safety.d.ts +0 -12
- package/dist/path-safety.d.ts.map +0 -1
- package/dist/path-safety.js +0 -50
- package/dist/path-scope.d.ts +0 -2
- package/dist/path-scope.d.ts.map +0 -1
- package/dist/path-scope.js +0 -1
- package/dist/private-file-store.d.ts +0 -60
- package/dist/private-file-store.d.ts.map +0 -1
- package/dist/private-file-store.js +0 -233
- package/dist/safe-open-sync.d.ts +0 -24
- package/dist/safe-open-sync.d.ts.map +0 -1
- package/dist/safe-open-sync.js +0 -71
- package/dist/safe-root.d.ts +0 -123
- package/dist/safe-root.d.ts.map +0 -1
- package/dist/safe-root.js +0 -1060
- package/dist/secure-temp-workspace.d.ts +0 -25
- package/dist/secure-temp-workspace.d.ts.map +0 -1
- package/dist/secure-temp-workspace.js +0 -136
- package/dist/sibling-temp-file.d.ts +0 -16
- package/dist/sibling-temp-file.d.ts.map +0 -1
- package/dist/sibling-temp-file.js +0 -73
- package/dist/sibling-temp-write.d.ts +0 -8
- package/dist/sibling-temp-write.d.ts.map +0 -1
- package/dist/sibling-temp-write.js +0 -40
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import { FsSafeError } from "./errors.js";
|
|
5
|
-
import { fileStore } from "./file-store.js";
|
|
6
|
-
import { isPathInside } from "./path.js";
|
|
7
|
-
import { readRegularFileSync } from "./regular-file.js";
|
|
8
|
-
import { root } from "./root.js";
|
|
9
|
-
import { writeSecretFileAtomic } from "./secret-file.js";
|
|
10
|
-
function resolvePrivateStorePath(rootDir, relativePath) {
|
|
11
|
-
const root = path.resolve(rootDir);
|
|
12
|
-
const raw = relativePath.trim();
|
|
13
|
-
if (!raw || path.isAbsolute(raw) || raw.includes("\0")) {
|
|
14
|
-
throw new Error("Private file path must be a relative path.");
|
|
15
|
-
}
|
|
16
|
-
const resolved = path.resolve(root, raw);
|
|
17
|
-
const rel = path.relative(root, resolved);
|
|
18
|
-
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
19
|
-
throw new Error("Private file path must stay under the private store root.");
|
|
20
|
-
}
|
|
21
|
-
return resolved;
|
|
22
|
-
}
|
|
23
|
-
export async function writePrivateTextAtomic(params) {
|
|
24
|
-
await writeSecretFileAtomic(params);
|
|
25
|
-
}
|
|
26
|
-
export async function readPrivateText(params) {
|
|
27
|
-
const rootDir = path.resolve(params.rootDir);
|
|
28
|
-
const filePath = path.resolve(params.filePath);
|
|
29
|
-
const relative = path.relative(rootDir, filePath);
|
|
30
|
-
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
31
|
-
throw new Error("Private file path must stay under the private store root.");
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
const storeRoot = await root(rootDir, { hardlinks: "reject", maxBytes: params.maxBytes });
|
|
35
|
-
const result = await storeRoot.read(relative);
|
|
36
|
-
return result.buffer.toString("utf8");
|
|
37
|
-
}
|
|
38
|
-
catch (err) {
|
|
39
|
-
if (err instanceof FsSafeError && err.code === "not-found") {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
throw err;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function assertPrivateReadPathSync(params) {
|
|
46
|
-
const rootDir = path.resolve(params.rootDir);
|
|
47
|
-
const filePath = path.resolve(params.filePath);
|
|
48
|
-
const relative = path.relative(rootDir, filePath);
|
|
49
|
-
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
50
|
-
throw new Error("Private file path must stay under the private store root.");
|
|
51
|
-
}
|
|
52
|
-
const rootStat = fs.lstatSync(rootDir);
|
|
53
|
-
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
54
|
-
throw new Error(`Private file root must be a directory: ${rootDir}`);
|
|
55
|
-
}
|
|
56
|
-
const rootReal = fs.realpathSync(rootDir);
|
|
57
|
-
const fileReal = fs.realpathSync(filePath);
|
|
58
|
-
if (!isPathInside(rootReal, fileReal)) {
|
|
59
|
-
throw new Error("Private file path must stay under the private store root.");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
export function readPrivateTextSync(params) {
|
|
63
|
-
try {
|
|
64
|
-
assertPrivateReadPathSync(params);
|
|
65
|
-
const result = readRegularFileSync({
|
|
66
|
-
filePath: path.resolve(params.filePath),
|
|
67
|
-
maxBytes: params.maxBytes,
|
|
68
|
-
});
|
|
69
|
-
if (result.stat.nlink > 1) {
|
|
70
|
-
throw new Error(`Private file target must not be hardlinked: ${params.filePath}`);
|
|
71
|
-
}
|
|
72
|
-
return result.buffer.toString("utf8");
|
|
73
|
-
}
|
|
74
|
-
catch (err) {
|
|
75
|
-
if (err.code === "ENOENT") {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
throw err;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
export async function readPrivateJson(params) {
|
|
82
|
-
const raw = await readPrivateText(params);
|
|
83
|
-
return raw === null ? null : JSON.parse(raw);
|
|
84
|
-
}
|
|
85
|
-
export function readPrivateJsonSync(params) {
|
|
86
|
-
const raw = readPrivateTextSync(params);
|
|
87
|
-
return raw === null ? null : JSON.parse(raw);
|
|
88
|
-
}
|
|
89
|
-
function ensurePrivateDirectorySync(rootDir, targetDir) {
|
|
90
|
-
const root = path.resolve(rootDir);
|
|
91
|
-
const target = path.resolve(targetDir);
|
|
92
|
-
const relative = path.relative(root, target);
|
|
93
|
-
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
94
|
-
throw new Error("Private file directory must stay under the private store root.");
|
|
95
|
-
}
|
|
96
|
-
let current = root;
|
|
97
|
-
fs.mkdirSync(current, { recursive: true, mode: 0o700 });
|
|
98
|
-
const rootStat = fs.lstatSync(current);
|
|
99
|
-
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
100
|
-
throw new Error(`Private file root must be a directory: ${current}`);
|
|
101
|
-
}
|
|
102
|
-
for (const segment of relative.split(path.sep).filter(Boolean)) {
|
|
103
|
-
current = path.join(current, segment);
|
|
104
|
-
try {
|
|
105
|
-
const stat = fs.lstatSync(current);
|
|
106
|
-
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
107
|
-
throw new Error(`Private file directory component must be a directory: ${current}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch (err) {
|
|
111
|
-
if (err.code !== "ENOENT") {
|
|
112
|
-
throw err;
|
|
113
|
-
}
|
|
114
|
-
fs.mkdirSync(current, { mode: 0o700 });
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
fs.chmodSync(current, 0o700);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
// Best-effort on platforms that do not enforce POSIX modes.
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
export function writePrivateTextAtomicSync(params) {
|
|
125
|
-
const root = path.resolve(params.rootDir);
|
|
126
|
-
const filePath = path.resolve(params.filePath);
|
|
127
|
-
const relative = path.relative(root, filePath);
|
|
128
|
-
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
129
|
-
throw new Error("Private file path must stay under the private store root.");
|
|
130
|
-
}
|
|
131
|
-
ensurePrivateDirectorySync(root, path.dirname(filePath));
|
|
132
|
-
try {
|
|
133
|
-
const stat = fs.lstatSync(filePath);
|
|
134
|
-
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
135
|
-
throw new Error(`Private file target must be a regular file: ${filePath}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
if (err.code !== "ENOENT") {
|
|
140
|
-
throw err;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const tempPath = path.join(path.dirname(filePath), `.tmp-${process.pid}-${randomUUID()}`);
|
|
144
|
-
let created = false;
|
|
145
|
-
try {
|
|
146
|
-
fs.writeFileSync(tempPath, params.content, { mode: 0o600, flag: "wx" });
|
|
147
|
-
created = true;
|
|
148
|
-
try {
|
|
149
|
-
fs.chmodSync(tempPath, 0o600);
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
// Best-effort on platforms that do not enforce POSIX modes.
|
|
153
|
-
}
|
|
154
|
-
fs.renameSync(tempPath, filePath);
|
|
155
|
-
created = false;
|
|
156
|
-
try {
|
|
157
|
-
fs.chmodSync(filePath, 0o600);
|
|
158
|
-
}
|
|
159
|
-
catch {
|
|
160
|
-
// Best-effort on platforms that do not enforce POSIX modes.
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
finally {
|
|
164
|
-
if (created) {
|
|
165
|
-
try {
|
|
166
|
-
fs.unlinkSync(tempPath);
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
// The temp file is best-effort cleanup after write failure.
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
export async function writePrivateJsonAtomic(params) {
|
|
175
|
-
const json = JSON.stringify(params.value, null, 2);
|
|
176
|
-
await writeSecretFileAtomic({
|
|
177
|
-
rootDir: params.rootDir,
|
|
178
|
-
filePath: params.filePath,
|
|
179
|
-
content: params.trailingNewline && !json.endsWith("\n") ? `${json}\n` : json,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
export function writePrivateJsonAtomicSync(params) {
|
|
183
|
-
const json = JSON.stringify(params.value, null, 2);
|
|
184
|
-
writePrivateTextAtomicSync({
|
|
185
|
-
rootDir: params.rootDir,
|
|
186
|
-
filePath: params.filePath,
|
|
187
|
-
content: params.trailingNewline && !json.endsWith("\n") ? `${json}\n` : json,
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
export function privateStateStore(options) {
|
|
191
|
-
const rootDir = path.resolve(options.rootDir);
|
|
192
|
-
const store = fileStore({ rootDir, private: true });
|
|
193
|
-
return {
|
|
194
|
-
...store,
|
|
195
|
-
rootDir,
|
|
196
|
-
path: (relativePath) => resolvePrivateStorePath(rootDir, relativePath),
|
|
197
|
-
readText: async (relativePath, options) => {
|
|
198
|
-
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
199
|
-
return await readPrivateText({
|
|
200
|
-
rootDir,
|
|
201
|
-
filePath: safePath,
|
|
202
|
-
maxBytes: options?.maxBytes,
|
|
203
|
-
});
|
|
204
|
-
},
|
|
205
|
-
readJson: async (relativePath, options) => {
|
|
206
|
-
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
207
|
-
return await readPrivateJson({
|
|
208
|
-
rootDir,
|
|
209
|
-
filePath: safePath,
|
|
210
|
-
maxBytes: options?.maxBytes,
|
|
211
|
-
});
|
|
212
|
-
},
|
|
213
|
-
writeText: async (relativePath, content) => {
|
|
214
|
-
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
215
|
-
await writePrivateTextAtomic({
|
|
216
|
-
rootDir,
|
|
217
|
-
filePath: safePath,
|
|
218
|
-
content,
|
|
219
|
-
});
|
|
220
|
-
return safePath;
|
|
221
|
-
},
|
|
222
|
-
writeJson: async (relativePath, value, options) => {
|
|
223
|
-
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
224
|
-
await writePrivateJsonAtomic({
|
|
225
|
-
rootDir,
|
|
226
|
-
filePath: safePath,
|
|
227
|
-
value,
|
|
228
|
-
trailingNewline: options?.trailingNewline,
|
|
229
|
-
});
|
|
230
|
-
return safePath;
|
|
231
|
-
},
|
|
232
|
-
};
|
|
233
|
-
}
|
package/dist/safe-open-sync.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
export type SafeOpenSyncFailureReason = "path" | "validation" | "io";
|
|
3
|
-
export type SafeOpenSyncResult = {
|
|
4
|
-
ok: true;
|
|
5
|
-
path: string;
|
|
6
|
-
fd: number;
|
|
7
|
-
stat: fs.Stats;
|
|
8
|
-
} | {
|
|
9
|
-
ok: false;
|
|
10
|
-
reason: SafeOpenSyncFailureReason;
|
|
11
|
-
error?: unknown;
|
|
12
|
-
};
|
|
13
|
-
export type SafeOpenSyncAllowedType = "file" | "directory";
|
|
14
|
-
export type SafeOpenSyncFs = Pick<typeof fs, "constants" | "lstatSync" | "realpathSync" | "openSync" | "fstatSync" | "closeSync">;
|
|
15
|
-
export declare function openVerifiedFileSync(params: {
|
|
16
|
-
filePath: string;
|
|
17
|
-
resolvedPath?: string;
|
|
18
|
-
rejectPathSymlink?: boolean;
|
|
19
|
-
rejectHardlinks?: boolean;
|
|
20
|
-
maxBytes?: number;
|
|
21
|
-
allowedType?: SafeOpenSyncAllowedType;
|
|
22
|
-
ioFs?: SafeOpenSyncFs;
|
|
23
|
-
}): SafeOpenSyncResult;
|
|
24
|
-
//# sourceMappingURL=safe-open-sync.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"safe-open-sync.d.ts","sourceRoot":"","sources":["../src/safe-open-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAGzB,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC;AAErE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAA;CAAE,GACtD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,OAAO,EAAE,EACT,WAAW,GAAG,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CACpF,CAAC;AAYF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,GAAG,kBAAkB,CA2DrB"}
|
package/dist/safe-open-sync.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { sameFileIdentity as hasSameFileIdentity } from "./file-identity.js";
|
|
3
|
-
function isExpectedPathError(error) {
|
|
4
|
-
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
|
|
5
|
-
return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
|
|
6
|
-
}
|
|
7
|
-
function sameFileIdentity(left, right) {
|
|
8
|
-
return hasSameFileIdentity(left, right);
|
|
9
|
-
}
|
|
10
|
-
export function openVerifiedFileSync(params) {
|
|
11
|
-
const ioFs = params.ioFs ?? fs;
|
|
12
|
-
const allowedType = params.allowedType ?? "file";
|
|
13
|
-
const openReadFlags = ioFs.constants.O_RDONLY |
|
|
14
|
-
(typeof ioFs.constants.O_NOFOLLOW === "number" ? ioFs.constants.O_NOFOLLOW : 0);
|
|
15
|
-
let fd = null;
|
|
16
|
-
try {
|
|
17
|
-
if (params.rejectPathSymlink) {
|
|
18
|
-
const candidateStat = ioFs.lstatSync(params.filePath);
|
|
19
|
-
if (candidateStat.isSymbolicLink()) {
|
|
20
|
-
return { ok: false, reason: "validation" };
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
const realPath = params.resolvedPath ?? ioFs.realpathSync(params.filePath);
|
|
24
|
-
const preOpenStat = ioFs.lstatSync(realPath);
|
|
25
|
-
if (!isAllowedType(preOpenStat, allowedType)) {
|
|
26
|
-
return { ok: false, reason: "validation" };
|
|
27
|
-
}
|
|
28
|
-
if (params.rejectHardlinks && preOpenStat.isFile() && preOpenStat.nlink > 1) {
|
|
29
|
-
return { ok: false, reason: "validation" };
|
|
30
|
-
}
|
|
31
|
-
if (params.maxBytes !== undefined &&
|
|
32
|
-
preOpenStat.isFile() &&
|
|
33
|
-
preOpenStat.size > params.maxBytes) {
|
|
34
|
-
return { ok: false, reason: "validation" };
|
|
35
|
-
}
|
|
36
|
-
fd = ioFs.openSync(realPath, openReadFlags);
|
|
37
|
-
const openedStat = ioFs.fstatSync(fd);
|
|
38
|
-
if (!isAllowedType(openedStat, allowedType)) {
|
|
39
|
-
return { ok: false, reason: "validation" };
|
|
40
|
-
}
|
|
41
|
-
if (params.rejectHardlinks && openedStat.isFile() && openedStat.nlink > 1) {
|
|
42
|
-
return { ok: false, reason: "validation" };
|
|
43
|
-
}
|
|
44
|
-
if (params.maxBytes !== undefined && openedStat.isFile() && openedStat.size > params.maxBytes) {
|
|
45
|
-
return { ok: false, reason: "validation" };
|
|
46
|
-
}
|
|
47
|
-
if (!sameFileIdentity(preOpenStat, openedStat)) {
|
|
48
|
-
return { ok: false, reason: "validation" };
|
|
49
|
-
}
|
|
50
|
-
const opened = { ok: true, path: realPath, fd, stat: openedStat };
|
|
51
|
-
fd = null;
|
|
52
|
-
return opened;
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
if (isExpectedPathError(error)) {
|
|
56
|
-
return { ok: false, reason: "path", error };
|
|
57
|
-
}
|
|
58
|
-
return { ok: false, reason: "io", error };
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
if (fd !== null) {
|
|
62
|
-
ioFs.closeSync(fd);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function isAllowedType(stat, allowedType) {
|
|
67
|
-
if (allowedType === "directory") {
|
|
68
|
-
return stat.isDirectory();
|
|
69
|
-
}
|
|
70
|
-
return stat.isFile();
|
|
71
|
-
}
|
package/dist/safe-root.d.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import type { Stats } from "node:fs";
|
|
2
|
-
import type { FileHandle } from "node:fs/promises";
|
|
3
|
-
import type { SafeDirEntry, SafePathStat } from "./types.js";
|
|
4
|
-
export type SafeOpenResult = {
|
|
5
|
-
handle: FileHandle;
|
|
6
|
-
realPath: string;
|
|
7
|
-
stat: Stats;
|
|
8
|
-
};
|
|
9
|
-
export type SafeLocalReadResult = {
|
|
10
|
-
buffer: Buffer;
|
|
11
|
-
realPath: string;
|
|
12
|
-
stat: Stats;
|
|
13
|
-
};
|
|
14
|
-
export type SafeRootContext = {
|
|
15
|
-
rootDir: string;
|
|
16
|
-
rootReal: string;
|
|
17
|
-
rootWithSep: string;
|
|
18
|
-
};
|
|
19
|
-
export type SafeRootOptions = {
|
|
20
|
-
rootDir: string;
|
|
21
|
-
defaults?: SafeRootDefaults;
|
|
22
|
-
};
|
|
23
|
-
export type SafeRootDefaults = {
|
|
24
|
-
append?: SafeRootAppendOptions;
|
|
25
|
-
copy?: SafeRootCopyOptions;
|
|
26
|
-
/** openFile merges read defaults first, then open defaults, then call options. */
|
|
27
|
-
open?: SafeRootOpenOptions;
|
|
28
|
-
openWritable?: SafeRootOpenWritableOptions;
|
|
29
|
-
read?: SafeRootReadOptions;
|
|
30
|
-
write?: SafeRootWriteOptions;
|
|
31
|
-
};
|
|
32
|
-
export type SafeRootSymlinkPolicy = "reject" | "follow-within-root";
|
|
33
|
-
export type SafeRootReadOptions = {
|
|
34
|
-
rejectHardlinks?: boolean;
|
|
35
|
-
nonBlockingRead?: boolean;
|
|
36
|
-
symlinkPolicy?: SafeRootSymlinkPolicy;
|
|
37
|
-
maxBytes?: number;
|
|
38
|
-
};
|
|
39
|
-
export type SafeRootOpenOptions = Omit<SafeRootReadOptions, "maxBytes">;
|
|
40
|
-
export type SafeRootWriteOptions = {
|
|
41
|
-
encoding?: BufferEncoding;
|
|
42
|
-
mkdir?: boolean;
|
|
43
|
-
};
|
|
44
|
-
export type SafeRootOpenWritableOptions = {
|
|
45
|
-
mkdir?: boolean;
|
|
46
|
-
mode?: number;
|
|
47
|
-
truncateExisting?: boolean;
|
|
48
|
-
append?: boolean;
|
|
49
|
-
};
|
|
50
|
-
export type SafeRootCopyOptions = {
|
|
51
|
-
maxBytes?: number;
|
|
52
|
-
mkdir?: boolean;
|
|
53
|
-
rejectSourceHardlinks?: boolean;
|
|
54
|
-
};
|
|
55
|
-
export type SafeRootWriteJsonOptions = SafeRootWriteOptions & {
|
|
56
|
-
replacer?: Parameters<typeof JSON.stringify>[1];
|
|
57
|
-
space?: Parameters<typeof JSON.stringify>[2];
|
|
58
|
-
trailingNewline?: boolean;
|
|
59
|
-
};
|
|
60
|
-
export type SafeRootCreateOptions = SafeRootWriteOptions;
|
|
61
|
-
export type SafeRootCreateJsonOptions = SafeRootWriteJsonOptions;
|
|
62
|
-
export type SafeRootAppendOptions = {
|
|
63
|
-
encoding?: BufferEncoding;
|
|
64
|
-
mkdir?: boolean;
|
|
65
|
-
prependNewlineIfNeeded?: boolean;
|
|
66
|
-
};
|
|
67
|
-
export declare class SafeRoot {
|
|
68
|
-
readonly rootDir: string;
|
|
69
|
-
readonly rootReal: string;
|
|
70
|
-
readonly rootWithSep: string;
|
|
71
|
-
readonly defaults: SafeRootDefaults;
|
|
72
|
-
constructor(context: SafeRootContext, defaults?: SafeRootDefaults);
|
|
73
|
-
private get context();
|
|
74
|
-
resolve(relativePath: string): Promise<string>;
|
|
75
|
-
openFile(relativePath: string, options?: SafeRootOpenOptions): Promise<SafeOpenResult>;
|
|
76
|
-
readFile(relativePath: string, options?: SafeRootReadOptions): Promise<SafeLocalReadResult>;
|
|
77
|
-
readFileBytes(relativePath: string, options?: SafeRootReadOptions): Promise<Buffer>;
|
|
78
|
-
readText(relativePath: string, options?: SafeRootReadOptions & {
|
|
79
|
-
encoding?: BufferEncoding;
|
|
80
|
-
}): Promise<string>;
|
|
81
|
-
readJson<T = unknown>(relativePath: string, options?: SafeRootReadOptions & {
|
|
82
|
-
encoding?: BufferEncoding;
|
|
83
|
-
}): Promise<T>;
|
|
84
|
-
readPath(filePath: string, options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): Promise<SafeLocalReadResult>;
|
|
85
|
-
reader(options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): (filePath: string) => Promise<Buffer>;
|
|
86
|
-
openWritable(relativePath: string, options?: SafeRootOpenWritableOptions): Promise<SafeWritableOpenResult>;
|
|
87
|
-
append(relativePath: string, data: string | Buffer, options?: SafeRootAppendOptions): Promise<void>;
|
|
88
|
-
removeFile(relativePath: string): Promise<void>;
|
|
89
|
-
mkdirp(relativePath: string, options?: {
|
|
90
|
-
allowRoot?: boolean;
|
|
91
|
-
}): Promise<void>;
|
|
92
|
-
writeFile(relativePath: string, data: string | Buffer, options?: SafeRootWriteOptions): Promise<void>;
|
|
93
|
-
createFile(relativePath: string, data: string | Buffer, options?: SafeRootCreateOptions): Promise<boolean>;
|
|
94
|
-
writeJson(relativePath: string, data: unknown, options?: SafeRootWriteJsonOptions): Promise<void>;
|
|
95
|
-
createJson(relativePath: string, data: unknown, options?: SafeRootCreateJsonOptions): Promise<boolean>;
|
|
96
|
-
copyFileFrom(sourcePath: string, relativePath: string, options?: SafeRootCopyOptions): Promise<void>;
|
|
97
|
-
statPath(relativePath: string): Promise<SafePathStat>;
|
|
98
|
-
listDir(relativePath: string, options?: {
|
|
99
|
-
withFileTypes?: false;
|
|
100
|
-
}): Promise<string[]>;
|
|
101
|
-
listDir(relativePath: string, options: {
|
|
102
|
-
withFileTypes: true;
|
|
103
|
-
}): Promise<SafeDirEntry[]>;
|
|
104
|
-
move(from: string, to: string, options?: {
|
|
105
|
-
overwrite?: boolean;
|
|
106
|
-
}): Promise<void>;
|
|
107
|
-
}
|
|
108
|
-
export declare function safeRoot(rootDir: string, defaults?: SafeRootDefaults): Promise<SafeRoot>;
|
|
109
|
-
export declare function readLocalFileSafely(params: {
|
|
110
|
-
filePath: string;
|
|
111
|
-
maxBytes?: number;
|
|
112
|
-
}): Promise<SafeLocalReadResult>;
|
|
113
|
-
export declare function openLocalFileSafely(params: {
|
|
114
|
-
filePath: string;
|
|
115
|
-
}): Promise<SafeOpenResult>;
|
|
116
|
-
export type SafeWritableOpenResult = {
|
|
117
|
-
handle: FileHandle;
|
|
118
|
-
createdForWrite: boolean;
|
|
119
|
-
realPath: string;
|
|
120
|
-
stat: Stats;
|
|
121
|
-
};
|
|
122
|
-
export declare function resolveOpenedFileRealPathForHandle(handle: FileHandle, ioPath: string): Promise<string>;
|
|
123
|
-
//# sourceMappingURL=safe-root.d.ts.map
|
package/dist/safe-root.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"safe-root.d.ts","sourceRoot":"","sources":["../src/safe-root.ts"],"names":[],"mappings":"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"}
|