@lousy-agents/lint 5.14.9 → 5.15.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/dist/116.js +4 -3
- package/dist/316.js +4 -3
- package/dist/475.js +2 -2
- package/dist/653.js +13 -10
- package/dist/index.js +416 -204
- package/package.json +1 -1
package/dist/116.js
CHANGED
|
@@ -2,9 +2,6 @@ export const __rspack_esm_id = 116;
|
|
|
2
2
|
export const __rspack_esm_ids = [116];
|
|
3
3
|
export const __webpack_modules__ = {
|
|
4
4
|
8711(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
5
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
6
|
-
prompt: () => (prompt)
|
|
7
|
-
});
|
|
8
5
|
/* import */ var node_util__rspack_import_0 = __webpack_require__(7975);
|
|
9
6
|
/* import */ var node_process__rspack_import_1 = __webpack_require__(1708);
|
|
10
7
|
/* import */ var node_readline__rspack_import_2 = __webpack_require__(481);
|
|
@@ -290,6 +287,10 @@ async function prompt(message, opts = {}) {
|
|
|
290
287
|
|
|
291
288
|
|
|
292
289
|
|
|
290
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
291
|
+
prompt: () => (prompt)
|
|
292
|
+
});
|
|
293
|
+
|
|
293
294
|
|
|
294
295
|
},
|
|
295
296
|
|
package/dist/316.js
CHANGED
|
@@ -4,9 +4,6 @@ export const __webpack_modules__ = {
|
|
|
4
4
|
5375(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
5
5
|
var zlib__rspack_import_11_namespace_cache;
|
|
6
6
|
__webpack_require__.r(__webpack_exports__);
|
|
7
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
8
|
-
t: () => (ce)
|
|
9
|
-
});
|
|
10
7
|
/* import */ var _rolldown_runtime_mjs__rspack_import_14 = __webpack_require__(421);
|
|
11
8
|
/* import */ var node_fs__rspack_import_0 = __webpack_require__(3024);
|
|
12
9
|
/* import */ var node_fs_promises__rspack_import_1 = __webpack_require__(1455);
|
|
@@ -2954,6 +2951,10 @@ var Da = (e) => {
|
|
|
2954
2951
|
};
|
|
2955
2952
|
|
|
2956
2953
|
|
|
2954
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
2955
|
+
t: () => (ce)
|
|
2956
|
+
});
|
|
2957
|
+
|
|
2957
2958
|
|
|
2958
2959
|
},
|
|
2959
2960
|
|
package/dist/475.js
CHANGED
|
@@ -1729,10 +1729,10 @@ class FSWatcher extends external_node_events_.EventEmitter {
|
|
|
1729
1729
|
const options = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
|
|
1730
1730
|
let stream = readdirp(root, options);
|
|
1731
1731
|
this._streams.add(stream);
|
|
1732
|
-
stream.once(STR_CLOSE, () => {
|
|
1732
|
+
stream.once((/* inlined export .STR_CLOSE */"close"), () => {
|
|
1733
1733
|
stream = undefined;
|
|
1734
1734
|
});
|
|
1735
|
-
stream.once(STR_END, () => {
|
|
1735
|
+
stream.once((/* inlined export .STR_END */"end"), () => {
|
|
1736
1736
|
if (stream) {
|
|
1737
1737
|
this._streams.delete(stream);
|
|
1738
1738
|
stream = undefined;
|
package/dist/653.js
CHANGED
|
@@ -3,13 +3,6 @@ export const __rspack_esm_ids = [653];
|
|
|
3
3
|
export const __webpack_modules__ = {
|
|
4
4
|
292(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
5
5
|
__webpack_require__.r(__webpack_exports__);
|
|
6
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
7
|
-
a: () => (y),
|
|
8
|
-
i: () => (S),
|
|
9
|
-
n: () => (ne),
|
|
10
|
-
r: () => (C),
|
|
11
|
-
t: () => (oe)
|
|
12
|
-
});
|
|
13
6
|
/* import */ var _rolldown_runtime_mjs__rspack_import_8 = __webpack_require__(421);
|
|
14
7
|
/* import */ var node_module__rspack_import_0 = __webpack_require__(8995);
|
|
15
8
|
/* import */ var node_fs__rspack_import_1 = __webpack_require__(3024);
|
|
@@ -689,12 +682,18 @@ async function fe(e = {}) {
|
|
|
689
682
|
}
|
|
690
683
|
|
|
691
684
|
|
|
685
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
686
|
+
t: () => (oe)
|
|
687
|
+
}, {
|
|
688
|
+
a: y,
|
|
689
|
+
i: S,
|
|
690
|
+
n: ne,
|
|
691
|
+
r: C
|
|
692
|
+
});
|
|
693
|
+
|
|
692
694
|
|
|
693
695
|
},
|
|
694
696
|
421(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
695
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
696
|
-
t: () => (__exportAll)
|
|
697
|
-
});
|
|
698
697
|
/* import */ var node_module__rspack_import_0 = __webpack_require__(8995);
|
|
699
698
|
|
|
700
699
|
//#region \0rolldown/runtime.js
|
|
@@ -711,6 +710,10 @@ var __exportAll = (all, no_symbols) => {
|
|
|
711
710
|
//#endregion
|
|
712
711
|
|
|
713
712
|
|
|
713
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
714
|
+
t: () => (__exportAll)
|
|
715
|
+
});
|
|
716
|
+
|
|
714
717
|
|
|
715
718
|
},
|
|
716
719
|
8900(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
package/dist/index.js
CHANGED
|
@@ -566,7 +566,7 @@ module.exports = webpackEmptyAsyncContext;
|
|
|
566
566
|
|
|
567
567
|
|
|
568
568
|
},
|
|
569
|
-
|
|
569
|
+
9884(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
570
570
|
|
|
571
571
|
// EXPORTS
|
|
572
572
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -1031,6 +1031,9 @@ function path_isPathInside(root, target) {
|
|
|
1031
1031
|
const firstSegment = relative.split(external_node_path_.posix.sep)[0];
|
|
1032
1032
|
return relative === "" || (firstSegment !== ".." && !external_node_path_.isAbsolute(relative));
|
|
1033
1033
|
}
|
|
1034
|
+
function isPathRelativeEscape(relativePath) {
|
|
1035
|
+
return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
|
|
1036
|
+
}
|
|
1034
1037
|
function resolveSafeBaseDir(rootDir) {
|
|
1035
1038
|
const resolved = path.resolve(rootDir);
|
|
1036
1039
|
return resolved.endsWith(path.sep) ? resolved : `${resolved}${path.sep}`;
|
|
@@ -1179,17 +1182,17 @@ function directory_guard_createNearestExistingSyncDirectoryGuard(rootReal, targe
|
|
|
1179
1182
|
|
|
1180
1183
|
|
|
1181
1184
|
|
|
1185
|
+
|
|
1182
1186
|
function isSameOrChildPath(candidate, parent) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
function isPathEscape(relativePath) {
|
|
1186
|
-
return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
|
|
1187
|
+
const parentPrefix = parent.endsWith(external_node_path_.sep) ? parent : `${parent}${external_node_path_.sep}`;
|
|
1188
|
+
return candidate === parent || candidate.startsWith(parentPrefix);
|
|
1187
1189
|
}
|
|
1188
1190
|
async function mkdirPathComponentsWithGuards(params) {
|
|
1189
1191
|
const root = external_node_path_.resolve(params.rootReal);
|
|
1192
|
+
const rootCanonical = external_node_path_.resolve(await promises_.realpath(root));
|
|
1190
1193
|
const target = external_node_path_.resolve(params.targetPath);
|
|
1191
1194
|
const relative = external_node_path_.relative(root, target);
|
|
1192
|
-
if (
|
|
1195
|
+
if (isPathRelativeEscape(relative)) {
|
|
1193
1196
|
throw new errors_FsSafeError("outside-workspace", "directory is outside workspace root");
|
|
1194
1197
|
}
|
|
1195
1198
|
let current = root;
|
|
@@ -1212,7 +1215,7 @@ async function mkdirPathComponentsWithGuards(params) {
|
|
|
1212
1215
|
}
|
|
1213
1216
|
// Node's recursive mkdir follows symlinks in missing components. Build one
|
|
1214
1217
|
// segment at a time and realpath-check each segment before descending.
|
|
1215
|
-
if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)),
|
|
1218
|
+
if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)), rootCanonical)) {
|
|
1216
1219
|
throw new errors_FsSafeError("outside-workspace", "directory escaped workspace root");
|
|
1217
1220
|
}
|
|
1218
1221
|
await directory_guard_createAsyncDirectoryGuard(next);
|
|
@@ -1299,6 +1302,191 @@ function guardedRmSync(params) {
|
|
|
1299
1302
|
}), { verifyAfter: params.verifyAfter });
|
|
1300
1303
|
}
|
|
1301
1304
|
|
|
1305
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/deny-mutations.js
|
|
1306
|
+
|
|
1307
|
+
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
async function pathExists(filePath) {
|
|
1311
|
+
try {
|
|
1312
|
+
await promises_.lstat(filePath);
|
|
1313
|
+
return true;
|
|
1314
|
+
}
|
|
1315
|
+
catch (err) {
|
|
1316
|
+
if (!path_isNotFoundPathError(err)) {
|
|
1317
|
+
throw err;
|
|
1318
|
+
}
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
async function resolvePathViaExistingAncestor(targetPath) {
|
|
1323
|
+
const normalized = external_node_path_.resolve(targetPath);
|
|
1324
|
+
let cursor = normalized;
|
|
1325
|
+
const missingSuffix = [];
|
|
1326
|
+
while (external_node_path_.dirname(cursor) !== cursor && !(await pathExists(cursor))) {
|
|
1327
|
+
missingSuffix.unshift(external_node_path_.basename(cursor));
|
|
1328
|
+
cursor = external_node_path_.dirname(cursor);
|
|
1329
|
+
}
|
|
1330
|
+
if (!(await pathExists(cursor))) {
|
|
1331
|
+
return normalized;
|
|
1332
|
+
}
|
|
1333
|
+
try {
|
|
1334
|
+
const resolvedAncestor = external_node_path_.resolve(await promises_.realpath(cursor));
|
|
1335
|
+
return missingSuffix.length === 0
|
|
1336
|
+
? resolvedAncestor
|
|
1337
|
+
: external_node_path_.resolve(resolvedAncestor, ...missingSuffix);
|
|
1338
|
+
}
|
|
1339
|
+
catch {
|
|
1340
|
+
return normalized;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
async function comparablePaths(rawPath) {
|
|
1344
|
+
path_assertNoNulPathInput(rawPath, "path contains a NUL byte");
|
|
1345
|
+
const resolved = external_node_path_.resolve(rawPath);
|
|
1346
|
+
return new Set([resolved, await resolvePathViaExistingAncestor(resolved)]);
|
|
1347
|
+
}
|
|
1348
|
+
function isSamePath(left, right) {
|
|
1349
|
+
return path_isPathInside(left, right) && path_isPathInside(right, left);
|
|
1350
|
+
}
|
|
1351
|
+
function hasPolicyEntries(policy) {
|
|
1352
|
+
return Boolean(policy?.paths?.length || policy?.prefixes?.length);
|
|
1353
|
+
}
|
|
1354
|
+
function policyPathEntries(entries) {
|
|
1355
|
+
const paths = [];
|
|
1356
|
+
for (const entry of entries ?? []) {
|
|
1357
|
+
if (entry.length === 0) {
|
|
1358
|
+
throw new errors_FsSafeError("invalid-path", "deny mutation paths must be non-empty");
|
|
1359
|
+
}
|
|
1360
|
+
path_assertNoNulPathInput(entry, "deny mutation path contains a NUL byte");
|
|
1361
|
+
if (!external_node_path_.isAbsolute(entry)) {
|
|
1362
|
+
throw new errors_FsSafeError("invalid-path", "deny mutation paths must be absolute");
|
|
1363
|
+
}
|
|
1364
|
+
paths.push(entry);
|
|
1365
|
+
}
|
|
1366
|
+
return paths;
|
|
1367
|
+
}
|
|
1368
|
+
async function assertMutationNotDenied(filePath, policy, options = {}) {
|
|
1369
|
+
if (!hasPolicyEntries(policy)) {
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const targetPaths = await comparablePaths(filePath);
|
|
1373
|
+
for (const deniedPath of policyPathEntries(policy.paths)) {
|
|
1374
|
+
const deniedPaths = await comparablePaths(deniedPath);
|
|
1375
|
+
for (const target of targetPaths) {
|
|
1376
|
+
for (const denied of deniedPaths) {
|
|
1377
|
+
if (isSamePath(denied, target) ||
|
|
1378
|
+
(options.protectAncestors === true && path_isPathInside(target, denied))) {
|
|
1379
|
+
throw new errors_FsSafeError("denied-path", "path is denied by denyMutations policy");
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
for (const deniedPrefix of policyPathEntries(policy.prefixes)) {
|
|
1385
|
+
const deniedPaths = await comparablePaths(deniedPrefix);
|
|
1386
|
+
for (const target of targetPaths) {
|
|
1387
|
+
for (const denied of deniedPaths) {
|
|
1388
|
+
if (path_isPathInside(denied, target) ||
|
|
1389
|
+
(options.protectAncestors === true && path_isPathInside(target, denied))) {
|
|
1390
|
+
throw new errors_FsSafeError("denied-path", "path is denied by denyMutations policy");
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
function mergeDenyMutationPolicies(defaultPolicy, callPolicy) {
|
|
1397
|
+
if (!defaultPolicy) {
|
|
1398
|
+
return callPolicy;
|
|
1399
|
+
}
|
|
1400
|
+
if (!callPolicy) {
|
|
1401
|
+
return defaultPolicy;
|
|
1402
|
+
}
|
|
1403
|
+
return {
|
|
1404
|
+
paths: [...(defaultPolicy.paths ?? []), ...(callPolicy.paths ?? [])],
|
|
1405
|
+
prefixes: [...(defaultPolicy.prefixes ?? []), ...(callPolicy.prefixes ?? [])],
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/opened-realpath.js
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
|
|
1415
|
+
async function resolveOpenedFileRealPathForHandle(handle, ioPath) {
|
|
1416
|
+
const handleStat = await handle.stat();
|
|
1417
|
+
const fdCandidates = process.platform === "linux"
|
|
1418
|
+
? [`/proc/self/fd/${handle.fd}`, `/dev/fd/${handle.fd}`]
|
|
1419
|
+
: process.platform === "win32"
|
|
1420
|
+
? []
|
|
1421
|
+
: [`/dev/fd/${handle.fd}`];
|
|
1422
|
+
for (const fdPath of fdCandidates) {
|
|
1423
|
+
try {
|
|
1424
|
+
const fdRealPath = await promises_.realpath(fdPath);
|
|
1425
|
+
const fdRealStat = await promises_.stat(fdRealPath);
|
|
1426
|
+
if (file_identity_sameFileIdentity(handleStat, fdRealStat)) {
|
|
1427
|
+
return fdRealPath;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
catch {
|
|
1431
|
+
// try next fd path
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
try {
|
|
1435
|
+
const ioRealPath = await promises_.realpath(ioPath);
|
|
1436
|
+
const ioRealStat = await promises_.stat(ioRealPath);
|
|
1437
|
+
if (file_identity_sameFileIdentity(handleStat, ioRealStat)) {
|
|
1438
|
+
return ioRealPath;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
catch (err) {
|
|
1442
|
+
if (!path_isNotFoundPathError(err)) {
|
|
1443
|
+
throw err;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
const parentResolved = await resolveOpenedFileRealPathFromParent(handleStat, ioPath);
|
|
1447
|
+
if (parentResolved) {
|
|
1448
|
+
return parentResolved;
|
|
1449
|
+
}
|
|
1450
|
+
throw new errors_FsSafeError("path-mismatch", "unable to resolve opened file path");
|
|
1451
|
+
}
|
|
1452
|
+
async function resolveOpenedFileRealPathFromParent(handleStat, ioPath) {
|
|
1453
|
+
let parentReal;
|
|
1454
|
+
try {
|
|
1455
|
+
parentReal = await promises_.realpath(external_node_path_.dirname(ioPath));
|
|
1456
|
+
}
|
|
1457
|
+
catch (err) {
|
|
1458
|
+
if (path_isNotFoundPathError(err)) {
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
throw err;
|
|
1462
|
+
}
|
|
1463
|
+
let entries;
|
|
1464
|
+
try {
|
|
1465
|
+
entries = await promises_.readdir(parentReal);
|
|
1466
|
+
}
|
|
1467
|
+
catch (err) {
|
|
1468
|
+
if (path_isNotFoundPathError(err)) {
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
throw err;
|
|
1472
|
+
}
|
|
1473
|
+
for (const entry of entries.toSorted()) {
|
|
1474
|
+
const candidatePath = external_node_path_.join(parentReal, entry);
|
|
1475
|
+
try {
|
|
1476
|
+
const candidateStat = await promises_.lstat(candidatePath);
|
|
1477
|
+
if (candidateStat.isFile() && file_identity_sameFileIdentity(handleStat, candidateStat)) {
|
|
1478
|
+
return await promises_.realpath(candidatePath);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
catch (err) {
|
|
1482
|
+
if (!path_isNotFoundPathError(err)) {
|
|
1483
|
+
throw err;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1302
1490
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-python-config.js
|
|
1303
1491
|
let overrideConfig = {};
|
|
1304
1492
|
function parseMode(value) {
|
|
@@ -1347,26 +1535,20 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
1347
1535
|
|
|
1348
1536
|
|
|
1349
1537
|
const PINNED_PYTHON_WORKER_SOURCE = String.raw `
|
|
1350
|
-
import base64
|
|
1351
|
-
import errno
|
|
1352
|
-
import json
|
|
1353
|
-
import os
|
|
1354
|
-
import secrets
|
|
1355
|
-
import stat
|
|
1356
|
-
import sys
|
|
1357
|
-
|
|
1538
|
+
import base64, errno, json, os, secrets, stat, sys
|
|
1358
1539
|
DIR_FLAGS = os.O_RDONLY
|
|
1359
1540
|
if hasattr(os, "O_DIRECTORY"):
|
|
1360
1541
|
DIR_FLAGS |= os.O_DIRECTORY
|
|
1361
1542
|
if hasattr(os, "O_NOFOLLOW"):
|
|
1362
1543
|
DIR_FLAGS |= os.O_NOFOLLOW
|
|
1363
1544
|
READ_FLAGS = os.O_RDONLY
|
|
1545
|
+
if hasattr(os, "O_NONBLOCK"):
|
|
1546
|
+
READ_FLAGS |= os.O_NONBLOCK
|
|
1364
1547
|
if hasattr(os, "O_NOFOLLOW"):
|
|
1365
1548
|
READ_FLAGS |= os.O_NOFOLLOW
|
|
1366
1549
|
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
1367
1550
|
if hasattr(os, "O_NOFOLLOW"):
|
|
1368
1551
|
WRITE_FLAGS |= os.O_NOFOLLOW
|
|
1369
|
-
|
|
1370
1552
|
def split_relative(value):
|
|
1371
1553
|
if value in ("", "."):
|
|
1372
1554
|
return []
|
|
@@ -1379,10 +1561,8 @@ def split_relative(value):
|
|
|
1379
1561
|
if part == "..":
|
|
1380
1562
|
raise OSError(errno.EPERM, "path traversal is not allowed")
|
|
1381
1563
|
return parts
|
|
1382
|
-
|
|
1383
1564
|
def open_dir(path_value, dir_fd=None):
|
|
1384
1565
|
return os.open(path_value, DIR_FLAGS, dir_fd=dir_fd)
|
|
1385
|
-
|
|
1386
1566
|
def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
1387
1567
|
current_fd = os.dup(root_fd)
|
|
1388
1568
|
try:
|
|
@@ -1400,14 +1580,12 @@ def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
|
1400
1580
|
except Exception:
|
|
1401
1581
|
os.close(current_fd)
|
|
1402
1582
|
raise
|
|
1403
|
-
|
|
1404
1583
|
def parent_and_basename(root_fd, relative):
|
|
1405
1584
|
segments = split_relative(relative)
|
|
1406
1585
|
if not segments:
|
|
1407
1586
|
raise OSError(errno.EPERM, "operation requires a non-root path")
|
|
1408
1587
|
parent_fd = walk_dir(root_fd, segments[:-1])
|
|
1409
1588
|
return parent_fd, segments[-1]
|
|
1410
|
-
|
|
1411
1589
|
def encode_stat(st):
|
|
1412
1590
|
mode = st.st_mode
|
|
1413
1591
|
return {
|
|
@@ -1423,14 +1601,12 @@ def encode_stat(st):
|
|
|
1423
1601
|
"size": st.st_size,
|
|
1424
1602
|
"uid": st.st_uid,
|
|
1425
1603
|
}
|
|
1426
|
-
|
|
1427
1604
|
def reject_unsafe_endpoint(st):
|
|
1428
1605
|
mode = st.st_mode
|
|
1429
1606
|
if stat.S_ISLNK(mode):
|
|
1430
1607
|
raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
|
|
1431
1608
|
if stat.S_ISREG(mode) and st.st_nlink > 1:
|
|
1432
1609
|
raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
|
|
1433
|
-
|
|
1434
1610
|
def copy_bytes(source_fd, dest_fd):
|
|
1435
1611
|
while True:
|
|
1436
1612
|
chunk = os.read(source_fd, 65536)
|
|
@@ -1442,7 +1618,6 @@ def copy_bytes(source_fd, dest_fd):
|
|
|
1442
1618
|
if written <= 0:
|
|
1443
1619
|
raise OSError(errno.EIO, "short write")
|
|
1444
1620
|
view = view[written:]
|
|
1445
|
-
|
|
1446
1621
|
def write_all(fd, data):
|
|
1447
1622
|
view = memoryview(data)
|
|
1448
1623
|
while view:
|
|
@@ -1450,11 +1625,9 @@ def write_all(fd, data):
|
|
|
1450
1625
|
if written <= 0:
|
|
1451
1626
|
raise OSError(errno.EIO, "short write")
|
|
1452
1627
|
view = view[written:]
|
|
1453
|
-
|
|
1454
1628
|
def link_unsupported(exc):
|
|
1455
1629
|
unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
|
|
1456
1630
|
return getattr(exc, "errno", None) in unsupported
|
|
1457
|
-
|
|
1458
1631
|
def link_no_replace(name, new_name, source_fd, target_fd):
|
|
1459
1632
|
linked = False
|
|
1460
1633
|
try:
|
|
@@ -1469,11 +1642,9 @@ def link_no_replace(name, new_name, source_fd, target_fd):
|
|
|
1469
1642
|
os.fsync(source_fd)
|
|
1470
1643
|
if source_fd != target_fd:
|
|
1471
1644
|
os.fsync(target_fd)
|
|
1472
|
-
|
|
1473
1645
|
def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basename, mode, expected=None, unlink_source=False):
|
|
1474
1646
|
source_fd = os.open(source_name, READ_FLAGS, dir_fd=source_parent_fd)
|
|
1475
|
-
dest_fd = None
|
|
1476
|
-
success = False
|
|
1647
|
+
dest_fd = None; success = False; dest_stat = None
|
|
1477
1648
|
try:
|
|
1478
1649
|
if expected is not None:
|
|
1479
1650
|
source_stat = os.fstat(source_fd)
|
|
@@ -1482,6 +1653,7 @@ def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basena
|
|
|
1482
1653
|
dest_fd = os.open(basename, WRITE_FLAGS, mode, dir_fd=target_parent_fd)
|
|
1483
1654
|
copy_bytes(source_fd, dest_fd)
|
|
1484
1655
|
os.fsync(dest_fd)
|
|
1656
|
+
dest_stat = os.fstat(dest_fd)
|
|
1485
1657
|
success = True
|
|
1486
1658
|
finally:
|
|
1487
1659
|
os.close(source_fd)
|
|
@@ -1497,19 +1669,40 @@ def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basena
|
|
|
1497
1669
|
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
1498
1670
|
except FileNotFoundError: pass
|
|
1499
1671
|
raise
|
|
1500
|
-
|
|
1501
|
-
def
|
|
1672
|
+
return dest_stat
|
|
1673
|
+
def same_identity(left, right):
|
|
1674
|
+
return left.st_dev == right.st_dev and left.st_ino == right.st_ino
|
|
1675
|
+
def verify_temp_name(parent_fd, temp_name, expected_stat):
|
|
1676
|
+
current_stat = os.lstat(temp_name, dir_fd=parent_fd)
|
|
1677
|
+
if stat.S_ISLNK(current_stat.st_mode) or not same_identity(current_stat, expected_stat):
|
|
1678
|
+
raise RuntimeError("fs-safe-temp-mismatch")
|
|
1679
|
+
def verify_committed_temp(parent_fd, basename, expected_stat):
|
|
1680
|
+
final_stat = os.lstat(basename, dir_fd=parent_fd)
|
|
1681
|
+
if not stat.S_ISLNK(final_stat.st_mode) and same_identity(final_stat, expected_stat):
|
|
1682
|
+
return final_stat
|
|
1683
|
+
try: os.unlink(basename, dir_fd=parent_fd)
|
|
1684
|
+
except FileNotFoundError: pass
|
|
1685
|
+
raise RuntimeError("fs-safe-temp-mismatch")
|
|
1686
|
+
def commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, expected_stat):
|
|
1687
|
+
verify_temp_name(parent_fd, temp_name, expected_stat)
|
|
1502
1688
|
if overwrite:
|
|
1503
1689
|
os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
|
|
1690
|
+
return verify_committed_temp(parent_fd, basename, expected_stat)
|
|
1504
1691
|
else:
|
|
1505
1692
|
try:
|
|
1506
1693
|
os.link(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd, follow_symlinks=False)
|
|
1694
|
+
final_stat = verify_committed_temp(parent_fd, basename, expected_stat)
|
|
1507
1695
|
os.unlink(temp_name, dir_fd=parent_fd)
|
|
1696
|
+
return final_stat
|
|
1508
1697
|
except OSError as exc:
|
|
1509
1698
|
if not link_unsupported(exc):
|
|
1510
1699
|
raise
|
|
1511
|
-
copy_file_no_replace(parent_fd, temp_name, parent_fd, basename, mode,
|
|
1512
|
-
|
|
1700
|
+
return copy_file_no_replace(parent_fd, temp_name, parent_fd, basename, mode, expected_stat, True)
|
|
1701
|
+
def assert_expected_root(root_fd, payload):
|
|
1702
|
+
if "rootDev" in payload or "rootIno" in payload:
|
|
1703
|
+
root_stat = os.fstat(root_fd)
|
|
1704
|
+
if root_stat.st_dev != int(payload["rootDev"]) or root_stat.st_ino != int(payload["rootIno"]):
|
|
1705
|
+
raise RuntimeError("fs-safe-root-mismatch")
|
|
1513
1706
|
def stat_path(root_fd, payload):
|
|
1514
1707
|
relative = payload.get("relativePath", "")
|
|
1515
1708
|
segments = split_relative(relative)
|
|
@@ -1523,7 +1716,6 @@ def stat_path(root_fd, payload):
|
|
|
1523
1716
|
return encode_stat(st)
|
|
1524
1717
|
finally:
|
|
1525
1718
|
os.close(parent_fd)
|
|
1526
|
-
|
|
1527
1719
|
def readdir_path(root_fd, payload):
|
|
1528
1720
|
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")))
|
|
1529
1721
|
try:
|
|
@@ -1539,12 +1731,9 @@ def readdir_path(root_fd, payload):
|
|
|
1539
1731
|
return entries
|
|
1540
1732
|
finally:
|
|
1541
1733
|
os.close(dir_fd)
|
|
1542
|
-
|
|
1543
1734
|
def mkdirp_path(root_fd, payload):
|
|
1544
1735
|
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")), mkdir_enabled=True)
|
|
1545
|
-
os.close(dir_fd)
|
|
1546
|
-
return None
|
|
1547
|
-
|
|
1736
|
+
os.close(dir_fd); return None
|
|
1548
1737
|
def remove_tree(parent_fd, basename):
|
|
1549
1738
|
st = os.lstat(basename, dir_fd=parent_fd)
|
|
1550
1739
|
if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
|
|
@@ -1557,7 +1746,6 @@ def remove_tree(parent_fd, basename):
|
|
|
1557
1746
|
os.rmdir(basename, dir_fd=parent_fd)
|
|
1558
1747
|
else:
|
|
1559
1748
|
os.unlink(basename, dir_fd=parent_fd)
|
|
1560
|
-
|
|
1561
1749
|
def remove_path(root_fd, payload):
|
|
1562
1750
|
parent_fd, basename = parent_and_basename(root_fd, payload.get("relativePath", ""))
|
|
1563
1751
|
try:
|
|
@@ -1640,14 +1828,15 @@ def write_path(root_fd, payload):
|
|
|
1640
1828
|
except FileNotFoundError:
|
|
1641
1829
|
pass
|
|
1642
1830
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
1831
|
+
os.fchmod(temp_fd, mode)
|
|
1643
1832
|
write_all(temp_fd, data)
|
|
1644
1833
|
os.fsync(temp_fd)
|
|
1834
|
+
temp_stat = os.fstat(temp_fd)
|
|
1645
1835
|
os.close(temp_fd)
|
|
1646
1836
|
temp_fd = None
|
|
1647
|
-
commit_temp_file(parent_fd, temp_name, basename, overwrite, mode)
|
|
1837
|
+
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
1648
1838
|
temp_name = None
|
|
1649
1839
|
os.fsync(parent_fd)
|
|
1650
|
-
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
1651
1840
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
1652
1841
|
finally:
|
|
1653
1842
|
if temp_fd is not None:
|
|
@@ -1678,6 +1867,7 @@ def copy_path(root_fd, payload):
|
|
|
1678
1867
|
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
|
|
1679
1868
|
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
1680
1869
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
1870
|
+
os.fchmod(temp_fd, mode)
|
|
1681
1871
|
written_bytes = 0
|
|
1682
1872
|
while True:
|
|
1683
1873
|
chunk = os.read(source_fd, 65536)
|
|
@@ -1693,12 +1883,12 @@ def copy_path(root_fd, payload):
|
|
|
1693
1883
|
raise OSError(errno.EIO, "short write")
|
|
1694
1884
|
view = view[written:]
|
|
1695
1885
|
os.fsync(temp_fd)
|
|
1886
|
+
temp_stat = os.fstat(temp_fd)
|
|
1696
1887
|
os.close(temp_fd)
|
|
1697
1888
|
temp_fd = None
|
|
1698
|
-
commit_temp_file(parent_fd, temp_name, basename, overwrite, mode)
|
|
1889
|
+
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
1699
1890
|
temp_name = None
|
|
1700
1891
|
os.fsync(parent_fd)
|
|
1701
|
-
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
1702
1892
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
1703
1893
|
finally:
|
|
1704
1894
|
os.close(source_fd)
|
|
@@ -1715,6 +1905,7 @@ def copy_path(root_fd, payload):
|
|
|
1715
1905
|
def run_operation(operation, root_path, payload):
|
|
1716
1906
|
root_fd = open_dir(root_path)
|
|
1717
1907
|
try:
|
|
1908
|
+
assert_expected_root(root_fd, payload)
|
|
1718
1909
|
if operation == "stat":
|
|
1719
1910
|
return stat_path(root_fd, payload)
|
|
1720
1911
|
if operation == "readdir":
|
|
@@ -1819,6 +2010,12 @@ function mapWorkerError(response) {
|
|
|
1819
2010
|
if (message.includes("fs-safe-source-mismatch")) {
|
|
1820
2011
|
return new errors_FsSafeError("path-mismatch", "source path changed during copy");
|
|
1821
2012
|
}
|
|
2013
|
+
if (message.includes("fs-safe-temp-mismatch")) {
|
|
2014
|
+
return new errors_FsSafeError("path-mismatch", "temp path changed during write");
|
|
2015
|
+
}
|
|
2016
|
+
if (message.includes("fs-safe-root-mismatch")) {
|
|
2017
|
+
return new errors_FsSafeError("path-mismatch", "root path changed during operation");
|
|
2018
|
+
}
|
|
1822
2019
|
if (message.includes("fs-safe-directory-noreplace-unsupported")) {
|
|
1823
2020
|
return new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
1824
2021
|
}
|
|
@@ -1999,9 +2196,7 @@ function validatePinnedOperationPayload(payload) {
|
|
|
1999
2196
|
}
|
|
2000
2197
|
}
|
|
2001
2198
|
function isPinnedHelperUnavailable(error) {
|
|
2002
|
-
return error instanceof Error &&
|
|
2003
|
-
"code" in error &&
|
|
2004
|
-
error.code === "helper-unavailable";
|
|
2199
|
+
return error instanceof Error && "code" in error && error.code === "helper-unavailable";
|
|
2005
2200
|
}
|
|
2006
2201
|
function validatePinnedRelativePath(relativePath) {
|
|
2007
2202
|
if (relativePath.length === 0 || relativePath === ".") {
|
|
@@ -2099,29 +2294,21 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
2099
2294
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
2100
2295
|
}
|
|
2101
2296
|
}
|
|
2102
|
-
function
|
|
2103
|
-
if (maxBytes === undefined) {
|
|
2104
|
-
return undefined;
|
|
2105
|
-
}
|
|
2297
|
+
async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
2106
2298
|
let bytes = 0;
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2299
|
+
for await (const chunk of stream) {
|
|
2300
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2301
|
+
bytes += buffer.byteLength;
|
|
2302
|
+
assertWithinMaxBytes(bytes, maxBytes);
|
|
2303
|
+
let offset = 0;
|
|
2304
|
+
while (offset < buffer.byteLength) {
|
|
2305
|
+
const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset);
|
|
2306
|
+
if (bytesWritten <= 0) {
|
|
2307
|
+
throw new errors_FsSafeError("helper-failed", "fallback stream write made no progress");
|
|
2113
2308
|
}
|
|
2114
|
-
|
|
2115
|
-
}
|
|
2116
|
-
});
|
|
2117
|
-
}
|
|
2118
|
-
async function pipelineWithMaxBytes(stream, destination, maxBytes) {
|
|
2119
|
-
const limiter = pinned_write_createMaxBytesTransform(maxBytes);
|
|
2120
|
-
if (limiter) {
|
|
2121
|
-
await (0,external_node_stream_promises_.pipeline)(stream, limiter, destination);
|
|
2122
|
-
return;
|
|
2309
|
+
offset += bytesWritten;
|
|
2310
|
+
}
|
|
2123
2311
|
}
|
|
2124
|
-
await (0,external_node_stream_promises_.pipeline)(stream, destination);
|
|
2125
2312
|
}
|
|
2126
2313
|
async function inputToBase64(input, maxBytes) {
|
|
2127
2314
|
if (input.kind === "buffer") {
|
|
@@ -2167,6 +2354,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
2167
2354
|
mode: params.mode || 0o600,
|
|
2168
2355
|
overwrite: params.overwrite !== false,
|
|
2169
2356
|
relativeParentPath: params.relativeParentPath,
|
|
2357
|
+
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
2170
2358
|
};
|
|
2171
2359
|
try {
|
|
2172
2360
|
return await runPinnedPythonOperation({
|
|
@@ -2197,6 +2385,7 @@ async function runPinnedCopyHelper(params) {
|
|
|
2197
2385
|
mode: params.mode || 0o600,
|
|
2198
2386
|
overwrite: params.overwrite !== false,
|
|
2199
2387
|
relativeParentPath: params.relativeParentPath,
|
|
2388
|
+
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
2200
2389
|
sourceDev: params.sourceIdentity.dev,
|
|
2201
2390
|
sourceIno: params.sourceIdentity.ino,
|
|
2202
2391
|
sourcePath: params.sourcePath,
|
|
@@ -2207,12 +2396,12 @@ async function runPinnedWriteFallback(params) {
|
|
|
2207
2396
|
const parentPath = params.relativeParentPath
|
|
2208
2397
|
? external_node_path_.join(params.rootPath, ...params.relativeParentPath.split("/"))
|
|
2209
2398
|
: params.rootPath;
|
|
2210
|
-
const parentGuard = await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
2211
2399
|
if (params.mkdir) {
|
|
2212
|
-
await
|
|
2213
|
-
await promises_.mkdir(parentPath, { recursive: true });
|
|
2214
|
-
});
|
|
2400
|
+
await mkdirPathComponentsWithGuards({ rootReal: params.rootPath, targetPath: parentPath });
|
|
2215
2401
|
}
|
|
2402
|
+
const parentGuard = params.mkdir
|
|
2403
|
+
? await directory_guard_createAsyncDirectoryGuard(parentPath)
|
|
2404
|
+
: await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
2216
2405
|
const targetPath = external_node_path_.join(parentPath, params.basename);
|
|
2217
2406
|
if (params.overwrite === false) {
|
|
2218
2407
|
let handle = await withAsyncDirectoryGuards([parentGuard], async () => await promises_.open(targetPath, external_node_fs_.constants.O_WRONLY | external_node_fs_.constants.O_CREAT | external_node_fs_.constants.O_EXCL, params.mode), {
|
|
@@ -2234,7 +2423,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
2234
2423
|
}
|
|
2235
2424
|
}
|
|
2236
2425
|
else {
|
|
2237
|
-
await
|
|
2426
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
2238
2427
|
}
|
|
2239
2428
|
const stat = await handle.stat();
|
|
2240
2429
|
created = false;
|
|
@@ -2255,7 +2444,9 @@ async function runPinnedWriteFallback(params) {
|
|
|
2255
2444
|
? external_node_fs_.constants.O_NOFOLLOW
|
|
2256
2445
|
: 0);
|
|
2257
2446
|
let handle;
|
|
2258
|
-
let
|
|
2447
|
+
let tempStat;
|
|
2448
|
+
let targetStat;
|
|
2449
|
+
let renamed = false;
|
|
2259
2450
|
try {
|
|
2260
2451
|
handle = await promises_.open(tempPath, tempFlags, params.mode);
|
|
2261
2452
|
if (params.input.kind === "buffer") {
|
|
@@ -2268,29 +2459,36 @@ async function runPinnedWriteFallback(params) {
|
|
|
2268
2459
|
}
|
|
2269
2460
|
}
|
|
2270
2461
|
else {
|
|
2271
|
-
|
|
2272
|
-
writable.once("close", () => {
|
|
2273
|
-
handleClosedByStream = true;
|
|
2274
|
-
});
|
|
2275
|
-
await pipelineWithMaxBytes(params.input.stream, writable, params.maxBytes);
|
|
2462
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
2276
2463
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2464
|
+
tempStat = await handle.stat();
|
|
2465
|
+
const tempPathStat = await promises_.lstat(tempPath);
|
|
2466
|
+
if (tempPathStat.isSymbolicLink() || !file_identity_sameFileIdentity(tempPathStat, tempStat)) {
|
|
2467
|
+
throw new errors_FsSafeError("path-mismatch", "fallback temp path changed during write");
|
|
2280
2468
|
}
|
|
2469
|
+
const expectedTempStat = tempStat;
|
|
2470
|
+
await handle.close().catch(() => undefined);
|
|
2471
|
+
handle = undefined;
|
|
2281
2472
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
2282
2473
|
await promises_.rename(tempPath, targetPath);
|
|
2474
|
+
renamed = true;
|
|
2475
|
+
targetStat = await promises_.lstat(targetPath);
|
|
2476
|
+
if (targetStat.isSymbolicLink() || !file_identity_sameFileIdentity(targetStat, expectedTempStat)) {
|
|
2477
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
|
|
2478
|
+
}
|
|
2283
2479
|
});
|
|
2284
2480
|
}
|
|
2285
2481
|
catch (error) {
|
|
2286
|
-
|
|
2287
|
-
|
|
2482
|
+
await handle?.close().catch(() => undefined);
|
|
2483
|
+
if (!renamed) {
|
|
2484
|
+
await promises_.rm(tempPath, { force: true }).catch(() => undefined);
|
|
2288
2485
|
}
|
|
2289
|
-
await promises_.rm(tempPath, { force: true }).catch(() => undefined);
|
|
2290
2486
|
throw error;
|
|
2291
2487
|
}
|
|
2292
|
-
|
|
2293
|
-
|
|
2488
|
+
if (!targetStat) {
|
|
2489
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target was not verified");
|
|
2490
|
+
}
|
|
2491
|
+
return { dev: targetStat.dev, ino: targetStat.ino };
|
|
2294
2492
|
}
|
|
2295
2493
|
|
|
2296
2494
|
// EXTERNAL MODULE: external "node:os"
|
|
@@ -2316,7 +2514,7 @@ async function resolveRootPath(params) {
|
|
|
2316
2514
|
const absolutePath = external_node_path_.resolve(params.absolutePath);
|
|
2317
2515
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
2318
2516
|
? external_node_path_.resolve(params.rootCanonicalPath)
|
|
2319
|
-
: await
|
|
2517
|
+
: await root_path_resolvePathViaExistingAncestor(rootPath);
|
|
2320
2518
|
const context = createBoundaryResolutionContext({
|
|
2321
2519
|
resolveParams: params,
|
|
2322
2520
|
rootPath,
|
|
@@ -2685,7 +2883,7 @@ async function resolveOutsideLexicalCanonicalPathAsync(params) {
|
|
|
2685
2883
|
if (path_isPathInside(params.rootPath, params.absolutePath)) {
|
|
2686
2884
|
return undefined;
|
|
2687
2885
|
}
|
|
2688
|
-
return await
|
|
2886
|
+
return await root_path_resolvePathViaExistingAncestor(params.absolutePath);
|
|
2689
2887
|
}
|
|
2690
2888
|
function resolveOutsideLexicalCanonicalPathSync(params) {
|
|
2691
2889
|
if (isPathInside(params.rootPath, params.absolutePath)) {
|
|
@@ -2732,11 +2930,11 @@ function buildResolvedRootPath(params) {
|
|
|
2732
2930
|
kind: params.kind.kind,
|
|
2733
2931
|
};
|
|
2734
2932
|
}
|
|
2735
|
-
async function
|
|
2933
|
+
async function root_path_resolvePathViaExistingAncestor(targetPath) {
|
|
2736
2934
|
const normalized = external_node_path_.resolve(targetPath);
|
|
2737
2935
|
let cursor = normalized;
|
|
2738
2936
|
const missingSuffix = [];
|
|
2739
|
-
while (!isFilesystemRoot(cursor) && !(await
|
|
2937
|
+
while (!isFilesystemRoot(cursor) && !(await root_path_pathExists(cursor))) {
|
|
2740
2938
|
missingSuffix.unshift(external_node_path_.basename(cursor));
|
|
2741
2939
|
const parent = external_node_path_.dirname(cursor);
|
|
2742
2940
|
if (parent === cursor) {
|
|
@@ -2744,7 +2942,7 @@ async function resolvePathViaExistingAncestor(targetPath) {
|
|
|
2744
2942
|
}
|
|
2745
2943
|
cursor = parent;
|
|
2746
2944
|
}
|
|
2747
|
-
if (!(await
|
|
2945
|
+
if (!(await root_path_pathExists(cursor))) {
|
|
2748
2946
|
return normalized;
|
|
2749
2947
|
}
|
|
2750
2948
|
try {
|
|
@@ -2829,7 +3027,7 @@ function relativeInsideRoot(rootPath, targetPath) {
|
|
|
2829
3027
|
if (!relative || relative === ".") {
|
|
2830
3028
|
return "";
|
|
2831
3029
|
}
|
|
2832
|
-
if (
|
|
3030
|
+
if (isPathRelativeEscape(relative)) {
|
|
2833
3031
|
return "";
|
|
2834
3032
|
}
|
|
2835
3033
|
return relative;
|
|
@@ -2856,7 +3054,7 @@ function shortPath(value) {
|
|
|
2856
3054
|
function isFilesystemRoot(candidate) {
|
|
2857
3055
|
return external_node_path_.parse(candidate).root === candidate;
|
|
2858
3056
|
}
|
|
2859
|
-
async function
|
|
3057
|
+
async function root_path_pathExists(targetPath) {
|
|
2860
3058
|
try {
|
|
2861
3059
|
await promises_.lstat(targetPath);
|
|
2862
3060
|
return true;
|
|
@@ -2878,7 +3076,7 @@ async function resolveSymlinkHopPath(symlinkPath) {
|
|
|
2878
3076
|
}
|
|
2879
3077
|
const linkTarget = await promises_.readlink(symlinkPath);
|
|
2880
3078
|
const linkAbsolute = external_node_path_.resolve(external_node_path_.dirname(symlinkPath), linkTarget);
|
|
2881
|
-
return
|
|
3079
|
+
return root_path_resolvePathViaExistingAncestor(linkAbsolute);
|
|
2882
3080
|
}
|
|
2883
3081
|
}
|
|
2884
3082
|
function resolveSymlinkHopPathSync(symlinkPath) {
|
|
@@ -2947,6 +3145,23 @@ function path_policy_shortPath(value) {
|
|
|
2947
3145
|
return value;
|
|
2948
3146
|
}
|
|
2949
3147
|
|
|
3148
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/read-opened-file.js
|
|
3149
|
+
|
|
3150
|
+
async function read_opened_file_readOpenedFileSafely(params) {
|
|
3151
|
+
if (params.maxBytes !== undefined && params.opened.stat.size > params.maxBytes) {
|
|
3152
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${params.opened.stat.size})`);
|
|
3153
|
+
}
|
|
3154
|
+
const buffer = await params.opened.handle.readFile();
|
|
3155
|
+
if (params.maxBytes !== undefined && buffer.byteLength > params.maxBytes) {
|
|
3156
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${buffer.byteLength})`);
|
|
3157
|
+
}
|
|
3158
|
+
return {
|
|
3159
|
+
buffer,
|
|
3160
|
+
realPath: params.opened.realPath,
|
|
3161
|
+
stat: params.opened.stat,
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
|
|
2950
3165
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path-stat.js
|
|
2951
3166
|
function pathStatFromStats(stat) {
|
|
2952
3167
|
return {
|
|
@@ -3274,6 +3489,10 @@ async function serializePathWrite(key, run) {
|
|
|
3274
3489
|
|
|
3275
3490
|
|
|
3276
3491
|
|
|
3492
|
+
|
|
3493
|
+
|
|
3494
|
+
|
|
3495
|
+
|
|
3277
3496
|
|
|
3278
3497
|
|
|
3279
3498
|
|
|
@@ -3472,6 +3691,7 @@ class RootHandle {
|
|
|
3472
3691
|
mkdir: this.defaults.mkdir,
|
|
3473
3692
|
mode: this.defaults.mode,
|
|
3474
3693
|
...options,
|
|
3694
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3475
3695
|
append: writeMode === "append",
|
|
3476
3696
|
truncateExisting: writeMode === "replace",
|
|
3477
3697
|
});
|
|
@@ -3483,18 +3703,29 @@ class RootHandle {
|
|
|
3483
3703
|
mkdir: this.defaults.mkdir,
|
|
3484
3704
|
mode: this.defaults.mode,
|
|
3485
3705
|
...options,
|
|
3706
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3486
3707
|
});
|
|
3487
3708
|
}
|
|
3488
|
-
async remove(relativePath) {
|
|
3709
|
+
async remove(relativePath, options = {}) {
|
|
3489
3710
|
assertValidRootRelativePath(relativePath);
|
|
3490
|
-
await removePathInRoot(this.context,
|
|
3711
|
+
await removePathInRoot(this.context, {
|
|
3712
|
+
relativePath,
|
|
3713
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3714
|
+
});
|
|
3491
3715
|
}
|
|
3492
|
-
async mkdir(relativePath) {
|
|
3716
|
+
async mkdir(relativePath, options = {}) {
|
|
3493
3717
|
assertValidRootRelativePath(relativePath);
|
|
3494
|
-
await mkdirPathInRoot(this.context, {
|
|
3718
|
+
await mkdirPathInRoot(this.context, {
|
|
3719
|
+
relativePath,
|
|
3720
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3721
|
+
});
|
|
3495
3722
|
}
|
|
3496
|
-
async ensureRoot() {
|
|
3497
|
-
await mkdirPathInRoot(this.context, {
|
|
3723
|
+
async ensureRoot(options = {}) {
|
|
3724
|
+
await mkdirPathInRoot(this.context, {
|
|
3725
|
+
relativePath: "",
|
|
3726
|
+
allowRoot: true,
|
|
3727
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3728
|
+
});
|
|
3498
3729
|
}
|
|
3499
3730
|
async write(relativePath, data, options = {}) {
|
|
3500
3731
|
await writeFileInRoot(this.context, {
|
|
@@ -3503,6 +3734,7 @@ class RootHandle {
|
|
|
3503
3734
|
mkdir: this.defaults.mkdir,
|
|
3504
3735
|
mode: this.defaults.mode,
|
|
3505
3736
|
...options,
|
|
3737
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3506
3738
|
});
|
|
3507
3739
|
}
|
|
3508
3740
|
async create(relativePath, data, options = {}) {
|
|
@@ -3512,6 +3744,7 @@ class RootHandle {
|
|
|
3512
3744
|
mkdir: this.defaults.mkdir,
|
|
3513
3745
|
mode: this.defaults.mode,
|
|
3514
3746
|
...options,
|
|
3747
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3515
3748
|
overwrite: false,
|
|
3516
3749
|
});
|
|
3517
3750
|
}
|
|
@@ -3534,6 +3767,7 @@ class RootHandle {
|
|
|
3534
3767
|
mkdir: this.defaults.mkdir,
|
|
3535
3768
|
mode: this.defaults.mode,
|
|
3536
3769
|
...options,
|
|
3770
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
3537
3771
|
});
|
|
3538
3772
|
}
|
|
3539
3773
|
async exists(relativePath) {
|
|
@@ -3577,6 +3811,12 @@ class RootHandle {
|
|
|
3577
3811
|
async move(fromRelative, toRelative, options = {}) {
|
|
3578
3812
|
assertValidRootRelativePath(fromRelative);
|
|
3579
3813
|
assertValidRootRelativePath(toRelative);
|
|
3814
|
+
const denyMutations = mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations);
|
|
3815
|
+
await assertMoveMutationAllowed(this.context, {
|
|
3816
|
+
fromRelative,
|
|
3817
|
+
toRelative,
|
|
3818
|
+
denyMutations,
|
|
3819
|
+
});
|
|
3580
3820
|
try {
|
|
3581
3821
|
await runPinnedHelper("rename", this.rootReal, {
|
|
3582
3822
|
from: fromRelative,
|
|
@@ -3588,6 +3828,7 @@ class RootHandle {
|
|
|
3588
3828
|
if (canFallbackFromPythonError(error)) {
|
|
3589
3829
|
await movePathFallback(this.context, {
|
|
3590
3830
|
fromRelative,
|
|
3831
|
+
denyMutations,
|
|
3591
3832
|
overwrite: options.overwrite ?? false,
|
|
3592
3833
|
toRelative,
|
|
3593
3834
|
});
|
|
@@ -3636,7 +3877,7 @@ async function openFileInRoot(root, params) {
|
|
|
3636
3877
|
async function readFileInRoot(root, params) {
|
|
3637
3878
|
const opened = await openFileInRoot(root, params);
|
|
3638
3879
|
try {
|
|
3639
|
-
return await
|
|
3880
|
+
return await read_opened_file_readOpenedFileSafely({ opened, maxBytes: params.maxBytes });
|
|
3640
3881
|
}
|
|
3641
3882
|
finally {
|
|
3642
3883
|
await opened.handle.close().catch(() => { });
|
|
@@ -3669,20 +3910,6 @@ async function openLocalFileSafely(params) {
|
|
|
3669
3910
|
assertNoNulPathInput(params.filePath, "file path contains a NUL byte");
|
|
3670
3911
|
return await openVerifiedLocalFile(params.filePath);
|
|
3671
3912
|
}
|
|
3672
|
-
async function readOpenedFileSafely(params) {
|
|
3673
|
-
if (params.maxBytes !== undefined && params.opened.stat.size > params.maxBytes) {
|
|
3674
|
-
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${params.opened.stat.size})`);
|
|
3675
|
-
}
|
|
3676
|
-
const buffer = await params.opened.handle.readFile();
|
|
3677
|
-
if (params.maxBytes !== undefined && buffer.byteLength > params.maxBytes) {
|
|
3678
|
-
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${buffer.byteLength})`);
|
|
3679
|
-
}
|
|
3680
|
-
return {
|
|
3681
|
-
buffer,
|
|
3682
|
-
realPath: params.opened.realPath,
|
|
3683
|
-
stat: params.opened.stat,
|
|
3684
|
-
};
|
|
3685
|
-
}
|
|
3686
3913
|
function emitWriteBoundaryWarning(reason) {
|
|
3687
3914
|
logWarn(`security: fs-safe write boundary warning (${reason})`);
|
|
3688
3915
|
}
|
|
@@ -3723,82 +3950,9 @@ async function verifyAtomicWriteResult(params) {
|
|
|
3723
3950
|
await opened.handle.close().catch(() => { });
|
|
3724
3951
|
}
|
|
3725
3952
|
}
|
|
3726
|
-
async function resolveOpenedFileRealPathForHandle(handle, ioPath) {
|
|
3727
|
-
const handleStat = await handle.stat();
|
|
3728
|
-
const fdCandidates = process.platform === "linux"
|
|
3729
|
-
? [`/proc/self/fd/${handle.fd}`, `/dev/fd/${handle.fd}`]
|
|
3730
|
-
: process.platform === "win32"
|
|
3731
|
-
? []
|
|
3732
|
-
: [`/dev/fd/${handle.fd}`];
|
|
3733
|
-
for (const fdPath of fdCandidates) {
|
|
3734
|
-
try {
|
|
3735
|
-
const fdRealPath = await promises_.realpath(fdPath);
|
|
3736
|
-
const fdRealStat = await promises_.stat(fdRealPath);
|
|
3737
|
-
if (file_identity_sameFileIdentity(handleStat, fdRealStat)) {
|
|
3738
|
-
return fdRealPath;
|
|
3739
|
-
}
|
|
3740
|
-
}
|
|
3741
|
-
catch {
|
|
3742
|
-
// try next fd path
|
|
3743
|
-
}
|
|
3744
|
-
}
|
|
3745
|
-
try {
|
|
3746
|
-
const ioRealPath = await promises_.realpath(ioPath);
|
|
3747
|
-
const ioRealStat = await promises_.stat(ioRealPath);
|
|
3748
|
-
if (file_identity_sameFileIdentity(handleStat, ioRealStat)) {
|
|
3749
|
-
return ioRealPath;
|
|
3750
|
-
}
|
|
3751
|
-
}
|
|
3752
|
-
catch (err) {
|
|
3753
|
-
if (!path_isNotFoundPathError(err)) {
|
|
3754
|
-
throw err;
|
|
3755
|
-
}
|
|
3756
|
-
}
|
|
3757
|
-
const parentResolved = await resolveOpenedFileRealPathFromParent(handleStat, ioPath);
|
|
3758
|
-
if (parentResolved) {
|
|
3759
|
-
return parentResolved;
|
|
3760
|
-
}
|
|
3761
|
-
throw new errors_FsSafeError("path-mismatch", "unable to resolve opened file path");
|
|
3762
|
-
}
|
|
3763
|
-
async function resolveOpenedFileRealPathFromParent(handleStat, ioPath) {
|
|
3764
|
-
let parentReal;
|
|
3765
|
-
try {
|
|
3766
|
-
parentReal = await promises_.realpath(external_node_path_.dirname(ioPath));
|
|
3767
|
-
}
|
|
3768
|
-
catch (err) {
|
|
3769
|
-
if (path_isNotFoundPathError(err)) {
|
|
3770
|
-
return null;
|
|
3771
|
-
}
|
|
3772
|
-
throw err;
|
|
3773
|
-
}
|
|
3774
|
-
let entries;
|
|
3775
|
-
try {
|
|
3776
|
-
entries = await promises_.readdir(parentReal);
|
|
3777
|
-
}
|
|
3778
|
-
catch (err) {
|
|
3779
|
-
if (path_isNotFoundPathError(err)) {
|
|
3780
|
-
return null;
|
|
3781
|
-
}
|
|
3782
|
-
throw err;
|
|
3783
|
-
}
|
|
3784
|
-
for (const entry of entries.toSorted()) {
|
|
3785
|
-
const candidatePath = external_node_path_.join(parentReal, entry);
|
|
3786
|
-
try {
|
|
3787
|
-
const candidateStat = await promises_.lstat(candidatePath);
|
|
3788
|
-
if (candidateStat.isFile() && file_identity_sameFileIdentity(handleStat, candidateStat)) {
|
|
3789
|
-
return await promises_.realpath(candidatePath);
|
|
3790
|
-
}
|
|
3791
|
-
}
|
|
3792
|
-
catch (err) {
|
|
3793
|
-
if (!path_isNotFoundPathError(err)) {
|
|
3794
|
-
throw err;
|
|
3795
|
-
}
|
|
3796
|
-
}
|
|
3797
|
-
}
|
|
3798
|
-
return null;
|
|
3799
|
-
}
|
|
3800
3953
|
async function openWritableFileInRoot(root, params) {
|
|
3801
3954
|
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
|
|
3955
|
+
await assertMutationNotDenied(resolved, params.denyMutations);
|
|
3802
3956
|
try {
|
|
3803
3957
|
await assertNoPathAliasEscape({
|
|
3804
3958
|
absolutePath: resolved,
|
|
@@ -3925,6 +4079,7 @@ async function appendFileInRoot(root, params) {
|
|
|
3925
4079
|
relativePath: params.relativePath,
|
|
3926
4080
|
mkdir: params.mkdir,
|
|
3927
4081
|
mode: params.mode,
|
|
4082
|
+
denyMutations: params.denyMutations,
|
|
3928
4083
|
truncateExisting: false,
|
|
3929
4084
|
append: true,
|
|
3930
4085
|
});
|
|
@@ -3952,8 +4107,8 @@ async function appendFileInRoot(root, params) {
|
|
|
3952
4107
|
await target.handle.close().catch(() => { });
|
|
3953
4108
|
}
|
|
3954
4109
|
}
|
|
3955
|
-
async function removePathInRoot(root,
|
|
3956
|
-
const resolved = await resolvePinnedRemovePathInRoot(root, relativePath);
|
|
4110
|
+
async function removePathInRoot(root, params) {
|
|
4111
|
+
const resolved = await resolvePinnedRemovePathInRoot(root, params.relativePath, params.denyMutations);
|
|
3957
4112
|
if (process.platform === "win32") {
|
|
3958
4113
|
await removePathFallback(resolved);
|
|
3959
4114
|
return;
|
|
@@ -4001,7 +4156,7 @@ async function writeFileInRoot(root, params) {
|
|
|
4001
4156
|
});
|
|
4002
4157
|
return;
|
|
4003
4158
|
}
|
|
4004
|
-
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode);
|
|
4159
|
+
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
|
|
4005
4160
|
await serializePathWrite(pinned.targetPath, async () => {
|
|
4006
4161
|
let identity;
|
|
4007
4162
|
try {
|
|
@@ -4057,7 +4212,7 @@ async function copyFileInRoot(root, params) {
|
|
|
4057
4212
|
});
|
|
4058
4213
|
return;
|
|
4059
4214
|
}
|
|
4060
|
-
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode);
|
|
4215
|
+
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
|
|
4061
4216
|
await serializePathWrite(pinned.targetPath, async () => {
|
|
4062
4217
|
let identity;
|
|
4063
4218
|
try {
|
|
@@ -4101,8 +4256,9 @@ async function copyFileInRoot(root, params) {
|
|
|
4101
4256
|
await source.handle.close().catch(() => { });
|
|
4102
4257
|
}
|
|
4103
4258
|
}
|
|
4104
|
-
async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode) {
|
|
4259
|
+
async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode, denyMutations) {
|
|
4105
4260
|
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath);
|
|
4261
|
+
await assertMutationNotDenied(resolved, denyMutations);
|
|
4106
4262
|
try {
|
|
4107
4263
|
await assertNoPathAliasEscape({
|
|
4108
4264
|
absolutePath: resolved,
|
|
@@ -4159,12 +4315,16 @@ async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode)
|
|
|
4159
4315
|
async function resolvePinnedPathInRoot(root, params) {
|
|
4160
4316
|
return await resolvePinnedOperationPathInRoot(root, {
|
|
4161
4317
|
allowRoot: params.allowRoot,
|
|
4318
|
+
denyMutations: params.denyMutations,
|
|
4319
|
+
protectDenyMutationAncestors: false,
|
|
4162
4320
|
relativePath: params.relativePath,
|
|
4163
4321
|
policy: PATH_ALIAS_POLICIES.strict,
|
|
4164
4322
|
});
|
|
4165
4323
|
}
|
|
4166
|
-
async function resolvePinnedRemovePathInRoot(root, relativePath) {
|
|
4324
|
+
async function resolvePinnedRemovePathInRoot(root, relativePath, denyMutations) {
|
|
4167
4325
|
return await resolvePinnedOperationPathInRoot(root, {
|
|
4326
|
+
denyMutations,
|
|
4327
|
+
protectDenyMutationAncestors: true,
|
|
4168
4328
|
relativePath,
|
|
4169
4329
|
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
4170
4330
|
});
|
|
@@ -4176,6 +4336,7 @@ async function resolvePinnedOperationPathInRoot(root, params) {
|
|
|
4176
4336
|
});
|
|
4177
4337
|
const relativeResolved = external_node_path_.relative(resolved.rootReal, resolved.canonicalPath);
|
|
4178
4338
|
if ((relativeResolved === "" || relativeResolved === ".") && params.allowRoot === true) {
|
|
4339
|
+
await assertMutationNotDenied(resolved.canonicalPath, params.denyMutations);
|
|
4179
4340
|
return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix: "" };
|
|
4180
4341
|
}
|
|
4181
4342
|
const firstSegment = relativeResolved.split(external_node_path_.sep)[0];
|
|
@@ -4189,6 +4350,9 @@ async function resolvePinnedOperationPathInRoot(root, params) {
|
|
|
4189
4350
|
if (!path_isPathInside(resolved.rootWithSep, resolved.canonicalPath)) {
|
|
4190
4351
|
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
4191
4352
|
}
|
|
4353
|
+
await assertMutationNotDenied(resolved.canonicalPath, params.denyMutations, {
|
|
4354
|
+
protectAncestors: params.protectDenyMutationAncestors,
|
|
4355
|
+
});
|
|
4192
4356
|
return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix };
|
|
4193
4357
|
}
|
|
4194
4358
|
async function resolvePinnedRootPathInRoot(root, params) {
|
|
@@ -4266,13 +4430,21 @@ async function listPathFallback(root, relativePath, withFileTypes) {
|
|
|
4266
4430
|
throw error;
|
|
4267
4431
|
}
|
|
4268
4432
|
}
|
|
4433
|
+
async function assertMoveMutationAllowed(root, params) {
|
|
4434
|
+
const source = await resolvePathInRoot(root, params.fromRelative);
|
|
4435
|
+
await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
|
|
4436
|
+
const target = await resolvePathInRoot(root, params.toRelative);
|
|
4437
|
+
await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
|
|
4438
|
+
}
|
|
4269
4439
|
async function movePathFallback(root, params) {
|
|
4270
4440
|
const source = await resolvePathInRoot(root, params.fromRelative);
|
|
4441
|
+
await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
|
|
4271
4442
|
await resolvePinnedRootPathInRoot(root, {
|
|
4272
4443
|
relativePath: params.fromRelative,
|
|
4273
4444
|
policy: PATH_ALIAS_POLICIES.strict,
|
|
4274
4445
|
});
|
|
4275
4446
|
const target = await resolvePathInRoot(root, params.toRelative);
|
|
4447
|
+
await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
|
|
4276
4448
|
await resolvePinnedRootPathInRoot(root, {
|
|
4277
4449
|
relativePath: params.toRelative,
|
|
4278
4450
|
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
@@ -4356,6 +4528,7 @@ async function writeFileFallback(root, params) {
|
|
|
4356
4528
|
relativePath: params.relativePath,
|
|
4357
4529
|
mkdir: params.mkdir,
|
|
4358
4530
|
mode: params.mode,
|
|
4531
|
+
denyMutations: params.denyMutations,
|
|
4359
4532
|
truncateExisting: false,
|
|
4360
4533
|
});
|
|
4361
4534
|
const destinationPath = target.realPath;
|
|
@@ -4401,6 +4574,7 @@ async function writeFileFallback(root, params) {
|
|
|
4401
4574
|
}
|
|
4402
4575
|
async function writeMissingFileFallback(root, params) {
|
|
4403
4576
|
const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath);
|
|
4577
|
+
await assertMutationNotDenied(resolved, params.denyMutations);
|
|
4404
4578
|
try {
|
|
4405
4579
|
await assertNoPathAliasEscape({
|
|
4406
4580
|
absolutePath: resolved,
|
|
@@ -4474,6 +4648,7 @@ async function copyFileFallback(root, params, source) {
|
|
|
4474
4648
|
relativePath: params.relativePath,
|
|
4475
4649
|
mkdir: params.mkdir,
|
|
4476
4650
|
mode: params.mode,
|
|
4651
|
+
denyMutations: params.denyMutations,
|
|
4477
4652
|
truncateExisting: false,
|
|
4478
4653
|
});
|
|
4479
4654
|
const destinationPath = target.realPath;
|
|
@@ -4634,7 +4809,7 @@ async function pathExistsWithinRoot(targetDir, relativePath) {
|
|
|
4634
4809
|
* FsSafeError as their `cause` and should be re-thrown rather than
|
|
4635
4810
|
* silently swallowed by per-file error handlers.
|
|
4636
4811
|
*/ function isFsSafeViolation(error) {
|
|
4637
|
-
return error instanceof Error && error.cause instanceof
|
|
4812
|
+
return error instanceof Error && error.cause instanceof errors_FsSafeError;
|
|
4638
4813
|
}
|
|
4639
4814
|
async function statWithinRoot(targetDir, relativePath) {
|
|
4640
4815
|
try {
|
|
@@ -19078,7 +19253,7 @@ function formatHeadingAsSetext(node, state) {
|
|
|
19078
19253
|
node.type === 'break'
|
|
19079
19254
|
) {
|
|
19080
19255
|
literalWithBreak = true
|
|
19081
|
-
return EXIT
|
|
19256
|
+
return (/* inlined export .EXIT */false)
|
|
19082
19257
|
}
|
|
19083
19258
|
})
|
|
19084
19259
|
|
|
@@ -33401,6 +33576,7 @@ const PackageJsonSchema = schemas_object({
|
|
|
33401
33576
|
*/
|
|
33402
33577
|
|
|
33403
33578
|
|
|
33579
|
+
|
|
33404
33580
|
/** Maximum skill file size: 1 MB */ const MAX_SKILL_FILE_BYTES = 1_048_576;
|
|
33405
33581
|
/**
|
|
33406
33582
|
* Skill directory locations to search for SKILL.md files.
|
|
@@ -33409,6 +33585,33 @@ const PackageJsonSchema = schemas_object({
|
|
|
33409
33585
|
(0,external_node_path_.join)(".claude", "skills"),
|
|
33410
33586
|
(0,external_node_path_.join)(".agents", "skills")
|
|
33411
33587
|
];
|
|
33588
|
+
/** Maximum lock file size: 256 KB */ const MAX_LOCK_FILE_BYTES = 262_144;
|
|
33589
|
+
/** Relative path to the skills lock file. */ const SKILLS_LOCK_PATH = "skills-lock.json";
|
|
33590
|
+
/** Zod schema for skills-lock.json structure */ const SkillsLockSchema = schemas_object({
|
|
33591
|
+
skills: record(schemas_string(), schemas_unknown()).optional()
|
|
33592
|
+
});
|
|
33593
|
+
/**
|
|
33594
|
+
* Reads the skills-lock.json file and returns the set of locked skill names.
|
|
33595
|
+
* Returns an empty set if the file does not exist or cannot be parsed.
|
|
33596
|
+
*/ async function readLockedSkillNames(targetDir) {
|
|
33597
|
+
try {
|
|
33598
|
+
const exists = await pathExistsWithinRoot(targetDir, SKILLS_LOCK_PATH);
|
|
33599
|
+
if (!exists) {
|
|
33600
|
+
return new Set();
|
|
33601
|
+
}
|
|
33602
|
+
const content = await readTextWithinRoot(targetDir, SKILLS_LOCK_PATH, MAX_LOCK_FILE_BYTES);
|
|
33603
|
+
const result = SkillsLockSchema.safeParse(JSON.parse(content));
|
|
33604
|
+
if (result.success && Object.hasOwn(result.data, "skills") && result.data.skills != null) {
|
|
33605
|
+
return new Set(Object.keys(result.data.skills));
|
|
33606
|
+
}
|
|
33607
|
+
return new Set();
|
|
33608
|
+
} catch (error) {
|
|
33609
|
+
if (isFsSafeViolation(error)) {
|
|
33610
|
+
throw error;
|
|
33611
|
+
}
|
|
33612
|
+
return new Set();
|
|
33613
|
+
}
|
|
33614
|
+
}
|
|
33412
33615
|
/**
|
|
33413
33616
|
* File system implementation of the skill lint gateway.
|
|
33414
33617
|
*/ class FileSystemSkillLintGateway {
|
|
@@ -33418,7 +33621,11 @@ const PackageJsonSchema = schemas_object({
|
|
|
33418
33621
|
const discovered = await this.discoverSkillsInDir(targetDir, relativeDir);
|
|
33419
33622
|
skills.push(...discovered);
|
|
33420
33623
|
}
|
|
33421
|
-
|
|
33624
|
+
const lockedNames = await readLockedSkillNames(targetDir);
|
|
33625
|
+
if (lockedNames.size === 0) {
|
|
33626
|
+
return skills;
|
|
33627
|
+
}
|
|
33628
|
+
return skills.filter((skill)=>!lockedNames.has(skill.skillName));
|
|
33422
33629
|
}
|
|
33423
33630
|
async discoverSkillsInDir(targetDir, skillsDir) {
|
|
33424
33631
|
try {
|
|
@@ -45923,10 +46130,11 @@ exports.visitAsync = visitAsync;
|
|
|
45923
46130
|
|
|
45924
46131
|
},
|
|
45925
46132
|
5492(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
46133
|
+
const t=Symbol.for(`__confbox_fmt__`),n=/^(\s+)/,r=/(\s+)$/;function i(e,t={}){return{sample:t.indent===void 0&&t.preserveIndentation!==!1&&e.slice(0,t?.sampleSize||1024),whiteSpace:t.preserveWhitespace===!1?void 0:{start:n.exec(e)?.[0]||``,end:r.exec(e)?.[0]||``}}}function a(e,n,r){!n||typeof n!=`object`||Object.defineProperty(n,t,{enumerable:!1,configurable:!0,writable:!0,value:i(e,r)})}function o(n,r){if(!n||typeof n!=`object`||!(t in n))return{indent:r?.indent??2,whitespace:{start:``,end:``}};let i=n[t];return{indent:r?.indent||e(i.sample||``).indent,whitespace:i.whiteSpace||{start:``,end:``}}}
|
|
45926
46134
|
__webpack_require__.d(__webpack_exports__, {
|
|
45927
46135
|
n: () => (a)
|
|
45928
46136
|
});
|
|
45929
|
-
|
|
46137
|
+
|
|
45930
46138
|
|
|
45931
46139
|
},
|
|
45932
46140
|
5975(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
@@ -46011,12 +46219,16 @@ __webpack_require__.t = function(value, mode) {
|
|
|
46011
46219
|
})();
|
|
46012
46220
|
// webpack/runtime/define_property_getters
|
|
46013
46221
|
(() => {
|
|
46014
|
-
__webpack_require__.d = (exports,
|
|
46015
|
-
|
|
46016
|
-
|
|
46017
|
-
|
|
46018
|
-
|
|
46019
|
-
|
|
46222
|
+
__webpack_require__.d = (exports, getters, values) => {
|
|
46223
|
+
var define = (defs, kind) => {
|
|
46224
|
+
for(var key in defs) {
|
|
46225
|
+
if(__webpack_require__.o(defs, key) && !__webpack_require__.o(exports, key)) {
|
|
46226
|
+
Object.defineProperty(exports, key, { enumerable: true, [kind]: defs[key] });
|
|
46227
|
+
}
|
|
46228
|
+
}
|
|
46229
|
+
};
|
|
46230
|
+
define(getters, "get");
|
|
46231
|
+
define(values, "value");
|
|
46020
46232
|
};
|
|
46021
46233
|
})();
|
|
46022
46234
|
// webpack/runtime/ensure_chunk
|
|
@@ -46118,7 +46330,7 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
46118
46330
|
// module factories are used so entry inlining is disabled
|
|
46119
46331
|
// startup
|
|
46120
46332
|
// Load entry module and return exports
|
|
46121
|
-
var __webpack_exports__ = __webpack_require__(
|
|
46333
|
+
var __webpack_exports__ = __webpack_require__(9884);
|
|
46122
46334
|
var __webpack_exports__DEFAULT_LINT_RULES = __webpack_exports__.Kd;
|
|
46123
46335
|
var __webpack_exports__LintValidationError = __webpack_exports__.F5;
|
|
46124
46336
|
var __webpack_exports__createFormatter = __webpack_exports__.xi;
|
package/package.json
CHANGED